MySQL (InnoDB)主要使用 B+ 树索引,而不是哈希、二叉树或B树。
使用B+树的主要原因是B+树对磁盘IO友好,减少了磁盘IO操作,从而提高了查询的速度。
想象你在一本1000页的书中查找某个知识点,如果没有目录(索引),你只能一页一页的翻,这其实就是全书查找(对比到数据库就是全表扫描),这样的查找方式非常慢(效率低下)。但如果这本书有详细的目录那查找起来是不是快了很多?而B+树不仅是有目录,还是多层级目录(根目录只存大概分类、第2层目录存细化分类、。。。、最终数据-叶子节点),这样查询效率自然就高了。
B+树的非叶子节点只存储键值,不存储数据,叶子节点存数据;另外B+树是多叉树(相对二叉树),通常一个节点包含多个子节点,这就减少了树的高度。
举个栗子🌰:如果一个B+树索引有1000个分叉,那差不多只需要3层即可存储10亿条数据,这意味着查询时最多只需要3次磁盘IO。
注意:实际应用中索引键的类型和行数据的大小将直接影响数据的存储量
// InnoDB存储引擎每个节点(页)的大小通常为16KB
// 每个索引项由索引键值和指向子节点的指针组成
// 假设键值以 bigint 类型为例,需占用 8 字节,指向子节点的指针占用固定的 6 字节
// 每个索引项的大小 = 8 + 6 = 14字节
每个节点可以存储的索引项数量 = 16KB / (8B + 6B) = 1170 个
// 三层 B+ 树的存储容量计算方法如下:
第一层(根节点):存储 1170 个指向第二层的指针;
第二层(中间节点):每个节点再存储 1170 个指针 = 1170 x 1170 = 1368900 个叶子节点;
第三层(叶子节点):存储实际的数据行指针。
假设每行数据大小为1KB,则每个叶子节点(16KB)可以存储16行数据,那么三层 B+ 树的
总存储容量 = 1368900(叶子节点数)x 16(每个叶子节点存储的数据行数)= 21902400 行,
也就是大约两千一百万行数据。
所以优化MySQL性能的一个可行方案就是控制索引键和要存储数据的大小,如下表,索引键占用的空间越大每个索引页能存储的索引项就越少,从而可能导致B+树的层级增加,以至查询性能下降(磁盘IO次数变多)。
数据类型 | 占用大小 | 是否适合索引? | 备注 |
---|---|---|---|
TINYINT | 1字节 | ✅ | |
SMALLINT | 2字节 | ✅ | |
INT | 4字节 | ✅ | |
BIGINT | 8字节 | ✅ | |
CHAR(10) | 10字节 | ⚠️ | 定长字符串,存储开销比INT大 |
VARCHAR(255) | 变长(最长767字节) | ❌ | 变长字符串会导致索引存储空间大,影响性能 |
TEXT/BLOB | 不定长(存储在外部页) | ❌ | MySQL不能直接索引TEXT/BLOB类型 |
另外也可以通过启动MySQL时指定 –innodb-page-size=32K 或 通过my.cnf修改MySQL默认节点的大小的方式来降低树的高度:
[mysqld]
innodb_page_sie = 32K # 支持的页大小 4K、8K、16K、32K、64K
通过my.conf方式修改后还需重新初始化MySQL数据目录:
rm -rf /var/lib/mysql
mysqld --initialize
B+树对范围查询(BETWEEN、ORDER BY、GROUP BY等)友好:
叶子节点通过双向链表相连,支持高效的区间扫描。
B+树支持顺序查询:
由于叶子节点是有序链表,所以使用索引时可以直接进行顺序扫描,提高查询效率。
为什么 MySQL 的索引通常选择 B+ 树,而不是 B 树或哈希索引?
与B树对比,B树每个节点既存键,也存数据,这就会导致数据分布在整个树上,树的高度变大了磁盘IO的次数也就变大了,范围查询时需要回溯多个节点,效率较低。而B+树是叶子节点存储所有数据,并通过双向链表连接,范围查询性能更好。
与哈希索引对比,哈希索引适用于等值查询(如 WHERE id =100),但不适用于范围查询(如 WHERE id > 100);哈希索引无法利用有序性进行范围查询或排序,而B+树索引可以;另外,哈希索引冲突可能导致性能下降。