有些状况,只查一行数据,执行的也会特别慢,接下来咱们就梳理一下,会出现这里现象的场景。
数据库压力
若是MySQL数据库自己就有很大的压力,致使CPU占用率很高,IO利用率很高,那么即便是执行一条SQL语句,也会执行的很慢。
锁
若是这条SQL语句被锁住,他就会执行的很慢。而MySQL中的锁,又分为全局锁、表级锁、行锁。首先咱们会构建一个表,接下来咱们会分状况进行分析。
CREATE TABLE `t` (
`id` int(11) NOT NULL,
`c` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
delimiter ;;
create procedure idata()
begin
declare i int;
set i=1;
while(i<=100000) do
insert into t values(i,i);
set i=i+1;
end while;
end;;
delimiter ;
call idata();
全局锁
全局锁的执行语句是:
flush tables with read lock;
flush tables t with read lock;
若是指定了表t,表明的是只关闭表t,若是没有指定表名,则表示关闭MySQL里全部打开的表。
可是这个操做是加全局的写锁,使整个表处于只读的状态,不会阻塞查询语句。若是查询语句被阻塞,那么必定是这行命令被其余命令阻塞了,接下来咱们来复现这个场景。
咱们能够经过show processlist来查看语句执行状态:
能够看到 id为27,28的语句都id为26的语句被阻塞了,因此咱们只要kill掉26号语句,便可解决这个问题,返回执行结果。
表级锁
若是有一个线程正在表t上请求或持有MDL写锁,就会把select语句阻塞住。
可是这种场景只能在 MySQL5.6版本复现,而MySQL5.7版本就修改了MDL加锁的策略,而是采用了Online DDL的方式对表结构进行修改(CopyOnWrite)因此这里咱们可使用给表加写锁的方式复现。
经过processlist来查看执行状态:
31pid的语句状态处于等待元数据锁。咱们能够在MySQL启动时设置performance_schema=on;(相比设置为off会有10%左右的性能损失)咱们能够直径经过sys.schema_table_lock_waits这张表,找到形成阻塞的process id;kill掉便可。
行锁
select * from t where id=1 lock in share mode;
若是咱们访问id=1这个记录时,加了读锁,这个时候其余事物持有了写锁,咱们的select语句就会被阻塞。
咱们经过processlist能够查询到语句执行状态:
SessionA启动了事物,更改了数据,SessionB查询这条数据的时候,因为SessionA尚未提交事物。致使SessionB获取不到最新的数据(当前读)被阻塞。(读写锁阻塞)
接下来咱们要找到是谁占着这个写锁,若是咱们使用的MySQL版本是5.7,则能够经过sys.innodb_lock_waits查到。
select * from t sys.innodb_lock_waits where locked_table=`'test'.'t'`\G
kill掉相应线程便可。
慢查询
若是一个查询(当前读)伴随着一个长事务大量的修改,那么这个查询操做就会很是的慢。
SessionB的update操做,会产生大量的回滚日志,SessionA的
select * from t where id = 1;
是一致性读,即基于MySQL事务开启时,所产生的一致性视图。因此返回的结果是1,而且查询很是快。
而SessionA的
select * from t where id = 1 lock in share mode;
是当前读,须要等待SessoinB的100万次update操做,提交事物后,再根据回滚日志查询到当前值。
因此执行的就会很是慢。