DynamoDB Single-Table Design: Membuat Blog (2)

4 min read

Disclaimer
Saya bekerja di AWS, semua opini adalah dari saya pribadi. (I work for AWS, my opinions are my own.)
DynamoDB Single Table Design
Ilustrasi: Single-Table Design

TeknoCerdas.com – Salam cerdas untuk kita semua. Salah satu konsep data modeling yang ada pada DyanamoDB adalah single-table design. Konsep yang mungkin terdengar aneh bagi yang datang dari background SQL database tradisional. Untuk memudahkan ilustrasi maka saya gunakan contoh sebuah blog database.

Artikel bagian pertama dapat dibaca pada link berikut:
DynamoDB Single-Table Design: Membuat Blog (1)

Tanggalkan konsep tentang normalisasi database, karena pada NoSQL database duplikasi data adalah hal yang wajar dan tidak perlu ditakutkan. Namun usahakan seminimal mungkin terjadi duplikasi data. Karena semakin sedikit duplikasi maka kompleksitas untuk menjaga konsistensi data juga menurun.

Jadi jika dipersingkat DynamoDB lebih dikhususkan untuk melakukan operasi OLTP dengan performa tinggi dan bukan didesain untuk OLAP. Artikel ini akan menitikberatkan pada pembahasan contoh single-table design yaitu sebuah blog.

Daftar Isi

Contoh Database Blog

Dibawah ini adalah contoh sebuah blog database yang sederhana. Jika dimodelkan dalam RDMBS maka terdiri dari empat tabel yaitu: author, articles, taxonomies dan article_taxonomies.

Contoh database blog
Contoh database blog dengan empat tabel

Pentingnya Access-Pattern

Pada konsep single-table design mengetahui access-pattern dari aplikasi yang dibuat adalah kunci nomor satu. Jadi jika aplikasi yang dibuat adalah sebuah blog maka tentukan access-pattern apa saja yang mungkin dilakukan pada sebuah blog.

Jadi nantinya hanya ada satu tabel bernama “blogs”. Tidak ada tabel lain, semua access pattern akan mengarah ke tabel tersebut dengan tambahan Global Secondary Index (GSI) jika diperlukan.

Filtering data pada sisi aplikasi harus dihindari sebisa mungkin agar kode tidak menjadi kompleks dalam memproses data. Data sudah harus siap digunakan dari DynamoDB.

Untuk menjaga performa agar tetap konsisten operasi Scan harus dihindari karena akan membaca seluruh item pada tabel sehingga ini tidak scalable. Operasi filter pada Query sebisa mungkin juga hindari ketika dataset sangat besar. Hal ini akan mempengerahui performa dan biaya.

Alternatif tanpa menggunakan filter adalah memasukkan beberapa data pada atribut yang sama sehingga menjadi composite key. Atribut yang umum digunakan untuk composite key adalah sort key. Pada bagian berikutnya akan dicontohkan data model yang menggunakan composite key.

Contoh Access-Pattern Blog

Setelah menentukan access-pattern maka gunakan tiga generic atribut untuk menyusun single-table yang dibuat yaitu pk untuk partition key, sk untuk sort key dan data untuk GSI sort key.

Simulasikan access-pattern dengan data kecil untuk mempermudah proses desain tabel. Sebelum melakukan data modeling tulis semua access-pattern yang mungkin digunakan oleh aplikasi dalam hal ini blog.

Ini adalah contoh access-pattern yang saya gunakan.

  • Get article by ID
  • Get articles by date, month or year
  • Get most viewed articles monthly
    • Get most commented article
    • Get articles by status
    • Get articles by tag
    • Get articles by category
    • Get articles by author
  • Get comment by ID
  • Get comments by article ID
  • Get category by ID
  • Get categories with number of articles
  • Get tag by ID
  • Get tags with number of articles

Untuk mendukung akses pattern diatas, saya juga memerlukan sebuah GSI dengan atribut sk sebagai partition key dan atribut data sebagai sort key. Dari access-pattern yang telah ditentukan, data modelnya dapat dicontohkan seperti dibawah.

Primary KeyAttributes
pkskdatatitlenameemail
author#ID-1authoractive#0000002 John Doejohndoe@example.com
comment#ID-1comment#article#ID-1published#2022-03-12 John Smithjohnsmith@example.com
category#ID-1categoryactive#0000001 Java 
category#ID-2categoryactive#0000001 PHP 
category#ID-3categoryactive#0000002 Programming 
tag#featuredtagactive#0000001 featured 
tag#securitytagactive#0000001 security 
article#ID-1articlesource#published#2022-03-10Java Programming  
article#ID-2articlesource#published#2022-03-10PHP Programming  
article#ID-1article#author#ID-1copy#published#2022-03-10Java Programming  
article#ID -2article#author#ID-1copy#published#2022-03-10PHP Programming  
article#ID -1article#category#ID-3copy#published#2022-03-10Java Programming  
article#ID -1article#category#ID-1copy#published#2022-03-10Java Programming  
article#ID -2article#category#ID-3copy#published#2022-03-10PHP Programming  
article#ID -2article#category#ID-2copy#published#2022-03-10PHP Programming  
article#ID -1article#tag#featuredcopy#published#2022-03-10Java Programming  
article#ID -1article#tag#securitycopy#published#2022-03-10Java Programming  
article#ID -1no_views#2022-030000001200Java Programming  
article#ID -2no_views#2022-030000000820PHP Programming  
article#ID -1no_comments#2022-0300002Java Programming  
article#ID -2no_comments#2022-0300000PHP Programming  
Tabel: blogs
 Primary KeyProjected Attributes
