Cara Sebuah GSI DynamoDB Disimpan Secara Internal
Sebuah Global Secondary Index bukanlah penunjuk kembali ke tabel Anda. Ia adalah tabel terpisah yang dikelola secara internal — partition-nya sendiri, key schema-nya sendiri, kapasitasnya sendiri — yang dijaga sinkron oleh DynamoDB dengan menyalin tulisan ke dalamnya secara asinkron.
Datang dari SQL, sebuah index adalah B-tree yang dibaut ke tabel fisik yang sama, diperbarui di dalam transaction yang sama. Sebuah GSI mematahkan kedua asumsi tersebut, dan hampir setiap kejutan GSI berakar pada satu fakta itu.
Bagaimana sebuah GSI DynamoDB disimpan?
Sebuah GSI DynamoDB disimpan sebagai tabel terpisah yang dikelola secara internal — dengan partition, key schema, dan kapasitasnya sendiri — bukan sebagai penunjuk ke dalam tabel basis. DynamoDB menyalin setiap tulisan ke dalam index secara asinkron, dan hanya menyimpan key GSI, key tabel basis, serta atribut apa pun yang diproyeksikan.
- Sebuah GSI adalah tabelnya sendiri. Ia punya ruang partition yang sepenuhnya independen yang di-key oleh partition key GSI, bukan tabel basis.
- Tulisan direplikasi secara asinkron. Tulisan Anda di-commit ke tabel basis dulu, lalu DynamoDB menyebarkannya ke setiap GSI di jalur latar.
- Hanya atribut yang diproyeksikan yang disimpan. Index menampung key GSI, key basis, ditambah atribut apa pun yang Anda proyeksikan — tak ada yang lain.
- Key GSI tak perlu unik. Banyak Item basis bisa berbagi satu partition/sort key GSI; primary key basis adalah pemecah-seri yang menjaga mereka tetap berbeda.
Mulai dengan satu Item basis
Ambil sebuah audit log SaaS. Setiap aksi privileged dalam sebuah workspace
menjadi event yang immutable. Tabel basis, WorkspaceEvents, di-key sehingga semua
event sebuah workspace tinggal dalam satu item collection, diurutkan berdasarkan
waktu:
| EventPK | EventSK | actorId | verb | targetRef |
|---|---|---|---|---|
| WS#orbit-9 | TS#2026-06-23T14:02:11Z | USR#kp | ROLE_GRANTED | USR#mara |
EventPK = "WS#orbit-9" mem-partition berdasarkan workspace; EventSK adalah
timestamp ISO sehingga sebuah Query mengembalikan event satu workspace dalam urutan
kronologis. Itu melayani "tunjukkan saya timeline workspace ini" dengan sempurna.
Ia tidak melayani yang lain. Anda tidak bisa bertanya "apa yang dilakukan USR#kp
di setiap workspace?" — actorId bukan key, jadi satu-satunya cara menjawabnya pada
tabel basis adalah sebuah Scan penuh. Itulah pola akses
yang ada untuk ditambahkan oleh sebuah GSI.
Tambahkan sebuah GSI dan saksikan tabel kedua muncul
Definisikan sebuah GSI, ByActor, yang mem-partition ulang event yang sama
berdasarkan siapa yang melakukannya:
ByActor (GSI)
GSI1PK = actorId ("USR#kp")
GSI1SK = EventSK ("TS#2026-06-23T14:02:11Z")
DynamoDB kini memelihara sebuah struktur fisik kedua. Event logis yang sama disimpan
dua kali — sekali di partition WS#orbit-9 tabel basis, dan lagi di partition
USR#kp GSI:
| GSI1PK | GSI1SK | EventPK | EventSK | verb |
|---|---|---|---|---|
| USR#kp | TS#2026-06-23T14:02:11Z | WS#orbit-9 | TS#2026-06-23T14:02:11Z | ROLE_GRANTED |
Perhatikan apa yang ikut serta: key tabel basis (EventPK, EventSK) disimpan
di setiap Item GSI secara otomatis. Begitulah sebuah hit GSI bisa mengarahkan Anda
kembali ke Item lengkap — dan mengapa sebuah index
KEYS_ONLY tetap memakan penyimpanan.
Apa yang sebenarnya tinggal di GSI
Index tidak menyalin seluruh Item. Setiap entri GSI menampung tepat tiga hal, dan Anda hanya mengontrol yang ketiga:
| Disimpan di GSI | Dari mana asalnya | Opsional? |
|---|---|---|
| Partition + sort key GSI | Atribut yang Anda namai sebagai key GSI | Tidak |
| Key tabel basis | Disalin dari setiap Item basis | Tidak |
| Atribut yang diproyeksikan | Pilihan Projection Anda | Ya |
Projection adalah KEYS_ONLY, INCLUDE (sebuah daftar bernama), atau ALL.
Sebuah Query pada GSI hanya bisa mengembalikan atribut yang ada di index.
Minta satu yang tidak diproyeksikan dan DynamoDB tidak mengambilnya secara transparan — Anda tidak mendapat apa pun untuk field itu. (dokumentasi GSI AWS)
Itulah jebakan relasional yang terbalik: SQL akan join kembali ke heap untuk kolom yang hilang. Sebuah GSI tidak pernah melakukannya. Proyeksi adalah seluruh kontrak.
Bagaimana sebuah tulisan mencapai index
Replikasi adalah bagian yang paling keras mematahkan intuisi SQL. Sebuah tulisan basis dan pembaruan index-nya bukan satu operasi atomik.
Saat Anda PutItem, DynamoDB meng-commit secara durabel ke tabel basis,
mengonfirmasi tulisan Anda, dan lalu menyebarkan perubahan ke jalur latar yang
memperbarui setiap GSI. Konfirmasi tidak menunggu index.
Berikut urutan peristiwa untuk tulisan audit kita, dari atas ke bawah:
Pemanggil mendapat 200 OK-nya di langkah tiga, sebelum langkah empat hingga enam
selesai — sehingga sebuah Query pada ByActor di sela itu bisa melewatkan event
yang baru saja masuk.
Asinkronisitas itu adalah desain, bukan cacat: ia adalah garis keturunan dari paper Amazon Dynamo 2007, yang memilih ketersediaan di atas konsistensi sinkron. Konsekuensi penuhnya ada di mengapa sebuah GSI eventually consistent.
Key GSI bukan key unik
Di SQL, index sekunder non-unik adalah default dan yang unik adalah constraint yang Anda pilih. Sebuah GSI sebaliknya: ia tidak memiliki jaminan keunikan, sama sekali.
Dua event audit dari aktor yang sama pada timestamp yang berbenturan akan berbagi
GSI1PK dan GSI1SK yang sama. DynamoDB menyimpan keduanya — ia membedakannya
secara internal melalui primary key tabel basis, yang selalu ikut terbawa.
Jadi sebuah Query GSI untuk satu aktor pada satu instan bisa secara sah
mengembalikan beberapa Item. Jika Anda berasumsi satu-baris-per-key seperti yang
akan diberikan index unik SQL, itu footgun-nya.
Saat Anda melakukan query index,
DynamoDB Expression Builder menulis
KeyConditionExpression dengan names dan values yang di-escape dengan benar — mis.
mencocokkan satu aktor sejak sebuah cutoff:
KeyConditionExpression: "#a = :actor AND #ts > :since"
ExpressionAttributeNames: { "#a": "actorId", "#ts": "EventSK" }
ExpressionAttributeValues: {
":actor": { "S": "USR#kp" },
":since": { "S": "TS#2026-06-01T00:00:00Z" }
}Kapasitas tinggal bersama index, bukan tabel
Karena GSI adalah tabelnya sendiri, ia memiliki read dan write capacity-nya
sendiri, ditagih dan di-throttle terpisah dari tabel basis. Sebuah pembacaan dari
ByActor mengonsumsi read unit GSI, tak pernah milik tabel.
Kopling terbalik adalah yang menggigit: setiap tulisan tabel-basis juga menulis index, dan jika GSI tak bisa menyerapnya, ia memberi tekanan-balik pada tulisan basis. Mekanisme itu mendapat panduannya sendiri — saat sebuah GSI men-throttle tulisan tabel-basis.
Inilah juga alasan partition key GSI sama pentingnya dengan tabel basis. Sebuah key GSI ber-cardinality-rendah menggumpalkan tulisan ke satu partition index bahkan saat tulisan basis tersebar sempurna — sebuah hot partition yang Anda ciptakan dengan me-re-key.
Jebakan dan langkah berikutnya
- Jangan harapkan atribut yang tak-diproyeksikan kembali. Sebuah
QueryGSI hanya mengembalikan apa yang disimpan index. Jika Anda butuh Item lengkap, proyeksikan atau ambil dari tabel basis melalui key yang ikut terbawa. - Jangan perlakukan key GSI sebagai unik. Rencanakan agar sebuah
Querymengembalikan lebih dari satu Item per key; primary key basis adalah satu-satunya identitas nyata. - Jangan baca sebuah GSI tepat setelah tulisan yang memberinya makan. Jalur async berarti index mungkin belum menampilkan tulisan Anda — baca tabel basis saat Anda butuh read-your-own-writes.
- Ukur kapasitas GSI secara sengaja. Ia independen pada pembacaan dan dependensi tersembunyi pada tulisan.
Seluruh permainannya adalah memilih bentuk key yang melayani pola Anda — single-table design meng-overload satu GSI di banyak darinya; GSI vs LSI membahas kapan index lokal pas sebaliknya.
Bangun dan pratinjau KeyConditionExpression GSI Anda di
DynamoDB Expression Builder, lalu
coba DynoTable untuk memeriksa atribut yang diproyeksikan sebuah index
dan menyaksikan tulisan direplikasi ke GSI pada tabel Anda sendiri.