我們經常發現,往往執行一條簡單的查詢語句,但是很長時間都沒有傳回,今天我們看看是什麼原因導緻的 第一類:查詢長時間不能傳回 執行下面語句
select * from t where id =1;
等待MDL鎖 我們按照下面操作,看看會發生什麼呢
我們發現sql語句很長時間都不見傳回響應,我們先看一下他的狀态,發現果然是被鎖住了.
此類問題我們直接可以找到誰持有MDL的寫鎖,直接kill. 可以用查詢sys.schema_table_lock_waits這張表,我們就可以直接找到阻塞的process id ,把這個連接配接用kill指令斷開即可(mysql啟動的時候設定performation_schema=on)
等待flush
下面我們說另外一種查詢被阻塞的情況,當一個線程正好對表進行flush操作,本身這個線程執行的很快,但是如果這個線程flush線程被其他線程阻塞,最終會導緻阻塞表t的查詢,如下圖所示
sessionA中,我們故意調用一次sleep(1),預設執行10萬秒,這個時候t表是打開的,使用flush去關閉表t,就必須等待sessionA結束,同時也會阻塞sessionC
等待行鎖 首先,我們看看下面sql語句
mysql> select * from t where id=1 lock in share mode;
要執行上面語句的時候,這個記錄就會要加讀鎖,如果這個時候已經有一個事物在這行記錄上持有一個寫鎖,我們select 語句就會被阻塞。
這個問題并并不難分析,問題是如何查出誰占着這個寫鎖,如果你用的mysql5.7,可以使用下面語句
mysql> select * from t sys.innodb_lock_waits where locked_table=`'test'.'t'`\G
可以看到4号線程就是阻塞的罪魁禍首,是以隻要幹掉他就可以了, 不過,這裡不應該顯示kill query 4,這個指令是指把正在執行的語句停止,但是我們的update語句已經執行完成了,這樣是無法去掉id=1的行鎖. 實際上,kill 4才有效,也就是直接斷開這個連接配接,這裡連接配接被斷開的時候,會自動復原這個連接配接裡面正在執行的線程,也就是釋放id=1上的行鎖.
第二類:查詢慢
我們執行下面語句
select * from t where c=50000 limit 1;
有字段c沒有索引,這個語句隻能全表掃描,是以要掃描5萬行,再看看慢日志的記錄.
發現掃描了50000行,消耗時間13.5毫秒,看起來很快,但是目前資料的資料隻有10萬行資料,如果資料量到千萬級别,這個sql就會消耗很多時間。 我們在看看另外sql,如下圖
select * from t where id=1select * from t where id=1 lock in share mode
按照上面操作我們再看看對應的慢查詢日志
我們發現lock in share mode加鎖操作居然時間比沒有加鎖的查詢塊了,超出了我們的預期,我們再看看每個sql查詢結果
此時我們就知道原因了,是因為session A先用start transaction with consistent snapshot啟動了一個事物,然後sessionB才進行更新語句,然後在執行完100萬次update語句後,此時的id是處于下圖的狀态
發現session B生産100萬復原日志(undo log),此時lock in share mode的sql語句,是目前讀,是以會直接讀到100001,速度很快,但是select * from t where id=1,是一緻性讀,是以從1000001開始,依次執行undo log,執行100萬次,才會把1傳回.
如果對您有一絲絲幫助,麻煩點個關注,也歡迎轉發,謝謝
掃碼關注