Önceki modülde mobil uygulamanın erişim modellerini tanımladık. Bu modülde DynamoDB tablosu için birincil anahtar tasarlayacağız ve temel erişim modellerini etkinleştireceğiz.

Modülü Tamamlama Süresi: 40 Dakika


DynamoDB tablosu için birincil anahtarı tasarlarken aşağıdaki en iyi uygulamaları aklınızda bulundurun:

  • Tablonuzdaki farklı varlıklar ile başlayın. Tek bir tabloda çalışanlar, bölümler, müşteriler ve siparişler gibi birden fazla farklı türde veriyi depoluyorsanız, birincil anahtarınızın her bir varlığı açıkça tanımlayabilmesi ve bireysel nesnelerde temel eylemler etkinleştirebilmesinin bir yolu olduğundan emin olun.
  • Varlık türlerini birbirinden ayırmak için ön ekler kullanın. Varlık türlerini birbirinden ayırmak için ön ekler kullanmak çakışmaları önleyebilir ve sorgulamada yardımcı olabilir. Aynı tabloda hem müşteriler hem de çalışanlar varsa, müşteri için birincil anahtar CUSTOMER#<CUSTOMERID> (MÜŞTERİ#<MÜŞTERİKİMLİĞİ>) olabilir ve bir çalışan için birincil anahtar EMPLOYEE#<EMPLOYEEID> (ÇALIŞAN#<ÇALIŞANKİMLİĞİ>) olabilir.
  • Önce tek öğe eylemlerine yoğunlaşın ve sonra mümkünse çoklu nesne eylemleri ekleyin. Bir birincil anahtar için tek nesneli API'leri kullanarak tek bir öğede okuma ve yazma seçeneklerini karşılamanız önemlidir: GetItem (ÖğeAl), PutItem (ÖğeKoy), UpdateItem (ÖğeGüncelle) ve DeleteItem (ÖğeeSil). Ayrıca, Sorgu kullanarak birincil anahtar ile çoklu nesne okuma modellerini karşılayabilirsiniz. İşe yaramazsa, Sorgu kullanım senaryolarını karşılaması için bir ikincil dizin ekleyebilirsiniz.

Bu en iyi uygulamaları aklımızda bulundurarak,birincil anahtar tasarlayalım ve bazı temel eylemleri gerçekleştirelim.


  • 1. Adım. Birincil anahtar tasarlama

    Girişte önerildiği üzere farklı varlıkları göz önünde bulunduralım. Mobil Uygulamada aşağıdaki varlıklar mevcuttur:

    • Kullanıcılar
    • Fotoğraflar
    • Tepkiler
    • Arkadaşlık

    Bu varlıklar, üç farklı tür veri ilişkisi göstermektedir.

    İlk olarak, mobil uygulamanızdaki her kullanıcının, tablonuzdaki bir Kullanıcı ile gösterilen tek bir kullanıcı profiline sahip olacaktır.

    Sonra, bir kullanıcının uygulamanızda gösterilen birden fazla fotoğrafı ve birden fazla tepki için bir fotoğrafı olacaktır. Bunların her ikisi de birden-çoka ilişkidir.

    Son olarak, Arkadaşlık varlığı, çoktan-çoka ilişkinin bir gösterimidir. Arkadaşlık varlığı, bir kullanıcı uygulamanızda başka bir kullanıcıyı takip etmesini temsil eder. Bir kullanıcı birden fazla diğer kullanıcıları takip edebileceği için bu çoktan-çoka bir ilişkidir ve bir kullanıcının birden fazla takipçisi olabilir.

    Çoktan-çoka eşleştirme genellikle aynı anda iki Sorgu modelini karşılamayı isteyeceğinizin belirtisidir ve uygulamamız bunun istisnası değildir. Arkadaşlık varlığında, belirli bir kullanıcıyı takip eden kullanıcıları bulmak için gerekli bir erişim modeline sahibiz ve aynı şekilde belirli bir kullanıcının takip ettiği tüm kullanıcıları bulmak için de bir erişim modelimiz bulunmaktadır.

    Bu nedenle, HASH (Özet) ve ARALIK değerleri olan birleşik bir birincil anahtar kullanacağız. Birleşik birincil anahtar, bize ihtiyacımız olan sorgu modellerinden birini karşılamak için HASH (Özet) anahtarında Sorgu yeteneği verir. DynamoDB API teknik özelliklerinde, bölüm anahtarına HASH (Özet) denir ve sıralama anahtarına ARALIK denir ve bu kılavuzda, özellikle kod veya DynamoDB JSON kablo formatı hakkında konuştuğumuzda, API terminolojisini değiştirilebilir şekilde kullanacağız.

    Bire-bir varlığının -- Kullanıcı-- ARALIK değeri için bir doğal özelliği bulunmamaktadır. Bire-bir bir eşleştirme olduğu için, erişim modeli temel bir anahtar-değer araması olacaktır. Tablo tasarımınız bir ARALIK özelliği gerektirdiği için ARALIK anahtarı için bir dolgu değeri sağlayabilirsiniz.

    Bunu aklımızda tutarak her varlık türü için HASH ve ARALIK değerleri için aşağıdaki modeli kullanalım.

    Varlık

    HASH

    ARALIK

    Kullanıcı

    USER#<USERNAME> (KULLANICI#<KULLANICIADI>)

    #METADATA#<USERNAME> (#METAVERİ#<KULLANICIADI>)

    Fotoğraf

    USER#<USERNAME> (KULLANICI#<KULLANICIADI>)

    PHOTO#<USERNAME>#<TIMESTAMP> (FOTOĞRAF#<KULLANICIADI>#<ZAMANDAMGASI>)

    Tepki

    REACTION#<USERNAME>#<TYPE> (TEPKİ#<KULLANICIADI>#<TÜR>)

    PHOTO#<USERNAME>#<TIMESTAMP> (FOTOĞRAF#<KULLANICIADI>#<ZAMANDAMGASI>)

    Arkadaşlık

    USER#<USERNAME> (KULLANICI#<KULLANICIADI>)

    #FRIEND#<FRIEND_USERNAME> (#ARKADAŞ#<ARKADAŞ_KULLANICIADI>)

    Önceki tablonun üzerinden geçelim.

    Kullanıcı varlığı için HASH değeri USER#<USERNAME> (KULLANICI#<KULLANICIADI>) olacaktIr. Varlığı tanımlamak ve varlık türleri arasındaki muhtemel çarpışmaları tanımlamak için ön ek kullandığınızı göz önünde bulundurun.

    Kullanıcı varlığındaki ARALIK değeri için #METADATA# (#METAVERİ#) statik ön ekini ve arkasından kullanıcı adı değerini kullanıyoruz. ARALIK değeri için kullanıcı adı gibi bilinen bir değerimiz olması önemlidir. Bu, GetItem (ÖğeAl), PutItem (ÖğeKoy), ve DeleteItem (ÖğeSil) gibi tek öğe eylemleri sağlar.

    Ancak, bir dizin için bu sütunu HASH anahtarı olarak kullanırsanız eşit bölümleme sağlamak için farklı Kullanıcı varlıkları genelinde farklı değerli ARALIK değeri istersiniz. Bu nedenle kullanıcı adını ARALIK anahtarının sonuna eklersiniz.

    İkinci olarak, Fotoğraf varlığı belirli bir Kullanıcı varlığının bir alt varlığıdır. Fotoğraflar için ana erişim modeli, bir kullanıcı için fotoğrafları tarihe göre sıralanmış şekilde almaktır. Belirli bir özelliğe göre sıralanmış birşeylere ihtiyaç duyduğunuzda, sıralamaya izin vermek için bu özelliği ARALIK anahtarınıza dahil etmeniz gerekecektir. Fotoğraf varlığı için, tek bir istekte hem kullanıcı profilini hem de kullanıcının fotoğraflarını almanıza olanak sağlayan, Kullanıcı ile aynı HASH anahtarını kullanın. ARALIK anahtarı için bir fotoğrafı tablonuzda tekil olarak tanımlamak için PHOTO#<USERNAME>#<TIMESTAMP> (FOTOĞRAF#<KULLANICIADI>#<ZAMANDAMGASI>) modelini kullanın.

    Üçüncü olarak, Tepki varlığı belirli bir Fotoğraf varlığının bir alt varlığıdır. Fotoğraf varlığı ile birden-çoka şeklinde bir ilişki vardır ve bu nedenle Fotoğraf varlığı ile benzer bir akıl yürtütme kullanacağız Sonraki modülde, ikincil bir indeks kullanarak tek bir sorguda bir fotoğrafı ve bu fotoğrafa ilişkin tepkileri nasıl alacağınızı göreceksiniz. Şimdilik, bir Tepki varlığı için ARALIK anahtarının, bir Fotoğraf varlığı için ARALIK anahtarı ile aynı modele sahip olduğuna dikkat edin. HASH anahtarı için tepkiyi oluşturan kullanıcının kullanıcı adını ve aynı zamanda uygulanan tepkinin türünü kullanacağız. Tepkinin türünün sona eklenmesi, bir kullanıcıya tek bir fotoğraf için birden fazla tepki eklemesine olanak sağlar.

    Son olarak, Arkadaşlık varlığı, Kullanıcı varlığı ile aynı HASH anahtarını kullanır. Bu sayede hem bir kullanıcının hem de kullanıcın tüm takipçilerinin meta verilerini tek bir sorgu ile çekebilirsiniz. Arkadaşlık varlığı için ARALIK anahtarı, #FRIEND#<FRIEND_USERNAME> (#ARKADAŞ#<ARKADAŞ_KULLANICIADI>) şeklindedir. Aşağıdaki 4. Adımda, Arkadaşlık varlığının ARALIK anahtarının başına neden bir ‘#’ değeri eklendiğini öğreneceksiniz.

    Sonraki adımda bu birincil anahtar tasarımı ile bir tablo oluşturacağız.

  • Adım 2: Bir tablo oluştur

    Birincil anahtarı tasarladığımıza göre bir tablo oluşturalım.

    Modül 1’in 3. Adımında indirdiğiniz kod scripts/ dizininde create_table.py isimli bir Python betiği içerir. Python betiğinin içerikleri aşağıdaki gibidir:

    import boto3
    
    dynamodb = boto3.client('dynamodb')
    
    try:
        dynamodb.create_table(
            TableName='quick-photos',
            AttributeDefinitions=[
                {
                    "AttributeName": "PK",
                    "AttributeType": "S"
                },
                {
                    "AttributeName": "SK",
                    "AttributeType": "S"
                }
            ],
            KeySchema=[
                {
                    "AttributeName": "PK",
                    "KeyType": "HASH"
                },
                {
                    "AttributeName": "SK",
                    "KeyType": "RANGE"
                }
            ],
            ProvisionedThroughput={
                "ReadCapacityUnits": 5,
                "WriteCapacityUnits": 5
            }
        )
        print("Table created successfully.")
    except Exception as e:
        print("Could not create table. Error:")
        print(e)

    Önceki betik AWS SDK for Python olan Boto 3’ü kullanarak CreateTable (TabloOluştur) işlemini kullanır. İşlem, birincil anahtarda kullanılacak türü belirtilen özellikler olan iki özellik tanımı açıklar. DynamoDB şemasız olmasına rağmen, birincil anahtarlar için kullanılan özelliklerin isimlerini ve türlerini açıklamalısınız. Özellikler tabloya yazılan her nesneye dâhil edilmelidir ve bu yüzden tablo oluştururken belirtilmelidir.

    Tek bir tabloda farklı varlıklar depoladığımız için birincil anahtarınız UserId (KullanıcıKimliği) gibi özellik isimleri kullanamaz. Depolanan varlık türüne bağlı olarak bu özellik farklı anlamlara gelebilir. Örneğin, bir kullanıcı için birincil anahtar USERNAME (KULLANICI ADI) iken bir tepki için birincil anahtar TYPE (Tür) olabilir. Buna göre PK (bölüm anahtarı için) ve SK (sıralama anahtarı için) gibi özellikler için genel isimler kullanırız.

    Anahtar şemasında özellikleri yapılandırdıktan sonra tablo için tedarik edilen aktarım hızını belirtiriz. DynamoDB’nin iki kapasite modu vardır: tedarik edilen ve isteğe bağlı. Tedarik edilen kapasite modunda tam olarak istediğiniz okuma ve yazma aktarım hızını belirtirsiniz. Kullansanız da kullanmasanız da bu kapasite için ödeme yaparsınız.

    DynomoDB isteğe bağlı kapasite modunda istek başına ödeme yapabilisiniz. Tedarik edilen aktarım hızını tamamen kullandığınızda istek üzerinenin masrafı daha yüksek olmasına rağmen kapasite planlamasıyla veya kısıtlanma endişesiyle vakit kaybetmek zorunda kalmazsınız. İsteğe bağlı mod, ani artışlar olan ve tahmin edilemeyen iş yüklerinde çok iyi çalışır. DynamoDB, ücretsiz kullanım kategorisine girdiği için bu laboratuvarda tedarik edilen kapasite modunu kullanıyoruz.

    Tablo oluşturmak için Python betiğini aşağıdaki komut ile çalıştırın.

    python scripts/create_table.py

    Betik şu mesajı vermelidir: “Table created successfully” (Tablo başarıyla oluşturuldu).

    Sonraki adımda bazı örnek verileri tabloya toplu yükleyeceğiz. 

  • Adım 3: Tabloya toplu veri yükleme

    Bu adımda, önceki adımda oluşturduğumuz DynamoDB tablosuna bazı verileri toplu şekilde yükleyeceğiz. Bu da ileriki adımlarda kullanacağımız bir örnek verimiz olacağı anlamına geliyor.

    scripts/ dizininde, items.json isimli bir dosya vardır. Bu dosya, projemiz için rastgele oluşturulmuş 967 örnek öğe içerir. Bu öğeler şunları içerir: Kullanıcı, Fotoğraf, Arkadaşlık ve Tepki varlıkları. Örneklerin bazılarını görmek istiyorsanız dosyayı açabilirsiniz.

    Ayrıca scripts/ dizininde items.json dosyasındaki öğeleri okuyan ve bunları DynamoDB tablosuna toplu yazan bulk_load_table.py adında bir dosya vardır. Bu dosyanın içerikleri aşağıdaki gibidir:

    import json
    
    import boto3
    
    dynamodb = boto3.resource('dynamodb')
    table = dynamodb.Table('quick-photos')
    
    items = []
    
    with open('scripts/items.json', 'r') as f:
        for row in f:
            items.append(json.loads(row))
    
    with table.batch_writer() as batch:
        for item in items:
            batch.put_item(Item=item)

    Bu betikte Boto 3’de düşük seviye istemci kullanmak yerine yüksek seviye bir Kaynak nesnesi kullanacağız. Kaynak nesneleri AWS API'leri kullanmak için daha kolay arabirim sağlar. Kaynak nesnesi bu durumda kullanışlıdır çünkü isteklerimizi grup haline getirir. BatchWriteItem API (ÖğeyiGrupHalindeYaz) işlemi tek bir istekte 25’e kadar öğe kabul eder. Kaynak nesnesi, verilerimizi 25 veya daha az nesneler halinde ayırmak zorunda kalmadan, bu verileri grup haline getirme işlemini bizim yerimize yapar.

    bulk_load_table.py betiğini çalıştırın ve aşağıdaki komutu çalıştırarak tablonuza veri yükleyin.

    python scripts/bulk_load_table.py

    Bir Tarama işlemi yürüterek ve sayısını elde ederek, tüm verilerinizin tabloya yüklendiğinden emin olabilirsiniz.

    Sayıyı elde etmek için AWS CLI aracını kullanarak aşağıdaki komutu çalıştırın.

    aws dynamodb scan \
     --table-name quick-photos \
     --select COUNT

    Bu işlem aşağıdaki sonuçları göstermelidir.

    {
        "Count": 967, 
        "ScannedCount": 967, 
        "ConsumedCapacity": null
    }
    

    967 Sayısını görmelisiniz bu da tüm öğelerinizin başarıyla yüklendiğini gösterir.

    Sonraki adımda tek bir istekte çoklu varlık türlerini alabileceğinizi göstereceğiz bu da uygulamanızda yaptığınız toplam ağ isteklerini düşürür ve uygulama performansınızı arttırır.

  • Adım 4: Tek bir istekte çoklu varlık türleri alma

    Önceki modülde söylediğimiz gibi aldığı istek sayısı için DynamoDB’nizi optimize etmelisiniz. Ayrıca DynamoDB’nin ilişkisel bir veri tabanının sahip olduğu birleşimlere sahip olmadığını söyledik. Bunun yerine isteklerinizde birleşim benzeri davranışlar sağlaması için tablonuzu tasarlarsınız.

    Bu adımda, tek bir istekte çoklu varlık türlerinin nasıl alınacağını göreceksiniz. Uygulamamızda, bir kullanıcı hakkında bilgileri çekmek isteyebiliriz. Bu, Kullanıcı varlığındaki tüm kullanıcı profili bilgilerini ve aynı zamanda bir kullanıcı tarafından yüklenen tüm fotoğrafları da içerecektir.

    Bu istek iki varlık türüne hitap eder: Kullanıcı varlığı ve Fotoğraf varlığı. Ancak bu birden fazla istek yapmamız gerektiği anlamına gelmez.

    İndirdiğiniz kodun, application/ dizininde fetch_user_and_photos.py isimli bir dosya vardır. Bu betik, tek bir istekte hem Kullanıcı varlığı ve Fotoğraf varlığını alabilmek için kodunuzu nasıl yapılandıracağınızı gösterir.

    Aşağıdaki kod fetch_user_and_photos.py betiğini oluşturur.

    import boto3
    
    from entities import User, Photo
    
    dynamodb = boto3.client('dynamodb')
    
    USER = "jacksonjason"
    
    
    def fetch_user_and_photos(username):
        resp = dynamodb.query(
            TableName='quick-photos',
            KeyConditionExpression="PK = :pk AND SK BETWEEN :metadata AND :photos",
            ExpressionAttributeValues={
                ":pk": { "S": "USER#{}".format(username) },
                ":metadata": { "S": "#METADATA#{}".format(username) },
                ":photos": { "S": "PHOTO$" },
            },
            ScanIndexForward=True
        )
    
        user = User(resp['Items'][0])
        user.photos = [Photo(item) for item in resp['Items'][1:]]
    
        return user
    
    
    user = fetch_user_and_photos(USER)
    
    print(user)
    for photo in user.photos:
        print(photo)

    En üst kısımda, uygulama kodumuzdaki nesneleri temsil etmesi için Boto 3 kütüphanesini ve bazı sınıfları içe aktarıyoruz. İsterseniz, application/entities.py dosyasında bu varlıkların tanımlarını görebilirsiniz.

    Gerçek iş, modülde tanımlanan fetch_user_and_photos fonksiyonunda gerçekleşmektedir. Bu, bu veriye ihtiyaç duyan herhangi bir uç nokta tarafından kullanılması için uygulamanızda tanımlayacağınız bir işleve benzer.

    Bu fonksiyonda, ilk olarak DynamoDB’ye bir Sorgu isteğinde bulunuyorsunuz. Sorgu, sonuç olarak döndürülen öğeleri belirli bir kullanıcı ile sınırlandırmak için USER#<Username> (KULLANICI#<Kullanıcıadı>) modelinin HASH anahtarını belirtir.

    Ardından Sorgu, #METADATA#<Username> ve PHOTO$ arasında olan bir ARALIK anahtar koşulu belirtir. Bu Sorgu, sıralama anahtarı #METADATA#<Username> olan bir Kullanıcı varlığını ve aynı zamanda bu kullanıcı için sıralama anahtarları PHOTO# ile başlayan tüm Fotoğraf varlıklarını döndürür. Dize türlerinin sıralama anahtarları ASCII karakter kodları ile sıralanır. ASCII’da dolar simgesi ($), pound simgesinden (#) hemen sonra gelir. Bu durum, tüm Fotoğraf varlıklarını almamızı sağlar.

    Bir yanıt aldığımızda öğelerimizi, uygulamamız tarafından bilinen nesnelere derleriz. Döndürülen ilk öğenin Kullanıcı varlığımız olduğunu biliyoruz, bu nedenle bu öğeden bir Kullanıcı nesnesi oluştururuz. Kalan öğelerde, her bir varlık için bir Fotoğraf nesnesi oluştururuz ve ardından Kullanıcı nesnesine kullanıcı dizisini ekleriz.

    Betiğin sonu işlevin kullanımını gösterir ve sonuç nesneleri gösterilir. Betiği aşağıdaki komut ile terminalinizde çalıştırabilirsiniz:

    python application/fetch_user_and_photos.py

    Kullanıcı nesnesini ve tüm Fotoğraf nesnelerini konsola yazdırması gerekir:

    User<jacksonjason -- John Perry>
    Photo<jacksonjason -- 2018-05-30T15:42:38>
    Photo<jacksonjason -- 2018-06-09T13:49:13>
    Photo<jacksonjason -- 2018-06-26T03:59:33>
    Photo<jacksonjason -- 2018-07-14T10:21:01>
    Photo<jacksonjason -- 2018-10-06T22:29:39>
    Photo<jacksonjason -- 2018-11-13T08:23:00>
    Photo<jacksonjason -- 2018-11-18T15:37:05>
    Photo<jacksonjason -- 2018-11-26T22:27:44>
    Photo<jacksonjason -- 2019-01-02T05:09:04>
    Photo<jacksonjason -- 2019-01-23T12:43:33>
    Photo<jacksonjason -- 2019-03-03T02:00:01>
    Photo<jacksonjason -- 2019-03-03T18:20:10>
    Photo<jacksonjason -- 2019-03-11T15:18:22>
    Photo<jacksonjason -- 2019-03-30T02:28:42>
    Photo<jacksonjason -- 2019-04-14T21:52:36>

    Bu betik tablonuzu nasıl modelleyebileceğinizi ve tek bir DynamoDB isteğinde çoklu varlık türleri alabileceğinizi gösterir. İlişkisel bir veri tabanında birleşimleri kullanarak tek bir istekte farklı tablolardan çoklu varlık türleri alabilirsiniz. DynamoDB ile birlikte erişmeniz gereken varlıkların tek bir tabloda birbirinin yanında konumlanması için verilerinizi özellikle modellersiniz. Bu yaklaşım tipik bir ilişkisel veri tabanında birleşime olan ihtiyacınızın yerine geçer ve siz ölçeklendirdikçe uygulamanızı yüksek performansta tutar.


    Bu modülde birincil bir anahtar tasarladık ve bir tablo oluşturduk. Sonra verileri tabloya toplu yükledik ve tek bir istekte çoklu varlık türleri için nasıl sorgu oluşturulacağını gördük.

    Mevcut birincil anahtar tasarımımız ile aşağıdaki erişim modellerini karşılayabiliriz:

    • Kullanıcı profili oluştur (Yazma)
    • Kullanıcı profilini güncelle (Yazma)
    • Kullanıcı profili edin (Okuma)
    • Fotoğraf yükle (Yazma)
    • Kullanıcı için fotoğrafları görüntüle (Okuma)
    • Kullanıcı için arkadaşları görüntüle (Okuma)

    Sonraki modülde ikincil bir dizin ekleyeceğiz ve seyrek dizin tekniğini öğreneceğiz. İkincil dizinler DynamoDB tablonuzda ek erişim modellerini desteklemenizi sağlar.