FLUSH TABLES WITH READ LOCK有多快


      最近有一台MySQL的從庫老是報延遲,觀察到:FLUSH TABLES WITH READ LOCK,阻塞了4個多小時,還有另外一條SQL語句select *,從現象上來看是select * 阻塞了flush tables with read lock。

      flush tables with read lock,關閉所有打開的表,同時對於所有數據庫中的表都加一個讀鎖,直到顯示地執行unlock tables,該操作常常用於數據備份的時候。也就是將所有的臟頁都要刷新到磁盤,然后對所有的表加上了讀鎖,於是這時候直接拷貝數據文件也就是安全的。但是如果你發出命令flush tables with read lock時,還有其他的操作,而起是很耗時的操作呢?先說寫操作,這個FTWRL肯定是得等的,等寫操作完成才能執行FTWRL,這個很好理解。那么對於其他的讀操作呢?比如說在FLWRL發出之前有一個query:select count(*) from tb,那么FTWRL也得等待(show processlist可以看到 waiting for table flush)。你可能會說在mysql中讀與讀不是不會排斥的嗎,為什么需要等待呢?因為FTWRL是要flush臟頁的,只有這樣才真的能保證數據一致性(比如說在xtrabackup備份myisam表的時候),而在select count(*) from tb執行的時候,因為所有的操作都是在內存中操作,所以此時還不能完全flush,因此FTWRL就得等待。或許你還會有疑問,select的頁不是臟頁,為什么FTWRL還要等待呢?難道mysql不能做得更完善點嗎?我覺得mysql還不是不會做的這么簡單吧,等待的原因是因為這個表很大,無法一次性將所有的頁都讀到內存中來,而query具有原子性,總不可能執行一般被堵塞吧,所以說還是得乖乖的讓它執行然,所以FTWRL就得等待了。

      flush tables with read lock在測試的時候,它有可能花幾毫秒就可以完成,就像我遇到的情況,在生產環境也可能花幾個小時才能完成。在此期間,MySQL服務完全block住了,而不僅僅是read-only。因為flush tables with read lock會做一下動作:

請求鎖

flush tables with read lock請求全局read lock。當這種情況發生時,其他進程如果有修改動作的話就會被阻塞。從理論上講,這種情況並不是很糟糕,因為flush tables with read lock只需要read lock,其它命令(只需要read lock的命令)可以和flush tables with read lock並存。然而,事實上,大多數表需要讀和寫鎖的。例如:第一個寫語句會被這個全局的讀鎖阻塞,而子查詢又會被第一個寫語句阻塞,所以真正有效果的是使用的是排它鎖,所有新請求就會被阻塞,包括讀查詢語句。

等待鎖

在flush tables with read lock成功獲得鎖之前,必須等待所有語句執行完成(包括SELECT)。所以如果有個慢查詢在執行,或者一個打開的事務,或者其他進程拿着表鎖,flush tables with read lock就會被阻塞,直到所有的鎖被釋放。請看下面的例子:

mysql> show processlist;
+----+------+-----------+------+------------+------+-------------------+----------------------------------------------------------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+------+-----------+------+------------+------+-------------------+----------------------------------------------------------------------+
| 4 | root | localhost | test | Query | 80 | Sending data | select count(*) from t t1 join t t2 join t t3 join t t4 where t1.b=0 |
| 5 | root | localhost | test | Query | 62 | Flushing tables | flush tables with read lock |
| 6 | root | localhost | test | Field List | 35 | Waiting for table | |
| 7 | root | localhost | test | Query | 0 | NULL | show processlist |
+----+------+-----------+------+------------+------+-------------------+----------------------------------------------------------------------+
4 rows in set (0.00 sec)

 

可以看到線程6沒有連進來,因為MySQL的客戶端連接時沒有指定-A,它嘗試獲取當前庫下的所有的表和列。線程5也沒有flush tables,因為它在等線程4釋放鎖。

刷新表

當flush tables with read lock拿到鎖后,必定flush data。對於MyISAM引擎,不光是刷新它自己的data,也刷新操作系統的data到disk上(MyISAM relies on the filesystem block cache for caching reads to the data rows and indexes, while InnoDB does this within the engine itself, combining the row caches with the index caches),所以如果是MyISAM表的話有可能會花費很長時間。

持有鎖

我們可以使用unlock tables或者其它命令來釋放鎖。

結論

一個備份系統一般都是在生產環境中用的,所以我們不能簡單的認為flush tables with read lock很快就執行完。在某些情況下,執行慢是沒法避免的。但是我們可以配置備份系統避免這種global lock。

翻譯自:mysqlperformanceblog 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM