truncate 時發生 system lock

system lock

線程是被mysql_lock_tables()函數調用,該線程未更新過狀態。

這種現象很普遍,造成的原因有多種:

  • 可能一個線程想請求或者正在等一個表的內部或者外部的system lock; 如:從庫複製sql_thread,在小事務較多時,會在加system lock的情況下對數據進行查找和修改。 show full processlist;

如果是大事務,雖然也會加system lock,但狀態爲reading event from the relay log或Executing event。

  • 也可能是InnoDB在執行lock tables的時候,等表級鎖; 如:truncate 操作
  • 也可能是請求內部鎖; 如:訪問相同MyISM表沒有用多個mysqld服務;

遇到這種情況,可以用--skip-external-locking選項,禁用內部的system locks。然而,內部鎖如果默認禁用的話,那個選項就會沒用了。如果是在show profile的時候遇到這種狀態,就說明這個線程正在請求鎖(不是等)。

truncate引起system lock分析

truncate table通過釋放存儲表數據所用的數據頁來刪除數據,並且只在事務日誌中記錄頁的釋放,不記錄每行刪除的日誌。

truncate是把表中數據全部清空,需要有drop權限; truncate操作分爲drop table 和create table操作; truncate不記錄二進制日誌且無法回滾; truncate如果有外鍵,則不能執行; truncate結果正常顯示0行受影響,表示無信息; truncate操作將自增值重置爲起始值; truncate分區表,保留分區信息;

現象:

空間緊張,需要清除歷史數據,有一個大表可以全部清空,我們知道這種操作,truncate最爲擅長。但在truncate過程中,出現system lock,導致CPU升高,性能下降。

分析: MySQL實例中的所有數據庫都維護一個查詢緩存,它們之間實際上沒有隔離。truncate將不得不使查詢緩存中的所有依賴查詢失效,這可能是鎖定的原因。如果查詢緩存足夠大,則刪除緩存可能需要更長時間。

MySQL在5.5.23版本之前的處理方式即同步模式: 當要drop table的時候,會在整個操作過程中持有buffer pool的mutex,然後掃描兩次LRU鏈表,把屬於這個table的page失效掉,buffer pool中page的個數越多,持有mutex時間就會越長,對在線業務的影響也就越明顯。

MySQL在5.5.23版本之後,對drop table的處理做了修改,即在掃描LRU鏈表過程中,如果dirty page屬於drop table,那麼就直接從flush list中remove掉,如果刪除的page個數超過了1024個數目的話,釋放buffer pool mutex,flush list mutex,釋放cpu資源,重新持有mutex再釋放。

buf_LRU_flush_or_remove_pages(id, BUF_REMOVE_FLUSH_NO_WRITE, 0);
buf_pool_mutex_enter(buf_pool);
err = buf_flush_or_remove_pages(buf_pool, id, flush, trx);
......
buf_pool_mutex_exit(buf_pool);
/* BUF_REMOVE_FLUSH_NO_WRITE:意思表示,只對dirty block進行remove操作,不做寫入。

雖然5.5.23版本後drop table解決了該問題,但truncate操作並沒有升級,還是採用5.5.23版本之前的刪除方式,直到8.0版本才解決。

truncate 修改後部分代碼:

Truncate calls row_discard_tablespace_for_mysql -> fil_discard_tablespace -> fil_delete_tablespace with evict_all=TRUE. When evict_all is true for the call to buf_LRU_flush_or_remove_pages then BUF_REMOVE_ALL_NO_WRITE is used.

        buf_LRU_flush_or_remove_pages(
                id, evict_all
                ? BUF_REMOVE_ALL_NO_WRITE
                : BUF_REMOVE_FLUSH_NO_WRITE);

... then the slow path is used. so this stall is expected. would be nice for it to be fixed.

                switch (buf_remove) {
                case BUF_REMOVE_ALL_NO_WRITE:
                        /* A DISCARD tablespace case. Remove AHI entries
                        and evict all pages from LRU. */

                        /* Before we attempt to drop pages hash entries
                        one by one we first attempt to drop page hash
                        index entries in batches to make it more
                        efficient. The batching attempt is a best effort
                        attempt and does not guarantee that all pages
                        hash entries will be dropped. We get rid of
                        remaining page hash entries one by one below. */
                        buf_LRU_drop_page_hash_for_tablespace(buf_pool, id);
                        buf_LRU_remove_all_pages(buf_pool, id);
                        break;

                case BUF_REMOVE_FLUSH_NO_WRITE:
                        /* A DROP table case. AHI entries are already
                        removed. No need to evict all pages from LRU
                        list. Just evict pages from flush list without
                        writing. */
                        buf_flush_dirty_pages(buf_pool, id);
                        break;
                }
        }

MySQL8.0針對該問題說明:

On a system with a large InnoDB buffer pool and innodb_adaptive_hash_index enabled, TRUNCATE TABLE operations could cause a temporary drop in system performance due to an LRU scan that occurred when removing an InnoDB table's adaptive hash index entries.

To address this problem, TRUNCATE TABLE now invokes the same code as DROP TABLE and CREATE TABLE. The problem was addressed for DROP TABLE in MySQL 5.5.23.

意思爲:

當InnoDB buffer pool比較大和innodb_adaptive_hash_index啓用時,TRUNCATE TABLE操作可能由於發生了LRU掃描,刪除InnoDB表的自適應散列索引項時,導致系統性能暫時下降。爲了解決這個問題,TRUNCATE TABLE現在調用與DROP TABLE相同的代碼刪除表。因爲在MySQL 5.5.23後,DROP TABLE解決了這個問題。

總結:

5.5.23版本之前,採用truncate+drop方式

5.5.23版本之後,採用drop方式

8.0版本之後,採用truncate方式

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章