19 | 为什么我只查一行的语句,也执行这么慢?

Page content

19 | 为什么我只查一行的语句,也执行这么慢?

建表,插入10万行数据

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

查询操作长时间不返回

select * from t where id=1;

原因:表被锁住了

等 MDL 锁

通过 show processlist 命令查看,发现 State 为 Waiting for table metadata lock,这是因为当前有某个线程持有表的 MDL 写锁,阻塞了 select 语句

等 flush

通过 show processlist 命令查看,发现 State 为 Waiting for table flush,这是因为当前有某个线程要对表做 flush 操作。

MySQL 对表做 flush 的操作有两个:

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

正常情况下两个语句执行的都很快,出现这种情况可能是因为它们也被阻塞了。

等行锁

id=1 的行,恰好被其它线程加了写锁

查询慢

select * from t where c=50000 limit 1;

c 上没有索引,该条语句要扫描50000行记录,扫描行数多自然就慢

select * from t where id=1;

一致性读导致的查询慢,selec的时候,刚好有其它事务对这行数据做了更改,导致这行数据存在很多很多新版本,需要使用 undo log 向前推算旧版本

问题讨论

select * from t where id=1 lock in share mode由于 id 上有索引,所以可以直接定位到 id=1 这一行,因此读锁也是只加在了这一行上。但如果是

begin;
    select * from t where c=5 for update;
commit;

这个语句序列是怎么加锁的呢?加的锁又是什么时候释放呢?

答案 在读提交隔离状态下,还是只对 c=5 的行加锁,并在事务提交时释放

在可重复读隔离级别下,对所有扫描的行以及行之间添加间隙锁,事务提交后释放