Rowkey adalah pengidentifikasi unik untuk setiap baris dalam tabel HBase. Rowkey menentukan cara data disimpan, dipartisi, dan diakses. Rancang rowkey dengan cermat sebelum menulis data dalam skala besar.
Topik ini mencakup lima pertimbangan desain, lengkap dengan kompromi dan contoh untuk data log serta data transaksi.
Cara kerja rowkey
Metode kueri
HBase mendukung dua metode kueri, masing-masing memiliki batasan berbeda terhadap desain rowkey:
| Method | Description | Constraint |
|---|---|---|
| GET | Mencari satu baris berdasarkan rowkey lengkapnya | Semua bidang yang membentuk rowkey harus diketahui |
| Scan | Membaca rentang baris antara kunci awal dan kunci akhir | Hanya rentang berbasis awalan (prefix) yang didukung |
Batasan awalan untuk scan: Scan mencocokkan baris yang dimulai dengan awalan tertentu, tetapi tidak dapat melakukan kueri berdasarkan akhiran (suffix) atau mencocokkan nilai di tengah rowkey. Sebagai contoh, jika rowkey berupa kata kamus, scan dapat menemukan semua kata yang diawali dengan pre, tetapi tidak dapat menemukan kata yang diakhiri dengan ing.
Untuk kueri yang tidak dapat dinyatakan sebagai scan berbasis awalan, gunakan salah satu pendekatan berikut:
Buat tabel indeks dengan struktur kunci terbalik
Terapkan filter sisi server untuk membuang baris yang tidak diinginkan
Gunakan indeks sekunder
Keunikan dan versi rowkey
Baris dengan rowkey yang sama dianggap sebagai satu catatan dengan beberapa versi. Secara default, GET mengembalikan versi terbaru. Rowkey harus unik kecuali Anda sengaja menggunakan Multi-Version Concurrency Control (MVCC).
Perlakukan rowkey seperti primary key dalam database. Rowkey dapat berupa satu bidang atau gabungan dari beberapa bidang:
[user_id]— satu catatan per pengguna[user_id][order_id]— beberapa catatan per pengguna
Pertimbangan desain
Distribusi data: hindari hot spot
HBase mendistribusikan baris ke Region server berdasarkan rentang rowkey (urutan leksikografis). Jika banyak operasi tulis memiliki awalan umum—misalnya, kunci yang diawali timestamp seperti 2024-01-01T00:00:01—semua tulisan akan menuju Region server yang sama. Hal ini menciptakan hot spot yang menurunkan throughput tulis dan membuat server lain menganggur.
Gunakan salah satu teknik berikut untuk menyebarkan tulisan ke berbagai Region server:
Salting dengan awalan hash
Tambahkan beberapa karakter pertama dari Hash MD5 ke awal rowkey. Karena hash bersifat deterministik, input yang sama selalu menghasilkan awalan yang sama, sehingga pembacaan tetap efisien.
[md5(user_id).subStr(0, 4)][user_id][order_id]Kompromi: baris untuk pengguna yang sama tersebar di Region berbeda. Memindai rentang untuk satu pengguna memerlukan beberapa GET terarah atau scan dengan filter.
Membalik kunci
Balikkan bidang awalan dengan kardinalitas tinggi. Misalnya, membalik user ID yang meningkat seiring waktu akan mengacak byte awalnya.
[reverse(user_id)][order_id]Kompromi: urutan alami hilang, sehingga scan rentang pada bidang yang dibalik tidak lagi bermakna.
Pengelompokan (bucketing) dengan operasi modulo
Tetapkan setiap baris ke bucket menggunakan operasi modulo, lalu tambahkan nomor bucket di awal. Pendekatan ini efektif untuk data deret waktu dengan timestamp yang meningkat secara monoton.
long bucket = timestamp % numBuckets;
[bucket][timestamp][hostname][log_event]Kompromi: untuk mengambil semua data dalam rentang waktu tertentu, Anda harus memindai semua rentang numBuckets dan menggabungkan hasilnya.
Menambahkan akhiran acak
Tambahkan bilangan acak untuk mendistribusikan tulisan ke beberapa baris.
[user_id][order_id][random(100)]Kompromi: membaca catatan tertentu memerlukan pengetahuan tentang akhiran acak tersebut. Pencarian titik (point lookup) menjadi tidak praktis tanpa indeks.
Cara memilih: Jika Anda perlu memindai baris yang didistribusikan (bukan hanya mencari catatan individual), gunakan hashing daripada akhiran acak—hashing bersifat deterministik, sehingga pembacaan dapat diarahkan secara efisien.
Panjang rowkey: buat sesingkat mungkin
Rowkey disimpan bersama setiap nilai kolom di HBase. Rowkey yang panjang akan melipatgandakan overhead penyimpanan di setiap kolom pada setiap baris. Buat rowkey sesingkat mungkin:
Ganti string dengan tipe numerik. Tipe
longmemakan 8 byte; string"2015122410"memakan 10 byte, dan string MD5 memakan 32 byte. GunakanLong(2015122410)daripada"2015122410".Gunakan kode daripada nama lengkap. Misalnya, gunakan
tbdaripada"Taobao".
Kejelasan batas bidang: cegah pencocokan parsial
Saat rowkey menggabungkan beberapa bidang tanpa pembatas, rentang scan dapat mengembalikan baris tambahan. Misalnya, jika rowkey berupa [column1][column2][column3] dan Anda memindai dari host1 hingga host2, baris host12... juga termasuk dalam rentang tersebut.
Dua pendekatan berikut mencegah hal ini:
Fixed-length padding: Isi setiap bidang hingga lebar tetap agar batasnya tidak ambigu.
[rpad(column1, 'x', 20)][column2]Delimiter: Pisahkan bidang dengan karakter pembatas.
[column1][_][column2]Padding panjang tetap lebih efisien untuk scan. Pembatas lebih mudah dibaca.
Urutan menurun: gunakan timestamp terbalik
Secara default, scan HBase mengembalikan baris dalam urutan kunci menaik. Jika Anda membutuhkan entri terbaru lebih dahulu, tersedia dua opsi:
Opsi 1: API scan terbalik (scan.setReverse(true))
Lebih mudah diimplementasikan, tetapi scan terbalik berkinerja lebih buruk daripada scan maju. Gunakan ini jika urutan menurun hanya kadang-kadang diperlukan.
Opsi 2: Timestamp terbalik dalam rowkey
Simpan Long.MAX_VALUE - timestamp daripada timestamp mentah. Ini membalik urutan pengurutan alami sehingga entri terbaru muncul lebih dahulu dalam scan maju.
timestamp = Long.MAX_VALUE - timestamp;
[hostname][log_event][timestamp]Gunakan ini jika urutan menurun merupakan pola akses utama dan kinerja scan sangat penting.
Contoh desain
Desain rowkey yang tepat bergantung pada pola akses utama Anda. Dataset yang sama dapat memerlukan desain berbeda tergantung cara pengkueriannya. Contoh berikut menunjukkan bagaimana pola akses mendorong keputusan desain.
Data log dan data deret waktu
Elemen data meliputi: hostname, log_event, timestamp.
| Access pattern | Rowkey design | Notes |
|---|---|---|
| Kueri metrik untuk host dalam rentang waktu | [hostname][log_event][timestamp] | Efisien untuk scan rentang per host. Dapat menimbulkan hot spot jika satu host mendominasi tulisan |
| Kueri catatan terbaru untuk host | [hostname][log_event][Long.MAX_VALUE - timestamp] | Timestamp terbalik menempatkan entri terbaru di awal dalam scan maju |
| Distribusikan tulisan secara merata sepanjang waktu (volume data besar atau tidak ada host dominan) | [bucket][timestamp][hostname][log_event] dengan bucket = timestamp % numBuckets | Memerlukan pemindaian semua rentang bucket untuk menggabungkan hasil dalam rentang waktu tertentu |
Cara memilih: Mulailah dengan [hostname][log_event][timestamp] jika kueri rentang per host merupakan kasus penggunaan utama. Beralihlah ke pola bucket jika muncul hot spot tulis atau jika kueri rentang waktu mencakup banyak host.
Data transaksi
Sebuah transaksi melibatkan tiga peran: pembeli, penjual, dan nomor pesanan. Pola akses berbeda memerlukan desain rowkey berbeda—dan sering kali memerlukan beberapa tabel.
| Access pattern | Table | Rowkey design |
|---|---|---|
| Kueri pesanan penjual dalam rentang waktu | Seller table | [seller_id][timestamp][order_number] |
| Kueri pesanan pembeli dalam rentang waktu | Buyer table | [buyer_id][timestamp][order_number] |
| Cari pesanan berdasarkan nomor pesanan | Index table | [order_number] |
Rancang ketiga tabel tersebut untuk mencakup ketiga pola akses. Gunakan tabel indeks untuk mencari order_number, lalu kueri tabel pembeli atau penjual dengan nilai tersebut.
Langkah selanjutnya
Ikhtisar model data HBase
Secondary indexes di HBase
Tuning kinerja untuk tabel HBase