ArgoDB 中的细粒度索引是基于 KV(key-value)的索引,提供高速的 KV 服务,索引文件的 Key 存储索引键,Value 存储相同索引键的值。它类似于一种聚簇索引,可以把大量相似的数据细分成更小的边界,从而使文件的查找变得更快更有效率。

图 40. 细粒度索引实现逻辑
因此在 ArgoDB 中使用细粒度索引,可以支持实时数仓场景下的多种诉求:
源表更新数据,同步细粒度索引的更新或重建功能,请参考后续章节中更新细粒度索引的部分。
- 细粒度索引的分区,分片同索引对应源表的分区,分片相同;
- 但索引分桶与源表分桶无关,是按索引列分桶(目前分桶数默认 163)。
ALTER TABLE <table_name> ADD FINE-GRAINED INDEX (<column_name>);
CREATE TABLE single_fgindex_t(trans_id INT,acc_num INT,trans_time DATE,trans_type STRING,stock_id STRING,price decimal,amount INT)
STORED AS HOLODESK;
BATCHINSERT INTO single_fgindex_t BATCHVALUES (
VALUES (1, 1, '2023-2-28', 'discount', 'NO20140201', 10.00,99),
VALUES(1, 2, '2023-3-1', 'online', 'NO20140201', 12.11,10),
VALUES (1, 1, '2019-10-1', 'wholesale', 'NO20191001', 8.99,1010),
VALUES (2, 4, '2019-8-21', 'discount', 'NO20191001', 8.99,10000),
VALUES (2, 5, '2015-5-6', 'wholesale', 'NO20191001', 8.99,5000),
VALUES (2, 6, '2008-5-12', 'wholesale', 'NO20191001', 8.99,10000),
VALUES (3, 7, '2024-1-31', 'online', 'NO20191001', 8.99,100000),
VALUES (3, 8, '2022-6-1', 'discount', 'NO20191001', 8.99,50000),
VALUES (4, 9, '2022-7-29', 'online', 'NO20191001', 8.99,80000),
VALUES (5, 10, '2019-10-1', 'wholesale', 'NO20140201', 8.99,1000));
ALTER TABLE single_fgindex_t ADD FINE-GRAINED INDEX (trans_id);
使用 SELECT HOLO_INDEX 查看指定数据库表中的索引信息。
SELECT HOLO_INDEX ("<database_name>", "<table_name>");
SELECT HOLO_INDEX ("demodata", "single_fgindex_t");
查询结果如下:
+--------------+----------------+----------------+-------------+
| column_name | table_name | database_name | index_type |
+--------------+----------------+----------------+-------------+
| TRANS_ID | single_fgindex_t | demodata | global |
+--------------+----------------+----------------+-------------+
where a = 1;
where a in (1, 2, 3);
where a in (1, 2, 3) and a > 2;
where 1 < b < 10 and a = 1;
查询语句中包含 or 的过滤组合,无法命中细粒度索引。
您可以通过 EXPLAIN 查询语句,查看是否命中了细粒度索引
[Explain] SELECT FROM <table_name> WHERE <index_column> = <value> and <filter_statement>;
当结果中出现 ArgoDB Index Scan: 字样,则表示本查询成功命中细粒度索引加速。
explain select * from single_fgindex_t where trans_id = 2;
explain select * from single_fgindex_t where trans_id in (1,2,3);
explain select * from single_fgindex_t where trans_id in (1,2,3) and trans_id > 3;
explain select * from single_fgindex_t where trans_id in (1,2,3) and trans_id > 3;
结果中均会出现如下信息:
TableScan
alias: fgindex_table
ArgoDB Index Scan: demodata.single_fgindex_t_07541918-7069-46f1-843f-06edcd468047_holodeskgi_TRANS_ID_fine_grained
使用 ALTER TABLE DROP FINE-GRAINED INDEX 删除索引,会直接删除指定索引文件,但不会删除源表信息。
ALTER TABLE <table_name> DROP FINE-GRAINED INDEX (<column_name1>,<column_name2>,...);
ALTER TABLE single_fgindex_t DROP FINE-GRAINED INDEX (trans_id);
细粒度索引只支持在源表发生 INSERT 操作时,在索引表中写入增量数据。当源表发生 UPDATE 或 DELETE 操作时,由于只涉及小部分数据的修改,若重建细粒度索引表,由于读取全表数据浪费大量资源。因此我们支持索引表随主表更新的功能,只对此次源表更新过程中涉及到的索引表数据部分进行重构,降低细粒度索引表的维护难度。
我们通过将细粒度索引表分为 L0 层与 L1 层实现细粒度索引的更新功能:
当一个索引 key 同时存在于 L0 和 L1 层时,若 L0 层写入时间先于 L1 层,即该 key 对应的源表数据在 INSERT 之后进行了 UPDATE/DELETE 操作,该条数据无法读取。
当一个索引 key 同时存在于 L0 和 L1 层时,若 L1 层写入时间先于 L0 层,即该 key 对应的源表数据在 UPDATE/DELETE 之后又重新 INSERT 写入,则该条新插入的数据能够被读取。

