InnoDB调优-索引优化策略

1、覆盖索引

覆盖索引(Covering Index)也叫索引覆盖,最早是在 InnoDB Plugin 中完成的,MySQL5.0 版本之后InnoDB存储引擎开始提供支持。

所谓覆盖索引,即从辅助索引中就可以得到查询数据,而不需要查询聚集索引中的记录。简单的说,就是辅助索引包含了(或者说覆盖了)所有需要查询的字段的值。

InnoDB存储引擎 在使用辅助索引进行查询操作时,会先根据辅助索引查询到键值和聚集索引键( 辅助索引的叶子节点包含键值和 bookmark ,而对于 InnoDB存储引擎来说 bookmark 就是相应行数据的聚集索引键),然后再通过聚集索引键去访问实际的数据文件。也就是说,这是进行了 2 次索引查找的。

辅助索引不包含整行记录的所有信息,所以其大小是要远比聚集索引小的;如果能够在第一次根据辅助索引查找的时候得到所要的数据,就能减少大量的IO操作。

在可以进行索引覆盖的情况下,优化器通常是不会选择通过聚集索引来进行查询的。例如表 t_test 同时存在聚集索引键 id 和 辅助索引键 index_n

create table t_test(
id bigint primary key not null auto_increment,
name varchar(50) not null,
age int,
phone varchar(50),
index index_n(name)
);

此时在进行查询时,如果能够满足覆盖索引,InnoDB存储引擎并不会使用聚集索引,而是会仅仅执行一次辅助索引来完成查询。例如这里对主键 id 、索引列 name 进行查询,因为这 2 列的值都可以在辅助索引上找到,所以这里只会执行一次索引查找

select id, name from t_test;
select id, name from t_test where name = '123456';

这里写图片描述

而如果在需要查询的列里面加入 phone ,因为 phone 的值无法在辅助索引中找到,此时便无法满足索引覆盖

select id, name, phone from t_test;

这里写图片描述

需要注意的是,在使用 count(*) 进行统计操作时,因为辅助索引远小于聚集索引,所以优化器同样也将会选择辅助索引

select count(*) from t_test;

这里写图片描述

另一方面,我们知道 InnoDB存储引擎 在根据多列索引(联合索引)进行查询时,索引的顺序十分重要;必须按照索引的最左列进行查找,而不能跳过某列。例如在下面这个表中,在列 namephone 上建立了多列索引 index_np

-- 创建含有联合索引的表
create table t_test(
id bigint primary key not null auto_increment,
name varchar(50) not null,
age int,
phone varchar(50),
index index_np( name, phone)
);

此时要若使用索引,必须从最左列(这里即为列 name)开始,否则将无法使用

-- 有效的使用了索引
select * from t_test where name = '张三';

这里写图片描述

-- 不是最左列(跳过了多列索引的最左列name)无法使用索引
select * from t_test where phone = '123456';

这里写图片描述

但是在对其进行统计操作的时候,由于能够满足覆盖索引,此时的优化器将会使用索引

select count(*) from t_test where phone = '123456';

这里写图片描述

2、索引提示

MySQL数据库支持索引提示(Index Hint),显式地告诉优化器使用哪个索引。

在新版本的MySQL数据库中,优化器有了很大的进步,在绝大部分情况下工作得都非常有效和正确。但这并不意味着就不会出现因为错误地选择了某个索引,而导致SQL语句运行的很慢的情况;也无法避免在可以选择的索引非常多的时候,优化器选择执行计划时间的开销大于SQL语句本身的情况发生。

如果在认为使用辅助索引能够提升查询性能时,可以尝试使用 USE INDEX 或者 FORCE INDEX 提示优化器使用某个索引。例如表中有索引 i_ni_p , 默认情况下同时使用这2各索引进行查询,优化器会选择2者之一进行使用

create table t_test(
id bigint primary key not null auto_increment,
name varchar(50) not null,
age int,
phone varchar(50),
index i_n ( name),
index i_p ( phone)
);

select * from t_test where name = '张三' and phone = '123456';

这里写图片描述

