程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 數據庫知識 >> MYSQL數據庫 >> MySQL綜合教程 >> FLUSH TABLE WITH READ LOCK詳解,flushlock

FLUSH TABLE WITH READ LOCK詳解,flushlock

編輯:MySQL綜合教程

FLUSH TABLE WITH READ LOCK詳解,flushlock


      FLUSH TABLES WITH READ LOCK簡稱(FTWRL),該命令主要用於備份工具獲取一致性備份(數據與binlog位點匹配)。由於FTWRL總共需要持有兩把全局的MDL鎖,並且還需要關閉所有表對象,因此這個命令的殺傷性很大,執行命令時容易導致庫hang住。如果是主庫,則業務無法正常訪問;如果是備庫,則會導致SQL線程卡住,主備延遲。本文將詳細介紹FTWRL到底做了什麼操作,每個操作的對庫的影響,以及操作背後的原因。 

FTWRL做了什麼操作?

FTWRL主要包括3個步驟:

1.上全局讀鎖(lock_global_read_lock)
2.清理表緩存(close_cached_tables)
3.上全局COMMIT鎖(make_global_read_lock_block_commit)

FTWRL每個操作的影響

     上全局讀鎖會導致所有更新操作都會被堵塞;關閉表過程中,如果有大查詢導致關閉表等待,那麼所有訪問這個表的查詢和更新都需要等待;上全局COMMIT鎖時,會堵塞活躍事務提交。由於FTWRL主要被備份工具使用,後面會詳細解釋每個步驟的作用,以及存在的必要性。FTWRL中的第1和第3步都是通過MDL鎖實現,關於MDL的實現,我之前總結了MDL鎖的文章,這裡主要介紹清理表緩存的流程。 

清理表緩存   

    每個表在內存中都有一個table_cache,不同表的cache對象通過hash鏈表維護。
訪問cache對象通過LOCK_open互斥量保護,每個會話打開的表時,引用計數share->ref_count++,
關閉表時,都會去對引用計數share->ref_count--。
若發現是share對象的最後一個引用(share->ref_count==0),並且share有old_version,
則將table_def_cache從hash鏈表中摘除,調用free_table_share進行處理。關鍵函數close table流程如下:

1.關閉所有未使用的表對象
2.更新全局字典的版本號
3.對於在使用的表對象,逐一檢查,若表還在使用中,調用MDL_wait::timed_wait進行等待
4.將等待對象關聯到table_cache對象中
5.繼續遍歷使用的表對象
6.直到所有表都不再使用,則關閉成功。

清理表緩存函數調用

mysql_execute_command->reload_acl_and_cache->close_cached_tables
->TABLE_SHARE::wait_for_old_version->MDL_wait::timed_wait->
inline_mysql_cond_timedwait

會話操作表流程

1.打開表操作,若發現還有old_version,則進行等待
2.share->ref_count++
3.操作完畢,檢查share->ref_count--是否為0
4.若為0,並且檢查發現有新版本號,則認為cache對象需要重載
5.將cache對象摘除,調用MDL_wait::set_status喚醒所有等待的線程。

關閉表對象函數調用

dispatch_command->mysql_parse->mysql_execute_command->
close_thread_tables->close_open_tables->close_thread_table->
intern_close_table->closefrm->release_table_share->my_hash_delete->
table_def_free_entry->free_table_share

關閉表導致業務庫堵住的典型場景

     假設有3個會話,會話A執行大查詢,訪問t表;然後一個備份會話B正處於關閉表階段,需要關閉表t;隨後會話C也請求訪問t表。三個會話按照這個順序執行,我們會發現備份會話B和會話C訪問t表的線程都處於“waiting for table flush”狀態。這就是關閉表引起的,這個問題很嚴重,因為此時普通的select查詢也被堵住了。下面簡單解釋下原因:

1.會話A打開表t,執行中……
2.備份會話B需要清理表t的cache,更新版本號(refresh_version++)
3.會話B發現表t存在舊版本(version != refresh_version),表示還有會話正在訪問表t,
等待,加入share對象的等待隊列
4.後續會話C同樣發現存在舊版本(version != refresh_version),
等待,加入share對象的等待隊列
......
5. 大查詢執行完畢,調用free_table_share,喚醒所有等待線程。

