MySQL索引优化与查询优化(三)

146 阅读6分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路

本文参考自宋红康老师的课程

6.GROUP BY优化

  • group by使用索引的原则几乎与order by一致,group by即使没有过滤条件用到索引,也可以直接使用索引。
  • group by先排序再分组,遵循最佳左前缀法则
  • 当无法使用索引列,增大max_length_for_sort_data和sort_buffer_size参数的设置
  • where效率高于having,能在where中限定的条件就不要在having中写
  • 减少使用order by,可以把排序放到程序端去做
  • 包含了order by、group by、distinct的语句,where过滤出来的结果集尽量保持在1000行内

7.优化分页查询

EXPLAIN SELECT * FROM student LIMIT 2000000,10;

上条语句需要前2000010条记录,仅返回2000000-2000010的记录,其他记录丢弃,查询代价大。

优化思路:

  • 在索引上完成排序分页操作,最后根据主键关联回原表查询所需要的其他列内容

    SELECT * FROM student t,(SELECT id FROM student ORDER BY id LIMIT 2000000,10) a WHERE t.id = a.id;

  • 主键自增的表中,可以把limit查询转换成某个位置的查询

    SELECT * FROM student WHERE id > 2000000 LIMIT 10;

8.优先考虑覆盖索引

一个索引包含了满足查询结果的数据叫做覆盖索引。索引列+主键包含SELECT 到 FROM之间查询的列。

8.1覆盖索引的利弊

好处:

  1. 避免InnoDB表进行索引的二次查询(回表)
  2. 可以把随机IO变成顺序IO加快查询效率

弊端:

索引字段的维护。

9.给字符串添加索引

邮箱字段:email varchar(64)

9.1 前缀索引

如果创建索引的语句不指定前缀长度,那么索引就会包含整个字符串。

alter table teacher add index index1(email);

alter table teacher add index index2(email(6));

由于email(6)这个索引结构中每个邮箱字段都只取前6个字节,所以占用的空间会更小,但是可能会增加额外的记录扫描次数。

select id, name, email from teacher where email = 'zhangssxyz@xxx.com';

如果使用的是整个字符串的索引结果:

  1. 从index1索引树找到满足索引值是'zhangssxyz@xxx.com'的记录,记录ID2的值
  2. 到主键上查到主键值是ID2的行,判断email的值是正确的,将这行记录加入结果集
  3. 取index1索引树上刚查到的位置的下一条记录,发现不满足'zhangssxyz@xxx.com'条件就结束循环

如果使用的是email(6)的索引结构:

  1. 从index2索引树找到索引值是'zhangs'的记录,找到第一个ID1
  2. 到主键上查找主键是ID1的行,判断email的值不是'zhangssxyz@xxx.com',丢弃记录
  3. 取index2上刚查到位置的下一条记录,重复操作

9.2 前缀索引对覆盖索引的影响

假设定义的索引为email(11),这时执行

select id, email from teacher where email = 'zhb@xxx.com';

虽然index2已经包含了所有信息,但是InnoDB还是要拿到id回表查一次。使用前缀索引就用不上覆盖索引对查询性能的优化了。

如果要查询身份证号,我国的身份证号一共18位,其中前6位是地址码,同一个县的身份证号前6位一般相同,这时如果对身份证做长度为6的前缀索引,区分度就很低,此时需要长度为12以上的前缀索引,为了减少索引长度,有以下处理方法:

  • 倒序存储

    把身份证号倒过来存储,每次查询时

    select field_list from teacher where id_card = reverse('input_id_card_string');

  • 使用hash字段

    可以在表上再创建一个整数字段来保存身份证的校验码,在这个字段上创建索引。每次插入新记录时,同时用crc32()函数得到校验码填到这个字段

10.索引下推

Index Condition Pushdown(ICP)是MySQL5.6中的新特性,是一种在存储引擎层使用索引过滤数据的一种优化方式。ICP可以减少存储引擎访问基表的次数以及MySQL服务器访问存储引擎的次数。

10.1 使用前后的扫描过程

不使用ICP时:

  • storage层:

    只将满足index key条件的索引记录对应的整行记录取出,返回server层。

  • server层:

    对返回的数据,使用where条件过滤,直至返回最后一行。

使用ICP时:

  • storage层:

    首先将index key条件满足的索引记录区间确定,然后再索引上使用index filter进行过滤。将满足的index filter条件的索引记录取出整行记录返回server层,不满足index filter条件的索引记录丢弃,不回表,也不返回server层。

  • server层:

    对返回的数据,使用table filter条件做最后的过滤。

使用前后的成本差别:

  1. 使用前存储层多返回了需要被index filter过滤掉的整行记录。
  2. 使用ICP后,直接去掉了不满足index filter条件的记录,省去了回表和传递到server层的成本。
  3. ICP的加速效果取决于在存储引擎内通过ICP筛选掉数据的比例。

10.2 ICP的使用条件

①只能用于二级索引

②explain显示的执行计划中type值(join类型)为range、ref、eq_ref或ref_or_null。

③并非全部where条件都可以用ICP筛选,如果where条件的字段不在索引列中,还是要读取整表的记录到server端做过滤。

④ICP可以用于MyISAM和InnoDB存储引擎。

⑤5.7版本开始支持分区表的ICP功能。

⑥当SQL使用覆盖索引时,不支持ICP优化方法。

10.3 ICP使用案例

SELECT * FROM tuser

WHERE NAME LIKE '张%' AND age = 10 AND ismale = 1;

11.普通索引和唯一索引

假设有一个主键为ID的表,字段k上有索引,k上的值都不重复:

create table test

( id int primary key,

k int not null,

name varchar(16),

index (k) )engine=InnoDB;

表中R1-R5的(ID,k)值分别为(100, 1)、(200, 2)、(300, 3)、(500, 5)、(600, 6)。

11.1 查询过程

select id from test where k = 5;

  • 普通索引:

    查找到满足第一个记录(5, 500)后,需要查找下一个记录,知道碰到第一个不满足k=5记录的条件。

  • 唯一索引:

    由于索引定义了唯一性,找到了第一个满足条件的记录后就会停止继续检索。