Di modul sebelumnya, kami menentukan pola akses aplikasi mobile. Dalam modul ini, kami akan merancang kunci primer untuk tabel DynamoDB dan memungkinkan pola akses inti.

Waktu untuk Menyelesaikan Modul: 40 Menit


Ketika merancang kunci primer untuk tabel DynamoDB, ingat praktik terbaik berikut:

  • Mulai dengan entitas berbeda di tabel Anda. Jika Anda menyimpan beberapa tipe data berbeda di dalam satu tabel, seperti karyawan, departemen, pelanggan, dan pesanan, pastikan kunci primer memiliki cara untuk mengidentikasi secara jelas setiap entitas dan memungkinkan tindakan inti pada item individu.
  • Gunakan prefiks untuk membedakan antara tipe entitas. Menggunakan prefiks untuk membedakan di antara tipe entitas dapat mencegah tabrakan dan membantu dalam membuat kueri. Misalnya, jika Anda memiliki pelanggan dan karyawan dalam satu tabel yang sama, kunci primer untuk pelanggan akan tampak seperti CUSTOMER#<CUSTOMERID>, dan kunci primer untuk karyawan adalah EMPLOYEE#<EMPLOYEEID>.
  • Fokus pada tindakan satu item terlebih dulu, kemudian tambahkan beberapa item tindakan jika memungkinkan. Untuk kunci primer, penting bahwa Anda dapat memenuhi opsi baca dan tulis pada satu item menggunakan API item tunggal: GetItem, PutItem, UpdateItem, dan DeleteItem. Jika Anda juga dapat memenuhi pola baca beberapa item dengan kunci primer menggunakan Query, itu juga sangat bagus. Jika tidak, Anda dapat menambahkan indeks sekunder untuk menangani kasus penggunaan Query.

Dengan pemikiran praktik terbaik ini, mari merancang kunci primer dan menjalankan beberapa tindakan dasar.


  • Langkah 1. Rancang kunci primer

    Mari pertimbangkan entitas berbeda, seperti yang disarankan dalam pendahuluan di awal. Dalam aplikasi mobile, kami memiliki beberapa entitas:

    • Users
    • Photos
    • Reactions
    • Friendship

    Entitas-entitas tersebut memiliki tiga jenis hubungan data berbeda.

    Pertama, setiap pengguna pada aplikasi Anda akan memiliki satu profil pengguna yang direpresentasikan dengan entitas User dalam tabel.

    Berikutnya, pengguna akan memiliki beberapa foto yang direpresentasikan dalam aplikasi Anda, dan sebuah foto dapat memiliki beberapa reaksi. Keduanya adalah hubungan satu ke banyak.

    Terakhir, entitas Friendship adalah representasi hubungan banyak ke banyak. Entitas Friendship merepresentasikan ketika satu pengguna mengikuti pengguna lainnya dalam aplikasi Anda. Ini adalah hubungan banyak ke banyak karena satu pengguna dapat mengikuti beberapa pengguna lainnya, dan seorang pengguna dapat memiliki beberapa pengikut.

    Memiliki pemetaan banyak ke banyak biasanya menunjukkan bahwa Anda ingin memenuhi dua pola Query, dan aplikasi kami tidak terkecuali. Pada entitas Friendship, kami memiliki pola akses yang perlu menemukan semua pengguna yang mengikuti pengguna tertentu serta pola akses untuk menemukan semua pengguna yang diikuti oleh pengguna tertentu.

    Karena itulah, kami akan menggunakan kunci primer komposit dengan nilai HASH dan RANGE. Kunci primer komposit akan memberi kami kemampuan Query pada kunci HASH untuk memenuhi pola kueri yang dibutuhkan. Dalam spesifikasi API DynamoDB, kunci partisi disebut HASH dan kunci sortir disebut RANGE, dan dalam panduan ini kami menggunakan terminologi API secara bergantian dan khususnya ketika mendiskusikan kode atau format protokol kabel JSON DynamoDB.

    Perhatikan bahwa entitas satu ke satu -- User -- tidak memiliki properti alami untuk nilai RANGE. Karena ini adalah pemetaan satu ke satu, pola akses akan menjadi pencarian kunci nilai dasar. Karena rancangan tabel Anda memerlukan properti RANGE , Anda dapat memberikan nilai pengisi untuk kunci RANGE.

    Dengan pemikiran ini, mari gunakan pola berikut untuk nilai HASH dan RANGE untuk setiap tipe entitas.

    Entitas

    HASH

    RANGE

    User

    USER#<USERNAME>

    #METADATA#<USERNAME>

    Foto

    USER#<USERNAME>

    PHOTO#<USERNAME>#<TIMESTAMP>

    Reaksi

    REACTION#<USERNAME>#<TYPE>

    PHOTO#<USERNAME>#<TIMESTAMP>

    Pertemanan

    USER#<USERNAME>

    #FRIEND#<FRIEND_USERNAME>

    Mari lihat tabel sebelumnya.

    Untuk entitas User, nilai HASH akan menjadi USER#<USERNAME>. Perhatikan bahwa Anda menggunakan prefiks untuk mengidentifikasi entitas dan mencegah segala kemungkinan benturan di seluruh tipe entitas.

    Untuk nilai RANGEpada entitas User, kami menggunakan prefiks statis #METADATA# yang diikuti oleh nilai nama pengguna. Untuk nilai RANGE, penting bahwa Anda memiliki nilai yang diketahui, seperti nama pengguna. Hal ini memungkinkan tindakan item tunggal seperti GetItem, PutItem, dan DeleteItem.

    Namun, Anda juga ingin nilai RANGE dengan nilai berbeda di seluruh entitas User untuk memungkinkan pembuatan partisi jika Anda menggunakan kolom ini sebagai kunci HASH untuk indeks. Karena alasan tersebut, Anda menambahkan nama pengguna ke kunci RANGE.

    Kedua, entitas Photo adalah entitas turunan dari entitas User tertentu. Pola akses utama untuk foto adalah dengan menarik foto untuk pengguna yang diurutkan menurut tanggal. Kapan pun Anda memerlukan sesuatu diurutkan menurut properti tertentu, Anda akan perlu menyertakan properti tersebut dalam kunci RANGEuntuk memungkinkan penyortiran. Untuk entitas Photo, gunakan kunci HASH yang sama dengan entitas User, yang akan memungkinkan Anda menarik profil pengguna dan foto pengguna dalam satu permintaan. Untuk kunci RANGE, gunakan PHOTO#<USERNAME>#<TIMESTAMP> untuk secara unik mengidentifikasi foto dalam tabel Anda.

    Ketiga, entitas Reaction Photo adalah entitas turunan dari entitas Photo tertentu. Terdapat hubungan satu ke banyak ke entitas Photo dan karenanya akan menggunakan alasan yang sama dengan entitas Photo. Dalam modul berikutnya, Anda akan melihat cara menarik foto dan semua reaksinya dalam satu kueri menggunakan indeks sekunder. Untuk saat ini, perhatikan bahwa kunci RANGE untuk entitas Reaction adalah pola yang sama dengan kunci RANGE untuk entitas Photo. Untuk kunci HASH, kami menggunakan nama pengguna dari pengguna yang membuat reaksi serta jenis reaksi yang diterapkan. Menambahkan tipe reaksi akan memungkinkan pengguna menambahkan beberapa tipe reaksi ke satu foto.

    Terakhir, entitas Pertemanan menggunakan kunci HASH sebagai entitas Pengguna. Ini akan memungkinkan Anda mengambil metadata untuk pengguna serta semua pengikut pengguna dalam satu kueri. Kunci RANGE untuk entitas Pertemanan adalah #FRIEND#<FRIEND_USERNAME>. Dalam langkah 4 di bawah, Anda akan mempelajari cara menambahkan kunci RANGE entitas Pertemanan di awal dengan “#”.

    Pada langkah berikutnya, kami membuat tabel dengan rancangan kunci utama ini.

  • Langkah 2: Membuat tabel

    Sekarang kami telah merancang kunci primer, mari kita membuat tabel.

    Kode yang Anda unduh di Langkah 3 Modul 1 menyertakan skrip Python di direktori scripts/ yang diberi nama create_table.py. Skrip konten Python adalah sebagai berikut:

    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)

    Skrip sebelumnya menggunakan operasi CreateTable menggunakan Boto 3, AWS SDK untuk Python. Operasi tersebut menjelaskan dua definisi atribut yang atribut bertipe untuk digunakan di dalam kunci utama. Meski DynamoDB tanpa skema, Anda harus menyatakan nama dan tipe atribut yang digunakan untuk kunci utama. Atribut harus disertakan di setiap item yang tertulis ke tabel dan oleh karena itu harus ditentukan saat Anda membuat tabel.

    Karena Anda menyimpan entitas berbeda dalam satu tabel, kunci utama Anda tidak dapat menggunakan nama atribut seperti UserId. Atribut berarti sesuatu yang berbeda berdasarkan tipe entitas yang disimpan. Misalnya, kunci utama untuk pengguna dapat berupa USERNAME mereka, dan kunci utama untuk reaksi dapat berupaTYPE-nya. Oleh karena itu, kami menggunakan nama generik untuk atribut -- PK (untuk kunci partisi) dan SK (untuk kunci sortir).

    Setelah mengonfigurasi dalam skema kunci, kami menentukan throughput yang disediakan untuk tabel. DynamoDB memiliki dua mode kapasitas: terprovisi dan sesuai permintaan. Dalam mode kapasitas terprovisi, Anda menentukan jumlah throughput baca dan tulis dengan tepat sesuai yang diinginkan. Anda membayar kapasitas ini kapan saja Anda ingin menggunakannya atau tidak.

    Dalam mode kapasitas sesuai permintaan DynamoDB, Anda dapat membayar sesuai permintaan. Biaya sesuai permintaan sedikit lebih tinggi dibanding jika Anda menggunakan throughput terprovisi sepenuhnya, tetapi Anda tidak perlu menghabiskan waktu melakukan perencanaan kapasitas atau khawatir akan dibatasi. Mode sesuai permintaan berfungsi sangat baik untuk beban kerja yang naik-turun atau tidak dapat diprediksi. Kami menggunakan mode kapasitas yang disediakan dalam lab ini karena sesuai dengan tingkat gratis DynamoDB.

    Untuk membuat tabel, jalankan skrip Python dengan perintah berikut.

    python scripts/create_table.py

    Skrip akan menghasilkan pesan ini: “Tabel berhasil dibuat.”

    Pada langkah berikutnya, kami memuat beberapa data contoh ke dalam tabel secara massal. 

  • Langkah 3: Memuat data secara massal ke dalam tabel

    Dalam langkah ini, kami akan memuat beberapa data secara massal ke tabel DynamoDB yang dibuat di langkah sebelumnya. Hal ini berarti bahwa dalam langkah-langkah berikutnya, kita akan memiliki data sampel untuk digunakan.

    Di direktori scripts/, terdapat file yang disebut items.json. File ini berisi 967 item sampel yang dihasilkan secara acak untuk proyek kita. Item ini meliputi entitas User, Photo, Friendship, dan Reaction. Anda dapat membuka file tersebut jika ingin melihat beberapa data contoh.

    Direktori scripts/ juga memiliki sebuah file yang disebut bulk_load_table.py yang akan membaca item dalam items.json dan menulisnya secara massal ke tabel DynamoDB. Konten file tersebut adalah sebagai berikut:

    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)

    Dalam skrip ini, dibanding menggunakan klien level rendah di Boto 3, kami menggunakan objek Resource yang lebih tinggi. Objek Resource menyediakan antarmuka yang lebih mudah untuk menggunakan API AWS. Objek Resource berguna dalam situasi ini karena objek ini mengumpulkan permintaan kita. Operasi BatchWriteItem API menerima hingga 25 item dalam satu permintaan. Objek Resource akan menangani pengumpulan tersebut untuk kita sebagai ganti membuat kita membagi data menjadi permintaan berisi 25 item atau kurang.

    Jalankan skrip bulk_load_table.py dan muat tabel Anda dengan data dengan menjalankan perintah di terminal berikut.

    python scripts/bulk_load_table.py

    Anda dapat memastikan bahwa semua data Anda dimuat dengan menjalankan operasi Scan dan menghitung.

    Jalankan perintah berikut untuk menggunakan AWS CLI untuk mendapatkan jumlah:

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

    Hal ini akan menampilkan hasil berikut.

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

    Anda akan melihat Count berisi 967, menunjukkan bahwa semua item Anda berhasil dimuat.

    Dalam langkah berikutnya, kami menunjukkan cara mengambil beberapa tipe entitas dalam satu permintaan, yang dapat mengurangi total permintaan jaringan yang Anda buat di aplikasi dan meningkatkan kinerja aplikasi.

  • Langkah 4: Mengambil beberapa tipe entitas dalam satu permintaan

    Seperti yang kami sebutkan di modul sebelumnya, Anda harus mengoptimalkan tabel DynamoDB untuk jumlah permintaan yang diterima. Kami juga menyebutkan bahwa DynamoDB tidak memiliki join seperti yang dimiliki database relasional. Sebagai gantinya, Anda merancang tabel untuk memungkinkan perilaku mirip join dalam permintaan Anda.

    Dalam langkah ini, kita akan melihat cara menarik beberapa tipe entitas dalam satu permintaan. Dalam aplikasi kita, kita mungkin ingin mengambil informasi mengenai pengguna. Ini akan meliputi semua informasi dalam profil pengguna pada entitas Pengguna serta semua foto yang telah diunggah oleh pengguna.

    Permintaan ini menjangkau dua tipe entitas -- entitas Pengguna dan entitas Foto. Namun, hal ini tidak berarti kita perlu membuat beberapa permintaan.

    Di kode yang Anda unduh, terdapat file di direktori application/ bernama fetch_photo_and_photos.py. Skrip ini menunjukkan bagaimana Anda dapat menetapkan struktur untuk kode untuk mengambil entitas Pengguna dan entitas Foto yang diunggah oleh pengguna dalam satu permintaan.

    Kode berikut menyusun skrip fetch_user_and_photos.py

    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)

    Di bagian atas, kami mengimpor pustaka Boto 3 dan beberapa kelas sederhana untuk merepresentasikan objek di kode aplikasi kami. Anda dapat melihat definisi untuk entitas tersebut di file application/entities.py jika tertarik.

    Pekerjaan sesungguhnya terjadi dalam fungsi fetch_user_and_photos yang ditentukan dalam modul. Hal ini serupa dengan fungsi yang Anda ingin tetapkan di aplikasi yang akan digunakan oleh titik akhir mana pun yang memerlukan data ini.

    Dalam fungsi ini, Anda terlebih dulu membuat permintaan Query ke DynamoDB. Query menetapkan kunci HASH USER#<Username> untuk mengisolasi item yang dihasilkan ke pengguna tertentu.

    Kemudian, Query menetapkan ekspresi kondisi kunci RANGE yang berada di antara #METADATA#<Username> dan PHOTO$. Query ini akan menghasilkan entitas User, karena kunci sortirnya adalah #METADATA#<Username>, serta semua entitas Photo untuk pengguna ini, yang kunci sortinya diawali dengan PHOTO#. Kunci sortir tipe String disortir berdasarkan kode karakter ASCII. Lambang dolar ($) berada tepat setelah lambang pagar (#) dalam ASCII, sehingga hal ini memastikan bahwa kita akan mendapatkan semua entitas Photo.

    Setelah menerima respons, kita merakit item menjadi objek yang dikenal oleh aplikasi. Kami tahu bahwa item pertam yang dihasilkan akan menjadi entitas User, jadi kita membuat objek User dari item tersebut. Untuk item tersisa, kami membuat objek Photo untuk masing-masing item, kemudian melampirkan barisan pengguna ke objek User.

    Pada akhir skrip menunjukkan penggunaan fungsi dan hasil objek yang dicetak. Anda dapat menjalankan skrip di terminal Anda dengan perintah berikut.

    python application/fetch_user_and_photos.py

    Skrip akan mencetak objek User dan semua objek Photo ke konsol:

    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>

    Skrip ini menunjukkan bagaimana Anda dapat membuat model tabel dan menulis kueri untuk mengambil beberapa tipe entitas dalam permintaan DynamoDB tunggal. Dalam database relasional, Anda menggunakan join untuk mengambil beberapa tipe entitas dari tabel berbeda dalam satu permintaan. Dengan DynamoDB, Anda memodelkan data secara spesifik, sehingga entitas yang harus Anda akses bersama terletak saling bersebelahan dalam satu tabel. Pendekatan ini menggantikan kebutuhan join di dalam database relasional dan menjaga aplikasi Anda tetap berkinerja tinggi saat Anda menaikkan skala.


    Dalam modul ini, kami merancang kunci primer dan membuat tabel. Kemudian, kami memuat data secara massal ke dalam tabel dan melihat cara membuat kueri beberapa tipe entitas dalam satu permintaan.

    Dengan rancangan kunci primer saat ini, kami bisa memenuhi pola akses berikut:

    • Membuat profil pengguna (Tulis)
    • Memperbarui profil pengguna (Tulis)
    • Mendapatkan profil pengguna (Baca)
    • Mengunggah foto (Tulis)
    • Melihat foto untuk Pengguna (Baca)
    • Melihat teman untuk pengguna (Baca)

    Dalam modul berikutnya, kami akan menambahkan indeks sekunder dan mempelajari teknik indeks terbalik. Indeks sekunder memungkinkan Anda mendukung pola akses tambahan di tabel DynamoDB Anda.