最近在看《高性能 MySQL》,里面有一个章节是“创建高性能的索引”。决定写篇博客记录一下我自己的一些总结和心得。
我们的问题就是:怎么建索引?怎么建合适的高效的索引?
关注索引选择性
什么是索引选择性?
索引的选择性是指,不重复的索引值(也称为基数,cardinality)和数据表的记录总数(#T)的比值,范围从 1/#T 到 1 之间。
这是书里面关于索引选择性的定义。索引选择性越接近 1 性能越好。我们来思考一下为什么。
首先我们先回忆索引的作用:它其实就是用来帮助数据库告诉筛选数据用的。而重复的索引值越少,同一个索引值所对应的数据行也就越少,性能也就越高。
至此,我们就有了建索引时的第一个考虑因素:选择性。
在多列索引下,选择合适的索引顺序
范围查询
我们先看一个 SQL 语句:
select A, B, C where A = 'a' and B = 'b' order by C;
这个时候我们要怎么样为 A,B,C 建立索引呢?在考虑建立索引之前,我们先回忆一下索引的数据结构。
稍稍搜索一下,我们就能知道索引是一个 B-Tree 结构。B-Tree 里面是按顺序存储索引值的。而在多列索引里面,则是按索引的定义顺序排序的。比如我们将索引定义为 (A,B,C) 这个时候,数据库可以根据索引找到 A,然后找到 B,最后范围查询 C。而假如我们将索引定义为 (C,B,A)。数据库可以根据索引找到 C。但是 A 和 B 就只能扫描索引检索出来的数据了。所以在这个例子里,C 应该放在多列索引的最后一列。
选择性
前面我们提到了选择性,在多列索引下同样存在选择性的问题,在设计多列索引的时候。应该尽量将选择性高的列放前面,这样才能尽量高效地利用索引。
最左前缀原则
其实讲第一点的时候应该就有所感悟了。对于索引 (A,B,C) 来说,数据库可以根据索引查到 A,B,这个和建立索引 (A,B) 的效果是一致的。这就是索引的一个最左前缀原则。
索引的大小
其实这一项主要是针对聚簇索引的。在聚簇索引里面,索引列的值是直接存在索引表里面的。那在索引设计的时候就要考虑不要因为索引值导致索引表过大。
以 URL 为例,”http://robingong.com/mysql-index/” 非常长,不适合直接存入索引表。要解决这个问题,有两个方案:
- 使用前缀索引,当然在 URL 这个例子不是太适合,因为前缀的重合度非常高。需要将 URL 反转之后再使用前缀索引。
- 对 URL 进行 hash 计算,使用 hash 只作为索引。在这里我们同样需要考虑一个问题:多长的 hash 值才能尽量减少空间的同时避免 hash 冲突。这里,我推荐阮一峰的一篇博客:哈希碰撞与生日攻击。
覆盖索引
如果数据库能够从索引里面拿到所需要的值,他就不会再访问数据行。利用这一点,可以对常用的数据列建立覆盖索引。
索引的成本
建索引并不是一个一本万利的事情。索引带来了查询的高性能,但也带来了插入时的额外消耗。所以对于那些频繁插入更新的数据要谨慎使用索引。
总结
以上,就是我认为在 MySQL 数据库建立索引时需要考虑的一些因素。总的来说,还是根据索引本身的一些特性出发。来让索引的工作更高效更符合我们的预期。