Tabel lebar di Lindorm menyimpan data dalam format berorientasi baris, yang efisien untuk pencarian titik tetapi lambat untuk agregasi skala besar pada jutaan baris. Indeks penyimpanan kolom menyimpan data yang sama dalam format kolom di dalam mesin komputasi, memungkinkan pemindaian paralel, agregasi, dan join yang cepat pada tabel lebar berskala besar. Kasus penggunaan khas mencakup agregasi telemetri perangkat untuk beban kerja Internet of Vehicles (IoV) dan Internet of Things (IoT), analitik e-commerce, serta statistik pesanan logistik.
Prasyarat
Sebelum memulai, pastikan Anda telah:
Mengaktifkan mesin komputasi. Lihat Aktifkan layanan.
Mengaktifkan LindormDFS versi 4.0.0 atau lebih baru.
Mengaktifkan mesin tabel lebar versi 2.5.0 atau lebih baru.
Catatan penggunaan
Indeks penyimpanan kolom tidak dapat dibuat secara sinkron. Proses pembuatan awal memakan waktu sekitar 15 menit; volume data besar atau banyaknya tugas latar belakang akan memperpanjang durasi ini.
Pembuatan indeks menghasilkan I/O baca pada tabel lebar. Jika pemisahan data panas dan dingin diaktifkan, pembatasan kecepatan pada penyimpanan dingin dapat memperlambat pembuatan indeks dan menyebabkan tekanan balik tulis.
Setiap tabel lebar hanya mendukung satu indeks penyimpanan kolom. Jika upaya pembuatan sebelumnya gagal, hapus indeks yang gagal tersebut sebelum membuat yang baru.
Baris yang kedaluwarsa berdasarkan TTL di tabel lebar tidak secara otomatis dihapus dari indeks penyimpanan kolom.
Aktifkan fitur columnstore index
Masuk ke Konsol Lindorm.
Di pojok kiri atas, pilih wilayah tempat instans ditempatkan.
Pada halaman Instances, klik ID instans, atau klik View Instance Details di kolom Actions.
Di panel navigasi kiri, pilih Wide Table Engine.
Klik tab Columnar Index, lalu klik Activate Now.
Pada dialog yang muncul, klik OK.
Panduan cepat
Panduan ini membuat indeks penyimpanan kolom pada tabel lebar bernama my_tbl:
+------------+-------------+---------+----------------+
| TABLE_NAME | COLUMN_NAME | TYPE | IS_PRIMARY_KEY |
+------------+-------------+---------+----------------+
| my_tbl | pk0 | INT | true |
| my_tbl | pk1 | VARCHAR | true |
| my_tbl | pt_d | VARCHAR | true |
| my_tbl | col0 | INT | false |
| my_tbl | col1 | VARCHAR | false |
| my_tbl | json_col0 | JSON | false |
+------------+-------------+---------+----------------+pk0 mengidentifikasi setiap baris dan memiliki kardinalitas tinggi. pt_d adalah tanggal pembuatan data; analisis biasanya dilakukan per hari.
Langkah 1: Buat indeks.
Pilih pernyataan yang sesuai dengan seberapa sering skema tabel Anda berubah:
Skema stabil — gunakan jika kolom
my_tbljarang berubah:CREATE INDEX my_tbl_idx USING COLUMNAR ON my_tbl(*) PARTITION BY ENUMERABLE (pt_d, bucket(128, pk0)) WITH ( `lindorm_columnar.user.index.database` = 'my_index_db', `lindorm_columnar.user.index.table` = 'my_index_tbl', `lindorm_columnar.user.syncer.lci.dynamicJsonColumns` = 'json_col0' );Skema indeks ditetapkan saat pembuatan berdasarkan skema tabel saat ini dan struktur
json_col0.Skema berkembang — tambahkan
dynamicSchema = 'true'jika kolom mungkin ditambahkan nanti:CREATE INDEX my_tbl_idx USING COLUMNAR ON my_tbl(*) PARTITION BY ENUMERABLE (pt_d, bucket(128, pk0)) WITH ( `lindorm_columnar.user.index.database` = 'my_index_db', `lindorm_columnar.user.index.table` = 'my_index_tbl', `lindorm_columnar.user.syncer.lci.dynamicJsonColumns` = 'json_col0', `lindorm_columnar.user.syncer.lci.dynamicSchema` = 'true' );Indeks secara otomatis melacak perubahan skema selanjutnya dan konten JSON baru.
Langkah 2: Periksa status indeks.
SHOW INDEX FROM my_tbl;Untuk penjelasan semua bidang output, lihat SHOW INDEX.
Langkah 3: Kueri menggunakan indeks.
Tambahkan petunjuk _use_ldps_ dan _columnar_index_ untuk mengarahkan kueri melalui mesin komputasi:
SELECT /*+ _use_ldps_(cg0), _columnar_index_ */ COUNT(*), SUM(col0)
FROM my_index_db.my_index_tbl
WHERE pt_d = '2024-01-15'
GROUP BY pk1;Buat columnstore index
Parameter
| Parameter | Deskripsi |
|---|---|
index_name | Nama indeks penyimpanan kolom. Menerima huruf besar, huruf kecil, angka, dan garis bawah. |
table_name | Nama tabel lebar. |
column_name [, ...] | Kolom yang disertakan. Harus mencakup semua kolom kunci primer. Untuk menyertakan semua kolom, gunakan (*). Jenis yang didukung: TINYINT, SMALLINT, INTEGER, BIGINT, LONG, FLOAT, DOUBLE, VARCHAR, BINARY, VARBINARY, BOOLEAN, DECIMAL, JSON, DATE, TIMESTAMP. |
PARTITION BY ENUMERABLE(...) | Mempartisi data indeks menggunakan algoritma enumerasi untuk pemfilteran efisien. Menerima nol atau lebih ekspresi partisi reguler dan tepat satu ekspresi partisi bucket. Semua kolom dalam kedua jenis ekspresi harus merupakan kolom kunci primer. |
bucket_num | Jumlah partisi bucket. Harus kurang dari 1024. Indeks bucket dihitung sebagai hash(column_name) % bucket_num. Gunakan kolom dengan kardinalitas tinggi untuk menghindari kesenjangan data. |
lindorm_columnar.user.index.database | Database yang menyimpan tabel indeks penyimpanan kolom. |
lindorm_columnar.user.index.table | Nama tabel indeks penyimpanan kolom. |
Pedoman ukuran partisi
Atur setiap partisi antara 50 MB hingga 512 MB:
Terlalu kecil (< 50 MB): menghasilkan terlalu banyak file kecil, menurunkan performa penyimpanan.
Terlalu besar (> 512 MB): mengurangi throughput baca/tulis dan menyebabkan kesenjangan data.
Hindari kolom kunci primer dengan kardinalitas tinggi sebagai kunci partisi reguler. Setiap nilai unik membuat partisi terpisah, menghasilkan banyak file kecil. Gunakan bidang waktu atau kategori (seperti tanggal) sebagai kunci partisi reguler, dan simpan kolom kardinalitas tinggi untuk ekspresi bucket.
Contohnya, jika volume data harian adalah 50 GB dan kunci primernya adalah (id, dt):
PARTITION BY ENUMERABLE (dt, bucket(200, id))Contoh
CREATE INDEX my_tbl_idx USING COLUMNAR
ON my_tbl(pk0, pk1, pk2, col0, col1)
PARTITION BY ENUMERABLE (pk1, pk2, bucket(128, pk0))
WITH (
`lindorm_columnar.user.index.database` = 'my_index_db',
`lindorm_columnar.user.index.table` = 'my_index_tbl'
);Lihat columnstore index
Setelah dibuat, indeks terus-menerus menyinkronkan data dari tabel lebar—pertama data historis, lalu data inkremental. Selama sinkronisasi inkremental, indeks biasanya tertinggal kurang dari 1 jam dari tabel utama.
SHOW INDEX FROM my_tbl;Untuk penjelasan semua bidang output, lihat SHOW INDEX.
Kueri dengan columnstore index
Tambahkan petunjuk _use_ldps_ dan _columnar_index_ untuk mengarahkan kueri SELECT Anda melalui mesin komputasi, yang menggunakan indeks penyimpanan kolom untuk mempercepat kueri.
Tanpa petunjuk ini, kueri dijalankan langsung pada tabel lebar dan tidak menggunakan indeks penyimpanan kolom.
Agregasi
SELECT /*+ _use_ldps_(cg0), _columnar_index_ */ COUNT(*), SUM(col0), MIN(col0), MAX(col0)
FROM my_index_db.my_index_tbl
WHERE col0 > 100 AND col0 < 200 OR col0 > 500
GROUP BY pk1;Pengurutan
SELECT /*+ _use_ldps_(cg0), _columnar_index_ */ pk0 + col0, pk1
FROM my_index_db.my_index_tbl
WHERE col0 > 100 AND col0 < 200 OR col0 > 500
ORDER BY pk1
LIMIT 100;Join antar beberapa tabel lebar
Jika Anda memiliki indeks penyimpanan kolom pada beberapa tabel lebar, lakukan join melalui tabel indeksnya:
SELECT /*+ _use_ldps_(cg0), _columnar_index_ */ *
FROM my_index_db.my_index_tbl0 AS t0
JOIN my_index_db.my_index_tbl1 AS t1
ON t0.pk0 = t1.pk0
AND t0.pk1 = t1.pk1
LIMIT 100;Hapus columnstore index
Gunakan DROP INDEX untuk menghapus indeks penyimpanan kolom. Untuk sintaks lengkap dan contoh, lihat DROP INDEX.
Penggunaan lanjutan
Ekspresi partisi kompleks
Ketika kunci primer tidak dapat digunakan langsung sebagai kunci partisi—misalnya, ketika berupa stempel waktu UNIX—gunakan fungsi transformasi dalam ekspresi partisi.
Tabel contoh my_ts_tbl:
+------------+-------------+---------+----------------+
| TABLE_NAME | COLUMN_NAME | TYPE | IS_PRIMARY_KEY |
+------------+-------------+---------+----------------+
| my_ts_tbl | id | INT | true |
| my_ts_tbl | ts | LONG | true |
| my_ts_tbl | col0 | VARCHAR | false |
| my_ts_tbl | col1 | INT | false |
+------------+-------------+---------+----------------+Buat partisi per hari menggunakan kolom ts (stempel waktu UNIX):
CREATE INDEX my_ts_idx USING COLUMNAR ON my_ts_tbl(*)
PARTITION BY ENUMERABLE (
ifnull(substring(from_unixtime(ts), 0, 10), 'unknown') AS dt,
bucket(128, id)
)
WITH (
`lindorm_columnar.user.index.database` = 'my_ts_index_db',
`lindorm_columnar.user.index.table` = 'my_ts_index_tbl'
);Kueri per hari menggunakan kolom partisi terkomputasi dt:
SELECT /*+ _use_ldps_ */ COUNT(1)
FROM lindorm_columnar.my_ts_index_db.my_ts_index_tbl
WHERE dt = '2020-06-06';Indeks hanya data inkremental
Untuk melewati data historis dan hanya mengindeks tulisan baru, atur lindorm_columnar.user.syncer.skip.fullsync = 'true':
CREATE INDEX my_tbl_idx USING COLUMNAR ON my_tbl(*)
PARTITION BY ENUMERABLE (pk1, pk2, bucket(128, pk0))
WITH (
`lindorm_columnar.user.index.database` = 'my_index_db',
`lindorm_columnar.user.index.table` = 'my_index_tbl',
`lindorm_columnar.user.syncer.skip.fullsync` = 'true'
);Ekspansi kolom JSON
Indeks penyimpanan kolom dapat meratakan kolom JSON menjadi kolom bertipe selama sinkronisasi. Tersedia dua metode:
| Metode | Cara kerja | Paling cocok untuk |
|---|---|---|
| Ekspansi statis | Menentukan jalur JSON dan tipenya saat pembuatan indeks. | Struktur JSON yang diketahui dan stabil. |
| Ekspansi dinamis (Pratinjau publik) | Menginferensi jalur dan tipe dari data aktual. | Struktur JSON yang berkembang atau tidak dapat diprediksi. |
Jangan konfigurasikan kolom JSON yang sama untuk ekspansi statis dan dinamis sekaligus.
Tabel dan data contoh:
+-------------+-------------+--------+----------------+
| TABLE_NAME | COLUMN_NAME | TYPE | IS_PRIMARY_KEY |
+-------------+-------------+--------+----------------+
| my_json_tbl | id | BIGINT | true |
| my_json_tbl | col1 | INT | false |
| my_json_tbl | json_col | JSON | false |
+-------------+-------------+--------+----------------+UPSERT INTO my_json_tbl (id, col1, json_col)
VALUES (2, 2, '{"a": {"b": {"c": "hello,world", "d": 123}, "e": false}, "f": 3.14}');Ekspansi statis
Tentukan aturan pemetaan yang menentukan setiap jalur JSON dan tipe targetnya:
CREATE INDEX columnar_idx USING COLUMNAR ON my_json_tbl(*)
PARTITION BY ENUMERABLE (ifnull(id%16, 0) AS dt, bucket(16, id))
WITH (
`lindorm_columnar.user.syncer.lci.jsonMapping.json_col` = 'a.b.c VARCHAR, a.e BOOLEAN, f DOUBLE',
`lindorm_columnar.user.index.database` = 'my_index_db',
`lindorm_columnar.user.index.table` = 'my_index_tbl'
);Format aturan pemetaan adalah <json_path> <type> [, ...]:
json_path: jalur ke nilai JSON yang dipisahkan titik, misalnyaa.b.c.type: salah satu dari BOOLEAN, BYTE, SHORT, INTEGER, LONG, FLOAT, DOUBLE, VARCHAR.
Untuk memetakan beberapa kolom JSON, tambahkan satu parameter lindorm_columnar.user.syncer.lci.jsonMapping.<column> per kolom JSON.
Ekspansi dinamis (Pratinjau publik)
Tentukan kolom JSON mana yang akan diekspansi secara dinamis. Indeks menginferensi tipe dari data aktual:
CREATE INDEX columnar_idx USING COLUMNAR ON my_json_tbl(*)
PARTITION BY ENUMERABLE (ifnull(id%16, 0) AS dt, bucket(16, id))
WITH (
`lindorm_columnar.user.syncer.lci.dynamicJsonColumns` = 'json_col',
`lindorm_columnar.user.index.database` = 'my_index_db',
`lindorm_columnar.user.index.table` = 'my_index_tbl'
);Tipe yang diinferensi terbatas pada BOOLEAN, LONG, DOUBLE, dan STRING. Ketika suatu bidang berisi beberapa tipe nilai, indeks menggunakan STRING.
Ekspansi dinamis tidak berlaku untuk data historis; hanya memproses tulisan inkremental.
Pemberian nama kolom hasil ekspansi
Ketika kolom JSON diekspansi (dengan metode apa pun), kolom JSON asli tidak disimpan di tabel berorientasi kolom. Sebagai gantinya, indeks membuat kolom untuk setiap jalur yang diekspansi, dengan awalan nama kolom JSON.
Menggunakan 'a.b.c VARCHAR, a.e BOOLEAN' sebagai pemetaan statis untuk json_col menghasilkan:
| Nama kolom | Tipe | Nilai yang disimpan |
|---|---|---|
json_col.a.b.c | STRING | Nilai a.b.c dari json_col |
json_col.a.e | BOOLEAN | Nilai a.e dari json_col |
Sinkronkan bidang JSON asli
Untuk juga menyimpan kolom JSON asli, tambahkan syncOriginalJsonContent = 'true':
WITH (
`lindorm_columnar.user.syncer.lci.jsonMapping.json_col` = 'a.b.c VARCHAR, a.e BOOLEAN',
`lindorm_columnar.user.syncer.lci.json.syncOriginalJsonContent` = 'true',
...
)Ini menambahkan kolom json_col bertipe STRING bersama kolom hasil ekspansi.
Omit awalan nama bidang JSON dari nama bidang di tabel berorientasi kolom
Untuk menghilangkan awalan nama kolom JSON dari nama kolom hasil ekspansi, tambahkan ignoreJsonMappingPrefix = 'true':
WITH (
`lindorm_columnar.user.syncer.lci.jsonMapping.json_col` = 'a.b.c VARCHAR, a.e BOOLEAN',
`lindorm_columnar.user.syncer.lci.json.ignoreJsonMappingPrefix` = 'true',
...
)Tabel berorientasi kolom kemudian menggunakan a.b.c dan a.e alih-alih json_col.a.b.c dan json_col.a.e.
Jika kolom JSON berbeda (misalnya, json_col1 dan json_col2) berisi pemetaan yang sama untuk mengekspansi dan menyimpan jalur yang sama (misalnya, a.b.c), pembuatan indeks penyimpanan kolom akan gagal.
Lacak evolusi skema secara dinamis (Pratinjau publik)
Atur lindorm_columnar.user.syncer.lci.dynamicSchema = 'true' agar skema tabel berorientasi kolom tetap selaras dengan tabel utama. Saat kolom ditambahkan ke tabel lebar, kolom tersebut secara otomatis ditambahkan ke indeks:
CREATE INDEX my_tbl_idx USING COLUMNAR
ON my_tbl(*)
PARTITION BY ENUMERABLE (pt_d, bucket(128, pk0))
WITH (
`lindorm_columnar.user.index.database` = 'my_index_db',
`lindorm_columnar.user.index.table` = 'my_index_tbl',
`lindorm_columnar.user.syncer.lci.dynamicSchema` = 'true'
);Jika tabel lebar menggunakan kolom dinamis atau kolom wildcard, tipe kolom tersebut juga disinkronkan ke indeks saat dynamicSchema = 'true'.
Tambahkan kolom tanpa pengindeksan ulang (Pratinjau publik)
Gunakan ALTER INDEX untuk menambahkan kolom ke indeks penyimpanan kolom yang sudah ada tanpa membangun ulang sepenuhnya.
Tabel contoh:
+-------------+-------------+---------+----------------+
| TABLE_NAME | COLUMN_NAME | TYPE | IS_PRIMARY_KEY |
+-------------+-------------+---------+----------------+
| my_json_tbl | id | BIGINT | true |
| my_json_tbl | col1 | INT | false |
| my_json_tbl | col2 | VARCHAR | false |
| my_json_tbl | json_col1 | JSON | false |
| my_json_tbl | json_col2 | JSON | false |
+-------------+-------------+---------+----------------+Pembuatan indeks awal (mencakup id, col1, dan json_col1):
CREATE INDEX columnar_idx USING COLUMNAR ON my_json_tbl(id, col1, json_col1)
PARTITION BY ENUMERABLE (ifnull(id%16, 0) AS dt, bucket(16, id))
WITH (
`lindorm_columnar.user.syncer.lci.jsonMapping.json_col1` = 'a.b.c VARCHAR, a.e BOOLEAN',
`lindorm_columnar.user.index.database` = 'my_index_db',
`lindorm_columnar.user.index.table` = 'my_index_tbl'
);Tambahkan kolom reguler:
ALTER INDEX IF EXISTS columnar_idx ON my_json_tbl ADD COLUMNS (col2);Tambahkan pemetaan JSON statis untuk json_col2:
ALTER INDEX IF EXISTS columnar_idx ON my_json_tbl
ADD COLUMNS (
json_extract_long(json_col2, '$.key1'),
json_extract_boolean(json_col2, '$.key2'),
json_extract_double(json_col2, '$.key3.key4')
);Fungsi ekstraksi JSON yang didukung: json_extract_boolean, json_extract_long, json_extract_double, json_extract_string.
Penagihan
Pembuatan indeks penyimpanan kolom mengenakan dua jenis biaya:
Penyimpanan: penyimpanan yang dikonsumsi oleh data indeks penyimpanan kolom.
Compute Unit (CU): CU yang digunakan untuk menyinkronkan data antara tabel lebar dan indeks.
FAQ
Apakah ada biaya tambahan untuk membuat columnstore index?
Ya. Biaya mencakup biaya penyimpanan untuk data indeks dan biaya CU untuk sinkronisasi antara tabel lebar dan indeks.
Apakah ekspresi filter partisi dapat mencakup kolom non-kunci primer?
Tidak. Semua kolom dalam ekspresi filter partisi—baik ekspresi partisi reguler maupun ekspresi bucket—harus merupakan kolom kunci primer dari tabel lebar.
Apakah ekspresi partisi bucket dapat mencakup logika perhitungan?
Tidak. Ekspresi partisi bucket hanya menerima bucket_num dan nama kolom bucket. Gunakan fungsi transformasi (seperti from_unixtime) dalam ekspresi partisi reguler sebagai gantinya.
Apa yang terjadi jika jumlah partisi terlalu banyak atau terlalu sedikit?
Partisi terlalu banyak menyebabkan pembengkakan data, meningkatkan overhead penyimpanan, dan memperlambat kueri. Pertahankan setiap partisi di atas 50 MB dan bucket_num di bawah 1024. Partisi terlalu sedikit membatasi throughput baca/tulis dan menyebabkan kesenjangan data. Pertahankan setiap partisi di bawah 512 MB.
Apakah saya dapat mengkueri columnstore index secara langsung melalui mesin komputasi Lindorm?
Ya. Tentukan nama tabel indeks dalam kueri Anda dan gunakan petunjuk _use_ldps_. Untuk detail penyiapan, lihat Akses data columnstore.
Hubungi dukungan teknis Lindorm (ID DingTalk: s0s3eg3) sebelum memodifikasi tabel indeks penyimpanan kolom secara langsung.
Apakah saya dapat membuat beberapa columnstore index untuk tabel lebar yang sama?
Tidak. Setiap tabel lebar hanya mendukung satu indeks penyimpanan kolom.
Apakah baris yang kedaluwarsa berdasarkan TTL secara otomatis dihapus dari columnstore index?
Tidak. Kedaluwarsa TTL di tabel lebar tidak diteruskan ke indeks penyimpanan kolom.
Pembuatan indeks saya gagal. Mengapa upaya pembuatan baru juga gagal?
Setiap tabel lebar hanya mendukung satu indeks penyimpanan kolom, bahkan ketika indeks berada dalam status gagal. Hapus indeks yang gagal menggunakan DROP INDEX, lalu buat yang baru.