索引是减少数据扫描量、加速查询的关键手段。阿里云SelectDB提供多种索引类型,覆盖前缀过滤、等值查询、全文检索和模糊匹配等典型场景。本文通过案例说明各类索引的适用场景、创建方法和验证手段,帮助您根据查询模式选择最合适的索引。
索引类型概览
阿里云SelectDB的索引分为两类:
-
内置索引(自动创建):前缀索引和ZoneMap索引由系统自动构建和维护,用户通过调整建表时的Key列顺序来优化索引效果。
-
二级索引(按需创建):需要用户根据查询模式手动创建。其中倒排索引覆盖了等值查询、范围过滤、全文检索和多条件组合等绝大多数场景,是按需创建索引的首选。Bloom Filter索引和NGram BF索引适用于特定场景。
|
索引类型 |
分类 |
创建方式 |
适用场景 |
|
前缀索引(Short Key Index) |
内置 |
基于建表Key列自动构建,可通过调整列顺序优化 |
Key列前缀的等值和范围过滤 |
|
ZoneMap索引 |
内置 |
每列每个数据块自动维护min/max |
数值和日期列的范围过滤 |
|
倒排索引(Inverted Index) |
二级(推荐) |
CREATE INDEX语句或建表时内联定义 |
等值、范围、全文检索、多条件组合过滤,覆盖绝大多数按需索引场景 |
|
Bloom Filter索引 |
二级 |
建表PROPERTIES或ALTER TABLE指定列 |
高基数列的等值过滤(=、IN) |
|
NGram Bloom Filter索引 |
二级 |
CREATE INDEX语句或建表时内联定义 |
LIKE '%关键词%' 模糊匹配加速 |
索引选择指南
根据查询模式选择对应的索引类型:
|
查询模式 |
推荐索引 |
说明 |
|
|
前缀索引 |
自动生效,无需额外创建。查询条件需匹配Key列前缀顺序。 |
|
|
倒排索引 |
支持中文和英文分词,用MATCH_ALL/MATCH_ANY进行检索。 |
|
|
倒排索引 |
对参与过滤的多列各建倒排索引,查询时自动组合加速。 |
|
|
Bloom Filter索引 |
适合设备ID、订单号等基数高的列。低基数列(如状态、性别)效果差。 |
|
|
NGram BF索引 |
将字符串拆为N-gram片段构建布隆过滤器,避免全表扫描。 |
案例1:优化前缀索引命中
前缀索引(Short Key Index)基于建表Key列自动构建,取Key列的前36个字节作为索引内容。查询条件必须匹配Key列的前缀顺序才能命中索引。
问题场景
建表时Key列顺序为(event_time, user_id),但业务高频查询按user_id过滤。由于user_id不是Key列前缀,查询无法命中前缀索引,导致全表扫描:
-- 建表:Key列顺序为 (event_time, user_id)
CREATE TABLE user_events (
event_time DATETIME NOT NULL,
user_id BIGINT NOT NULL,
event_type VARCHAR(32),
device_id VARCHAR(64),
page_url TEXT
)
DUPLICATE KEY(event_time, user_id)
DISTRIBUTED BY HASH(user_id) BUCKETS AUTO;
-- 高频查询:按 user_id 过滤,无法命中前缀索引
SELECT * FROM user_events WHERE user_id = 12345;
优化方案
方案A:调整Key列顺序(建议新表使用)
将高频过滤列放在Key定义的首位:
-- 推荐:将高频过滤列 user_id 放在 Key 首位
CREATE TABLE user_events (
user_id BIGINT NOT NULL,
event_time DATETIME NOT NULL,
event_type VARCHAR(32),
device_id VARCHAR(64),
page_url TEXT
)
DUPLICATE KEY(user_id, event_time)
DISTRIBUTED BY HASH(user_id) BUCKETS AUTO;
方案B:创建Rollup(已有表使用)
对已有表无法修改Key列顺序时,通过Rollup构建以目标列为前缀的索引视图:
-- 为已有表创建Rollup,以 event_type 为前缀
ALTER TABLE user_events ADD ROLLUP rollup_event_type(event_type, event_time, user_id, device_id, page_url);
Rollup创建完成后,查询按event_type过滤时优化器会自动选择该Rollup。
前缀索引设计原则
-
高频过滤列在前:将查询中最常出现在WHERE条件中的列放在Key列的前面位置。
-
定长类型在前:前缀索引仅取前36字节,VARCHAR类型会占用较多索引字节。将INT、BIGINT等定长类型放前面可覆盖更多列。
-
等值查询列在范围查询列之前:范围条件会终止前缀匹配,等值列放前面能保证后续列继续参与索引过滤。
案例2:使用倒排索引加速全文检索与多条件过滤
倒排索引(Inverted Index)是SelectDB中功能最丰富的索引类型,支持全文检索(中英文分词)、字符串等值匹配和多列组合过滤。
场景A:全文检索
对文本列创建带分词器的倒排索引,使用MATCH_ALL(所有词都匹配)或MATCH_ANY(任意词匹配)进行全文检索:
-- 建表时内联定义倒排索引(中文分词)
CREATE TABLE articles (
id BIGINT NOT NULL,
title VARCHAR(256),
content TEXT,
publish_time DATETIME,
INDEX idx_content(content) USING INVERTED PROPERTIES("parser" = "chinese")
)
DUPLICATE KEY(id)
DISTRIBUTED BY HASH(id) BUCKETS AUTO;
-- 全文检索:查找同时包含"SelectDB"和"索引"的文章
SELECT * FROM articles WHERE content MATCH_ALL 'SelectDB 索引';
-- 全文检索:查找包含"优化"或"导入"的文章
SELECT * FROM articles WHERE content MATCH_ANY '优化 导入';
分词器选择:
-
中文文本:
parser = "chinese" -
英文文本:
parser = "unicode"(支持大小写归一化,配合"lower_case" = "true") -
不需要分词的等值匹配:不指定parser(默认不分词)
场景B:多条件组合过滤
对多列分别建立倒排索引,查询时优化器自动组合各列的索引结果进行过滤:
-- 对已有表的多个列各创建倒排索引
CREATE INDEX idx_title ON articles(title) USING INVERTED;
CREATE INDEX idx_publish_time ON articles(publish_time) USING INVERTED;
-- 多条件组合查询:倒排索引自动加速
SELECT * FROM articles
WHERE title = 'SelectDB最佳实践'
AND publish_time >= '2024-01-01';
建表时内联定义
除了通过CREATE INDEX在已有表上追加,也可以在建表时直接内联定义多个倒排索引:
CREATE TABLE articles (
id BIGINT NOT NULL,
title VARCHAR(256),
content TEXT,
publish_time DATETIME,
INDEX idx_title(title) USING INVERTED,
INDEX idx_content(content) USING INVERTED PROPERTIES("parser" = "chinese"),
INDEX idx_pub_time(publish_time) USING INVERTED
)
DUPLICATE KEY(id)
DISTRIBUTED BY HASH(id) BUCKETS AUTO;
案例3:使用Bloom Filter索引加速等值查询
Bloom Filter索引适用于非Key列的高基数等值查询(=、IN)。查询时通过布隆过滤器快速排除不包含目标值的数据块,减少磁盘I/O。
创建方式
建表时指定:
CREATE TABLE user_events (
event_time DATETIME NOT NULL,
user_id BIGINT NOT NULL,
event_type VARCHAR(32),
device_id VARCHAR(64),
page_url TEXT
)
DUPLICATE KEY(event_time, user_id)
DISTRIBUTED BY HASH(user_id) BUCKETS AUTO
PROPERTIES (
"bloom_filter_columns" = "device_id"
);
已有表追加:
-- 追加 Bloom Filter 列(需列出所有要保留的列)
ALTER TABLE user_events SET ("bloom_filter_columns" = "device_id, page_url");
适用场景
-- 按设备ID精确查询(device_id 基数高,Bloom Filter 有效)
SELECT * FROM user_events WHERE device_id = 'device_abc_123';
-- IN 查询同样有效
SELECT * FROM user_events WHERE device_id IN ('device_001', 'device_002', 'device_003');
反例:低基数列
对取值种类少的列(如性别、状态字段)创建Bloom Filter索引,由于大量数据块都包含目标值,过滤器几乎无法排除任何块,索引失去作用:
-- 反例:event_type 取值仅有 click/view/purchase 三种,Bloom Filter 无法有效过滤
PROPERTIES ("bloom_filter_columns" = "event_type") -- 不推荐
使用限制
-
仅对等值查询(=、IN)有效,不加速范围查询(>、<、BETWEEN)。
-
不支持TINYINT、FLOAT、DOUBLE类型的列。
-
同一列不能同时存在Bloom Filter索引和NGram BF索引,二者互斥。
案例4:使用NGram BF索引加速LIKE模糊匹配
当查询使用LIKE '%关键词%'时,如果没有索引则需要逐行全文扫描。NGram BF索引将字符串拆为固定长度的N-gram片段并构建布隆过滤器,在读取数据块之前快速判断是否可能包含目标子串。
创建方式
-- 对 title 列创建 NGram BF 索引
CREATE INDEX idx_title_ngram ON articles(title) USING NGRAM_BF PROPERTIES("gram_size" = "3", "bf_size" = "1024");
创建后,以下查询可利用该索引减少扫描量:
-- LIKE 模糊匹配,NGram BF 索引自动加速
SELECT * FROM articles WHERE title LIKE '%最佳实践%';
gram_size选择原则
gram_size决定拆分片段的长度,设置为搜索关键词的最小字符数效果最佳:
-
关键词通常为2~3个字(如中文产品名):设置
gram_size = "2"或"3"。 -
关键词较长(如英文单词、URL片段):设置
gram_size = "4"或"5"。 -
gram_size越小,索引越大、误判率越高;越大,对短关键词无法生效。建议与实际查询中最短的关键词长度匹配。
与倒排索引的选择对比
|
对比维度 |
倒排索引 |
NGram BF索引 |
|
匹配方式 |
分词后按词匹配(MATCH_ALL/MATCH_ANY) |
子串匹配(LIKE '%关键词%') |
|
中文支持 |
需指定parser = "chinese"进行分词 |
按字符拆分,天然支持中文 |
|
典型用途 |
关键词搜索、日志检索 |
URL片段匹配、产品名模糊查找 |
使用限制
-
同一列不能同时存在Bloom Filter索引和NGram BF索引,二者互斥。
-
仅支持字符串类型列(CHAR、VARCHAR、STRING)。
验证索引效果
使用EXPLAIN查看查询计划,确认索引是否生效。
查看EXPLAIN输出
EXPLAIN SELECT * FROM user_events WHERE device_id = 'device_abc_123';
-- 在输出的 OlapScanNode 中关注以下字段:
-- PARTITIONS: 1/1 -- 分区裁剪结果
-- TABLETS: 10/10 -- 扫描的 tablet 数量
-- tabletList=... -- 涉及的具体 tablet
使用EXPLAIN VERBOSE可查看更详细信息,包括索引过滤条件的下推情况。
查看已创建的索引
-- 查看表上的所有索引
SHOW INDEX FROM user_events;
-- 查看表的Bloom Filter配置
SHOW CREATE TABLE user_events;
常见问题
已有表如何追加索引?
Bloom Filter通过ALTER TABLE追加:
ALTER TABLE table_name SET ("bloom_filter_columns" = "col1, col2");
倒排索引和NGram BF索引通过CREATE INDEX追加:
CREATE INDEX idx_name ON table_name(column_name) USING INVERTED;
CREATE INDEX idx_name ON table_name(column_name) USING NGRAM_BF PROPERTIES("gram_size" = "3", "bf_size" = "1024");
索引创建为异步操作(Schema Change),通过SHOW ALTER TABLE COLUMN查看进度。同一张表不能同时执行多个Schema Change操作。
可以在同一列上叠加多种索引吗?
Bloom Filter索引和NGram BF索引互斥,同一列只能有其中一种。倒排索引可以与Bloom Filter或NGram BF共存。前缀索引和ZoneMap索引自动创建,不受影响。
索引会影响写入性能吗?
索引会增加写入时的计算和存储开销。Bloom Filter和NGram BF的额外开销较小。倒排索引(特别是带分词器的)开销相对较大,但对大多数场景仍可接受。建议仅对实际查询需要的列创建索引,避免盲目为所有列添加索引。
如何删除不需要的索引?
倒排索引和NGram BF索引通过DROP INDEX删除:
DROP INDEX idx_name ON table_name;
Bloom Filter索引通过清空列列表删除:
ALTER TABLE table_name SET ("bloom_filter_columns" = "");