MySQL实战45讲Day18----为什么只查一行的语句,也执行这么慢

对于表t:

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();

一、查询不返回:

<1>、等MDL锁:

mysql> select * from t where id=1;

 (1)、判断方式:

  使用show processlist命令查看,如果state列出现Waiting for table metadata lock,则该行的Info列语句等MDL锁。

 (2)、处理方式:

  找到谁持有MDL写锁,然后把它kill掉。但是,由于在show processlist的结果里面,持有MDL写锁的Command列是“Sleep”,导致查找起来很不方便。不过有了performance_schema和sys系统库以后,就方便多了。(MySQL启动时需要设置performance_schema=on,相比于设置为off会有10%左右的性能损失)。通过查询sys.schema_table_lock_waits这张表,就可以直接找出造成阻塞的process id,把这个连接用kill 命令断开即可。

<2>、等flush:

mysql> select * from information_schema.processlist where id=1;

 (1)、判断方式:

  使用show processlist命令查看,如果state列出现Waiting for table flush,则该行的Info列语句等flush。表示现在有一个线程正要对表t做flush操作。

 (2)、MySQL里面对表做flush操作的用法,一般有以下两个:

flush tables t with read lock;
flush tables with read lock;

  这两个flush语句,如果指定表t的话,代表的是只关闭表t;如果没有指定具体的表名,则表示关闭MySQL里所有打开的表。但是正常这两个语句执行起来都很快,除非它们也被别的线程堵住了。所以,出现Waiting for table flush状态的可能情况是:有一个flush tables命令被别的语句堵住了,然后它又堵住了我们的select语句。

 (3)、处理方式:

  将how processlist命令中state列中为user sleep的该行Info列语句kill。

<3>、等行锁

mysql> select * from t where id=1 lock in share mode; 

 (1)、判断方式:

  使用show processlist命令查看,如果state列出现Statistics,则该行的Info列语句等行锁。

 (2)、处理方式:

①、查询谁占着写锁的方法:

  如果MySQL版本是MySQL 5.7版本,可以通过sys.innodb_lock_waits 表查到。
查询方法是:

mysql> select * from t sys.innodb_lock_waits where locked_table=`'test'.'t'`\G

查询结果中的blocking_pid显示的就是占着写锁的线程的ID。
sql_kill_blocking_query显示的KILL QUERY 占着写锁的线程的ID。(KILL QUERY 4)
sql_kill_blocking_connection显示的是KILL 占着写锁的线程的ID。(KILL 4)
注意:“KILL QUERY 4”。这个命令表示停止4号线程当前正在执行的语句,而这个方法其实是没有用的。因为占有行锁的是update语句,这个语句已经是之前执行完成了的,现在执行KILL QUERY,无法让这个事务去掉id=1上的行锁。

②、处理方法:

  直接kill“sql_kill_blocking_query”或者“sql_kill_blocking_connection”显示的线程。但当该线程是CRUD时,则只能kill“sql_kill_blocking_connection”。也就是说直接断开这个连接。这里隐含的一个逻辑就是,连接被断开的时候,会自动回滚这个连接里面正在执行的线程,也就释放了id=1上的行锁。

二、查询不返回:

<1>、:

Q1:mysql> select * from t where id=1;
Q2:select * from t where id=1 lock in share mode

Q1执行时间比Q2长的原因:
  带lock in share mode的SQL语句,是当前读,因此会直接读这个结果,所以速度很快;而select * from t where id=1这个语句,是一致性读,因此需要从当前结果开始,依次执行回滚日志(undo log)以后,才将1这个结果返回。简而言之,Q1是直接返回结果,而Q2是先回滚,然后再返回结果。注意,二者返回结果也是不同的。

你可能感兴趣的:(MySQL实战45讲Day18----为什么只查一行的语句,也执行这么慢)