MySQL只学有用的--给字符串添加索引,order by性能优化, count()性能优化

怎么给字符串字段添加索引

根据前一篇文章咱们知道,两个概念:web

  1. 索引的长度不宜过长
  2. 前缀索引

根据这两个概念,咱们在给字符串字段设置索引的时候就能够设置索引的长度。若是不设置,默认索引会包含整个字符串。算法

举个例子sql

select table user add index(email);
或者 
select table user add index(email(6));

第一个语句建立索引会包含整个字符串。第二个语句建立索引会只会包含前6个字符串。性能优化

在使用第二条语句的建立索引的时候,也要有必定的技巧,要注意索引的区分度,若是索引过短的话,字段的值大量重复,在搜索的时候就会出现大量回表操做。 所以索引的长度咱们是须要计算的。并发

字符串索引计算长度步骤

  1. 首先,你可使用下面这个语句,查询这个列上有多少个值。
select count(distinct email) as sum from user;

而后,依次选取不一样长度的前缀来看这个值,好比咱们要看一下4~7个字节的前缀索引,能够用这个语句 :svg

select 
count(distinct left(email,4)) as l4,
count(distinct left(email,5)) as l5
count(distinct left(email,6)) as l6,
count(distinct left(email,7)) as l7,
from user;

固然,使用前缀索引极可能会损失区分度,因此你须要预先设定一个能够接受的损失比例,好比5%。 而后,在返回的L4~L7中,找出不小于L*95%的值,假设这里L6和L7都知足,你就能够选择前缀长度为6。函数

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

看下面这个语句性能

select id,email from user where email= "xiaowang@gmail.com";

当咱们查询这个语句的时候,若是使用的email索引是全索引的话,这个时候是可使用索引覆盖的,由于普通索引中的值是主键索引的值。而若是咱们在建立索引的时候使用了前缀索引,就没法使用索引覆盖了,会进行回表操做。
即便咱们在建立索引的时候,指定的长度就是字段的长度也会进行回表操做。由于innoDB会认为咱们使用了前缀索引。认识这个索引的值是不全的。优化

count统计的相关玩法

因为MySQL不一样引擎使用的计算方法不一致,这里只聊一聊InnoDB引擎。spa

InnoDB引擎count()原理分析

InnoDB引擎在执行count(*)的时候,须要把数据一行一行地从引擎里面读出来,而后累积计数。这是由于即便是在同一个时刻的多个查询,因为多版本并发控制(MVCC)的缘由,InnoDB表“应该返回多少行”也是不肯定的。

假设表t中如今有10000条记录,咱们设计了三个用户并进行会话。

  • 会话A先启动一个事务并查询一次表的总行数。
  • 会话B启动事务,插入一行记录后,查询表的总行数。
  • 会话C先启动一个单独的语句,插入一行记录后,查询表的总行数。

在这里插入图片描述
你会看到,在最后一个时刻,三个会话A,B,C会同时查询表T的总行数,但拿到的结果却不相同。

这和InnoDB的事务设计有关系,可重复读是它的默认的隔离级别,在代码上就是经过多版本并发控制,也就是MVCC来实现的。第一行记录都要判断本身是否对这个会话可见,所以对于count(*)请求来讲,InnoDB只好把数据一行一行地读出依次判断,可见的行才可以用于计算“基于这个查询”的表的总行数。
MySQL会尽可能的走普通索引,由于普通索引的值是主键值,总体比较小。

对比一下count(*) count(主键)、count(字段)、count(1)

  1. count(主键ID),InnoDB引擎会遍历整张表,把每一行的ID值都取出来,返回给server层。server层拿到ID后,判断是不可能为空的,就按行累加。
  2. count(1),InnoDB引擎遍历整张表,但不取值。server层对于返回的每一行,放一个数字"1"进行,判断是不可能为空的,按行累加。

单看这两个用法的差异的话,你能对比出来,count(1) 执行得要比count(主键ID)快。由于从引擎返回ID会涉及到解析数据行,以及拷贝字段值的操做。

  1. count(字段), 须要一行一行的读记录,若是在定义字段的时候设置为not null则直接累加,不然就须要先判断是否为null 才会累加。

  2. count(*),并不会把所有字段取出来,而是专门作了优化,不取值,直接按行累加。

若是表中有普通索引,count(1)和count(*)都会选择走一个最短的普通索引。

在这里插入图片描述
按照效率排序:
count(字段)<count(主键ID)<count(1)≈count()
建议直接使用count(
) 就行了。

Order By 排序

order by 排序算法有两种rowId 和全字段排序。

全字段排序: InnoDB有一个sort_buffer,会将数据查询出来放到sort_buffer中,在进行排序。sort_buffer的大小是由一个sort_buffer_size设置。
若是在排序的数据太大,sort_buffer内存中放不下,则不得不利用磁盘临时文件辅助排序。就是将数据分别放入到磁盘中的临时文件进行排序,在将这些小的有序文件,合并成一个大的有序文件,返回。

rowid排序: 只会将须要排序的字段和主键放到sort_buffer中,先排序在拿id去查询出,须要返回的数据。

set max_length_for_sort_data = 16

max_length_for_sort_data是MySQL中专门控制用于排序的行数据的长度的一个参数。它的意思是,若是单行的长度超过这个值,MySQL就认为单行太大,就会采用rowid这种算法。
以上都是InnoDB引擎自动的优化点。

利用索引

咱们知道InnoDB的索引有这样两个特性

  1. 有序
  2. 索引覆盖
    当咱们查询的SQL要查询的字段是索引覆盖的内容,排序的字段上也创建了索引,就会简化整个过程。

咱们先创建索引

alter table t add index city_user_age(city,name,age)

在这里插入图片描述
能够看到extra上少了 “Using filesort”, “Using index condition”. 多了一个"Using index",表示的就是使用了索引覆盖,性能上会快不少。

若是只建立了city,name这两个联合索引的话就会出来这样的状况。
在这里插入图片描述
发现少了"Using filesort",也是少了排序这个步骤直接返回结果,只是须要进行回表操做。 这样也很快了。

小结

咱们聊了String加索引的办法使用前缀索引, 并聊了聊建立前缀索引的时候须要注意辨识度,要让索引中值的重复度下降。

咱们还聊了聊统计函数count(),咱们需优先使用count(*), Mysql 会自动选择一个最小的普通索引来进行查询。

咱们还聊了排序Order by, 咱们在可能的状况下要优先利用索引覆盖这个原则。如是不行的话,也尽量的利用索引的有序性。

引用

《Mysql实战45讲》,count(1)和count(*)的对比

以上内容均为读书所得,若有错误请联系。

交个朋友好

以上内容均为读书所得, 更多有趣有料的科技资讯请关注公众号。(交个朋友)

在这里插入图片描述