存储索引概述
大家好,我是老张,干数据库调优这行快十年了。今天咱们聊聊索引——这个看似基础、实则门道极深的话题。说实话,我见过不少开发同学,写SQL很溜,但一提到索引就两眼一抹黑。嗯,这节课咱们就把索引的底裤扒干净。
1.1 索引是什么?——我的理解
索引,说白了就是数据的“目录”。你想想看,一本500页的书,你想找“哈希索引”这个关键词,没有目录你得一页一页翻,有了目录直接翻到第238页。数据库里也是一样,全表扫描就像翻书,索引就是那个目录。
我记得刚入行时,一个同事跟我说“索引就是B树”。当时我似懂非懂,后来才明白,索引不只B树,还有哈希、位图,甚至还有全文索引、空间索引。但最常用的,还是今天要讲的这三兄弟。
索引本质:一种以空间换时间的数据结构,通过额外的存储来加速数据检索。
1.2 索引到底解决了什么问题?
我把它总结为三点:
- 减少磁盘I/O:不用每次都把整张表读进内存。一个好的索引能把随机I/O变成顺序I/O。
- 避免排序:如果查询要排序,索引本身就有序,直接拿数据就行。
- 保证唯一性:主键索引、唯一索引能直接从逻辑上避免重复数据。
当然,索引不是万能的。我曾经在一个项目里,给一张日增百万记录的表建了十几个索引,结果插入速度慢到怀疑人生。所以,懂索引的人,知道什么时候该建,什么时候不该建。
1.3 三大主流索引类型
咱们一个一个聊。
1.3.1 B树索引
B树(Balanced Tree)是最常见的索引结构。几乎所有关系型数据库的默认索引都是B+树,这玩意是B树的变种。
特点:
- 所有叶子节点在同一层,保证查询稳定(稳定在O(log n))。
- 叶子节点用指针串成链表,方便范围查询(大于、小于、between)。
- 非叶子节点只存键值,不存数据,所以树很矮。
适用场景:范围查询、排序、前缀匹配。比如 WHERE age BETWEEN 20 AND 30,B树索引非常高效。
我曾经帮一个电商系统调优,订单表按月查询特别慢。建了一个B树索引在order_date上,查询时间从12秒降到0.2秒。那种感觉,很爽。
1.3.2 哈希索引
哈希索引(Hash Index)用哈希函数把键值映射到槽位。你给它一个值,它直接算出位置,一步到位。理论上时间复杂度O(1)。
但有一个大坑——哈希索引不支持范围查询。因为哈希函数把值打乱了,WHERE id > 100这种查询,它只能全扫。还有就是哈希冲突,不同键算出同一个槽位,就得链式处理,性能会退化。
避坑指南:我曾经在一个用户登录系统里,看到有人对用户名建了哈希索引。用户登录查单条记录确实快,但后来需求变了,要查“所有注册时间在昨天的用户”,结果哈希索引完全用不上,只能全表扫描。所以哈希索引只适合等值查询,比如WHERE user_id = 123。
适用场景:精确匹配,键值唯一或接近唯一。比如MySQL的Memory引擎默认使用哈希索引,适合临时表、缓存表。
1.3.3 位图索引
位图索引(Bitmap Index)用二进制位表示某个值是否存在。每个键值对应一个位图,位图的长度就是表的行数。如果某行有该值,对应位为1,否则为0。
特点:
- 空间压缩:对于低基数列(比如性别、状态),位图非常省空间。
- 快速交并:两个位图做AND/OR运算比B树快得多,尤其适合数据仓库里的组合查询。
但位图索引在高并发写入场景下是噩梦。因为每次更新一行,可能要修改多个位图。我记得在Oracle数据仓库项目里,给一个分区表建了位图索引,跑ETL时锁冲突严重,后来改成了B树。
适用场景:数据仓库、OLAP、低基数(比如性别、省份、状态)且读多写少的表。
下面这张表对比了三种索引的核心差异,干活的时候常回头看看:
| 特性 | B树索引 | 哈希索引 | 位图索引 |
|---|---|---|---|
| 典型数据结构 | B+树 | 哈希表 | 位图数组 |
| 查询时间复杂度 | O(log n) | O(1)(平均) | O(列基数 × 行数)(构建) |
| 支持范围查询 | 是 | 否 | 部分(需位图运算) |
| 适合基数 | 高基数 | 高基数 | 低基数 |
| 写入性能 | 良好 | 好 | 差(高并发锁) |
| 典型数据库 | MySQL InnoDB, PostgreSQL | MySQL Memory, Redis | Oracle, Greenplum |
感觉清晰了吗?我再用一张图帮你理一下思路:
选型小贴士:先看你的查询模式——等值多选哈希,范围多选B树,低基数多条件组合选位图。当然,实际中经常混用,比如MySQL InnoDB主键用B树,但也能用自适应哈希索引(AHI)做二级加速。
1.4 小结
这一章我们聊了索引的定义、作用和三大类型。总结一下:
- 索引是加速数据访问的数据结构,用空间换时间。
- B树最适合范围查询,哈希适合等值查询,位图适合低基数组合分析。
- 没有银弹,别给所有列都建索引,否则写入会崩。
我个人习惯在接手一个新系统时,先通过慢查询日志找出热点SQL,再根据查询类型反推应该建哪种索引。你看,学索引不是为了背概念,而是为了在做决定时有依据。好了,这章就到这,希望下次你在建索引时能想起今天的内容。