使用 FORCE INDEX 指定优化器使用 i_p 索引后,优化器将会使用指定的索引进行查询

select * from t_test force index( i_p) where name = '张三' and phone = '123456';

这里写图片描述

需要注意的是, USE INDEX 只是告诉优化器可以选择该索引,实际上优化器还是会再根据自己的判断进行选择。如果用户确定指定某个索引来完成查询,那么最可靠的是使用 FORCE INDEX ,而不是 USE INDEX

当然,MySQL数据库除了允许强制使用索引之外,还允许允许强制不使用索引,即忽略索引。例如对主键进行忽略,然后再根据主键去查询,此时优化器对数据进行查找便不会使用主键

select * from t_test ignore index ( PRIMARY) where id = 1;

这里写图片描述

3、Multi-Range Read(MRR)优化

Multi-Range Read(MRR)优化是 MySQL 5.6 版本时引入的新特性,其作用就是将随机访问转化为较为顺序的访问,以达到减少磁盘的随机访问的目的。

对于 InnoDB和MyISAM存储引擎 的范围查询和 JOIN 查询操作,MRR 会先将查询到的辅助索引值存放在一个缓存中,接着将缓存中的键值根据 RolwId 进行排序,最后根据 RolwId 的排序顺序来访问实际的数据文件。

启用 MRR 优化后,在查询执行计划中的 Extra 上能够看到有 Using MRR 标识

-- 表包含辅助索引键
create table t_test(
id bigint primary key not null auto_increment,
name varchar(50),
age int not null,
index ia( age)
);

-- 根据辅助索引进行范围查询
select * from t_test where age > 30 and age <= 100;

这里写图片描述

顺序访问数据除了能减少磁盘随机访问之外,还带来了其他好处。例如,在 InnoDB和MyISAM存储引擎 的缓冲池不足以放下一张表中的所有数据时,频繁的离散读操作还会导致缓存中的页被替换出缓冲池,然后又不断地被读入缓冲池;若是按照主键顺序进行访问,则可以将这一重复行为降为最低。

另一方面,MMR 还可以将某些范围查询拆分为键值对,以此来进行批量数据查询。在拆分的过程中过滤一些不符合查询条件的数据,使查询的性能得到提升。

Multi-Range Read优化 可以通过 optimizer_switch 变量中的 mrrmrr_cost_based 标记(flag)来控制

set @@optimizer_switch='mrr=on,mrr_cost_based=off';

其中 mrr 标记表示是否启用 Multi-Range Read优化,on 为启用。而 mrr_cost_based 标记则表示是否通过 cost based 的方式来选择是否启用;若将 mrr 设为 on,mrr_cost_based 设为 off,则总是启用 Multi-Range Read优化。

Multi-Range Read优化可适用于range、ref、eq_ref类型的查询。

4、Index Condition Pushdown(ICP)优化

Index Condition Pushdown(ICP)优化同样是MySQL 5.6 版本引入的新特性,通过减少上层SQL层对记录的FETCH(索取),从而提高数据库的整体性能。

MySQL数据库根据索引进行查询操作时,首先是通过索引找到数据所在的页,然后把页读入到内存,再交由SQL层进行过滤,最后得到要查找的数据。

而在支持 Index Condition Pushdown 优化后,MySQL数据库会在取出索引的同时,判断是否可以进行 WHERE 条件的过滤,也就是将 WHERE 条件部分过滤操作放在了存储引擎层。

启用 Index Condition Pushdown 优化后,在根据辅助索引进行查询时会同时过滤 WHERE 条件,在 Extra 上能够看到有 Using index condition 标识

 select * from t_test where age between 20 and 40 and name = '张三';

这里写图片描述

Index Condition Pushdown优化默认是开启的,可以通过 optimizer_switch 变量的 index_condition_pushdown 标志(flag)对其进行控制

 set @@optimizer_switch='index_condition_pushdown=on';

Index Condition Pushdown优化支持 range、ref、eq_ref、ref_or_null 类型的查询,当前支持 InnoDB 和 MyISAM存储引。需要注意,Index Condition Pushdown优化在 MySQL5.6 版本不支持分区表。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值