mysql metadata lock(三)


前言

MDL鎖主要用來保護Mysql內部對象的元數據,通過MDL機制保證DDL與DML以及SELECT查詢操作的並發。MySQL Meta Lock(一)MySQL Meta Lock(二)已經講了一些關於MDL知識,本文將會對MDL進行一個補充,並解釋查詢堵塞和mysqldump獲取一致性備份的原理。

一、MDL鎖類型

1.按類型划分

參見MySQL Meta Lock(二)

2.按對象/范圍維度划分

屬性

含義

范圍/對象

GLOBAL

全局鎖

范圍

COMMIT

提交保護鎖

范圍

SCHEMA

庫鎖

對象

TABLE

表鎖

對象

FUNCTION

函數鎖

對象

PROCEDURE

存儲過程鎖

對象

TRIGGER

觸發器鎖

對象

EVENT

事件鎖

對象

MDL鎖主要包括DB對象和范圍兩個維度,對象的MDL我們很好理解,為了保護對象的元數據。那么范圍級別的鎖呢?鎖本質是為了保護共享資源,所以范圍和對象都可以理解為一種資源。MYSQL約定某些操作必需要上COMMIT范圍鎖或GLOBAL范圍鎖,通過這種同步機制,保證各個線程有序運轉。后面會結合案例詳細討論COMMIT和GLOBAL鎖的應用場景。

3.按請求/釋放鎖持續時間划分

屬性

含義

MDL_ STATEMENT

語句級別

MDL_TRANSACTION

事務級別

MDL_EXPLICIT

需要顯示釋放

MDL另外一個屬性是持有時間。如果是STATEMENT,則表示單個語句執行完畢后,MDL釋放;TRANSACTION級別表示,事務結束后,MDL鎖釋放;前兩者都是隱式鎖,即請求鎖和釋放鎖都是系統內部行為,用戶無需發指令,而MDL_EXPLICIT表示MDL鎖是顯示請求和釋放的。比如:flush table with read lock這個指令會顯示上GLOBAL:MDL_EXPLICIT:SHARED和COMMIT:MDL_EXPLICIT:SHARED鎖;需要通過UNLOCK TABLES指令顯示釋放。

4.范圍鎖的兼容性矩陣

5.舉個栗子

begin:

update t3 set c1=1 where id=1;

commit;

流程

執行語句

執行內容

字典鎖

1

begin

 

釋放MDL

release_transactional_locks

2

update t3 set c1=1 where id=1;

 

請求

STATEMENT  MDL

GLOBAL:STATMENT

MDL_INTENTION_EXCLUSIVE

3

請求

TRANSACTION MDL

TABLE:TRANSACTION

MDL_SHARED_WRITE

6

執行更新

 

7

釋放

STATEMENT MDL

GLOBAL:STATMENT

8

commit;

 

請求

COMMIT MDL

COMMIT: MDL_EXPLICIT

MDL_INTENTION_EXCLUSIVE

9

執行提交

 

11

釋放

COMMIT MDL

釋放

TRANSACTION MDL

COMMIT: MDL_EXPLICIT

MDL_INTENTION_EXCLUSIVE

12

release_transactional_locks

TABLE:TRANSACTION

 

二、請求鎖/釋放鎖原理

鎖兼容矩陣在metadata lock(二)已經有詳細的介紹,仔細看代碼發現MDL的鎖兼容矩陣實際上包含兩部分:活躍鎖兼容矩陣,等待鎖兼容矩陣。當請求鎖時,需要保證兩個矩陣對應的值都兼容,才能請求鎖成功。為什么要設計等待鎖兼容矩陣?我理解這里主要是優先保證DDL操作。因為如果DDL操作在等待一個查詢操作時,其他查詢還源源不斷地進入,可能會導致DDL永遠也拿不到鎖,而實際情況下,DDL操作的重要性往往比查詢或DML重要地多。

1. 請求鎖兼容性檢查:

1) 檢查請求鎖是否與已經存在的活躍鎖沖突,若沖突,則等待;

2) 檢查請求鎖是否與已經存在的等待鎖沖突,若沖突,則等待。

3) 請求鎖成功。

2. 釋放鎖時機:

1) 語句執行結束后,釋放STAMENT類型的鎖

2) 事務提交時,先后請求COMMIT類型和釋放COMMIT類型的鎖

3) 事務提交后,釋放TRANSACTION類型的鎖

三、MDL應用場景分析

MDL是Mysql層面很重要的鎖,很多常見問題的源頭以及功能實現都依賴於MDL,以下我會舉幾個常見的問題和功能進行分析。

1. 為什么查詢也會被阻塞?

我們在實際運維過程中,一個常見的場景是,接到手機threadrunning飆高告警,登上主機,show processlist看到一大片線程處於“Waiting for table metadata lock”狀態,當然其中也包含查詢。下面我通過一個簡單的例子重新這個場景。

時間點

會話A

會話B

會話C

1

begin

update t3 set c1=1 where id=1;

 

 

2

返回

 

 

3

 

alter table t3 add column c3 int;

 

4

 

等待

 

5

 

 

select * from t3

6

 

 

等待

7

show processlist

