一、SelectDB索引体系全景图
SelectDB的索引体系分为两大阵营:点查索引和跳数索引。这个分类方式很巧妙,不是按数据结构分,而是按查询场景分,这让我想起了当年学MySQL时死记硬背B+树、哈希索引的日子,其实从使用场景出发更容易理解。
1.1 点查索引:精准定位的狙击手
点查索引的核心思想是"精准打击"——通过索引直接定位到满足条件的行,然后直接读取数据。这玩意儿在满足条件的行数较少时效果炸裂,但在大范围查询时反而会拖后腿。
前缀索引是SelectDB的默认玩法,它按照排序键以有序方式存储数据,每隔1024行创建一个稀疏索引。这个设计很聪明,既保证了索引的稀疏性(节省空间),又能在查询时快速定位到目标数据块。但这里有个坑:如果你的查询条件不包含排序键的前缀,这索引就废了。我们曾经有个表按时间排序,但业务经常按用户ID查询,结果前缀索引完全没起作用,全表扫描慢得让人想砸键盘。
倒排索引是SelectDB的杀手锏,也是我重点要说的。它给每个值建立了一个到行号的映射表,等值查询时直接通过倒排表找到行号集合,然后直接读取数据,避免了逐行扫描。这玩意儿不仅支持等值查询,还能加速范围过滤和文本关键词匹配。
1.2 跳数索引:批量过滤的扫荡机
跳数索引的思路正好相反:通过索引确定不满足条件的数据块,直接跳过这些垃圾数据,只读取可能满足条件的数据块,再进行一次逐行过滤。这玩意儿在满足条件的行比较多时效果拔群。
ZoneMap索引是自动维护的统计信息,为每个数据文件和数据块记录最大值、最小值、是否有NULL、Sum等统计信息。这让我想起了MySQL的统计信息,但SelectDB做得更彻底,直接在数据块级别维护这些信息,查询时直接通过统计信息判断是否满足条件,不满足的直接跳过。
BloomFilter索引和NGram BloomFilter索引则是针对特定场景的优化。BloomFilter适合等值查询,NGram BloomFilter专门针对LIKE查询优化。但这里有个大坑:BloomFilter有误判率,虽然不会漏掉满足条件的数据,但可能会把不满足条件的数据也拉进来,导致额外的过滤开销。我们曾经在一个高并发的场景下滥用BloomFilter,结果CPU直接飙到90%,后来发现是BloomFilter的误判率导致大量无效数据被加载。
二、实战踩坑:那些年我们交过的学费
2.1 案例一:倒排索引的"过度设计"陷阱
去年我们做日志分析系统,表里有十几个字段,业务同学说"每个字段都可能被查询",我脑子一热给所有字段都建了倒排索引。结果数据导入速度从每秒10万条降到每秒1万条,磁盘空间占用翻了两倍。
排查过程:通过监控发现,每次数据导入都要更新所有索引,写放大严重。而且很多字段的基数很低(比如状态字段只有几个值),倒排索引的效果微乎其微。
解决方案:删掉低基数字段的索引,只保留高基数字段(用户ID、订单号等)的倒排索引。同时把频繁更新的字段从倒排索引中移除,改用ZoneMap索引。数据导入速度恢复到每秒8万条,查询性能反而提升了。
经验总结:
-
倒排索引不是越多越好,高基数字段(选择性>0.1)才值得建
-
频繁更新的字段不要建倒排索引,写放大太严重
-
低基数字段用ZoneMap索引就够了,空间占用小,维护成本低
2.2 案例二:前缀索引的"最左匹配"陷阱
我们有个订单表,按(user_id, create_time)建了前缀索引。业务有个查询是WHERE create_time > '2024-01-01',结果走了全表扫描。
排查过程:执行计划显示没有使用索引,因为查询条件没有包含user_id。这就是前缀索引的"最左匹配原则"——必须从最左列开始匹配,不能跳过中间列。
解决方案:给create_time单独建了一个ZoneMap索引,查询性能从10秒降到100毫秒。但这里有个权衡:ZoneMap索引虽然能加速范围查询,但会占用额外的存储空间。我们最终的选择是:如果这个查询频率很高,就单独建索引;如果频率不高,就接受全表扫描。
经验总结:
-
前缀索引的字段顺序至关重要,高频查询条件必须放在最左
-
如果某个字段经常单独查询,考虑单独建索引
-
不要为了"可能用到的查询"而过度设计索引,按需创建才是王道
2.3 案例三:BloomFilter的"误判率"陷阱
我们有个用户行为表,每天写入上亿条数据,给user_id建了BloomFilter索引。结果发现某个查询的响应时间波动很大,有时候几十毫秒,有时候几百毫秒。
排查过程:通过监控发现,当BloomFilter误判率较高时,会加载大量无效数据块,导致额外的过滤开销。虽然BloomFilter的误判率可以配置,但降低误判率会增加内存占用。
解决方案:对于user_id这种高基数字段,我们改用了倒排索引。虽然倒排索引的存储空间更大,但查询性能更稳定。对于低基数字段(如性别、状态),继续使用BloomFilter,因为误判率对性能影响不大。
经验总结:
-
BloomFilter适合低基数字段,高基数字段用倒排索引更合适
-
BloomFilter的误判率需要根据业务场景调整,不是越小越好
-
监控BloomFilter的误判率,定期评估是否需要调整配置
三、索引设计黄金法则
经过这些年的实战,我总结了一套索引设计的"黄金法则",希望能帮大家少走弯路。
3.1 索引选型决策树
面对一个字段,如何选择索引类型?我画了个决策树:
-
是否高基数(选择性>0.1)?
-
是 → 选择倒排索引
-
否 → 进入下一步
-
-
是否频繁更新?
-
是 → 选择ZoneMap索引
-
否 → 进入下一步
-
-
是否等值查询为主?
-
是 → 选择BloomFilter索引
-
否 → 选择ZoneMap索引
-
这个决策树不是绝对的,需要根据具体业务场景调整。比如虽然某个字段基数低,但如果查询频率极高,也可以考虑用倒排索引。
3.2 索引数量控制
我们团队有个不成文的规定:单表索引数量不超过5个。这不是硬性规定,而是基于以下考虑:
-
写放大:每个索引都会增加写入开销,索引越多,写入越慢
-
存储成本:索引占用存储空间,大表可能翻倍
-
维护成本:索引越多,优化器选择执行计划越复杂,可能选错索引
我们的做法是:定期分析慢查询日志,找出真正需要优化的查询,针对性建索引。不要为了"可能用到的查询"而提前建索引,那是过度设计。
3.3 索引监控与调优
索引不是一劳永逸的,需要持续监控和调优。我们团队的做法:
-
慢查询监控:每天分析慢查询日志,找出TOP 10慢SQL
-
索引使用率统计:定期统计每个索引的使用频率,删除长期不用的索引
-
索引碎片整理:对于频繁更新的表,定期重建索引,减少碎片
-
执行计划分析:对于关键查询,定期检查执行计划,确保走最优索引
四、高级技巧:那些教科书上不会告诉你的
4.1 覆盖索引的妙用
覆盖索引是指索引包含了查询所需的所有字段,无需回表查询。这玩意儿用好了能让查询性能提升一个数量级。
我们有个用户表,经常查询SELECT user_id, name FROM users WHERE user_id = ?。最初我们只给user_id建了索引,查询时需要回表获取name字段。后来我们建了联合索引(user_id, name),查询直接从索引返回结果,性能提升了5倍。
关键点:覆盖索引不是万能的,它会增加索引大小,需要权衡存储成本和查询性能。我们的经验是:对于高频查询,即使索引大一点也值得;对于低频查询,接受回表开销。
4.2 索引下推(ICP)的威力
索引下推是SelectDB的一个黑科技,它允许在索引遍历过程中就对无法用索引过滤的WHERE条件进行判断,从而减少回表次数。
举个例子:SELECT * FROM users WHERE name LIKE '张%' AND age > 30,如果只有name的索引,没有ICP的话,会先通过name找到所有姓张的用户,然后逐个回表检查age>30。有了ICP,在索引层就判断age>30,只有同时满足两个条件的才回表。
我们曾经用ICP把一个查询的响应时间从500毫秒降到50毫秒,效果惊人。但ICP不是自动启用的,需要确保查询条件能被下推,避免在索引列上使用函数或表达式。
4.3 分区与索引的配合
SelectDB支持分区表,分区可以和索引配合使用,实现更细粒度的数据管理。
我们的做法是:按时间分区(比如按天),然后在每个分区内建索引。这样查询时可以先通过分区裁剪过滤掉大部分数据,再通过索引定位具体数据。对于历史数据查询,效果尤其明显。
但分区也有代价:分区过多会增加元数据管理开销,影响DDL操作性能。我们的经验是:单表分区数不超过1000个,超过这个数就要考虑分表了。
五、避坑指南:这些错误千万别犯
5.1 索引失效的常见场景
-
函数操作:
WHERE YEAR(create_time) = 2024,索引失效 -
隐式类型转换:
WHERE user_id = '123'(user_id是数字类型),索引失效 -
前导通配符:
WHERE name LIKE '%张',索引失效 -
OR条件:
WHERE user_id = 1 OR age > 30,如果age没有索引,整个查询走全表扫描 -
NOT条件:
WHERE status != 'deleted',索引可能失效
5.2 索引维护的坑
-
索引碎片:频繁增删改会导致索引碎片,定期重建索引是必要的
-
统计信息过期:数据分布变化后,统计信息可能过期,导致优化器选错索引
-
索引冗余:多个索引可能覆盖相同的查询,需要定期清理冗余索引
5.3 生产环境注意事项
-
在线建索引:大表建索引会锁表,影响业务,建议在业务低峰期操作
-
索引变更回滚:建索引失败可能导致表锁,要有回滚预案
-
监控告警:索引变更后要密切监控系统负载,发现问题及时回滚
六、总结与展望
SelectDB的索引体系非常强大,但用好了是利器,用不好就是自残。我的核心建议是:
索引设计三原则:
-
按需创建:不要为了"可能用到的查询"而提前建索引
-
高频优先:优先为高频查询建索引,低频查询可以接受全表扫描
-
持续优化:索引不是一劳永逸的,需要定期监控和调优
性能优化三步骤:
-
监控慢查询:找出真正的性能瓶颈
-
分析执行计划:确认是否走最优索引
-
针对性优化:缺索引补索引,索引失效改SQL
954

被折叠的 条评论
为什么被折叠?