图 41. 细粒度索引分层更新
set argodb.fine.grained.index.update.enabled = true;
set argodb.fine.grained.index.update.rows.limit = 1000000; -- 默认为 100 万行
ArgoDB 中的细粒度索引是基于 KV(key-value)的索引,提供高速的 KV 服务,索引文件的 Key 存储索引键,Value 存储相同索引键的值。它类似于一种聚簇索引,可以把大量相似的数据细分成更小的边界,从而使文件的查找变得更快更有效率。

图 40. 细粒度索引实现逻辑
因此在 ArgoDB 中使用细粒度索引,可以支持实时数仓场景下的多种诉求:
源表更新数据,同步细粒度索引的更新或重建功能,请参考后续章节中更新细粒度索引的部分。
- 细粒度索引的分区,分片同索引对应源表的分区,分片相同;
- 但索引分桶与源表分桶无关,是按索引列分桶(目前分桶数默认 163)。
ALTER TABLE <table_name> ADD FINE-GRAINED INDEX (<column_name>);
CREATE TABLE single_fgindex_t(trans_id INT,acc_num INT,trans_time DATE,trans_type STRING,stock_id STRING,price decimal,amount INT)
STORED AS HOLODESK;
BATCHINSERT INTO single_fgindex_t BATCHVALUES (
VALUES (1, 1, '2023-2-28', 'discount', 'NO20140201', 10.00,99),
VALUES(1, 2, '2023-3-1', 'online', 'NO20140201', 12.11,10),
VALUES (1, 1, '2019-10-1', 'wholesale', 'NO20191001', 8.99,1010),
VALUES (2, 4, '2019-8-21', 'discount', 'NO20191001', 8.99,10000),
VALUES (2, 5, '2015-5-6', 'wholesale', 'NO20191001', 8.99,5000),
VALUES (2, 6, '2008-5-12', 'wholesale', 'NO20191001', 8.99,10000),
VALUES (3, 7, '2024-1-31', 'online', 'NO20191001', 8.99,100000),
VALUES (3, 8, '2022-6-1', 'discount', 'NO20191001', 8.99,50000),
VALUES (4, 9, '2022-7-29', 'online', 'NO20191001', 8.99,80000),
VALUES (5, 10, '2019-10-1', 'wholesale', 'NO20140201', 8.99,1000));
ALTER TABLE single_fgindex_t ADD FINE-GRAINED INDEX (trans_id);
使用 SELECT HOLO_INDEX 查看指定数据库表中的索引信息。
SELECT HOLO_INDEX ("<database_name>", "<table_name>");
SELECT HOLO_INDEX ("demodata", "single_fgindex_t");
查询结果如下:
+--------------+----------------+----------------+-------------+
| column_name | table_name | database_name | index_type |
+--------------+----------------+----------------+-------------+
| TRANS_ID | single_fgindex_t | demodata | global |
+--------------+----------------+----------------+-------------+
where a = 1;
where a in (1, 2, 3);
where a in (1, 2, 3) and a > 2;
where 1 < b < 10 and a = 1;
查询语句中包含 or 的过滤组合,无法命中细粒度索引。
您可以通过 EXPLAIN 查询语句,查看是否命中了细粒度索引
[Explain] SELECT FROM <table_name> WHERE <index_column> = <value> and <filter_statement>;
当结果中出现 ArgoDB Index Scan: 字样,则表示本查询成功命中细粒度索引加速。
explain select * from single_fgindex_t where trans_id = 2;
explain select * from single_fgindex_t where trans_id in (1,2,3);
explain select * from single_fgindex_t where trans_id in (1,2,3) and trans_id > 3;
explain select * from single_fgindex_t where trans_id in (1,2,3) and trans_id > 3;
结果中均会出现如下信息:
TableScan
alias: fgindex_table
ArgoDB Index Scan: demodata.single_fgindex_t_07541918-7069-46f1-843f-06edcd468047_holodeskgi_TRANS_ID_fine_grained
使用 ALTER TABLE DROP FINE-GRAINED INDEX 删除索引,会直接删除指定索引文件,但不会删除源表信息。
ALTER TABLE <table_name> DROP FINE-GRAINED INDEX (<column_name1>,<column_name2>,...);
ALTER TABLE single_fgindex_t DROP FINE-GRAINED INDEX (trans_id);
细粒度索引只支持在源表发生 INSERT 操作时,在索引表中写入增量数据。当源表发生 UPDATE 或 DELETE 操作时,由于只涉及小部分数据的修改,若重建细粒度索引表,由于读取全表数据浪费大量资源。因此我们支持索引表随主表更新的功能,只对此次源表更新过程中涉及到的索引表数据部分进行重构,降低细粒度索引表的维护难度。
我们通过将细粒度索引表分为 L0 层与 L1 层实现细粒度索引的更新功能:
当一个索引 key 同时存在于 L0 和 L1 层时,若 L0 层写入时间先于 L1 层,即该 key 对应的源表数据在 INSERT 之后进行了 UPDATE/DELETE 操作,该条数据无法读取。
当一个索引 key 同时存在于 L0 和 L1 层时,若 L1 层写入时间先于 L0 层,即该 key 对应的源表数据在 UPDATE/DELETE 之后又重新 INSERT 写入,则该条新插入的数据能够被读取。

图 41. 细粒度索引分层更新
set argodb.fine.grained.index.update.enabled = true;
set argodb.fine.grained.index.update.rows.limit = 1000000; -- 默认为 100 万行