LSI vs GSI di DynamoDB: Kapan Harus Pakai yang Mana?

Kamu baru saja menyadari bahwa query DynamoDB kamu hanya bisa filter berdasarkan partition key dan sort key — padahal data perlu diakses lewat atribut lain. Ini momen di mana banyak engineer langsung tanya: pakai LSI atau GSI? Keduanya adalah secondary index di DynamoDB, tapi keputusan yang salah di sini bisa mengunci arsitektur tabel kamu selamanya atau menghasilkan biaya yang tidak terduga.

TL;DR: LSI vs GSI di DynamoDB

Aspek LSI (Local Secondary Index) GSI (Global Secondary Index)
Partition Key Sama dengan tabel utama Atribut apapun (berbeda boleh)
Sort Key Atribut berbeda dari tabel utama Atribut apapun (opsional)
Kapan dibuat Hanya saat tabel pertama kali dibuat Kapan saja, bahkan setelah tabel ada
Konsistensi baca Strongly consistent atau eventually consistent Eventually consistent saja
Kapasitas Berbagi dengan tabel utama Kapasitas tersendiri (provisioned atau on-demand)
Batas per tabel Maksimal 5 LSI Maksimal 20 GSI
Item collection size Dibatasi 10 GB per partition key Tidak ada batasan item collection

Cara Kerja Secondary Index di DynamoDB

DynamoDB adalah key-value store yang dioptimalkan untuk akses berdasarkan primary key. Tanpa index tambahan, satu-satunya cara query efisien adalah lewat partition key (dan opsional sort key). Kalau kamu perlu akses data lewat atribut lain, kamu butuh secondary index — yang pada dasarnya adalah salinan data tabel dengan urutan kunci berbeda.

LSI dan GSI keduanya menyimpan proyeksi atribut dari tabel utama. Bedanya ada di scope partisi data tersebut:

  • LSI bersifat 'lokal' — index hanya mencakup item yang memiliki partition key yang sama dengan tabel utama. Artinya, LSI tidak bisa digunakan untuk query lintas partition key.
  • GSI bersifat 'global' — index mencakup seluruh tabel, dengan partition key yang bisa berbeda sama sekali dari tabel utama.
graph TD Table["Tabel Utama
PK: customerId / SK: orderId"] LSI["LSI: OrderDateIndex
PK: customerId / SK: orderDate"] GSI["GSI: StatusDateIndex
PK: status / SK: orderDate"] Data["Item Data"] Data -->|tulis| Table Table -->|replikasi otomatis| LSI Table -->|replikasi otomatis| GSI QueryLSI["Query LSI
butuh customerId tertentu"] QueryGSI["Query GSI
lintas semua customerId"] QueryLSI --> LSI QueryGSI --> GSI style LSI fill:#d4edda,stroke:#28a745 style GSI fill:#cce5ff,stroke:#004085 style Table fill:#fff3cd,stroke:#856404
  1. Tabel Utama diakses via primary key (partition key + sort key).
  2. LSI berbagi partition key yang sama, hanya sort key-nya berbeda. Query LSI tetap terikat pada satu partition key tertentu.
  3. GSI memiliki partition key sendiri yang independen. Query GSI bisa menjangkau seluruh data tabel tanpa terikat partition key tabel utama.
  4. Kedua index menyimpan proyeksi atribut — bisa ALL, KEYS_ONLY, atau INCLUDE (atribut tertentu saja).

LSI: Local Secondary Index — Fleksibilitas Sort Key, Harga Keterikatan

LSI memberikan kemampuan untuk query dalam satu partition key menggunakan sort key alternatif. Misalnya, tabel order dengan partition key customerId dan sort key orderId — dengan LSI kamu bisa query order milik customer tertentu diurutkan berdasarkan orderDate atau totalAmount.

Ada satu aturan keras yang sering menjebak: LSI hanya bisa dibuat saat tabel pertama kali dibuat. Tidak ada cara menambahkan LSI ke tabel yang sudah ada. Ini bukan batasan operasional yang bisa di-workaround — ini adalah constraint arsitektur DynamoDB.

Batasan lain yang krusial: item collection size limit sebesar 10 GB. Item collection adalah semua item yang memiliki partition key yang sama, termasuk di semua LSI. Kalau satu partition key sudah mendekati 10 GB, operasi write akan mulai gagal dengan error ItemCollectionSizeLimitExceededException. Ini bukan sekadar warning — write benar-benar ditolak.

Bayangkan LSI seperti indeks di belakang buku — kamu hanya bisa cari berdasarkan topik yang sudah ditentukan saat buku dicetak. Setelah buku jadi, kamu tidak bisa tambah indeks baru.

Membuat Tabel dengan LSI

🔽 Klik untuk melihat contoh CLI pembuatan tabel dengan LSI
aws dynamodb create-table \
  --table-name Orders \
  --attribute-definitions \
      AttributeName=customerId,AttributeType=S \
      AttributeName=orderId,AttributeType=S \
      AttributeName=orderDate,AttributeType=S \
  --key-schema \
      AttributeName=customerId,KeyType=HASH \
      AttributeName=orderId,KeyType=RANGE \
  --local-secondary-indexes \
      '[{
        "IndexName": "OrderDateIndex",
        "KeySchema": [
          {"AttributeName": "customerId", "KeyType": "HASH"},
          {"AttributeName": "orderDate", "KeyType": "RANGE"}
        ],
        "Projection": {
          "ProjectionType": "INCLUDE",
          "NonKeyAttributes": ["totalAmount", "status"]
        }
      }]' \
  --billing-mode PAY_PER_REQUEST \
  --region us-east-1

Query Menggunakan LSI

aws dynamodb query \
  --table-name Orders \
  --index-name OrderDateIndex \
  --key-condition-expression 'customerId = :cid AND orderDate BETWEEN :start AND :end' \
  --expression-attribute-values \
      '{":cid": {"S": "CUST-001"}, ":start": {"S": "2024-01-01"}, ":end": {"S": "2024-12-31"}}' \
  --region us-east-1

Perhatikan bahwa query LSI tetap mengharuskan kamu menyertakan partition key (customerId) — kamu tidak bisa query LSI tanpa menyebutkan partition key tabel utama.

GSI: Global Secondary Index — Fleksibilitas Penuh, Biaya Tersendiri

GSI memungkinkan query menggunakan atribut apapun sebagai partition key baru, tanpa terikat partition key tabel utama. Ini yang paling sering dibutuhkan dalam praktik: misalnya query semua order berdasarkan status, atau semua produk berdasarkan categoryId, tanpa tahu partition key aslinya.

GSI bisa ditambahkan ke tabel yang sudah ada kapan saja. DynamoDB akan membangun index secara asinkron di background — tabel tetap bisa diakses selama proses ini berlangsung. Namun ada konsekuensi penting: GSI hanya mendukung eventually consistent reads. Tidak ada opsi strongly consistent untuk GSI, berbeda dengan LSI.

GSI memiliki kapasitas throughput tersendiri yang terpisah dari tabel utama. Kalau GSI kehabisan kapasitas write (misalnya karena hot partition key di index), write ke tabel utama tetap berhasil — tapi GSI akan throttle dan data index bisa tertinggal. Ini adalah sumber bug yang tidak langsung terlihat: query ke GSI mengembalikan data lama bukan karena eventually consistent, tapi karena index sedang throttled.

sequenceDiagram participant App as Aplikasi participant Table as Tabel Utama participant GSI as GSI (StatusDateIndex) participant CW as CloudWatch App->>Table: PutItem (status=PENDING) Table-->>App: 200 OK Table->>GSI: Replikasi ke index Note over GSI: GSI throttled karena
hot partition key 'PENDING' GSI-->>Table: ThrottledRequest CW-->>CW: Catat ThrottledRequests Note over App,GSI: Tabel utama OK,
tapi GSI lag bertambah App->>GSI: Query status=PENDING GSI-->>App: Data lama (stale)
  1. Write ke tabel utama berhasil meski GSI sedang throttled — ini yang membuat masalah tidak langsung terdeteksi.
  2. GSI replication lag bisa terjadi bukan hanya karena eventual consistency normal, tapi karena kapasitas GSI tidak cukup.
  3. Monitor metrik ConsumedWriteCapacityUnits dan ThrottledRequests di GSI secara terpisah dari tabel utama.

Menambahkan GSI ke Tabel yang Sudah Ada

🔽 Klik untuk melihat contoh CLI penambahan GSI
aws dynamodb update-table \
  --table-name Orders \
  --attribute-definitions \
      AttributeName=status,AttributeType=S \
      AttributeName=orderDate,AttributeType=S \
  --global-secondary-index-updates \
      '[{
        "Create": {
          "IndexName": "StatusDateIndex",
          "KeySchema": [
            {"AttributeName": "status", "KeyType": "HASH"},
            {"AttributeName": "orderDate", "KeyType": "RANGE"}
          ],
          "Projection": {
            "ProjectionType": "ALL"
          },
          "ProvisionedThroughput": {
            "ReadCapacityUnits": 5,
            "WriteCapacityUnits": 5
          }
        }
      }]' \
  --region us-east-1

Untuk tabel dengan billing mode PAY_PER_REQUEST, parameter ProvisionedThroughput di GSI tidak diperlukan — kapasitas mengikuti mode on-demand tabel.

Query Menggunakan GSI

aws dynamodb query \
  --table-name Orders \
  --index-name StatusDateIndex \
  --key-condition-expression '#s = :status AND orderDate >= :since' \
  --expression-attribute-names '{"#s": "status"}' \
  --expression-attribute-values \
      '{":status": {"S": "PENDING"}, ":since": {"S": "2024-06-01"}}' \
  --region us-east-1

Pengalaman Nyata: Salah Diagnosa GSI Throttling

Skenario yang sering terjadi: tim melaporkan bahwa query ke GSI kadang mengembalikan data yang sudah beberapa menit ketinggalan. Asumsi pertama hampir selalu 'ya wajar, GSI eventually consistent.' Lalu tim melanjutkan tanpa investigasi lebih lanjut.

Yang sebenarnya terjadi: GSI partition key adalah status dengan nilai yang sangat terbatas (PENDING, PROCESSING, COMPLETED). Semua write order baru masuk ke partition PENDING di GSI — hot partition yang menghabiskan write capacity GSI. Tabel utama terus menerima write tanpa masalah, tapi replikasi ke GSI tertunda karena throttling.

Cara memverifikasi kondisi ini:

aws cloudwatch get-metric-statistics \
  --namespace AWS/DynamoDB \
  --metric-name ThrottledRequests \
  --dimensions \
      Name=TableName,Value=Orders \
      Name=GlobalSecondaryIndexName,Value=StatusDateIndex \
      Name=Operation,Value=PutItem \
  --start-time 2024-01-15T00:00:00Z \
  --end-time 2024-01-15T23:59:59Z \
  --period 300 \
  --statistics Sum \
  --region us-east-1

Kalau ThrottledRequests di GSI tinggi sementara tabel utama normal, masalahnya bukan eventual consistency — tapi desain partition key GSI yang menghasilkan hot partition. Solusinya adalah redesain GSI dengan partition key yang kardinalitasnya lebih tinggi, bukan menambah kapasitas semata.

Panduan Keputusan: LSI atau GSI?

graph TD Start(["Perlu query
via atribut lain?"]) Q1{"Tabel sudah ada?"} Q2{"Query lintas
partition key?"} Q3{"Butuh strongly
consistent reads?"} Q4{"Data per partition
key bisa > 10 GB?"} UseGSI["Gunakan GSI"] UseLSI["Gunakan LSI
(saat buat tabel)"] WarnLSI["Pertimbangkan ulang
desain tabel atau GSI"] Start --> Q1 Q1 -->|Ya| UseGSI Q1 -->|Belum| Q2 Q2 -->|Ya| UseGSI Q2 -->|Tidak| Q3 Q3 -->|Ya| Q4 Q3 -->|Tidak| UseGSI Q4 -->|Ya| WarnLSI Q4 -->|Tidak| UseLSI style UseGSI fill:#cce5ff,stroke:#004085 style UseLSI fill:#d4edda,stroke:#28a745 style WarnLSI fill:#f8d7da,stroke:#721c24

Gunakan panduan ini sebagai titik awal:

  • Pakai LSI jika: kamu perlu query dalam satu partition key dengan sort key alternatif, tabel belum dibuat, dan kamu butuh strongly consistent reads dari index.
  • Pakai GSI jika: kamu perlu query lintas partition key, tabel sudah ada, atau partition key index berbeda dari tabel utama.
  • Waspadai LSI jika: data per partition key bisa tumbuh mendekati 10 GB — pertimbangkan ulang desain partition key atau gunakan GSI.
  • Waspadai GSI jika: partition key GSI memiliki kardinalitas rendah — hot partition di GSI bisa menyebabkan throttling write ke index.

IAM: Mengontrol Akses ke Secondary Index

Akses ke secondary index dikontrol lewat IAM action dynamodb:Query dengan kondisi dynamodb:index. Kamu bisa membatasi akses hanya ke index tertentu tanpa memberikan akses query ke tabel utama secara langsung.

🔽 Klik untuk melihat contoh IAM policy akses GSI
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "dynamodb:Query",
      "Resource": "arn:aws:dynamodb:us-east-1:123456789012:table/Orders/index/StatusDateIndex"
    }
  ]
}

ARN untuk index menggunakan format arn:aws:dynamodb:<region>:<account-id>:table/<TableName>/index/<IndexName>. Akses ke index dan akses ke tabel utama adalah resource yang berbeda dalam IAM.

Memeriksa Status Index yang Ada

Untuk melihat semua index yang terdefinisi pada sebuah tabel beserta statusnya:

aws dynamodb describe-table \
  --table-name Orders \
  --query 'Table.{LSIs:LocalSecondaryIndexes,GSIs:GlobalSecondaryIndexes}' \
  --region us-east-1

Status GSI yang perlu diperhatikan: CREATING (index sedang dibangun), ACTIVE (siap digunakan), DELETING, dan UPDATING. GSI dalam status CREATING belum bisa diquery.

Wrap-Up: LSI vs GSI di DynamoDB dan Langkah Selanjutnya

Perbedaan mendasar antara LSI dan GSI di DynamoDB bukan sekadar teknis — ini adalah keputusan arsitektur yang berdampak jangka panjang. LSI terikat pada waktu pembuatan tabel dan partition key yang sama, tapi memberikan strongly consistent reads dan tidak menambah biaya kapasitas terpisah. GSI jauh lebih fleksibel tapi membutuhkan perhatian ekstra pada desain partition key untuk menghindari hot partition dan throttling tersembunyi.

Kalau tabel sudah ada dan kamu perlu index baru, pilihan satu-satunya adalah GSI. Kalau tabel belum ada dan kamu butuh query dalam satu partition dengan sort key alternatif plus strongly consistent reads, LSI adalah pilihan yang tepat — tapi pastikan kamu sudah mempertimbangkan batas 10 GB per item collection sebelum commit.

Referensi resmi untuk eksplorasi lebih lanjut:

Glosarium

Istilah Penjelasan
Partition Key Atribut yang menentukan partisi fisik tempat item disimpan di DynamoDB. Disebut juga hash key.
Sort Key Atribut kedua dalam composite primary key yang menentukan urutan item dalam satu partisi. Disebut juga range key.
Item Collection Semua item yang memiliki partition key yang sama, termasuk di semua LSI terkait. Dibatasi 10 GB untuk tabel dengan LSI.
Proyeksi Index Atribut yang disalin ke dalam index. Pilihan: ALL (semua atribut), KEYS_ONLY (hanya kunci), atau INCLUDE (atribut tertentu).
Hot Partition Kondisi di mana satu partition key menerima jauh lebih banyak traffic dibanding partisi lain, menyebabkan throttling.

Related Posts

Komentar

Postingan populer dari blog ini

EC2 Tidak Bisa Akses Internet di Custom VPC: Cara Pasang Internet Gateway dan Update Route Table