全局鎖和表鎖 :給表加個字段怎麼有這麼多阻礙?&&閱讀筆記

數據庫鎖設計的初衷是處理併發問題

根據加鎖的範圍,MySQL裏面的鎖大致可以分成全局鎖表級鎖行鎖

全局鎖

對整個數據庫實例加鎖,命令是Flush tables with read lock (FTWRL)
只想讓整個庫處於只讀狀態時,可以使用這個命令,之後其他線程的以下語句會被阻塞:數據更新語句(數據的增刪改)、數據定義語句(包括建表、修改表結構等)和更新類事務的提交語句。

典型的使用場景:做全庫邏輯備份

但是讓整個庫只讀,非常危險:

  • 如果在主庫上備份,業務停擺
  • 如果在從庫備份,備份期間從庫 不能執行主庫同步過來的binlog,主從延遲

備份不加鎖的問題:

用戶賬戶餘額表和用戶課程表。

現在發起一個邏輯備份。假設備份期間,有一個用戶,他購買了一門課程,業務邏輯裏就要扣掉他的餘額,然後往已購課程裏面加上一門課。

如果時間順序上是先備份賬戶餘額表(u_account),然後用戶購買,然後備份用戶課程表(u_course),會怎麼樣呢?你可以看一下這個圖:
在這裏插入圖片描述
結果是:💴沒扣,課倒是多了。用戶爽了。

如果備份表順序反過來,用戶就要鬧了,錢扣了課不給我。

如果數據庫引擎支持事務(如InnoDB),可以開啓一個可重複讀的事務,在此期間數據可以正常更新。

如果不支持,那隻能加全局讀鎖Flush tables with read lock (FTWRL)了。

既然要全局可讀,爲什麼不set global readonly=true?

  • readonly的值可能會被用來做其他邏輯
  • 在異常處理機制上有差異。如果執行FTWRL命令之後客戶端發生異常斷開,MySQL會自動釋放這個全局鎖,整個庫回到可以正常更新的狀態。而將整個庫設置爲readonly之後,如果客戶端發生異常,數據庫會一直保持readonly狀態,這樣會導致整個庫長時間處於不可寫狀態,風險較高。

表級鎖

表級別的鎖有兩種:表鎖元數據鎖(meta data lock,MDL)

表鎖語法:lock tables … read/write,與FTWRL類似,可用unlock tables主動釋放鎖,也可以在客戶端斷開的時候自動釋放。lock tables語法除了會限制別的線程的讀寫外,也限定了本線程接下來的操作對象
eg:
如果在某個線程A中執行lock tables t1 read, t2 write; 這個語句,則其他線程寫t1、讀寫t2的語句都會被阻塞。同時,線程A在執行unlock tables之前,也只能執行讀t1、讀寫t2的操作。連寫t1都不允許,自然也不能訪問其他表

元數據鎖(meta data lock):不需要顯示使用,訪問表時自動加上。
作用是保證讀寫正確性

在MySQL 5.5引入了MDL,當對一個表做增刪改查操作的時候,加MDL讀鎖;當要對錶做結構變更操作的時候,加MDL寫鎖

  • 讀鎖之間不互斥,因此可有多個線程同時對一張表增刪改查
  • 讀寫鎖、寫鎖之間是互斥的,來保證變更表結構操作的安全性。如果有兩個線程要同時給一個表加字段,其中一個要等另一個執行完才能開始執行。

在這裏插入圖片描述

  • sessionA啓動,對錶加MDL讀鎖
  • sessionB啓動,這裏也是MDL讀鎖,前面提到讀鎖之間不互斥,正常執行
  • sessionC改變表結構,需要MDL寫鎖,但是sessionA的MDL讀鎖還沒有釋放,前面提到讀寫鎖之間是互斥的,sessionC被堵塞
  • 由於sessionC堵塞,後面所有要在表t上新申請MDL讀鎖的請求也會被session C阻塞,現在是完全不可讀寫狀態了。

如果某個表上的查詢語句頻繁,而且客戶端有重試機制,也就是說超時後會再起一個新session再請求的話,這個庫的線程很快就會爆滿。

事務中的MDL鎖,在語句執行開始時申請,但是語句結束後並不會馬上釋放,而會等到整個事務提交後再釋放

如何安全地給表加字段?

首先解決長事務,事務不提交,就會一直佔着MDL鎖。在MySQL的information_schema 庫的 innodb_trx 表中可查到當前執行中的事務。如果做DDL變更的表剛好有長事務在執行,要考慮先暫停DDL,或者kill掉這個長事務。

但如果要變更的表是一個熱點表,雖然數據量不大,但是上面的請求很頻繁,而你不得不加個字段,該怎麼做呢?

這時候kill可能未必管用,因爲新的請求馬上就來了。比較理想的機制是,在alter table語句裏面設定等待時間,如果在這個指定的等待時間裏面能夠拿到MDL寫鎖最好,拿不到也不要阻塞後面的業務語句,先放棄。之後開發人員或者DBA再通過重試命令重複這個過程。

MariaDB已經合併了AliSQL的這個功能,所以這兩個開源分支目前都支持DDL NOWAIT/WAIT n這個語法。

ALTER TABLE tbl_name NOWAIT add column …
ALTER TABLE tbl_name WAIT N add column …

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