pksk (GSI PK)data (GSI SK)title, summary, meta, categories, author
GSI: data_index

Saya menambahkan sebuah Global Secondary Index (GSI) dengan atribut sk sebagai partition key dan data sebagai sort key. Dengan menggunakan data model diatas maka saya dapat memenuhi access-pattern yang telah ditentukan sebelumnya.

Access PatternTabel/GSIDynamoDB Query
Get article by IDblogspk="article#ID-1"
Get articles by date, month or yeardata_indexsk="article" and data.startWith("source#published#2022-03")
Get most viewed articles monthlydata_indexsk="no_views#2022-03"
Get most commented articledata_indexsk="no_comments#2022-03"
Get articles by statusdata_indexsk="article# and data.startWith("copy#published#")
Get articles by categorydata_indexsk="article#category#ID-1 " and data.startWith("copy#published#")
Get articles by authordata_indexsk="article#author#ID-1 " and data.startWith("copy#published#")
Get comment by IDblogspk="comment#ID-1"
Get comments by article IDdata_indexsk="comment#article#ID-1" and data.startWith("published#")
Get category by IDblogspk="category#ID-1"
Get categories with number of articlesdata_indexsk="category" and data.startWith("published#")
Get tag by IDblogspk="tag#featured"
Get tags with number of articlesdata_indexsk="tag" and data.startWith(active#")
Contoh query untuk access-pattern blog yang telah ditentukan

Query pada tabel blogs sebagian besar digunakan untuk mendapatkan sebuah single item dari tabel. Sedangkan GSI data_index digunakan untuk sebagian besar query yang mengembalikan lebih dari satu item. Karena pada DynamoDB tidak mengenal join maka strategi yang saya lakukan adalah melakukan duplikasi data pada relasi many-to-many seperti article-category dan article-tag.

Saya juga menggunakan composite key dimana saya menumpuk beberapa nilai pada sebuah sort key. Sebagai contoh source#published#2022-03-10 dengan composite key seperti itu saya dapat melakukan filter status dan tanggal menggunakan operator start-with tanpa perlu menggunakan filter.

DynamoDB akan dengan efisien akan menemukan lokasi partisi dari data dengan cepat dan menghemat biaya karena semakin sedikit data yang dibaca.

Data model yang saya gunakan lebih dioptimalkan untuk access-pattern baca. Sehingga pada beberapa kondisi data harus diduplikasi untuk setiap perubahan yang terjadi. Ini adalah salah satu kekurangan yang harus diterima.

Untuk menjaga konsistensi data karena adanya duplikasi maka saya menggunakan DyanmoDB Stream. Dimana setiap ada item baru, perubahan item atau item dihapus maka informasi perubahan tersebut akan dikirim ke fungsi Lambda yang telah disiapkan. Fungsi ini akan melakukan aksi yang diperlukan diperlukan seperti melakukan duplikasi data atau menghapus data yang tidak diperlukan.

Berikut adalah pseudocode untuk fungsi Lambda ketika ada article baru dibuat:

if eventName == 'INSERT' and record.type == 'article'

  createNewArticleItem -> pk = record.pk, sk = 'article#author#{record.pk}, ...

  foreach record.categories as item
    createNewArticleItem -> pk = record.pk, sk = 'article#category#{item.pk}, ...

  foreach record.tags as item
    createNewArticleItem -> pk = record.pk, sk = 'article#tag#{item.pk}, ...

endif

Kesimpulan

Untuk menyimpan data secara efisien pada DynamoDB maka yang diperlukan:

  • Kesampingkan pengetahuan anda tentang RDMBS seperti normalisasi
  • Tentukan access-pattern dari aplikasi yang dibuat sebelum mendesain data model
  • Melakukan duplikasi bukanlah pelanggaran di NoSQL. Untuk menjaga konsistensi dapat dilakukan pada sisi aplikasi dengan memanfaatkan DynamoDB Stream.
  • Hindari penggunaan operasi Scan pada tabel besar karena akan membaca setiap item ditabel sehingga tidak scalable dan akan menggunan RCU lebih banyak.
  • Hindari penggunaan filter expression pada operasi query pada dataset yang besar. Sebisa mungkin gunakan composite key daripada filter.
  • Jangan membuat GSI jika memang tidak diperlukan oleh access-pattern.
  • Gunakan composite key yaitu menumpuk beberapa nilai sekaligus pada sebuah atribut yang akan digunakan sebagai sort key. Sehingga tidak perlu menggunakan operasi filter.

Berikut adalah beberapa referensi yang bagus untuk mempelajari single-table design DynamoDB: