Hubungan many-to-many di DynamoDB
Seorang siswa mendaftar di banyak kursus; suatu kursus menampung banyak siswa.
Di SQL Anda menjangkau tabel join dan JOIN tiga tabel.
DynamoDB tidak punya join, jadi hubungan harus hidup di key — dan tipunya
adalah menyimpan setiap edge pendaftaran dalam bentuk yang kedua belah pihak
bisa Query langsung.
Panduan ini menelusuri masalah siswa ↔ kursus dari ujung ke ujung: access pattern, pattern yang menyelesaikannya, skema key asli yang bisa Anda salin, dan cara membaca kedua arah kembali tanpa pernah men-Scan tabel.
Bagaimana Anda memodelkan hubungan many-to-many di DynamoDB?
DynamoDB tidak punya join, jadi Anda memodelkan hubungan many-to-many dengan pattern : simpan setiap tautan sebagai item edge sendiri yang di-key by satu sisi, lalu tambah GSI terbalik yang menukar key. Satu edge, ditulis sekali, lalu menjawab query dari kedua arah dengan murah.
- Simpan setiap pendaftaran sebagai item edge sendiri, bukan atribut list di salah satu sisi.
- Key edge by siswa (
PK = STU#…,SK = ENROLL#CRS#…) agar satuQuerymengembalikan seluruh daftar kursus siswa. - Tambah terbalik yang menukar peran (
GSI1PK = CRS#…) agar edge yang sama juga menjawab "siapa di kursus ini?". - Satu edge, ditulis sekali, dibaca murah kedua arah — itulah seluruh permainannya.
Bingkai access pattern dulu
Pemodelan DynamoDB adalah access-pattern-first: Anda memutuskan baca sebelum memilih satu nama atribut. Hubungan many-to-many hampir selalu punya dua baca simetris plus lookup entitas:
- Ambil profil siswa, dan daftar setiap kursus yang diikuti siswa itu.
- Ambil metadata kursus, dan daftar setiap siswa yang terdaftar di kursus itu.
- Lookup satu edge pendaftaran — untuk meng-update nilai atau drop kursus.
Dua baca daftar menunjuk arah berlawanan lintas himpunan edge yang sama. Desain
naif melayani satu dengan murah dan memaksa Scan untuk yang lain — jebakan
tepat yang dibahas di Query vs Scan.
Tugasnya membuat kedua arah menjadi satu Query.
Pakai pattern adjacency-list
Panduan AWS sendiri untuk hubungan adalah adjacency list: model setiap hubungan sebagai item yang partition key-nya satu endpoint dan sort key-nya yang lain.
AWS mendokumentasikan ini di halaman Best Practices for Managing Many-to-Many Relationships DynamoDB Developer Guide.
Mengapa key dan bukan tabel kedua? Karena primitif yang DynamoDB berikan adalah
Query terhadap satu partisi.
Query membaca rentang kontigu nilai sort key di bawah satu partition key dalam
satu operasi yang ditagih — itu satu-satunya "join" yang ditawarkan engine.
Untuk mendapatkan hubungan yang dibaca murah dari kedua sisi, Anda menduplikasi edge: tulis sekali di-key by siswa, lalu pakai secondary index untuk memproyeksikan edge yang sama di-key by kursus.
Ini pemikiran overloaded-key dari Single-Table Design, diterapkan ke hubungan alih-alih hierarki parent-child.
Bentuknya dua tampilan bertumpuk dari edge yang sama — tabel dasar di-key by siswa, GSI terbalik di-key by kursus:
Setiap edge ditulis sekali di tabel dasar dan diproyeksikan ke GSI dengan key
ditukar, sehingga Query terhadap partisi mana pun membaca hubungan dengan
murah.
Garis keturunannya kembali ke paper Dynamo Amazon 2007: partition key adalah unit distribusi, dan akses single-key adalah jalur cepat.
Hubungan di DynamoDB adalah latihan membengkokkan baca many-to-many ke jalur cepat itu.
Kerjakan contoh: siswa ↔ kursus
Pakai satu tabel dengan key generik, PK dan SK, dan encode tipe entitas di
nilainya. Edge pendaftaran adalah jantungnya:
| PK | SK | attributes |
|---|---|---|
| STU#a91 | PROFILE | name, year, major |
| STU#a91 | ENROLL#CRS#math204 enrolledOn, grade | |
| STU#a91 | ENROLL#CRS#cs101 | enrolledOn, grade |
| CRS#math204 | METADATA | title, credits, term |
| CRS#cs101 | METADATA | title, credits, term |
Satu Query PK = "STU#a91" mengembalikan profil siswa dan setiap
pendaftaran dalam satu baca. Persempit dengan SK begins_with "ENROLL#" untuk
hanya mendapat edge kursus. Itu menyelesaikan "daftar kursus siswa".
Tetapi "daftar siswa kursus" menunjuk arah lain — dan tabel dasar tidak bisa menjawabnya, karena id siswa ada di partition key, bukan sort key.
Tambah global secondary index terbalik yang menukar peran. Beri item edge
pasangan GSI1PK/GSI1SK generik yang menampung kursus di sisi partisi dan
siswa di sisi sort:
| PK | SK | GSI1PK | GSI1SK |
|---|---|---|---|
| STU#a91 | ENROLL#CRS#math204 | CRS#math204 | STU#a91 |
| STU#b30 | ENROLL#CRS#math204 | CRS#math204 | STU#b30 |
| STU#a91 | ENROLL#CRS#cs101 | CRS#cs101 | STU#a91 |
Sekarang Query GSI1 WHERE GSI1PK = "CRS#math204" mendaftar setiap siswa di
kursus itu — baca yang tabel dasar tidak bisa layani. Satu item edge, ditulis
sekali, menjawab kedua arah.
Harus GSI, bukan LSI: partisi kursus sepenuhnya berbeda dari partisi siswa, dan LSI berbagi partition key tabel dasar.
Indeks mencakup banyak partisi, jadi harus global — lihat GSI vs LSI.
GSI di DynamoDB diisi secara asinkron. Pendaftaran brand-new bisa butuh sejenak
untuk muncul di arah CRS#….
Perlakukan baca roster kursus sebagai — yang Developer Guide sebutkan secara eksplisit untuk global secondary index.
Tulis dan baca di DynoTable
Menulis pendaftaran berarti menyetel empat atribut key plus data edge sendiri.
Kondisi yang menghentikan siswa mendaftar dua kali di kursus yang sama adalah
penjaga attribute_not_exists(PK) pada composite key.
Itu tepat jenis kondisi yang bisa Anda susun secara visual dengan
DynamoDB Expression Builder alih-alih
menulis tangan ExpressionAttributeNames dan nilai placeholder.
Di DynoTable Anda arahkan Query ke GSI1, set GSI1PK = "CRS#math204", dan
roster kembali sebagai tabel yang bisa Anda baca, urut, dan edit in place —
kedua arah hubungan bisa dijelajahi dari satu skema.

Jebakan dan langkah selanjutnya
- Jangan simpan satu sisi sebagai atribut list. Array
courseIdspada item siswa terasa rapi sampai kursus butuh rosternya, array menabrak langit-langit item 400 KB, atau dua pendaftaran race dan saling clobber. Item edge diskrit berskala dan meng-update secara independen. - Jaga data edge di edge.
gradedanenrolledOnpendaftaran milik item edge, bukan diduplikasi ke siswa atau kursus — ada tepat satu baris per pasangan (siswa, kursus) untuk di-update. - Perhatikan propagasi GSI. Arah inverted-index eventually consistent, jadi baca segera setelah pendaftaran mungkin tertinggal sepecahan detik.
- Project hanya yang dibutuhkan roster. Proyeksi
KEYS_ONLYatau sempit menjaga GSI kecil saat tampilan roster hanya butuh id.
Untuk lebih dalam pada pattern di sekitarnya, baca Single-Table Design untuk overloaded key dan GSI vs LSI untuk kapan inverted index harus global. Dan untuk mulai dari hubungan Anda sendiri, alat gratis Single-Table Design mengubah daftar access pattern seperti "daftar kursus siswa / daftar siswa kursus" menjadi rencana PK/SK/GSI dengan contoh item.
Lalu unduh DynoTable untuk memodelkan skema siswa ↔ kursus secara
nyata — tulis edge, bangun kondisi dengan Expression Builder, dan Query kedua
arah hubungan tanpa satu pun scan. Dan saat Anda ingin tampilan JOIN tiga
tabel klasik tetap, SQL Workbench DynoTable
menjalankannya atas tabel live Anda.


