groupby很多字段是不是会很慢_为什么你的SQL执⾏很慢当我们碰到MySQL的⼀些异常或者问题时,应该要有全局观,这样能够帮助你理解问题,更为快速地定位并解决问题。
下⾯我给出的是MySQL的基本架构⽰意图,从中你可以清楚地看到SQL语句在MySQL的各个功能模块中的执⾏过程。
通过以上图⽚,我们清楚地看到SQL语句在MySQL的各个功能模块中的执⾏过程。但和sql执⾏效率相关的主要是优化器和执⾏器,同时查询缓存也有⼀定的影响作⽤。
查询缓存(适⽤于查询语句)
MySQL拿到⼀个查询请求后,会先到查询缓存看看,之前是不是执⾏过这条语句。之前执⾏过的语句及其结果可能会以key-value对的形式,被直接缓存在内存中。key是查询的语句,value是查询的结果。如果你的查询能够直接在这个缓存中到key,那么这个value就会被直接返回给客户端。
如果语句不在查询缓存中,就会继续后⾯的执⾏阶段。执⾏完成后,执⾏结果会被存⼊查询缓存中。你可以看到,如果查询命中缓
但是⼤多数情况下我会建议你不要使⽤查询缓存,为存,MySQL不需要执⾏后⾯的复杂操作,就可以直接返回结果,这个效率会很⾼。但是⼤多数情况下我会建议你不要使⽤查询缓存,为什么呢?因为查询缓存往往弊⼤于利。
查询缓存的失效⾮常频繁,只要有对⼀个表的更新,这个表上所有的查询缓存都会被清空。因此很可能你费劲地把结果存起来,还没使⽤呢,就被⼀个更新全清空了。对于更新压⼒⼤的数据库来说,查询缓存的命中率会⾮常低。除⾮你的业务就是有⼀张静态表,很长时间才会更新⼀次。
需要注意的是,MySQL 8.0版本直接将查询缓存的整块功能删掉了,也就是说8.0开始彻底没有这个功能了。
优化器(索引影响执⾏效率)
为了⽅便说明问题,这⾥先给出建表语句:
mysql> CREATE TABLE `t` (
`id` int(11) NOT NULL PRIMARY KEY,
`c` int(11) DEFAULT NULL,
`d` varchar(20) DEFAULT NULL,
) ENGINE=InnoDB;
1.字段没有索引
例如你要查询这条语句:
select * from t where 100 <c and c < 100000;
刚好你的 c 字段上没有索引,那么抱歉,只能⾛全表扫描了,你就体验不会索引带来的乐趣了,所以,这回导致这条查询语句很慢。
2.字段有索引,但⽤索引失效
(1)运算操作导致没有⽤上索引
给 c 这个字段加上了索引,然后⼜查询了⼀条语句
select * from t where c - 1 = 1000;
正确的查询应该如下:
(select * from t where c = 1000 + 1;
为什么对索引字段进⾏了计算操作之后,就不会⾛索引了呢?
这⾥我们需要明⽩,⾛索引的本质其实是利⽤了B+树的有序性以便进⾏快速定位。但是对索引字段进⾏计算操作之后,有可能会破坏这种有序性(⾮线性计算),导致⽆法利⽤B+树的这⼀特性,因此优化器会放弃使⽤B+树的树搜索功能。
(2)函数操作导致没有⽤上索引(原因同运算操作)
如果我们在查询的时候,对字段进⾏了函数操作,也是会导致没有⽤上索引的,例如
select * from t where pow(c,2) = 1000;
(3)like操作
在like操作中,当%在前⾯时,也不会⾛索引。
groupby是什么函数
select * from t where d like"%1";
(4)隐式转换导致索引失效.
在MySQL中,每张表都可以单独指定其字符集,最常见的就是utf8和utf8mb4了,当两张表字符集不同时,进⾏联表操作会存在字符集转换,从⽽导致索引失效。
错误的例⼦:
select * from t where d=1;
正确的例⼦:
select * from test t d='1';
(5)not in 操作
MySQL5.6之前的版本,not in操作也会导致索引失效。但在MySQL5.6引⼊ICP优化之后,not in操作也是可以⾛索引的,请看:
select * from t where c not in (1,2);
所谓ICP,全称Index Condition Pushdown(索引条件下推),即在索引遍历过程中,对索引中包含的字段先做判断,过滤掉不满⾜条件的记录,如此⼀来就可以减少回表次数,提升性能。
关于索引失效还有许多,这⾥就不详细介绍了,具体参考索引失效原因总结 - 梦中⼭河 - 博客园
3.扫描⾏数太多
select * from t where 100 < c and c < 100000;
当访问的数据占全表数据较⼤时,优化器会放弃辅助索引⽽直接全表扫描。
我们知道选择索引时优化器的⼯作,优化器在决定使⽤哪个索引时会综合考虑扫描⾏数、是否使⽤临
时表、是否排序等信息,在我们这个例⼦中显然就是扫描⾏数的原因。
那么在执⾏语句之前,优化器是怎么知道扫描⾏数的呢?这⾥就需要提到索引区分度了,也就是索引Cardinality(基数),这是⼀个统计信息,⽤于统计索引中不重复记录的个数。我们可以⽤show index命令看下。有时候你会看到主键的基数⽐⾏数还多,这是什么原因?
因为基数本⾝其实是⼀个预估值,如果通过全表扫描的⽅式来精确统计的话,每次发⽣变更都需要做⼀次全表扫描,在⽣产环境这是不可接受的。所以InnoDB是通过采样的⽅式来预估的,既然是预估,那么就有可能不准确。
因此,当基数统计不准确的时候,优化器就有可能不⾛索引或者选错索引,针对这种场景,我们有三种解决⽅案:
不过呢,我们有时候也可以通过强制⾛索引的⽅式来查询,例如
select * from t force index(a) where c < 100 and c < 100000;
我们也可以通过:
show index from t;
来查询索引的基数和实际是否符合,如果和实际很不符合的话,我们可以重新来统计索引的基数,可以⽤这条命令
analyze table t;
来重新统计分析。
4.主键索引和⾮主键索引
为了说明这个我们再创建⼀个表,这个表的建表语句是:
mysql> create table T
( `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),两棵树的⽰例⽰意图如下。
从InnoDB的索引组织结构从图中不难看出,根据叶⼦节点的内容,索引类型分为主键索引和⾮主键索引。主键索引的叶⼦节点存的是整⾏数据。在InnoDB⾥,主键索引也被称为聚簇索引(clustered index)。⾮主键索引的叶⼦节点内容是主键的值。在InnoDB⾥,⾮主键索引也被称为⼆级索引(secondary index)。
基于主键索引和普通索引的查询有什么区别?
根据上⾯的索引结构说明,我们来讨论⼀个问题:基于主键索引和普通索引的查询有什么区别?
如果语句是select * from T where ID=500,即主键查询⽅式,则只需要搜索ID这棵B+树;
如果语句是select * from T where k=5,即普通索引查询⽅式,则需要先搜索k索引树,得到ID的值为500,再到ID索引树搜索⼀次。这个过程称为回表。
也就是说,基于⾮主键索引的查询需要多扫描⼀棵索引树。因此,我们在应⽤中应该尽量使⽤主键查询。
执⾏器
我们还是从⼀个表的⼀条更新语句说起,下⾯是这个表的创建语句,这个表有⼀个主键ID和⼀个整型字段c:
mysql> create table T(ID int primary key, c int);
如果要将ID=2这⼀⾏的值加1,SQL语句就会这么写:
update T set c=c+1 where ID=2;
这⾥我给出这个update语句的执⾏流程图,图中浅⾊框表⽰是在InnoDB内部执⾏的,深⾊框表⽰是在执⾏器中执⾏的。(与查询流程不⼀
样的是,更新流程还涉及两个重要的⽇志模块,即:redo log(重做⽇志)和 binlog(归档⽇志)。)
在这些过程中redo log以及事务进⾏状态下遇到的锁也会影响sql的执⾏效率
1.刷脏页
为了保证事务的持久性,InnoDB引擎采⽤了Write Ahead Log(WAL)策略,即事务提交时,先写⽇志,再写磁盘。当然在写⽇志之前会更新内存,⽽这⾥的⽇志⾃然也就是redo log了。
当内存中的数据页相对磁盘的数据页发⽣变化时,我们称该内存页为脏页,反之则为⼲净页。从持久性上考虑,脏页是必须要刷回磁盘,这
那么什么情况会触发MySQL的刷脏页操作呢?个过程我们称之为刷脏页,⽽这个过程中就有可能导致平时执⾏很快的SQL突然变慢了。那么什么情况会触发MySQL的刷脏页操作呢?⼀般来说有以下4种情况:
我们知道redo log区别于binlog的⼀个特点是容量有限、循环写⼊。因此当redo log写满时,必须停⽌其它所有更新操作来将redo log 写⼊磁盘
因为脏页是存在于内存中,因此当内存不够⽤时,需要淘汰⼀部分数据页,如果淘汰的是脏页,就需要先将脏页同步到磁盘
MySQL认为系统⽐较空闲的时候
MySQL正常关闭的时候
⽽在这4种情况中,对我们影响最⼤的就是redo log写满的场景,因为⼀旦出现这种情况,整个系统将不再接受更新,这种场景需要尽量避
免。另外如果内存不够⽤,需要淘汰的脏页太多,也会明显影响性能。所以控制好脏页刷新的机制很重要。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。