为啥你这个sql怎么慢呢

深渊向深渊呼唤

目录

sql执行慢 偶尔很慢 数据库在刷新脏页 拿不到锁 一直很慢 没用上索引 数据库选错索引 总结

sql执行慢

​ 有些一句简单的sql看起来平平无奇,但是却执行很慢,一般存在这么几种可能性:

大多数情况下还可以,只是偶尔出现慢的情况 在数据量不变的情况下,这条sql语句一直执行很慢

偶尔很慢

​ 这种情况下,由于大多数场合下还是比较快的,具有偶发性,一般来说这条sql的书写本身没什么问题,更多的问题在于其他的外部原因导致的

数据库在刷新脏页

​ 要知道,我们往数据库里插入一条数据,或者更新一条数据的时候,数据库会在内存中把相应字段的数据给更新了,但是更新以后,数据不会立马同步持久化到磁盘中去,而是把这些记录写到redo log日志中去。等到空闲的时候,再通过redo log里的日志把最新的数据同步到磁盘中去。

​ 不过容易造成一个问题,就是redo log里面的容量是有限的,如果数据库一直很忙,更新又很频繁,这个时候redo log被塞满了,这个时候就只能暂停其他的操作,先把数据同步到磁盘中去,这个时候就导致我们平时执行很快的sql变得很慢。

拿不到锁

​ 我们执行的这个SQL语句的时候,正好这条sql语句涉及到的表,被人加锁了,我们暂时拿不到锁,就只好等待。或者是表没有加锁,而是要使用到的某一行被加锁了。

​ 所以我们要判断一下,是否是因为锁问题导致的,可以使用show processlist这个来查看当前的状态(mysql)。

一直很慢

​ 先假设一个表,其中只有主键id和两个普通字段age和salary

CREATE TABLE `t` (
`id` INT ( 11 ) NOT NULL,
`age` INT ( 11 ) DEFAULT NULL,
`salary` INT ( 11 ) DEFAULT NULL,
PRIMARY KEY ( `id` ) 
) ENGINE = INNODB;

没用上索引

字段没有索引

字段有索引却没有用上索引

例如:
select * from t where age-1=19;

因为我们在字段的左边做了运算,导致在查询的时候就用不上。

函数操作导致没有用上索引

我们在查询的时候对字段进行了函数操作,这也会导致没有用上索引。

数据库选错索引

例子:

select * from t where 1<age and age<100;

​ 我们知道主键索引和给主键索引时有区别的,也就是说如果走age这个字段的索引的话最后会查询到对应主键的值,然后再根据主键的值走主键索引,查询到整行数据返回。

​ 即就算age有索引,系统也不一定会走age这个字段的索引,而是可以会直接扫描,扫描全表。

主键索引(聚簇索引):叶子节点存放的值是整行字段的数据。 非主键索引(二级索引):叶子节点存放的是主键的值

​ 在执行这条语句的时候系统会进行预测到底是走age的索引行数少,还是直接全表扫描的行数少,毕竟age索引找到主键后还需要通过主键索引来找整行数据。

​ 所以系统就通过索引的区分度来判断:一个索引上不同的值越多,意味着出现相同数值的索引越少,意味着索引的区分度越高。我们也把区分度称之为基数,即区分度越高,基数越大。

​ 即索引的基数越大就走索引查询。

​ 但系统通过采样来判断索引基数的大小,所以存在误差判断失误。

即由于统计失误,导致系统没有走索引而是走了全表扫描,导致我们sql执行缓慢

可以通过SQL语句强制走索引:

select * from t force index(a) where 1<age and age<100;

也可以通过 show index from t ;来查询索引的基数和实际是否符合,如果不符合可以重新统计:analyze table t

总结

一条sql 执行很慢的原因:

偶尔很慢: 数据库在刷新脏页,例如redo log满了需要同步到磁盘 执行的时候遇到锁:表锁、行锁 一直很慢: 没有用上索引: 该字段没有索引 由于对字段进行运算或者函数操作导致无法使用索引 数据库选错了索引
栏目