free_table_share //逐一喚醒所有等待的線程。
{
while ((ticket= it++))
ticket->get_ctx()->m_wait.set_status(MDL_wait::GRANTED);
}

第4步與第5步之間,所有的訪問該表的會話都處於“waiting for table flush”狀態,唯有大查詢結束後,等待狀態才能解除。

主備切換場景

      在生產環境中,為了容災一般mysql服務都由主備庫組成,當主庫出現問題時,可以切換到備庫運行,保證服務的高可用。在這個過程中有一點很重要,避免雙寫。因為導致切換的場景有很多,可能是因為主庫壓力過大hang住了,也有可能是主庫觸發mysql bug重啟了等。當我們將備庫寫開啟時,如果老主庫活著,一定要先將其設置為read_only狀態。“set global read_only=1”這個命令實際上也和FTWRL類似,也需要上兩把MDL,只是不需要清理表緩存而已。如果老主庫上還有大的更新事務,將導致set global read_only hang住,設置失敗。因此切換程序在設計時,要考慮這一點。

關鍵函數:fix_read_only

1.lock_global_read_lock(),避免新的更新事務,阻止更新操作
2.make_global_read_lock_block_commit,避免活躍的事務提交

FTWRL與備份

      Mysql的備份方式,主要包括兩類,邏輯備份和物理備份,邏輯備份的典型代表是mysqldump,物理備份的典型代表是extrabackup。根據備份是否需要停止服務,可以將備份分為冷備和熱備。冷備要求服務器關閉,這個在生產環境中基本不現實,而且也與FTWRL無關,這裡主要討論熱備。Mysql的架構支持插件式存儲引擎,通常我們以是否支持事務劃分,典型的代表就是myisam和innodb,這兩個存儲引擎分別是早期和現在mysql表的默認存儲引擎。我們的討論也主要圍繞這兩種引擎展開。對於innodb存儲引擎而言,在使用mysqldump獲取一致性備份時,我們經常會使用兩個參數,--single-transaction和--master-data,前者保證innodb表的數據一致性,後者保證獲取與數據備份匹配的一致性位點,主要用於搭建復制。現在使用mysql主備集群基本是標配,所以也是必需的。對於myisam,就需要通過--lock-all-tables參數和--master-data來達到同樣的目的。我們在來回顧下FTWRL的3個步驟:

1. 上全局讀鎖
2. 清理表緩存
3. 上全局COMMIT鎖

第一步的作用是堵塞更新,備份時,我們期望獲取此時數據庫的一致狀態,不希望有更多的更新操作進來。對於innodb引擎而言,其自身的MVCC機制,可以保證讀到老版本數據,因此第一步對它使多余的。第二步,清理表緩存,這個操作對於myisam有意義,關閉myisam表時,會強制要求表的緩存落盤,這對於物理備份myisam表是有意義的,因為物理備份是直接拷貝物理文件。對於innodb表,則無需這樣,因為innodb有自己的redolog,只要記錄當時LSN,然後備份LSN以後的redolog即可。第三步,主要是保證能獲取一致性的binlog位點,這點對於myisam和innodb作用是一樣的。

     所以總的來說,FTWRL對於innodb引擎而言,最重要的是獲取一致性位點,前面兩個步驟是可有可無的,因此如果業務表全部是innodb表,這把大鎖從原理上來講是可以拆的,而且percona公司也確實做了這樣的事情,具體大家可以參考blog鏈接。此外,官方版本的5.5和5.6對於mysqldump做了一個優化,主要改動是,5.5備份一個表,鎖一個表,備份下一個表時,再上鎖一個表,已經備份完的表鎖不釋放,這樣持續進行,直到備份完成才統一釋放鎖。5.6則是備份完一個表,就釋放一個鎖,實現主要是通過innodb的保存點機制。相關的bug可以參考鏈接:http://bugs.mysql.com/bug.php?id=71017。

參考文獻

https://www.percona.com/blog/2014/03/11/introducing-backup-locks-percona-server-2/

https://www.percona.com/blog/2012/03/23/how-flush-tables-with-read-lock-works-with-innodb-tables/

http://bugs.mysql.com/bug.php?id=71017

http://www.cnblogs.com/bamboos/p/3458233.html

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved