MySQL的索引策略(1)

索引提供了快速的查询方案,同时增长了删除、修改的开销;续上一篇博客《MySQL索引基础》,根据索引的优缺点,本文列举一些高效的MySQL策略。mysql

  • 不参与计算

select id from table where id+1<10sql

select * from table where to_days(current_data)-to_days(date) <=10数据库

相似上述查询,将索引列写进数学表达式,或者做为函数的一部分,都会引发索引失效。缓存

  • 前缀索引

​​​​​​​上篇博客提到了模拟哈希索引在长值字段的应用。相似状况,还可使用前缀索引,即只对字段开始的前n个字符创建索引。前缀索引是牺牲索引选择性,换取创建索引效率的方案。索引选择性是衡量索引对字段区分能力的指标,即 count(distinct(column))/count(*);选择性越大,对数据的区分度越好,则查询效率越高。例如,对美国城市名作前缀索引,取三位,则San字段会有不少重复名(旧金山、圣塔芭芭拉),总体选择性会降低。除了索引的选择性,还要考虑前缀的分布状况,尽可能均匀,或者在高频查询的数据行有良好的区分性。并发

MySQL没法基于前缀索引作ORDER BY和GROUP BY操做。逆序字符串的前缀索引就变成了后缀索引,在电子邮箱查询等特定场景有不错的效果。函数

  • 多列索引

​​​​​​​多条件查询很常见,此时就须要创建多列索引。尤为是OR操做,耗费大量资源用于缓存、排序、合并,这些都不会被优化器计算进查询成本,影响并发性。能够用 EXPLAIN ${SQL}查看,若是有索引合并,也说明这种场景须要多列索引。性能

多列索引须要合理的顺序,以知足:1. 尽量快地查讯 2.知足ORDER BY和GROUP BY操做  3.尽可能知足覆盖索引。优化

简单地,能够讲选择性最高的列放在最左。spa

mysql> SELECT SUM(staff_id = 2), SUM(customer_id = 584) FROM payment\G
*************************** 1. row ***************************
SUM(staff_id = 2): 7992
SUM(customer_id = 584): 30

mysql> SELECT SUM(staff_id = 2) FROM payment WHERE customer_id = 584\G
*************************** 1. row ***************************
SUM(staff_id = 2): 17

mysql> SELECT COUNT(DISTINCT staff_id)/COUNT(*) AS staff_id_selectivity,
> COUNT(DISTINCT customer_id)/COUNT(*) AS customer_id_selectivity,
> COUNT(*)
> FROM payment\G
*************************** 1. row ***************************
staff_id_selectivity: 0.0001
customer_id_selectivity: 0.0373
COUNT(*): 16049

第一个查询代表,customer_id为584对应的行更少。第二个查询说明在customer_id=584时,staff_id具备良好的选择性。第三个查询显示,总体上,customer_id具备更好的选择性。因此这个例子符合选择性最高的列放在最左的原则。须要注意,查询1和2的结果,依赖于选定的具体值,可能会对其余值产生偏见指针

mysql> EXPLAIN SELECT COUNT(DISTINCT threadId) AS COUNT_VALUE
-> FROM Message
-> WHERE (groupId = 10137) AND (userId = 1288826) AND (anonymous = 0)
-> ORDER BY priority DESC, modifiedDate DESC

id: 1
select_type: SIMPLE
table: Message
type: ref
key: ix_groupId_userId
key_len: 18
ref: const,const
rows: 1251162
Extra: Using where

mysql> SELECT COUNT(*), SUM(groupId = 10137),
-> SUM(userId = 1288826), SUM(anonymous = 0)
-> FROM Message\G
*************************** 1. row ***************************
count(*): 4142217
sum(groupId = 10137): 4092654
sum(userId = 1288826): 1288496
sum(anonymous = 0): 4141934

第一个查询显示,该表有一个(groupId, userId)的联合索引。但第二个查询代表,这两列上的索引选择性都很低,基本失效。这类问题不能在数据库层面解决,须要在业务上特殊处理这些用户和组,好比禁止相关id查询这条SQL。这类问题比较广泛,好比对全部访客固定一个ID,或者拥有大量好友、评论等的用户。

还有一个重要的原则,考察该表上执行各种条件查询的频率,其相关字段是创建联合索引的重要依据。

  • 聚簇索引

​​​​​​​聚簇索引是一种存储方式,InnoDB中,在同一个结构中保存了索引和数据行。即非叶节点保存索引,叶子节点保存数据,与B+树的定义一致。InnoDB经过主键汇集数据,也就是对主键列聚簇索引。聚簇索引的优点以下:

  1. 相关数据连续存储(减小磁盘IO)。例如,邮箱数据,以用户id为主键,能够用较少的IO取出与某用户全部往来邮件。
  2. 访问速度更快。数据与索引在同一个树中。
  3. 能够直接使用叶子页中的主键值。

​​​​​​​聚簇索引的缺陷以下:

  1. 若是全部数据都放进内存,那么访问顺序的影响就会变小。
  2. 插入顺序影响插入速度;以主键顺序的插入的速度最快。
  3. 更新代价很高。会把对应行移动到新位置,其余行的位置跟着改变。
  4. 插入或者更新,某列插入已满的页,则须要分裂页,占用更大的空间。
  5. 致使全表扫描变慢。
  6. 二级索引(即非聚簇索引)更大,由于在叶子节点包含了引用行的主键列。因此二级索引的工做顺序是:在二级索引中查找主键->在聚簇索引中查找行,因此作了两次B+树搜索(顾名思义?)。

简单说,聚簇索引只能索引一列,一般是主键;全部数据是按照聚簇索引列的顺序连续排列的。

​​​​​​​如下表为例,对比一下InnoDB聚簇索引和MyISAM中数据存储的方式。

CREATE TABLE layout_test (
col1 int NOT NULL,
col2 int NOT NULL,
PRIMARY KEY(col1),
KEY(col2)
);

假设col1的值在1-10000,在磁盘上行的顺序随机;col2取值在1-100,有不少重复值。

MyISAM按照插入顺序存储。

这种方式能够直接根据行号查找到数据,索引中只需保存行号。主键索引和col2索引以下图:

MyISAM的主键索引与其余列的索引并没有差别;不过是知足了惟1、为空的索引。

InnoDB支持聚簇索引,其索引和数据分布状况以下图。

一个显著的不一样是 ,聚簇索引存储了整张表;即便主键是列前缀也如此。

回滚指针用于事务和MVCC。

InnoDB的二级索引与主键索引不一样。

用主键值代替“行指针”的优点是,非主键列的插入、更新不会引发大量移动、也分裂;缺陷是占用了更大的存储空间。这二者在数据存储时的对比,可抽象为下图。

基于插入性能的考虑,InnoDB表的主键能够定义为自增列。一方面保证了顺序写入,另外一方面在主键关联时性能也更好。同时,还能节约索引空间。

其余几类索引策略将在下一篇博客中介绍。

相关文章
相关标签/搜索