情况一:大多数情况是正常的,只是偶尔会出现很慢的情况
1:数据库在刷新脏页(flush)
什么叫脏页:
当内存数据页跟磁盘数据页内容不一致的时候,我们称这个内存页为“脏页”。
内存数据写入到磁盘后,内存和磁盘上的数据页的内容就一致了,称为“干净页”。
在往数据库插入 删除、或者要更新一条数据的时候
1:数据库会在内存中把对应字段的数据更新
2:更新的字段并不会马上同步持久化到磁盘中去
3:而是把这些更新的记录写入到 redo log 日记中
4:等到空闲的时候,再通过 redo log 里的日记把最新的数据同步到磁盘中去。
刷脏页有下面4种场景(后两种不用太关注“性能”问题):
1:redolog写满了:
redo log 里的容量是有限的,如果数据库一直很忙,更新又很频繁,
这个时候 redo log 很快就会被写满了,这个时候就没办法等到空闲的时候再把数据同步到磁盘的,
只能暂停其他操作,全身心来把数据同步到磁盘中去的;
所以说,数据库在在同步数据到磁盘的时候,就有可能导致我们的SQL语句执行的很慢了。
2:内存不够用了:
如果一次查询较多的数据,恰好碰到所查数据页不在内存中时,需要申请内存,
而此时恰好内存不足的时候就需要淘汰一部分其他的内存数据页,如果是干净页,就直接释放,如果恰好是脏页就需要刷脏页。
3:MySQL 认为系统“空闲”的时候:
这时系统没什么压力。
4:MySQL 正常关闭的时候:
MySQL 会把内存的脏页都 flush 到磁盘上,这样下次 MySQL 启动的时候,就可以直接从磁盘上读数据,启动速度会很快。
2:拿不到锁
要执行的这条语句,刚好这条语句涉及到的表,别人在用,并且加锁了,我们拿不到锁,只能慢慢等待别人释放锁了。
或者,表没有加锁,但要使用到的某个一行被加锁了;
可以使用show processlist来查看当前状态
情况二:针对一直都这么慢的情况
1:字段没有索引,导致全表扫描
2:字段有索引,但却没有用索引
没有使用索引:对应等号的左侧使用了运算或者聚合函数
select * from t where field - 1 = 1000;
使用索引:
select * from t where field = 1000 + 1;
3:数据库自己选错索引
select * from t where 100 < field and field < 1000;
系统在执行这条语句的时候,会进行预测:
究竟是走 c 索引扫描的行数少,还是直接扫描全表扫描的行数少呢?
如果是扫描全表的话,那么扫描的次数就是这个表的总行数了,假设为 n;
如果走索引 field 的话,我们通过索引 field 找到主键之后,还得再通过主键索引来找我们整行的数据,也就是说,需要走两次索引。
因此我们也不知道符合 100 field < and field < 1000
这个条件的数据有多少行,万一这个表是全部数据都符合呢?这个时候意味着,走 c 索引不仅扫描的行数是 n,同时还得每行数据走两次索引。
那么系统是怎么预测判断的呢?(全表 还是 走索引)
系统是通过索引的区分度来判断:
一个索引上不同的值越多,意味着出现相同数值的索引越少,意味着索引的区分度越高。
区分度越高:
意味着符合 100 < field and field < 1000 这个条件的行数越少。
系统又是怎么获取区分度的呢?
通过采样的方式,来预测索引的区分度的。
假如采样的那一部分数据刚好区分度很小,就认为以为索引的区分度也很小;
然后会不走索引而是全表扫描;
解决方法强制走索引:
select * from t force index(field_index) where 100< field and field < 1000
查询索引的区分度和实际是否符合:
show index from t;
重新来统计索引的区分度:
analyze table t;