訪問列存數(shù)據(jù)
列存即列式存儲(chǔ),是一種將數(shù)據(jù)按列進(jìn)行存儲(chǔ)和處理的數(shù)據(jù)管理方式。Lindorm計(jì)算引擎支持將半結(jié)構(gòu)化、結(jié)構(gòu)化數(shù)據(jù)以列存方式進(jìn)行存儲(chǔ),相較于行式存儲(chǔ),列式存儲(chǔ)的查詢響應(yīng)時(shí)間更短,消耗IO更少。本文介紹如何通過計(jì)算引擎訪問Lindorm列存數(shù)據(jù)。
背景信息
Lindorm列存是面向海量半結(jié)構(gòu)化、結(jié)構(gòu)化數(shù)據(jù)設(shè)計(jì)的列格式分布式存儲(chǔ)服務(wù),適用于車聯(lián)網(wǎng)、物聯(lián)網(wǎng)、訂單、日志等大規(guī)模存儲(chǔ)場(chǎng)景,核心能力包括:
計(jì)算分析
Lindorm計(jì)算引擎可以訪問列存數(shù)據(jù),完成海量數(shù)據(jù)的交互式分析和離線計(jì)算。列存提供豐富的索引能力和數(shù)據(jù)分布特征,可以有效加速計(jì)算過程中的數(shù)據(jù)定位與排布,通過SQL即可完成海量主鍵數(shù)據(jù)的增刪改查。
高吞吐
列存引擎吞吐能力支持水平擴(kuò)展,提供每分鐘TB級(jí)數(shù)據(jù)的讀寫能力。適用于車聯(lián)網(wǎng)數(shù)據(jù)快速導(dǎo)入、模型訓(xùn)練數(shù)據(jù)集存取和大規(guī)模報(bào)表分析生產(chǎn)等高吞吐數(shù)據(jù)場(chǎng)景。
低成本
通過列格式高壓縮比算法、高密度低成本介質(zhì)、冷熱分離、多壓縮編碼和數(shù)據(jù)冷歸檔等技術(shù),Lindorm列存相比自建系統(tǒng)存儲(chǔ)成本顯著降低,滿足海量數(shù)據(jù)歸檔留存等低成本存儲(chǔ)需求。
高可用
通過糾刪碼等技術(shù),Lindorm列存保證了分布式數(shù)據(jù)集的高可用性,同時(shí)保證了數(shù)據(jù)訪問無單點(diǎn)。
開源兼容
兼容Iceberg開源標(biāo)準(zhǔn)接口,與Spark、Flink等多種計(jì)算引擎互聯(lián)互通,無縫對(duì)接主流數(shù)據(jù)生態(tài)。
前提條件
已閱讀使用須知。
根據(jù)不同的作業(yè)形態(tài),請(qǐng)確保已經(jīng)完成了以下操作:
JDBC開發(fā)實(shí)踐:JDBC開發(fā)實(shí)踐。
JAR作業(yè)開發(fā)實(shí)踐:JAR作業(yè)開發(fā)實(shí)踐。
Python作業(yè)開發(fā)實(shí)踐:Python作業(yè)開發(fā)實(shí)踐。
功能說明
DDL
命名空間
USE lindorm_columnar;
DROP NAMESPACE mydb;
表
USE lindorm_columnar;
CREATE TABLE mydb.mytable (
id INT NOT NULL,
city STRING NOT NULL,
name STRING,
score INT)
PARTITIONED BY (city, bucket(128,id))
TBLPROPERTIES(
'primary-key' = 'id,city');
以下分別對(duì)主鍵、數(shù)據(jù)分區(qū)方式進(jìn)行說明。
主鍵
創(chuàng)建表時(shí),可以通過設(shè)置主鍵創(chuàng)建主鍵表或不設(shè)置主鍵創(chuàng)建非主鍵表。以下將分別介紹主鍵表和非主鍵表的創(chuàng)建方法,以及創(chuàng)建時(shí)需遵循的規(guī)則。
創(chuàng)建主建表。創(chuàng)建表時(shí),設(shè)置
TBLPROPERTIES
中primary-key參數(shù)的值,指定表的主鍵字段即可。主鍵表遵循以下規(guī)則:多個(gè)主鍵字段需用英文逗號(hào)分隔。支持字段類型:BOOLEAN、BYTE、SHORT、INT、LONG、FLOAT、DOUBLE、STRING和BINARY。
列存表主鍵具備唯一性。
當(dāng)相同主鍵數(shù)據(jù)多次寫入,新數(shù)據(jù)將覆蓋舊數(shù)據(jù)。
必須指定分區(qū),表分區(qū)表達(dá)式字段必須來自主鍵字段,最后一級(jí)分區(qū)必須為bucket分區(qū)。
創(chuàng)建非主鍵表。創(chuàng)建表時(shí),不設(shè)置
TBLPROPERTIES
中的primary-key參數(shù)的值即可。非主鍵表無分區(qū)要求且數(shù)據(jù)無唯一性保證,允許存在重復(fù)數(shù)據(jù)。
數(shù)據(jù)分區(qū)方式
您可以在創(chuàng)建表時(shí)通過PARTITIONED BY([普通分區(qū)表達(dá)式],{bucket(bucketNum,bucketCol)})
指定數(shù)據(jù)分區(qū)方式。
bucket分區(qū)表達(dá)式
bucketNum為分片數(shù)量,直接影響數(shù)據(jù)寫入和掃描的并發(fā)度。
說明不同的bucket分區(qū)有不同的分區(qū)號(hào)(bucket_index)。bucketNum決定了一個(gè)普通分區(qū)下的bucket分區(qū)數(shù)量。
bucket分區(qū)號(hào)的計(jì)算方式是基于分區(qū)字段求Hash值,然后對(duì)bucketNum取余得出。以示例表
mydb.mytable
中的數(shù)據(jù)為例,bucket_index=hash(id)%128
。對(duì)于每個(gè)不同的bucket_index,底層存儲(chǔ)將進(jìn)行物理劃分。建議您在創(chuàng)建表前評(píng)估數(shù)據(jù)總量,并合理設(shè)置bucketNum,保證單bucket分區(qū)的數(shù)據(jù)量在50M~512M之間。
bucketCol為具體的bucket分區(qū)字段。
重要設(shè)置bucket分區(qū)字段時(shí)為避免數(shù)據(jù)傾斜,需確保bucket分區(qū)字段具有足夠的離散特征。
示例
創(chuàng)建表時(shí)僅指定bucket分區(qū)。
示例一:
USE lindorm_columnar; CREATE TABLE mydb.mytable ( id INT NOT NULL, city STRING, name STRING, score DOUBLE) PARTITIONED BY (bucket(1024,id)) TBLPROPERTIES( 'primary-key' = 'id');
示例二:
USE lindorm_columnar; CREATE TABLE mydb.mytable ( id INT NOT NULL, timestamp LONG NOT NULL, city STRING, name STRING, score DOUBLE) PARTITIONED BY (bucket(512,timestamp)) TBLPROPERTIES( 'primary-key' = 'id,timestamp');
普通分區(qū)表達(dá)式
對(duì)于普通分區(qū)表達(dá)式每個(gè)不同的值,底層存儲(chǔ)將進(jìn)行物理劃分,保證數(shù)據(jù)掃描的裁剪能力。
重要請(qǐng)您確保普通分區(qū)表達(dá)式的取值相對(duì)集中,常見普通分區(qū)字段包括:日期、城市、性別等等。如果您的普通分區(qū)表達(dá)式取值過于離散,例如時(shí)間戳,將導(dǎo)致列存元數(shù)據(jù)壓力過大。
示例
創(chuàng)建表時(shí)同時(shí)指定普通分區(qū)和bucket分區(qū)。
示例一:
USE lindorm_columnar; CREATE TABLE mydb.mytable ( id INT NOT NULL, year STRING NOT NULL, month STRING NOT NULL, day STRING NOT NULL, city STRING, name STRING, score DOUBLE) PARTITIONED BY (year, month, day, bucket(1024,id)) TBLPROPERTIES( 'primary-key' = 'id, year, month, day');
示例二:
USE lindorm_columnar; CREATE TABLE mydb.mytable ( id INT NOT NULL, date STRING NOT NULL, city STRING NOT NULL, name STRING, score DOUBLE) PARTITIONED BY (date, city, bucket(1024,id)) TBLPROPERTIES( 'primary-key' = 'id,date,city');
USE lindorm_columnar;
USE mydb;
SHOW TABLES;
您可以執(zhí)行以下SQL語句查看表結(jié)構(gòu)。
USE lindorm_columnar;
SHOW CREATE TABLE mydb.mytable;
DESC mydb.mytable;
USE lindorm_columnar;
-- 刪除表保留數(shù)據(jù)文件
DROP TABLE mydb.mytable;
-- 刪除表刪除數(shù)據(jù)文件
DROP TABLE mydb.mytable PURGE;
USE lindorm_columnar;
TRUNCATE TABLE mydb.mytable;
分區(qū)
您可以通過DELETE FROM語法指定WHERE條件匹配分區(qū)來刪除分區(qū),示例如下。
USE lindorm_columnar;
DELETE FROM mydb.mytable WHERE city = 'beijing';
DML
表
示例一:
USE lindorm_columnar; SELECT * from mydb.mytable where id=0;
示例二:
USE lindorm_columnar; SELECT count(1), sum(score) from mydb.mytable where city = 'beijing';
分區(qū)整理
在列存分區(qū)寫入數(shù)據(jù),經(jīng)過一段時(shí)間后,您可以執(zhí)行rewrite_data_files或rewrite_manifest命令,整理分區(qū)數(shù)據(jù),減少數(shù)據(jù)或元數(shù)據(jù)冗余,提升數(shù)據(jù)查詢性能。詳細(xì)語法信息請(qǐng)參見rewrite_data_files語法和rewrite_manifest語法。
示例一:
USE lindorm_columnar; CALL lindorm_columnar.system.rewrite_data_files(table => 'mydb.mytable');
示例二:
USE lindorm_columnar; CALL lindorm_columnar.system.rewrite_data_files(table => 'mydb.mytable', where => 'city=\"beijing\"');
示例三:
USE lindorm_columnar; CALL lindorm_columnar.system.rewrite_manifest('mydb.mytable');
最佳實(shí)踐
您可以通過以下方案,加速數(shù)據(jù)查詢或計(jì)算。
主鍵數(shù)據(jù)查詢
如果表中存儲(chǔ)了海量數(shù)據(jù)集,查詢時(shí)可以指定通過主鍵過濾條件,實(shí)現(xiàn)加速效果。查詢時(shí),主鍵的數(shù)據(jù)范圍設(shè)置得越小,加速效果越好。
假設(shè)表結(jié)構(gòu)如下:
USE lindorm_columnar;
CREATE TABLE orders (
o_orderkey INT NOT NULL,
o_custkey INT,
o_orderstatus STRING,
o_totalprice DOUBLE,
o_orderdate STRING,
o_orderpriority STRING,
o_clerk STRING,
o_shippriority INT,
o_comment STRING)
PARTITIONED BY (bucket(1024,o_orderkey))
TBLPROPERTIES(
'primary-key' = 'o_orderkey');
示例一:
USE lindorm_columnar; SELECT * FROM orders WHERE o_orderkey=18394;
示例二:
USE lindorm_columnar; SELECT count(*) FROM orders WHERE o_orderkey>100000 AND o_orderkey<200000;
示例三:
USE lindorm_columnar; SELECT count(*) FROM orders WHERE o_orderkey>100000;
添加分區(qū)過濾
Lindorm列存引擎中不同分區(qū)之間彼此物理隔離,因此,通過添加分區(qū)過濾條件,可以加速數(shù)據(jù)查詢。
假設(shè)表結(jié)構(gòu)如下:
USE lindorm_columnar;
CREATE TABLE orders (
o_orderkey INT NOT NULL,
o_custkey INT,
o_orderstatus STRING,
o_totalprice DOUBLE,
o_orderdate STRING NOT NULL,
o_orderpriority STRING,
o_clerk STRING,
o_shippriority INT,
o_comment STRING)
PARTITIONED BY (o_orderdate, bucket(1024,o_orderkey))
TBLPROPERTIES(
'primary-key' = 'o_orderdate,o_orderkey');
示例一:
USE lindorm_columnar; SELECT o_orderdate, count(*) FROM orders WHERE o_orderdate='2022-01-01' GROUP BY o_orderdate;
示例二:
USE lindorm_columnar; SELECT o_orderdate, count(*) FROM orders WHERE o_orderdate>='2022-01-01' AND o_orderdate<='2022-01-07' GROUP BY o_orderdate;
查詢加速
對(duì)指定表或者表中的指定分區(qū)進(jìn)行數(shù)據(jù)整理(Rewrite),可以增強(qiáng)數(shù)據(jù)的有序性或緊湊性,從而提升數(shù)據(jù)掃描性能。
假設(shè)表結(jié)構(gòu)如下:
CREATE TABLE mydb.mytable (
id INT NOT NULL,
city STRING NOT NULL,
name STRING,
score INT)
partitioned by (city, bucket(4, id))
tblproperties('primary-key' = 'id,city');
示例一:對(duì)mydb.mytable全表進(jìn)行數(shù)據(jù)整理。
CALL lindorm_columnar.system.rewrite_data_files(table => 'mydb.mytable');
示例二:對(duì)指定分區(qū)進(jìn)行數(shù)據(jù)整理。
CALL lindorm_columnar.system.rewrite_data_files(table => 'mydb.mytable', where => 'city=\"beijing\"');
完成數(shù)據(jù)整理后,如果想要進(jìn)一步提升后續(xù)查詢的效率,可以執(zhí)行以下語句設(shè)置表的相關(guān)參數(shù)來加速后續(xù)查詢:
ALTER TABLE mydb.mytable SET TBLPROPERTIES ('read.scan-major-rewritten-files-only' = true);
參數(shù)說明
read.scan-major-rewritten-files-only:指定數(shù)據(jù)查詢范圍。數(shù)據(jù)類型為BOOLEAN。取值如下:
true:只查詢已完成數(shù)據(jù)整理的數(shù)據(jù),忽略增量寫入且未完成數(shù)據(jù)整理的數(shù)據(jù)。
false:默認(rèn)值。查詢所有數(shù)據(jù)。
非主鍵條件查詢
針對(duì)分區(qū)整理過程,列存表默認(rèn)按主鍵排序,可以在建表后按需配置排序鍵,從而加速非主鍵條件查詢。
自定義排序鍵加速查詢效果需要在配置排序鍵后進(jìn)行分區(qū)整理,且只掃描已進(jìn)行分區(qū)整理的數(shù)據(jù),不再掃描增量數(shù)據(jù)。
假設(shè)表結(jié)構(gòu)如下:
USE lindorm_columnar;
CREATE TABLE orders (
o_orderkey INT NOT NULL,
o_custkey INT,
o_orderstatus STRING,
o_totalprice DOUBLE ,
o_orderdate STRING ,
o_orderpriority STRING,
o_clerk STRING,
o_shippriority INT,
o_comment STRING)
PARTITIONED BY (bucket(1024,o_orderkey))
TBLPROPERTIES(
'primary-key' = 'o_orderkey',
'read.scan-major-rewritten-files-only' = 'true');
執(zhí)行以下語句配置排序鍵:
ALTER TABLE orders WRITE ORDERED BY o_shippriority,o_totalprice;
執(zhí)行以下語句整理分區(qū):
CALL lindorm_columnar.system.rewrite_data_files(table => 'orders');
您可以使用以下SQL語句查詢已完成分區(qū)整理的表中的數(shù)據(jù)。
示例一:
USE lindorm_columnar; SELECT count(*) FROM orders WHERE o_shippriority=0;
示例二:
USE lindorm_columnar; SELECT count(*) FROM orders WHERE o_shippriority=0 AND o_totalprice>999.9;