Pemula5 menit baca

Projection Expression DynamoDB

Sebuah projection expression adalah SELECT col1, col2-nya DynamoDB: daftar nama atribut yang dipisah koma yang memberi tahu GetItem, Query, atau Scan untuk mengembalikan hanya atribut itu alih-alih seluruh Item.

Apakah projection expression DynamoDB mengurangi biaya baca?

Tidak. ProjectionExpression memangkas payload respons, bukan read capacity yang ditagihkan. DynamoDB membaca item penuh dari penyimpanan, menghitung berdasarkan ukuran item di disk, lalu membuang atribut yang tidak Anda sebut sebelum dikirim keluar. Untuk benar-benar memangkas biaya baca, gunakan covering sebagai gantinya.

  • Ia memangkas payload, bukan biaya baca. DynamoDB membaca (dan menagih) Item penuh dari penyimpanan, lalu menjatuhkan atribut yang tak Anda sebut dalam perjalanan keluar. ProjectionExpression adalah optimasi jaringan, bukan optimasi kapasitas.
  • Ia adalah cara Anda mengambil subset publik. Sebut beberapa atribut yang boleh dilihat caller; sisanya tak pernah meninggalkan tabel.
  • Gunakan placeholder #name untuk apa pun yang mungkin reserved. Nama atribut polos dalam expression bentrok dengan ~570 reserved word DynamoDB dan menggagalkan request.
  • Untuk penghematan baca nyata, gunakan covering index. Sebuah GSI yang memproyeksikan hanya kolom yang Anda butuh dibaca pada ukurannya (yang lebih kecil) sendiri.

Apa yang sebenarnya ia hemat

Datang dari SQL, Anda berasumsi SELECT a, b men-scan lebih sedikit daripada SELECT *. Di DynamoDB intuisi itu salah. Capacity unit untuk sebuah pembacaan dihitung dari ukuran Item pada disk, dibulatkan ke atas ke 4 KB berikutnya — sebelum projeksi diterapkan. AWS eksplisit: sebuah ProjectionExpression tak mengubah read capacity yang dikonsumsi sebuah request.1

Jadi sebuah projeksi menghemat dua hal, keduanya nyata tetapi keduanya di hilir pembacaan:

  • Byte melintasi kabel. Item 6 KB yang dikembalikan sebagai dua atribut kecil adalah respons mungil. Pada sebuah Query yang mengembalikan ratusan Item, itu menumpuk cepat.
  • Pekerjaan sisi-klien. Lebih sedikit untuk dideserialisasi, lebih sedikit ditahan di memori, lebih sedikit bocor ke log atau respons API secara tak sengaja.

Yang tidak ia hemat adalah RCU. Itulah footgun-nya: orang meraih projeksi untuk memangkas tagihan, tak melihat perubahan, dan menyimpulkan DynamoDB rusak. Ia tidak — Anda mengukur tuas yang salah.

Proyeksikan profil user publik

Misalkan Anda menjalankan direktori user. Setiap profil adalah satu Item, berkey agar Anda bisa mengambil seseorang berdasarkan handle:

PK = "PROFILE#ada"      (partition key)
SK = "PROFILE#ada"      (sort key — collection ber-Item-tunggal)

Item-nya gemuk. Ia membawa wajah publik akun plus tumpukan atribut privat dan operasional:

{
  "PK": "PROFILE#ada",
  "SK": "PROFILE#ada",
  "displayName": "Ada L.",
  "avatarUrl": "https://cdn.example.com/u/ada.png",
  "bio": "Builds things.",
  "emailAddress": "ada@example.com",
  "passwordResetToken": "…",
  "billingCustomerId": "cus_…",
  "lastLoginIp": "…",
  "internalRiskScore": 0.02
}

Kartu profil publik butuh tiga field. Mengambil seluruh Item berarti emailAddress, lastLoginIp, dan internalRiskScore berkelana ke konteks yang seharusnya tak pernah melihatnya. Sebut hanya subset publiknya:

GetItem  PK = "PROFILE#ada"  SK = "PROFILE#ada"
ProjectionExpression: displayName, avatarUrl, bio

Responsnya membawa tiga atribut. Yang privat tetap di tabel — tidak difilter keluar oleh aplikasi Anda setelah tiba, melainkan tak pernah diserialisasi ke dalam respons sama sekali. Itulah kemenangan keamanannya, dan itu yang sulit dibatalkan begitu sebuah rahasia telah menyeberangi batas.

