什麼情況下 MySQL 連查詢都能被阻塞?

MySQL 的鎖也是不少,在哪種情況下會連查詢都能被阻塞?這是一個有意思的問題。

工作中,很多開發和 DBA 可能接觸較多的鎖也就行鎖了。對於行鎖,阻塞寫能理解,阻塞讀實在是想不到。能阻塞讀的那肯定是顆粒度更大的鎖了,比如表級別的。

作者:賈特特,MySQL DBA 從業者,公衆號『數據庫運維札記』作者,目前任職於某遊戲公司擔任DBA工程師

愛可生開源社區出品,原創內容未經授權不得隨意使用,轉載請聯繫小編並註明來源。

本文約 2000 字,預計閱讀需要 8 分鐘。

MySQL 的鎖也是不少,在哪種情況下會連查詢都能被阻塞?這是一個有意思的問題。

工作中,很多開發和 DBA 可能接觸較多的鎖也就行鎖了。對於行鎖,阻塞寫能理解,阻塞讀實在是想不到。能阻塞讀的那肯定是顆粒度更大的鎖了,比如表級別的。

本文操作環境爲 MySQL 8.0。

MySQL 表級鎖有兩種實現

  1. 服務器(SERVER)層:本層的鎖定主要是元數據鎖(metadata lock,MDL)。
  2. 存儲引擎(ENGINE)層:本層不同的存儲引擎可能會實現不同的鎖定策略。例如 MyISAM 引擎實現了表級鎖,InnoDB 存儲引擎實現了行級鎖和表級鎖,其中表級鎖是通過意向鎖體現的。

元數據鎖(MDL)是由 SERVER 層管理,用於鎖定數據庫對象的元數據信息,如:表結構、索引等。元數據鎖可以阻止對錶結構的改變,以確保數據定義的一致性。

元數據鎖的類型

點擊放大

每種鎖類型後面會詳細介紹。簡單來說,對於元數據鎖而言,當對一個表進行增刪改查操作的時候,會加 元數據讀鎖。當對錶數據結構進行變更的時候會加 元數據寫鎖它讀寫互斥,寫寫互斥,只有讀讀不衝突。

意向鎖是在存儲引擎層實現的,用於協調不同事務對錶級鎖和行級鎖的請求。當一個事務在某個層次(表級或行級)上獲取鎖時,會首先獲取對應層次的意向鎖,以提示其他事務該事務在該層次上有鎖的意向。這樣可以在更高層次上減少鎖衝突,提高併發性能。

InnoDB 存儲引擎的意向鎖種類

  1. 意向共享鎖(Intention Shared Lock,IS):事務打算給數據行加共享鎖(S 鎖)。
  2. 意向排他鎖(Intention Exclusive Lock,IX):事務打算給數據行加排他鎖(X 鎖)。

這樣看來,表對象不可讀寫有種情況可能就是元數據鎖互斥所導致的。

Waiting for table metadata lock

本節中未完成的讀寫事務,在實際中可能是未完成的大事務,也可能是未顯式結束的事務。

元數據鎖互斥(未完成的讀事務)

會話 1 執行:有未完成的讀事務,此時獲取了元數據共享讀鎖。

MDL_SHARED_READ: 這個鎖允許會話讀取表的數據,並允許其他會話獲取 SHARED_READ 或 SHARED_WRITE 鎖,但不允許獲取 SHARED_NO_READ_WRITE 或 EXCLUSIVE 鎖。

會話 2 執行:ALTER 表結構變更語句,此時 ALTER 語句要獲取元數據排它鎖。

MDL_EXCLUSIVE: 這個鎖允許會話讀取和修改表的數據和結構,但不允許其他會話獲取任何類型的鎖。

元數據鎖互斥等待,之後其他會話對於所涉及表不可讀寫。

元數據鎖互斥(未完成的寫事務)

會話 1 執行:有未完成的寫事務,此時獲取了元數據寫鎖。

MDL_SHARED_WRITE: 這個鎖允許會話讀取和修改表的數據,並允許其他會話獲取 SHARED_READ 鎖,但不允許獲取 SHARED_WRITE、SHARED_NO_READ_WRITE 或 EXCLUSIVE 鎖。

會話 2 執行:ALTER 表結構變更語句,此時 ALTER 語句要獲取元數據排它鎖。

**MDL_EXCLUSIVE:**這個鎖允許會話讀取和修改表的數據和結構,但不允許其他會話獲取任何類型的鎖。

元數據鎖互斥等待,之後其他會話對於所涉及表不可讀寫。

LOCK TABLES ... READ/WRITE

LOCK TABLES 可以顯式獲取表鎖,需要注意的是會話只能自己獲取和釋放表鎖。UNLOCK TABLES 可以顯式釋放當前會話的表鎖。

LOCK TABLES … READ

