構造一張表,表有兩個字段id和c,再里面插入了10萬行記錄
create table 't' ('id' int(11) not null,'c' int(11) default null,primary key ('id')
) engine = InnoDB;delimiter ;;
create procedure idata()
begindeclare i int;set i = 1;while( i <= 100000) doinsert into t values(i,i);set i = i+1;end while;
end;;
delimiter ;call idata();
查詢長時間不返回
在表t執行:
select * from t where id = 1;
查詢結果長時間不返回。
等MDL鎖
大概率是表t被鎖住了,接下來分析原因:一般都是首先執行show processlist命令,看看當前語句處于什么狀態。
表示現在有個線程正在表t上請求或者持有MDL寫鎖,把select語句阻塞了:
session A通過lock table命令持有表t的MDL寫鎖,而sessionB 的查詢需要獲取MDL讀鎖,所以session B 進入等待狀態。
處理方式:找到誰持有MDL寫鎖,然后把它kill掉。
通過
select blocking_pid from sys.schema_table_lock_waits;
得到blocking_pid = 4;
然后用kill命令斷開即可。
等flush
在表t上執行下面語句:
select * from information_schema.processlist where id=1;
可以查看出該線程的狀態是Waiting for table flush;
表示現在有一個線程正要對表t做flush操作。
flush tables t with read lock; --只關閉表t
--or
flush tables with read lock; --關閉MySQL里面所有打開的表
正常來說,這兩個語句執行起來都很快,除非它們也被別的線程堵住了。
所以可能是:有一個flush tables
命令被別的語句堵住了,然后它又堵住了我們的select語句。
下圖是執行結果:
等行鎖
select * from t where id = 1 lock in share mode;
由于訪問id = 1這個記錄時要加讀鎖,如果這時候已經有一個事務在這行記錄上持有一個寫鎖,我們的select語句就會被堵住,如下:
session A啟動事務,占用寫鎖,但是不提交,導致session B被堵住。
可以通過:
mysql> select * from t sys.innodb_lock_waits where locked_table='`test`.`t`'\G
進行查詢,查出是誰占著這個寫鎖
發現是4號線程,然后我們kill 4
查詢慢
select * from t where c = 50000 limit 1;
由于字段c上沒有索引,所以這個語句只能走id主鍵順序掃描,因此需要掃描5萬行。
掃描行數多,所以執行慢,這個很好理解。
而下面的這條語句掃描行數為1,但是執行時間取卻較長
select * from t where id = 1;
在這個場景下,session A先啟動了一個事務,之后session B才開始執行update語句。
session B 更新完 100 萬次,生成了 100 萬個回滾日志 (undo log)
帶lock in share mode 的sql是當前讀,因此會直接讀到 1000001 ,所以速度很快。
select * from t where id = 1語句是一致性讀,因此需要從 1000001 開始,依次執行undo log,執行100萬次后,才將1返回
**回滾日志過大引起的一致性讀慢,當前讀快 **