中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何理解mysql Meta Lock 機制

發布時間:2021-11-16 15:45:41 來源:億速云 閱讀:290 作者:柒染 欄目:MySQL數據庫

如何理解mysql Meta Lock 機制,針對這個問題,這篇文章詳細介紹了相對應的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。

  原因   

  最近在上線的 過程中出現主從數據不一致的現象 。發現問題是 slave 進行ddl 操作時候等待   Waiting for table metadata lock

 發現查詢用戶有幾個進程在連接上,字節把這些線程kill掉。slave 基本上瞬間執行完同步

因為是停應用的上線操作對對等待時間較長。但是對業務未有有影響。

   發現時間,未啟動應用。也造成從庫的雪崩問題。

為了解決該問題。并且在未來對線上停機時間要求比較嚴格。停機時間較短 or 記性讀寫分離 or 進行ddl時候 進行下列分析

如何理解mysql Meta Lock 機制

為什么要有MDL 鎖

在MySQL5.1及之前的版本中,如果有未提交的事務trx,當執行DROP/RENAME/ALTER TABLE RENAME操作時,不會被其他事務阻塞住。這會導致如下問題(MySQL bug#989)

master:

未提交的事務,但SQL已經完成(binlog也準備好了),表schema發生更改,在commit的時候不會被察覺到。

slave:

在binlog里是以事務提交順序記錄的,DDL隱式提交,因此在備庫先執行DDL,后執行事務trx,由于trx作用的表已經發生了改變,因此trx會執行失敗。

在DDL時的主庫DML壓力越大,這個問題觸發的可能性就越高

一個簡單的例子:

session1,set autocommit=0,對表b執行一條DML

root@xxx 11:48:28>set autocommit = 0;

Query OK, 0 rows affected (0.00 sec)

root@xxx 11:48:35>insert into b values (NULL,4);

Query OK, 1 row affected (0.00 sec)

session2,執行rename table a to tmp_b

root@xxx 11:48:23>rename table b to tmp_b;

Query OK, 0 rows affected (0.01 sec)

session1:commit;

root@xxx 11:49:00>show binlog events;

+——————+—–+—-———+———–+——–—–+—————————————+

| Log_name         | Pos | Event_type  | Server_id | End_log_pos | Info                                  |

+——————+—–+—-———+———–+——–—–+—————————————+

| mysql-bin.000001 |   4 | Format_desc |        12 |         106 | Server ver: 5.1.48-log, Binlog ver: 4 |

| mysql-bin.000001 | 106 | Query       |        12 |         191 | use `xxx`; rename table b to tmp_b    |

| mysql-bin.000001 | 191 | Query       |        12 |         258 | BEGIN                                 |

| mysql-bin.000001 | 258 | Table_map   |        12 |         298 | table_id: 195 (xxx.b)                 |

| mysql-bin.000001 | 298 | Write_rows  |        12 |         336 | table_id: 195 flags: STMT_END_F       |

| mysql-bin.000001 | 336 | Xid         |        12 |         363 | COMMIT /* xid=737 */                  |

+——————+—–+—-———+———–+——–—–+—————————————+

顯然當這樣的Binlog同步到備庫的話,必然會導致復制中斷。

在5.1里可以通過如下步驟繞過bug:

>set autocommit = 0;

>lock tables t1 write;

> drop table t1 / alter table t1 rename to t2

rename table t1 to t2這樣的DDL不適用于上述方法。

在5.5引入了MDL(meta data lock)鎖來解決在這個問題,至于5.1,官方已經明確回復不會FIX,太傷感了。。。

MDL 的類型
  metadata lock也是一種鎖。每個metadata lock都會定義鎖住的對象,鎖的持有時間和鎖的類型。
2.1 按照對象/范圍劃分

屬性 含義 范圍/對象
GLOBAL 全局鎖 范圍
COMMIT 提交保護鎖 范圍
SCHEMA 庫鎖 對象
TABLE 表鎖 對象
FUNCTION 函數鎖 對象
PROCEDURE 存儲過程鎖 對象
TRIGGER 觸發器鎖 對象
EVENT 事件鎖 對象


MDL按鎖住的對象來分類,可以分為global,commit,schema, table, function,procedure,trigger,event,這些對象發生鎖等待時,我們在show processlist可以分別看到如下等待信息。
 

  1. Waiting for global read lock 

  2. Waiting for commit lock

  3. Waiting for schema metadata lock

  4. Waiting for table metadata lock

  5. Waiting for stored function metadata lock

  6. Waiting for stored procedure metadata lock

  7. Waiting for trigger metadata lock

  8. Waiting for event metadata lock

2.2 按照鎖的持有時間

屬性 含義
MDL_STATEMENT 從語句開始執行時獲取,到語句執行結束時釋放。
MDL_TRANSACTION 在一個事務中涉及所有表獲取MDL,一直到事務commit或者rollback(線程中終清理)才釋放。
MDL_EXPLICIT 需要MDL_context::release_lock()顯式釋放。
語句或者事務結束,也仍然持有,如
Lock table, flush .. with lock語句等。


2.3 按照操作的對象

屬性 含義 事例
MDL_INTENTION_EXCLUSIVE(IX) 意向排他鎖用于global和commit的加鎖。 truncate table t1;
insert into t1 values(3,'abcde');會加如下鎖(GLOBAL,MDL_STATEMENT,MDL_INTENTION_EXCLUSIVE)                            (SCHEMA,MDL_TRANSACTION,MDL_INTENTION_EXCLUSIVE)

MDL_SHARED(S) 只訪問元數據 比如表結構,不訪問數據。 set golbal_read_only =on 加鎖
(GLOBAL,MDL_EXPLICIT,MDL_SHARED)
MDL_SHARED_HIGH_PRIO(SH) 用于訪問information_scheam表,不涉及數據。 select * from information_schema.tables;
show create table xx; desc xxx;會加如下鎖:
(TABLE,MDL_TRANSACTION,MDL_SHARED_HIGH_PRIO)
MDL_SHARED_READ(SR) 訪問表結構并且讀表數據 select * from t1; lock table t1 read;
會加如下鎖:
(TABLE,MDL_TRANSACTION,MDL_SHARE_READ)
MDL_SHARED_WRITE(SW) 訪問表結構并且寫表數據 insert/update/delete/select .. for update
會加如下鎖:
(TABLE,MDL_TRANSACTION,MDL_SHARE_WRITE)
MDL_SHARED_UPGRADABLE(SU) 是mysql5.6引入的新的metadata lock,
在alter table/create index/drop index會加該鎖;可以說是為了online ddl才引入的。特點是允許DML,防止DDL;
(TABLE,MDL_TRANSACTION,MDL_SHARED_UPGRADABLE)
MDL_SHARED_NO_WRITE(SNW) 可升級鎖,訪問表結構并且讀寫表數據,并且禁止其它事務寫。 alter table t1 modify c bigint;(非onlineddl)
(TABLE,MDL_TRANSACTION,MDL_SHARED_NO_WRITE)
MDL_SHARED_NO_READ_WRITE(SNRW) 可升級鎖,訪問表結構并且讀寫表數據,并且禁止其它事務讀寫。 lock table t1 write;加鎖
(TABLE,MDL_TRANSACTION,MDL_SHARED_NO_READ_WRITE
MDL_EXCLUSIVE(X) 防止其他線程讀寫元數據 CREATE/DROP/RENAME TABLE,其他online DDL在rename階段也持有X鎖
(TABLE,MDL_TRANSACTION,MDL_EXCLUSIVE)


關于global對象
   主要作用是防止DDL和寫操作的過程中,執行set golbal_read_only =on或flush tables with read lock;
 關于commit對象鎖
  主要作用是執行flush tables with read lock后,防止已經開始在執行的寫事務提交。
  insert/update/delete在提交時都會上(COMMIT,MDL_EXPLICIT,MDL_INTENTION_EXCLUSIVE)鎖。

2.4 MDL 鎖的兼容性矩陣

如何理解mysql Meta Lock 機制

如何理解mysql Meta Lock 機制

三、幾種典型語句的加(釋放)鎖流程
1.select語句操作MDL鎖流程
   1)Opening tables階段,加共享鎖
     a)   加MDL_INTENTION_EXCLUSIVE鎖
     b)   加MDL_SHARED_READ鎖
   2)事務提交階段,釋放MDL鎖
     a)   釋放MDL_INTENTION_EXCLUSIVE鎖
     b)   釋放MDL_SHARED_READ鎖