會話 1 執行:lock tables db_version read;

MDL_SHARED_READ_ONLY: 這個鎖允許會話讀取表的數據,並允許其他會話獲取 SHARED_READ 鎖,但不允許獲取 SHARED_WRITE、SHARED_NO_READ_WRITE 或 EXCLUSIVE 鎖。

此時 db_version 加了元數據共享只讀鎖。

會話 2 執行:ALTER 表結構變更語句,此時 ALTER 語句要獲取元數據排它鎖,元數據鎖互斥等待。

之後所涉及表對象將不可讀寫。

LOCK TABLES … WRITE

會話 1 執行:lock tables db_version write;

MDL_SHARED_NO_READ_WRITE: 這個鎖允許當前會話讀取和修改表的數據,但不允許其他會話獲取任何類型的鎖。

此時 db_version 加上了獨佔寫鎖。只能在 會話 1 讀寫,它會阻止其他會話獲取任何類型的鎖。

因此其他會話既不能讀也不能寫,當然查詢也會被阻塞了。

需要注意的是,此時 會話 1 對其他表也會不可讀寫。

FLUSH TABLES & WAITING FOR TABLE FLUSH

FLUSH TABLES 關閉所有打開的表,強制關閉所有正在使用的表,並刷新準備好的語句緩存。當存在活動的 LOCK TABLES 時,不允許執行 FLUSH TABLES 操作。

FLUSH TABLES

  1. ALTER 表結構時,執行 FLUSH TABLES 阻塞,從而導致表對象不可讀寫。

  1. LOCK TABLES 後,執行 FLUSH TABLES 會被阻塞,從而導致表對象不可讀寫。

    • 會話 1 執行:lock tables db_version read;
    • 會話 2 執行:flush tables;

此時,會話 2 會被阻塞,其他會話對所涉及表將不可讀寫。SHOW PROCESSLIST 中會提示 Waiting for table flush

需要說明的是,會話 1 執行完 lock tables...read lock 後,其他會話執行 DML 增刪改語句,雖然會因獲取不到元數據鎖而阻塞,但不會阻塞其他會話執行 SELECT 查詢。

換言之,執行 lock tables...read 後,當遇到元數據鎖排它鎖互斥阻塞(ALTER 語句)或者 FLUSH TABLES 發生阻塞後,纔會發生所涉及表對象不可讀寫。

處理延伸

如何處理並找到源頭 SQL 呢?

對於因元數據鎖互斥而導致的表不可讀寫,一般可以通過 sys 庫下的內置視圖來查看。可能會涉及的表:

sys.schema_table_lock_waits: 可直接通過 sys 下內置視圖,看到元數據鎖互斥的相關信息。

information_schema.innodb_trx: 找到長時間未提交的事務。

對於因 FLUSH TABLE 等待而導致的表不可讀寫的場景,通過上述視 圖/表 是不一定有數據的。大致會有以下兩種情況:

Waiting for table flush: 可以按如下方式尋找源頭。這種情況主要出現於因 FLUSH TABLES 而等待後,執行 DML 語句。

SELECT
  b.PROCESSLIST_ID,
  b.THREAD_ID,
  a.OBJECT_NAME,
  a.LOCK_TYPE,
  a.LOCK_STATUS,
  b.PROCESSLIST_STATE 
FROM
  `performance_schema`.metadata_locks a
  LEFT JOIN `performance_schema`.threads b ON a.OWNER_THREAD_ID = b.THREAD_ID 
WHERE
  a.OBJECT_SCHEMA = 'tmp';

也可以通過線程 ID 找到會話最近的 10 條語句進一步判斷確認。

select THREAD_ID,event_id,sql_text from 
`performance_schema`.events_statements_history
where THREAD_ID =  14503
order by event_id;

Waiting for table metadata lock: 可以參考元數據鎖互斥而導致的表不可讀寫處理。這種情況主要出現於因 FLUSH TABLES 而等待後,執行 DDL 語句如 ALTER TABLE

總結

以下情況會導致表對象不可讀寫:

  1. Waiting for table metadata lock 而導致的表對象不可讀寫。
  2. Waiting for table flush 而導致的表對象不可讀寫。

更多技術文章,請訪問:https://opensource.actionsky.com/

關於 SQLE

SQLE 是一款全方位的 SQL 質量管理平臺,覆蓋開發至生產環境的 SQL 審覈和管理。支持主流的開源、商業、國產數據庫,爲開發和運維提供流程自動化能力,提升上線效率,提高數據質量。

SQLE 獲取

類型 地址
版本庫 https://github.com/actiontech/sqle
文檔 https://actiontech.github.io/sqle-docs/
發佈信息 https://github.com/actiontech/sqle/releases
數據審覈插件開發文檔 https://actiontech.github.io/sqle-docs/docs/dev-manual/plugins/howtouse
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章