Lanjutan6 menit baca

Key Overloading di DynamoDB

Datang dari SQL, sebuah kolom berarti satu hal selamanya: orders.created_at selalu sebuah tanggal, users.email selalu sebuah email. Key overloading membuang itu. Anda memberi partition dan sort key nama generik — pk, sk — dan membiarkan setiap tipe item menuangkan makna berbeda ke dalamnya. Satu tabel, banyak entitas, satu bentuk.

Apa itu key overloading di DynamoDB?

Key overloading adalah menyimpan banyak tipe entitas dalam satu tabel di bawah nama key generik seperti pk/sk, dengan mengenkode tipenya pada nilai key (USER#u_3001, INVOICE#2026-0014). Nama atribut tetap netral sehingga pengguna, invoice, dan event berbagi satu partition; nilainya yang membawa tipe, dan prefix sort-key memungkinkan satu Query mengiris setiap entitas lewat begins_with.

  • Nama key generik, nilai bertipe. Beri nama key Anda pk/sk dan taruh tipe entitas di nilainya: pk = "TENANT#acme", sk = "USER#u_3001". Namanya bodoh; nilainya membawa tipenya.
  • Itulah yang membuat single-table design bekerja. Tanpa overloading, sebuah tabel bersama hanyalah laci sampah. Dengannya, setiap entitas duduk di sebuah partition yang bisa Anda Query.
  • begins_with adalah hasilnya. Sebuah prefix tipe pada sort key membiarkan satu Query menarik seluruh entitas, atau satu potongnya, tanpa Scan dan tanpa filter.
  • Biayanya: keterbacaan. Sebuah dump pk/sk mentah tidak memberi tahu Anda apa pun. Anda butuh viewer yang men-decode prefix-nya, atau Anda akan menyipitkan mata pada string.

Mengapa nama generik mengalahkan nama asli

DynamoDB punya persis dua atribut key per tabel, dan sebuah Query hanya bisa menargetkan satu partition key. Jadi jika Anda menamai key Anda userId, hanya item user yang bisa tinggal di tabel itu dengan rapi — segala yang lain harus memalsukan sebuah userId atau pindah ke tabelnya sendiri.

Overloading menyiasati itu. Sebuah nama netral seperti pk tidak berkomitmen ke entitas mana pun, jadi sebuah user, sebuah invoice, dan sebuah audit event semua bisa berbagi atribut key yang sama dan tabel yang sama. Nilainya, bukan nama atributnya, yang mengatakan apa item itu.

Ini adalah langkah yang mengubah single-table design dari teori menjadi sesuatu yang bisa Anda query betulan. Tabel bersama adalah wadahnya; overloading adalah yang membuat entitas berbeda hidup berdampingan di dalamnya.

Contoh multi-tenant

Misalkan Anda menjalankan sebuah produk billing SaaS. Setiap tenant punya member, invoice, dan jejak audit. Alih-alih tiga tabel, taruh semuanya dalam satu dan overload key-nya:

pkskattributes
TENANT#acmeMETAname="Acme Inc", plan="team"
TENANT#acmeUSER#u_3001email, role="admin"
TENANT#acmeUSER#u_3002email, role="member"
TENANT#acmeINVOICE#2026-0014amount_cents, status="paid"
TENANT#acmeINVOICE#2026-0015amount_cents, status="open"
TENANT#acmeEVENT#2026-06-23T09:12Zactor="u_3001", action="invite"

Setiap baris berbagi pk = "TENANT#acme", jadi mereka membentuk satu item collection — semua ko-lokasi, semua terjangkau dalam satu pembacaan partition.

Partition: TENANT#acmesk: METAsk: USER#u_3001sk: INVOICE#2026-0015sk: EVENT#2026-06-23T09:12ZSatu Query

Prefix sort-key mengerjakan kerja sesungguhnya. Ia mengelompokkan entitas sekaligus mengurutkannya.

Query collection yang ter-overload

Karena tipe-nya tinggal di prefix sort-key, begins_with mengiris partition berdasarkan entitas tanpa men-scan apa pun:

Query pk = "TENANT#acme"  -- seluruh tenant, setiap tipe
Query pk = "TENANT#acme" AND begins_with(sk, "USER#")  -- hanya member
Query pk = "TENANT#acme" AND begins_with(sk, "INVOICE#")  -- hanya invoice

Anda hanya membayar untuk item yang dicocokkan kondisi, bukan seluruh partition — kebalikan dari sebuah Scan yang difilter, di mana Anda membayar untuk membaca baris yang lalu Anda buang. AWS menyebut ini sebuah condition key; ia berjalan pada key sebelum data apa pun meninggalkan partition.

Jika Anda membangun kondisi begins_with itu dengan tangan, buat tag tipe-nya benar — sebuah USERS# yang nyasar alih-alih USER# mengembalikan kosong, secara diam-diam. Expression builder menghasilkan KeyConditionExpression dan map ExpressionAttributeValues sehingga prefix-nya cocok dengan apa yang sebenarnya Anda tulis.

Overload indeks juga

Trik yang sama berlaku untuk sebuah GSI. Beri ia nama key generik — gsi1pk, gsi1sk — dan biarkan setiap entitas menulis apa pun yang dibutuhkannya. Satu indeks lalu menjawab pola yang base table tak bisa.

pkskgsi1pkgsi1sk
TENANT#acmeINVOICE#2026-0015STATUS#open2026-06-30
TENANT#acmeINVOICE#2026-0014STATUS#paid2026-06-12
TENANT#betaINVOICE#2026-0099STATUS#open2026-06-25

Kini Query gsi1 WHERE gsi1pk = "STATUS#open" mendaftarkan setiap invoice terbuka di seluruh tenant, terurut berdasarkan due-date — sebuah tampilan lintas-partition yang key tenant-scoped base table tak akan pernah bisa layani. Sebuah entitas berbeda bisa memakai ulang gsi1 dengan maknanya sendiri (misalnya gsi1pk = "ROLE#admin"), jadi satu indeks mencakup beberapa pembacaan. Hanya ingat bahwa sebuah GSI eventually consistent — penulisannya tertinggal di belakang base table.

Lakukan di DynoTable

Key ter-overload mentah sukar dibaca: INVOICE#2026-0015 dan EVENT#2026-06-23T09:12Z membaur dalam sebuah daftar datar. Sebuah viewer yang mengelompokkan berdasarkan partition dan memunculkan prefix-nya mengubah laci sampah kembali menjadi entitas.

DynoTable menjelajahi item collection satu tenant — item META, USER, INVOICE, dan EVENT dikelompokkan di bawah satu partition key yang ter-overload.
DynoTable menjelajahi item collection satu tenant — item META, USER, INVOICE, dan EVENT dikelompokkan di bawah satu partition key yang ter-overload.

Jebakan

  • Pilih delimiter sekali dan jangan pernah mengubahnya. # adalah konvensinya. Mencampur # dan : di seluruh entitas merusak begins_with dengan cara yang tak ada peringatannya.
  • Jangan overload nilai yang butuh perhitungan rentang. Sebuah sort key INVOICE#2026-0015 terurut secara leksikal, bukan numerik — zero-pad id dan pakai tanggal ISO-8601 agar urutan string cocok dengan urutan yang Anda maksud.
  • Cadangkan ruang nama prefix. Dua tipe entitas yang sama-sama mulai dengan USER (misalnya USER# dan USERGROUP#) akan bertabrakan di bawah begins_with(sk, "USER"). Buat prefix tak ambigu dari karakter pertama.
  • Rencanakan pembacaan sebelum key-nya. Overloading melayani pola akses yang telah Anda enumerasi. Jika Anda belum tahu pembacaan Anda, lihat single-table design lebih dulu — key-nya berada di hilir query.

Petakan sebuah partition, lalu unduh DynoTable untuk menjelajahi key ter-overload Anda sendiri dan amati satu Query menarik seluruh tenant kembali sekaligus.

Diperbarui