返回結果

 

 

8

init                          

show processlist                             

 

 

Waiting for table metadata lock

alter table t3 add column c3 int

Waiting for table metadata lock

select * from t3

      從表2可以看到A會話未提交的事務堵住了回話B的DDL語句,而DDL語句進而又堵住了會話C,從第8步來看,會話B和會話C都處於“Waiting for table metadata lock”狀態。從表1我們可以看到,會話A的DML操作會請求TABLE- TRANSACTION- MDL_SHARED_WRITE鎖,由於沒有執行COMMIT,會一直持有;會話B的DDL操作會請求TABLE-TRANSACTION-EXCLUSIVE鎖,由於兩把鎖互斥,等待;會話C的查詢操作會請求TABLE- TRANSACTION- MDL_SHARED_READ鎖,雖然MDL_SHARED_READ與活躍鎖MDL_SHARED_WRITE不沖突,但是與回話B的等待鎖EXCLUSIVE沖突,因此也會等待。遇到這種情況,首先要看看是否存在堵住的DDL,如果存在DDL,然后查詢是否有大查詢或者未提交的事務,這兩種情況都會導致DDL堵住,進而影響普通的查詢和DML操作。

2. Mysqldump與全局鎖

      在實際生產環境中,為了容災和負載均衡,數據庫服務一般由一主一備一對實例組成,主庫對外提供讀寫服務,備庫提供只讀服務,或純粹為了容災使用。在這種體系下,通過mysqldump搭建新的實例時,需要獲得一個一致性備份集,並且獲得對應的位點(拉取主庫binlog的依據),通過全量+增量的方式復制一個數據庫實例。Mysqldump中為了保證一致性備份和獲取對應的位點,需要設置兩個關鍵的參數--master-data=2 和--single-transaction。我們可以通過mysqld的trace功能,跟蹤Mysqldump執行的語句。假設我們要備份chuck庫,命令如下:

 ./bin/mysqldump -uchuck -pchuck -P4006 –h127.0.0.1 --databases chuck mysql --master-data=2 --single-transaction --default-character-set=utf8 > chuck_dump.sql 2>chuck_dump.log

開啟mysqld的trace功能

--debug=d,query,general:O,/kkk/mysqld.trace

通過mysqld.trace,我大概整理了關鍵的語句,分析和結果如下圖所示。可以看到,mysqldump獲取增量位點主要是通過FLUSH TABLES WITH READ LOCK語句的兩把字典鎖來實現(GLOBAL LOCK和COMMIT LOCK)。整個過程持續時間不長(如果語句沒有被阻塞),獲取位點后,立即將MDL鎖釋放。第2到第6步即為持有MDL鎖的時間,這段時間內無法開啟新事務,已有的事務也無法提交,保證位點的正確性。設置隔離級別為RR,則是獲取一致性備份的關鍵,一致性備份的獲取源於MVCC,關於MVCC的實現,后續可以單獨寫一篇文章。第8到第9步為一個表的備份過程, select語句會獲取MDL鎖(TABLE:TRANSACTION: MDL_SHARED_READ),執行完畢后,通過rollback語句釋放MDL鎖。因此除了備份的當前表不能做DDL操作;當前表的DML,以及其它表的DDL和DML都不受影響,所以Mysqldump備份過程中,基本不會阻塞線上的其他語句執行。 

步驟

關鍵語句

含義

作用

1

FLUSH /*!40101 LOCAL */ TABLES
   

關閉打開表

清空查詢緩存

2

FLUSH TABLES WITH READ LOCK

上GLOBAL字典鎖:

GLOBAL:

MDL_SHARED

COMMIT:

MDL_SHARED

 

阻塞新事務,以及活躍事務提交。

為獲取一致性位點做准備

3

SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ

設置事務的隔離級別為RR

保證快照讀

4

START TRANSACTION /*!40100 WITH CONSISTENT SNAPSHOT */

開啟事務

 

5

SHOW MASTER STATUS

獲取binlog 位點

用來獲取主庫的增量的位點

 

6

UNLOCK TABLES

釋放

GLOBAL字典鎖

允許其它事務更新

7

SAVEPOINT sp

設置保存點

后續可以逐個表

釋放MDL

8

SELECT /*!40001 SQL_NO_CACHE */ * FROM `t1`

獲取表t1的數據

TABLE:TRANSACTION

MDL_SHARED_READ

一致性讀

9

ROLLBACK TO SAVEPOINT sp

釋放:

TABLE:TRANSACTION

MDL_SHARED_READ

10

SELECT /*!40001 SQL_NO_CACHE */ * FROM `t2`

獲取表t2的數據

TABLE:TRANSACTION

MDL_SHARED_READ

 

一致性讀

11

ROLLBACK TO SAVEPOINT sp

釋放:

TABLE:TRANSACTION

MDL_SHARED_READ

12

……

 

 

13

 

 

備份完成

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

參考文章

http://www.percona.com/blog/2010/04/24/how-fast-is-flush-tables-with-read-lock/

http://blogread.cn/it/article/2338?f=hot1

http://imysql.cn/2008_10_24_deep_into_mysqldump_options


免責聲明!

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



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