Tablestore secara otomatis membagi data ke dalam beberapa partisi berdasarkan rentang kunci partisi. Pemilihan kunci partisi yang buruk dapat menyebabkan hotspot, kesenjangan data, atau bottleneck penskalaan. Rancang tabel berkinerja tinggi dan dapat diskalakan dengan menerapkan praktik terbaik dalam desain kunci primer, desain kolom atribut, dan pemisahan tabel.
Desain kunci primer
Tablestore secara otomatis membagi data ke dalam beberapa partisi berdasarkan rentang kunci partisi (kolom kunci primer pertama) dan mendistribusikannya ke berbagai node layanan. Rancang kunci primer agar distribusi data dan beban akses merata serta menghindari hotspot.
Prinsip desain kunci partisi
Kunci partisi adalah kolom kunci primer pertama dalam tabel data. Tablestore secara otomatis membagi data ke dalam beberapa partisi berdasarkan rentang kunci partisi dan menjadwalkan setiap partisi ke node layanan yang berbeda. Ikuti prinsip-prinsip berikut saat merancang kunci partisi:
Pastikan volume data di bawah satu nilai kunci partisi tidak melebihi 10 GB.
Pastikan data di bawah nilai kunci partisi yang berbeda bersifat logis independen.
Hindari memusatkan beban akses pada rentang kecil nilai kunci partisi yang berurutan.
Jika beban kerja rendah (TPS/QPS di bawah 1.000, volume data dalam 10 GB, dan tidak diperkirakan tumbuh signifikan), dampak hotspot minimal dan satu partisi saja dapat menangani beban tersebut. Namun, jangan merancang arsitektur Anda bergantung pada kapasitas partisi tunggal.
Distribusi data
Dalam sistem data terdistribusi, distribusi data yang tidak merata menyebabkan masalah berikut:
Throughput baca/tulis dibatasi oleh satu partisi, menciptakan bottleneck yang jelas.
Distribusi hotspot yang tidak merata menyebabkan efek ekor panjang (long-tail effect) yang memperlambat pemrosesan secara keseluruhan.
Partisi hotspot menjadi bottleneck bagi seluruh pipeline bisnis dan memengaruhi sistem hulu dan hilir.
Ketika data didistribusikan secara merata ke seluruh partisi, beban baca/tulis tersebar ke semua partisi. Setiap permintaan hanya mencakup data lokal, dan Anda dapat melakukan penskalaan horizontal dengan menambahkan lebih banyak sumber daya.
Masalah hotspot umum dan solusinya
Pertimbangkan skenario pemantauan di mana tabel data menyimpan nilai metrik untuk setiap mesin pada setiap titik waktu, dengan desain kunci primer sebagai berikut:
|
Timestamp (kunci partisi) |
MachineIp |
Masalah |
|
|
|
Setiap penulisan ditambahkan ke akhir tabel (partisi terakhir), menciptakan hotspot ekor (tail hotspot). |
|
|
|
|
|
|
|
Karena data dipartisi berdasarkan rentang kunci partisi, semua penulisan terkonsentrasi pada partisi terakhir, menciptakan hotspot ekor yang tidak dapat diselesaikan melalui pemisahan partisi.
Pendekatan 1: Ubah urutan kunci partisi
Pindahkan MachineIp ke kolom kunci primer pertama dan Timestamp ke kolom kedua. Kunci primer yang disesuaikan menjadi MachineIp = "10.10.0.1", Timestamp = 1718000001. Beban penulisan kemudian tersebar ke berbagai partisi berdasarkan mesin. Bidang lain yang secara alami terdistribusi seperti UserId, DeviceId, dan OrderId juga berfungsi sama baiknya sebagai kunci partisi.
Jika penulisan dari rentang IP tertentu terkonsentrasi pada satu partisi, Tablestore secara otomatis membagi partisi tersebut untuk mendistribusikan beban ke beberapa partisi.
Pendekatan 2: Tambahkan awalan MD5
Hitung hash MD5 dari MachineIp dan tambahkan 4 karakter heksadesimal pertama ke depan IP sebagai kunci partisi. Misalnya, 4 karakter MD5 pertama dari 10.10.0.1 adalah a1b2, sehingga kunci partisi menjadi a1b2,10.10.0.1. Pendekatan ini memecah urutan berurutan dari rentang IP. Awalan heksadesimal juga membantu sistem melakukan pre-splitting.
Pendekatan 3: Alternatif untuk kebutuhan pengurutan global
Jika bisnis Anda memerlukan kueri terurut global berdasarkan waktu tetapi ingin menghindari hotspot ekor, pertimbangkan alternatif berikut:
Pengurutan lokal: Tempatkan bidang terdistribusi di kolom pertama. Data dengan nilai bidang terdistribusi yang sama tetap terurut berdasarkan waktu. Misalnya, data di bawah MachineIp tertentu tetap terurut berdasarkan Timestamp.
Penulisan berbasis bucket: Gunakan nilai waktu modulo N sebagai kolom kunci primer pertama (0 hingga N-1) dan waktu sebagai kolom kedua. Misalnya, dengan 16 bucket, kunci partisi untuk penulisan adalah
timestamp % 16 = 3. Untuk membaca, lakukan kueri ke semua 16 bucket secara paralel dan gabungkan hasilnya. Meningkatkan jumlah bucket mendistribusikan beban lebih merata tetapi meningkatkan jumlah kueri paralel saat membaca.Search index: Tulis data ke tabel data dengan distribusi merata, dan gunakan Search index untuk melakukan kueri terurut berdasarkan waktu atau bidang lainnya. Search index secara otomatis mendistribusikan data ke beberapa shard dan menggabungkan hasil selama kueri.
Batas panjang kolom kunci primer dan volume data
Pastikan volume data di bawah satu nilai kunci partisi tidak melebihi 10 GB (tidak ada batas keras). Baris dengan nilai kunci partisi yang sama tidak dapat dibagi lebih lanjut.
Batas panjang kolom kunci primer adalah 1 KB. Kunci primer yang lebih pendek meningkatkan kinerja kueri.
Gabungkan kolom menjadi kunci partisi
Jika total volume data untuk semua baris di bawah satu nilai kunci partisi berpotensi melebihi 10 GB, gabungkan beberapa kolom kunci primer menjadi kunci partisi baru untuk mendistribusikan data. Ikuti aturan berikut saat menggabungkan:
Kunci partisi hasil penggabungan harus secara efektif membagi catatan yang sebelumnya memiliki nilai kunci partisi yang sama ke nilai kunci partisi yang berbeda.
Tambahkan nol di depan kolom kunci primer bertipe integer untuk menjaga konsistensi pengurutan. Misalnya,
OrderNumber = 123menjadi00000123setelah padding, sehingga urutan leksikografis tetap konsisten dengan urutan numerik.-
Pilih karakter pembatas dengan nilai ASCII lebih rendah daripada semua karakter dalam data untuk mempertahankan urutan leksikografis kunci partisi baru. Pilihan spesifik tergantung pada set karakter data bisnis Anda. Misalnya, jika nilai SellerID berisi angka dan huruf (seperti
a100dana1001), pembatas yang berbeda memengaruhi pengurutan sebagai berikut:Pembatas
Urutan pengurutan setelah penggabungan
Alasan
Hasil
:000054:a1001diurutkan sebelum000054:a100:Nilai ASCII
:lebih besar daripada angka. Pada karakter ke-5,a1001membandingkan1<:Pengurutan salah
,000054,a100,diurutkan sebelum000054,a1001Nilai ASCII
,lebih rendah daripada semua angka dan huruf, sehingga urutan leksikografis sesuai dengan urutan data asliPengurutan benar
Tambahkan awalan hash ke kunci partisi
Jika Anda harus menggunakan kolom yang meningkat secara berurutan sebagai kunci partisi, tambahkan awalan hash untuk mengacak distribusi data yang berdekatan dalam tabel dan mendistribusikan beban akses secara merata.
Setelah menambahkan awalan hash, data yang sebelumnya berurutan menjadi tersebar. Anda tidak lagi dapat menggunakan operasi baca rentang untuk membaca data yang logis berurutan. Gunakan search index sebagai alternatif untuk kueri rentang.
Desain kolom atribut
Kontrol lebar baris: Tablestore mendukung baris lebar (hingga ratusan ribu kolom atribut), tetapi membaca baris yang sangat lebar dalam satu permintaan dapat timeout. Batasi kolom atribut hingga sepuluh ribu per baris. Gunakan spesifikasi nama kolom atau pagination untuk membaca baris lebar.
Batas ukuran kolom atribut: Nilai satu kolom atribut tidak boleh melebihi 2 MB. Bagi data yang melebihi batas ini ke beberapa kolom, atau simpan di Object Storage Service (OSS).
Pisahkan tabel berdasarkan frekuensi akses: Jika satu baris memiliki banyak kolom atribut dengan frekuensi akses yang sangat berbeda, simpan kolom berfrekuensi tinggi dan rendah di tabel terpisah. Misalnya, dalam sistem manajemen produk, kuantitas dan harga produk diakses sering, sedangkan deskripsi produk (teks besar) jarang diakses. Pisahkan keduanya ke dua tabel.
Kompres teks besar: Kompres teks kolom atribut besar dan simpan sebagai biner untuk menghemat ruang penyimpanan dan mengurangi overhead baca/tulis.
Gunakan tipe Long untuk nilai moneter: Tablestore tidak mendukung tipe BigDecimal. Untuk bidang yang memerlukan perhitungan presisi seperti jumlah uang, gunakan tipe Long untuk menyimpan nilai dalam satuan terkecil (misalnya, simpan 5,32 USD sebagai 532 sen) guna menghindari kehilangan presisi dengan tipe Double.
Pemisahan tabel dan perencanaan kapasitas
Pemisahan tabel
Pastikan volume data satu search index tidak melebihi 20 miliar baris. Jika volume data melebihi 20 miliar baris, hubungi dukungan teknis Tablestore untuk evaluasi dan desain pemisahan tabel.
Misalnya, jika tabel log terbesar saat ini memiliki 6,1 miliar baris dan bertambah 2,1 miliar baris per tahun, tabel tersebut tidak akan melebihi 20 miliar baris dalam 3 hingga 5 tahun ke depan dan tidak memerlukan pemisahan. Rencanakan strategi pemisahan tabel lebih awal jika data yang ada sudah besar dan tumbuh pesat.
Pemisahan data panas/dingin
Pola akses data biasanya sensitif terhadap waktu: data terbaru sering diakses, sedangkan data lama secara bertahap menjadi dingin. Menyimpan data panas dan dingin dalam satu tabel menyebabkan beban akses tidak merata dan throughput baca/tulis yang dicadangkan tidak dimanfaatkan optimal.
Simpan data panas dan dingin di tabel terpisah dengan throughput baca/tulis yang dicadangkan berbeda. Bangun search index pada tabel data panas untuk kueri multi-kondisi, dan gunakan secondary index dengan pola kueri tetap pada tabel data dingin untuk mengurangi biaya. Search index mendukung TTL (time-to-live) data untuk secara otomatis memisahkan data panas dan dingin.
Impor data massal
Saat mengimpor data ke Tablestore secara massal, menulis dalam urutan kunci primer memusatkan beban tulis pada satu partisi dan memperlambat proses impor. Lakukan langkah-langkah berikut:
Pisahkan set data menjadi subset yang lebih kecil dan gunakan beberapa thread pekerja untuk memilih dan mengimpor subset secara acak secara paralel.
Hubungi dukungan teknis Tablestore untuk melakukan pre-splitting sebelum menulis data, sehingga data tersebar ke beberapa partisi sejak awal.
Gunakan TableStoreWriter untuk penulisan asinkron berkonkurensi tinggi, yang secara otomatis menangani distribusi bucket dan pengiriman batch.
Studi kasus desain: Catatan transaksi kartu mahasiswa
Contoh berikut menggunakan sistem transaksi kartu mahasiswa universitas untuk menggambarkan proses pengambilan keputusan desain tabel. Kolom kunci primer tabel data mencakup CardID (ID kartu mahasiswa), SellerID (ID merchant), DeviceID (ID terminal), dan OrderNumber. Aturan bisnisnya adalah sebagai berikut:
Setiap kartu mahasiswa dipetakan ke satu CardID, dan setiap merchant dipetakan ke satu SellerID.
Setiap terminal dipetakan ke DeviceID yang unik secara global.
OrderNumber unik dalam satu terminal dan meningkat seiring waktu, tetapi tidak unik secara global.
Catatan transaksi ditulis secara real time.
Pemilihan kunci primer dan kunci partisi
Pertama, tentukan kunci partisi. Bandingkan efek pendekatan partisi berbeda berdasarkan karakteristik masing-masing kolom kunci primer:
|
Pendekatan partisi |
Analisis |
|
CardID sebagai kunci partisi |
Setiap mahasiswa melakukan transaksi terbatas per hari. Beban tulis tersebar ke puluhan ribu kartu, memberikan distribusi data yang baik. Direkomendasikan. |
|
SellerID sebagai kunci partisi |
Jumlah merchant kecil, dan sebagian besar transaksi terkonsentrasi pada beberapa merchant, sehingga mudah menciptakan hotspot. Tidak direkomendasikan. |
|
DeviceID sebagai kunci partisi |
Perangkat terminal unik secara global, memberikan distribusi yang baik. Namun, beberapa terminal bertrafik tinggi (seperti pembaca kartu di kantin populer) masih dapat menciptakan hotspot lokal. |
|
OrderNumber sebagai kunci partisi |
OrderNumber meningkat seiring waktu dalam satu terminal. Menggunakannya langsung sebagai kunci partisi menyebabkan hotspot ekor. Diperlukan awalan hash. |
Kesimpulan desain
Berdasarkan analisis, CardID memberikan distribusi data terbaik (puluhan ribu kartu, beberapa transaksi per mahasiswa per hari) dan merupakan kunci partisi yang direkomendasikan. Urutan kunci primer adalah CardID, DeviceID, SellerID, OrderNumber. Untuk sebagian besar skenario bisnis, pemilihan kunci primer dan kunci partisi sudah cukup.
Optimasi lanjutan
Pemilihan kunci partisi dasar mungkin tidak memenuhi persyaratan kinerja. Berikut dua pendekatan optimasi lanjutan yang umum.
Skenario 1: Gabungkan beberapa kolom menjadi kunci partisi jika satu kunci partisi memiliki terlalu banyak data
Jika bisnis memerlukan kueri berdasarkan DeviceID dan Anda memilihnya sebagai kunci partisi, volume data di bawah satu DeviceID dapat melebihi 10 GB. Dalam kasus ini, gabungkan DeviceID, SellerID, dan CardID menjadi kunci partisi baru untuk membagi catatan dari terminal yang sama berdasarkan merchant dan kartu mahasiswa.
Sebelum penggabungan
|
DeviceID (kunci partisi) |
SellerID |
CardID |
OrderNumber |
Masalah |
|
|
|
|
|
Semua catatan untuk DeviceID=54 berada dalam satu partisi. Seiring waktu, volume data terakumulasi dapat melebihi 10 GB. |
|
|
|
|
|
|
|
|
|
|
|
Setelah penggabungan
|
DeviceID,SellerID,CardID (kunci partisi baru) |
OrderNumber |
Efek |
|
|
|
Catatan transaksi dari terminal yang sama dibagi ke nilai kunci partisi berbeda berdasarkan merchant dan kartu mahasiswa. |
|
|
|
|
|
|
|
Skenario 2: Tambahkan awalan hash jika nilai kunci partisi meningkat secara berurutan
Jika bisnis memerlukan kueri berdasarkan OrderNumber dan Anda memilihnya sebagai kunci partisi, peningkatan berurutan OrderNumber dalam satu terminal menyebabkan hotspot ekor. Hitung awalan hash MD5 untuk OrderNumber guna mendistribusikan data secara merata:
|
Original OrderNumber |
4 Karakter MD5 Pertama |
HashOrderNumber (kunci partisi) |
|
|
|
|
|
|
|
|
|
|
|
|
Setelah menambahkan awalan hash, Anda dapat menghitung awalan hash untuk OrderNumber apa pun dengan algoritma yang sama untuk mendapatkan HashOrderNumber-nya. Namun, awalan hash mengacak urutan asli, sehingga Anda tidak lagi dapat menggunakan operasi baca rentang untuk membaca catatan yang logis berurutan.
Pemisahan data panas/dingin
Catatan transaksi kartu mahasiswa memiliki pola sensitif waktu yang jelas: catatan terbaru sering dikueri, sedangkan catatan mahasiswa yang telah lulus jarang diakses. Rencanakan pemisahan data panas/dingin sebagai berikut:
Simpan data aktif terbaru (misalnya, satu tahun terakhir) dan data historis di tabel terpisah dengan throughput baca/tulis yang dicadangkan berbeda.
Konfigurasikan throughput yang dicadangkan lebih tinggi untuk tabel aktif dan throughput lebih rendah untuk tabel historis guna mengurangi biaya penyimpanan.
Bangun search index pada tabel aktif untuk kueri multidimensi (misalnya berdasarkan merchant atau rentang waktu). Tabel historis hanya memerlukan kueri kunci primer.
Lakukan migrasi berkala data yang melebihi periode retensi dari tabel aktif ke tabel historis.