Anda bisa merakit dan menyalin request persisnya — nama, placeholder, dan panggilan SDK — di DynamoDB Expression Builder, yang memancarkan ProjectionExpression dan peta ExpressionAttributeNames untuk Anda.

Escape reserved word dengan placeholder #

Di sinilah projeksi yang bersih meledak. DynamoDB memesan daftar panjang kata — name, status, comment, size, timestamp, dan ratusan lainnya.2 Jika atribut yang Anda proyeksikan salah satunya, nama mentah dalam expression ditolak.

Misalkan profil juga punya atribut status ("active", "suspended"). Ini gagal:

ProjectionExpression   displayName, status

status adalah reserved. Solusinya adalah expression attribute name — placeholder ber-prefiks # yang dipetakan ke nama nyata:

ProjectionExpression       displayName, #s
ExpressionAttributeNames   { "#s": "status" }

Mekanisme yang sama menjangkau atribut bersarang. Untuk menarik satu field dari sebuah map, atau satu elemen dari list, gunakan sintaks document-path — dan placeholder-kan setiap segmen, karena salah satunya bisa reserved:

ProjectionExpression       #addr.#city, tags[0]
ExpressionAttributeNames   { "#addr": "address", "#city": "city" }

Aturan praktis: placeholder-kan segalanya. Anda tak pernah harus mengingat reserved word ~570 mana yang sedang Anda injak, dan expression-nya terbaca sama entah bagaimana.

Kapan covering index mengalahkan projeksi

Jika Anda benar-benar perlu memangkas biaya baca — bukan hanya payload — tuasnya adalah Global Secondary Index yang memproyeksikan hanya atribut yang Anda baca. Sebuah GSI adalah salinan data terpisah; Anda memilih KEYS_ONLY, INCLUDE, atau ALL untuk projeksinya.3 Index KEYS_ONLY atau INCLUDE yang sempit secara fisik lebih kecil per Item, jadi sebuah Query terhadapnya dimeter pada ukuran yang lebih kecil itu.

Itulah covering index: query dijawab sepenuhnya dari index, tanpa perjalanan balik ke tabel dasar. Gunakan ia saat pola baca yang hot hanya pernah butuh beberapa atribut dari Item besar.

ProjectionExpressionCovering GSI
Memangkas payloadYaYa
Memangkas biaya bacaTidakYa — dibaca pada ukuran index
Penyimpanan ekstraTidak adaSalinan kedua field yang diproyeksikan
Biaya tulis ekstraTidak adaPenulisan berpropagasi ke index
Terbaik untukMenyembunyikan field privat; kemenangan kecilPembacaan hot beberapa field dari Item besar

Trade-off-nya jujur: index membebani Anda penyimpanan dan write capacity untuk menghemat read capacity. Layak untuk pembacaan sering atas irisan tipis dari Item berat; tak layak untuk memangkas GetItem sekali-pakai. Lihat GSI vs LSI untuk memilih tipe index, dan kapan pembacaan GSI bisa basi sebelum Anda menaruh satu di jalur hot.

Jebakan dan langkah berikutnya

  • Jangan harapkan tagihan lebih kecil. Projeksi sendiri tak pernah mengubah RCU. Jika angkanya tak bergerak, itu perilaku yang terdokumentasi, bukan bug.
  • Placeholder-kan reserved word. name atau status telanjang dalam expression menggagalkan request — #-petakan ia.
  • Memproyeksikan atribut key itu gratis dan sering berguna — DynamoDB mengembalikannya murah, dan mereka membiarkan Anda mem-page atau mengambil ulang.
  • Raih covering index hanya saat pola hot membaca beberapa field dari Item besar; timbang biaya tulis/penyimpanan dulu.

Bangun ProjectionExpression dan peta attribute-name-nya di Expression Builder, dan coba DynoTable untuk menjalankan projeksi ini terhadap tabel Anda sendiri dan menyaksikan respons menyusut.


  1. AWS DynamoDB Developer Guide, Working with Read and Write Operations — read capacity didasarkan pada ukuran Item sebelum ProjectionExpression mana pun diterapkan. https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/
  2. AWS DynamoDB Developer Guide, Reserved Words in DynamoDB. https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ReservedWords.html
  3. AWS DynamoDB Developer Guide, Attribute Projections (KEYS_ONLY / INCLUDE / ALL). https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GSI.html

Diperbarui