MySql 索引类型 BTree 和 Hash
MySql 索引类型 BTree 和 Hash
索引是帮助mysql获取数据的数据结构。最常见的索引是Btree索引和Hash索引。主键索引叶子节点存储的就是 MySql 的整个数据行,普通索引的叶子节点存储的是索引列和主键值。
不同的引擎对于索引有不同的支持:Innodb 和 MyISAM 默认的索引是 Btree 索引;而 Mermory 默认的索引是 Hash 索引。
一、BTree
BTree 索引是最常用的 mysql 数据库索引算法,因为它不仅可以被用在 =, >, >=, <, <= 和 between 这些比较操作符上,而且还可以用于 like 操作符,只要它的查询条件是一个不以通配符开头的常量,例如:
- select * from user where name like ‘jack%’;
- select * from user where name like ‘jac%k%’;
如果一通配符开头,或者没有使用常量,则不会使用索引,例如:
- select * from user where name like ‘%jack’;
- select * from user where name like simply_name;
二、Hash
Hash 索引只能用于对等比较,例如 = ,<=>(相当于 = )操作符。由于是一次定位数据,不像 BTree 索引需要从根节点到枝节点,最后才能访问到页节点这样多次 IO 访问,所以检索效率远高于 BTree 索引。
但为什么我们使用 BTree 比使用 Hash 多呢?主要 Hash 本身由于其特殊性,也带来了很多限制和弊端:
- Hash索引仅仅能满足=, IN, <=> 查询,不能使用范围查询。
- 联合索引中,Hash索引不能利用部分索引键查询。对于联合索引中的多个列,Hash 是要么全部使用,要么全部不使用,并不支持 BTree 支持的联合索引的最优前缀,也就是联合索引的前面一个或几个索引键进行查询时,Hash 索引无法被利用。
- Hash 索引无法避免数据的排序操作
由于 Hash 索引中存放的是经过 Hash 计算之后的 Hash 值,而且 Hash 值的大小关系并不一定和 Hash 运算前的键值完全一样,所以数据库无法利用索引的数据来避免任何排序运算。 - Hash 索引任何时候都不能避免表扫描
Hash 索引是将索引键通过 Hash 运算之后,将 Hash 运算结果的 Hash 值和所对应的行指针信息存放于一个 Hash 表中,由于不同索引键存在相同 Hash 值,所以即使满足某个 Hash 键值的数据的记录条数,也无法从 Hash 索引中直接完成查询,还是要通过访问表中的实际数据进行比较,并得到相应的结果。 - Hash 索引遇到大量 Hash 值相等的情况后性能并不一定会比 BTree 高
对于选择性比较低的索引键,如果创建 Hash索引,那么将会存在大量记录指针信息存于同一个 Hash 值相关联。这样要定位某一条记录时就会非常麻烦,会浪费多次表数据访问,而造成整体性能底下。
三、聚簇、非聚簇索引
聚簇、非聚簇说白了就是是索引的存储方式
- 聚簇索引的叶子节点就是数据节点(主键一定是聚簇索引)
- 非聚簇索引的叶子节点是保存了对应数据块指针的索引节点
MyISAM 没有聚簇索引
对于如下的 sql 语句,直接根据主键查询有所字段信息,此时主键是聚簇索引,因为主键对应的索引叶子节点上保存了所有的数据:
1 | select * from students where id = 1; |
对于如下的 sql 语句,根据 no 查询编号和姓名,编号 no 本身是一个索引,当初查询命中索引时,由于查询的字段包含姓名。而 no 索引的叶子节点中保存的是主键 id,还要根据主键 id 再去查询姓名字段,所以此时的 no 索引不是聚簇索引
1 | select no,name from students where no = 1001; |
对于如下的 sql 语句,根据 no 查询编号,编号 no 本身是一个索引,当初查询命中索引时,直接返回编号,不需要回表查询,这种场景下 no 是聚簇索引
1 | select no from students where no = 1002; |
总结
- hash 索引查找数据基本上能一次定位数据,当然有大量碰撞的话性能也会下降。而 btree 索引就得在节点上挨着查找了,很明显在数据精确查找方面 hash 索引的效率是要高于 btree 的;
- 那么不精确查找呢,也很明显,因为 hash 算法是基于等值计算的,所以对于 like 等范围查找 hash 索引无效,不支持;
- 对于 btree 支持的联合索引的最优前缀,has 也是无法支持的,联合索引中的字段要么全用要么全不用。
- hash 不支持索引排序,索引值和计算出来的 hash 值大小并不一定一致。