2. DML語句操作MDL鎖流程
  1)Opening tables階段,加共享鎖
     a)   加MDL_INTENTION_EXCLUSIVE鎖
     b)   加MDL_SHARED_WRITE鎖
  2)事務提交階段,釋放MDL鎖
     a)   釋放MDL_INTENTION_EXCLUSIVE鎖
     b)   釋放MDL_SHARED_WRITE鎖
3. alter操作MDL鎖流程
  1)Opening tables階段,加共享鎖
     a)   加MDL_INTENTION_EXCLUSIVE鎖
     b)   加MDL_SHARED_UPGRADABLE鎖,升級到MDL_SHARED_NO_WRITE鎖
  2)操作數據,copy data,流程如下:
     a)   創建臨時表tmp,重定義tmp為修改后的表結構
     b)   從原表讀取數據插入到tmp表
  3)將MDL_SHARED_NO_WRITE讀鎖升級到MDL_EXCLUSIVE鎖
     a)   刪除原表,將tmp重命名為原表名
  4)事務提交階段,釋放MDL鎖
     a)   釋放MDL_INTENTION_EXCLUSIVE鎖
     b)   釋放MDL_EXCLUSIVE鎖

四、典型問題分析
通常情況下我們關注MDL鎖,大部分情況都是線上DB出現異常了。那么出現異常后,我們如何去判斷是MDL鎖導致的呢。監視MDL鎖主要有兩種方法,一種是通過show  processlist命令,判斷是否有事務處于“Waiting for table metadata lock”狀態,另外就是通過mysql的profile,分析特定語句在每個階段的耗時時間。
拋出幾個問題:
select 與alter是否會相互阻塞
dml與alter是否會相互阻塞
select與DML是否會相互阻塞
結合第三節幾種語句的上鎖流程,我們很容易得到這三個問題的答案。語句會在阻塞在具體某個環節,可以通過profile來驗證我們的答案是否正確。
第一個問題,當執行select語句時,只要select語句在獲取MDL_SHARED_READ鎖之前,alter沒有執行到rename階段,那么select獲取MDL_SHARED_READ鎖成功,后續有alter執行到rename階段,請求MDL_EXCLUSIVE鎖時,就會被阻塞。rename階段會持有MDL_EXCLUSIVE鎖,但由于這個過程時間非常短(大頭都在copy數據階段),并且是alter的最后一個階段,所以基本感覺不到alter會阻塞select語句。由于MDL鎖在事務提交后才釋放,若線上存在大查詢,或者存在未提交的事務,則會出現ddl卡住的現象。這里要注意的是,ddl卡住后,若再有select查詢或DML進來,都會被堵住,就會出現threadrunning飆高的情況。
第二個問題,alter在opening階段會將鎖升級到MDL_SHARED_NO_WRITE,rename階段再將升級為MDL_EXCLUSIVE,由于MDL_SHARED_NO_WRITE與MDL_SHARED_WRITE互斥,所以先執行alter或先執行DML語句,都會導致語句阻塞在opening tables階段。

第三個問題,顯然,由于MDL_SHARED_WRITE與MDL_SHARED_READ兼容,所以它們不會因為MDL而導致等待的情況。

關于5.7 對MDL 鎖的改進

在MySQL 5.7里對MDL子系統做了更為徹底的優化。主要從以下幾點出發:

第一,盡管對MDL HASH進行了分區,但由于是以表名+庫名的方式作為key值進行分區,如果查詢或者DML都集中在同一張表上,就會hash到相同的分區,引起明顯的MDL HASH上的鎖競爭

針對這一點,引入了LOCK-FREE的HASH來存儲MDL_lock,LF_HASH無鎖算法基于論文"Split-Ordered Lists: Lock-Free Extensible Hash Tables",實現還比較復雜。 注:實際上LF_HASH很早就被應用于Performance Schema,算是比較成熟的代碼模塊。

由于引入了LF_HASH,MDL HASH分區特性自然直接被廢除了 。

對應WL#7305, PATCH(Rev:7249)

第二,從廣泛使用的實際場景來看,DML/SELECT相比DDL等高級別MDL鎖類型,是更為普遍的,因此可以針對性的降低DML和SELECT操作的MDL開銷。

為了實現對DML/SELECT的快速加鎖,使用了類似LOCK-WORD的加鎖方式,稱之為FAST-PATH,如果FAST-PATH加鎖失敗,則走SLOW-PATH來進行加鎖。

每個MDL鎖對象(MDL_lock)都維持了一個long long類型的狀態值來標示當前的加鎖狀態,變量名為MDL_lock::m_fast_path_state 舉個簡單的例子:(初始在sbtest1表上對應MDL_lock::m_fast_path_state值為0)

Session 1: BEGIN;

Session 1: SELECT * FROM sbtest1 WHERE id =1; //m_fast_path_state = 1048576, MDL ticket 不加MDL_lock::m_granted隊列

Session 2: BEGIN;

Session 2: SELECT * FROM sbtest1 WHERE id =2; //m_fast_path_state=1048576+1048576=2097152,同上,走FAST PATH

Session 3: ALTER TABLE sbtest1 ENGINE = INNODB; //DDL請求加的MDL_SHARED_UPGRADABLE類型鎖被視為unobtrusive lock,可以認為這個是比上述SQL的MDL鎖級別更高的鎖,并且不相容,因此被強制走slow path。而slow path是需要加MDL_lock::m_rwlock的寫鎖。m_fast_path_state = m_fast_path_state | MDL_lock::HAS_SLOW_PATH | MDL_lock::HAS_OBTRUSIVE

注:DDL還會獲得庫級別的意向排他MDL鎖或者表級別的共享可升級鎖,但為了表述方便,這里直接忽略了,只考慮涉及的同一個MDL_lock鎖對象。

Session 4: SELECT * FROM sbtest1 WHERE id =3; // 檢查m_fast_path_state &HAS_OBTRUSIVE,如果DDL還沒跑完,就會走slow path。

從上面的描述可以看出,MDL子系統顯式的對鎖類型進行了區分(OBTRUSIVE or UNOBTRUSIVE),存儲在數組矩陣m_unobtrusive_lock_increment。 因此對于相容類型的MDL鎖類型,例如DML/SELECT,加鎖操作幾乎沒有任何讀寫鎖或MUTEX開銷。

對應WL#7304, WL#7306 , PATCH(Rev:7067,Rev:7129)(Rev:7586)

第三,由于引入了MDL鎖,實際上早期版本用于控制Server和引擎層表級并發的THR_LOCK 對于Innodb而言已經有些冗余了,因此Innodb表完全可以忽略這部分的開銷。

不過在已有的邏輯中,Innodb依然依賴THR_LOCK來實現LOCK TABLE tbname READ,因此增加了新的MDL鎖類型來代替這種實現。

實際上代碼的大部分修改都是為了處理新的MDL類型,Innodb的改動只有幾行代碼。

對應WL#6671,PATCH(Rev:8232)

第四,Server層的用戶鎖(通過GET_LOCK函數獲取)使用MDL來重新實現。

用戶可以通過GET_LOCK()來同時獲取多個用戶鎖,同時由于使用MDL來實現,可以借助MDL子系統實現死鎖的檢測。

注意由于該變化,導致用戶鎖的命名必須小于64字節,這是受MDL子系統的限制導致。

對應WL#1159, PATCH(Rev:8356)

關于如何理解mysql Meta Lock 機制問題的解答就分享到這里了,希望以上內容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關注億速云行業資訊頻道了解更多相關知識。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

温泉县| 莱西市| 慈溪市| 忻城县| 松阳县| 永康市| 舒城县| 牙克石市| 黑山县| 肥西县| 邢台县| 鄯善县| 南丹县| 浮山县| 呈贡县| 河东区| 醴陵市| 宽城| 辉南县| 津市市| 左云县| 云霄县| 淮阳县| 雷波县| 秀山| 张家口市| 永泰县| 靖远县| 洪泽县| 柳林县| 疏勒县| 巫溪县| 渑池县| 竹溪县| 体育| 河东区| 岑溪市| 公主岭市| 怀来县| 夏津县| 鄂尔多斯市|