剖析MYSQL鎖機制

2021-10-09 17:19:57 字數 3578 閱讀 5744

資料庫鎖設計的初衷是處理併發問題。作為多使用者共享的資源,當出現併發訪問的時候,資料庫需要合理地控制資源的訪問規則。鎖是計算機協調多個程序或執行緒併發訪問某一資源的機制。在資料庫中,資料是一種供許多使用者共享的資源。資料庫的鎖機制,就是資料庫為了保證資料的一致性,而使各種共享資源在被併發訪問變得有序所設計的一種規則。

從對資料操作的型別分類

根據加鎖的範圍,mysql 裡面的鎖大致可以分成全域性鎖、表級鎖和行鎖三類

為了盡可能提高資料庫的併發度,每次鎖定的資料範圍越小越好,理論上每次只鎖定當前操作的資料的方案會得到最大的併發度,但是管理鎖是很耗資源的事情(涉及獲取,檢查,釋放鎖等動作),因此資料庫系統需要在高併發響應和系統效能兩方面進行平衡,因此需要根據具體的業務需求選用合適的鎖。

全域性鎖全域性鎖是對整個資料庫例項加鎖,加鎖後整個資料庫處於唯讀狀態,之後的資料更新語句(資料的增刪改)、資料定義語句(包括建表、修改表結構等)和更新類事務的提交語句都將被阻塞執行。

mysql加全域性鎖的方式是mysql> flush tables with read lock;釋放全域性鎖的命令為:mysql>unlock tables;,或者斷開加鎖的session的連線即可。

顯然,一旦加上全域性鎖,整個資料庫就不能更新,即新資料無法寫入,這是非常影響業務的。因此全域性鎖的典型適用範圍就是給資料庫做全域性邏輯備份(mysqldump),也就是把整庫每個表都 select 出來存成文字。

讓整個資料庫處於唯讀狀態是有比較大的風險的:

但是如果在全域性備份時不加全域性鎖,可能會導致資料的不一致性,即如果已經將某乙個資料備份了之後,又對該資料進行了修改,那麼就會造成主從資料庫不一致的現象,並且binlog日誌也會有不一致。也就是說,不加鎖的話,備份系統備份的得到的庫不是乙個邏輯時間點,這個資料是邏輯不一致的。

官方自帶的邏輯備份工具是 mysqldump。當 mysqldump 使用引數–single-transaction的時候,導資料之前就會啟動乙個事務,來確保拿到一致性快照檢視。但是並不是所有的引擎都支援single-transaction,myisam 這種不支援事務的引擎,如果備份過程中有更新,總是只能取到最新的資料,那麼就破壞了備份的一致性。因此single-transaction 方法只適用於所有的表使用事務引擎的庫。如果有的表使用了不支援事務的引擎,那麼備份就只能通過 ftwrl 方法。

既然要全庫唯讀,為什麼不使用 set global readonly=true 的方式?

表級鎖mysql 裡面表級別的鎖有兩種:一種是表鎖,一種是元資料鎖(meta data lock,mdl)

表鎖的語法是 lock tables … read/write,可以用 unlock tables 主動釋放鎖,也可以在客戶端斷開的時候自動釋放。如果在某個執行緒 a 中執行 lock tables t1 read, t2 write; 這個語句,則其他執行緒寫 t1、讀寫 t2 的語句都會被阻塞。同時,執行緒 a 在執行 unlock tables 之前,也只能執行讀 t1、讀寫 t2 的操作,也不能去訪問其他表。

另一類表級的鎖是 mdl(metadata lock)。mdl 不需要顯式使用,在訪問乙個表的時候會被自動加上,mdl 的作用是,保證讀寫的正確性。在 mysql 5.5 版本中引入了 mdl,當對乙個表做增刪改查操作的時候,加 mdl讀鎖;當要對錶做結構變更操作的時候,加 mdl 寫鎖。讀鎖是不互斥的,多個讀鎖可以同時加上去,但加上讀鎖後不可以加上去寫鎖,寫鎖是互斥的,加上寫鎖後不可以再加其他鎖。

通常來說,表級鎖更適合於以查詢為主,只有少量按索引條件更新資料的應用,如web應用。

myisam 的表鎖有兩種模式:

myisam 表的讀操作與寫操作之間,以及寫操作之間是序列的。當乙個執行緒獲得對乙個表的寫鎖後, 只有持有鎖的執行緒可以對錶進行更新操作。 其他執行緒的讀、 寫操作都會等待,直到鎖被釋放為止。

預設情況下,寫鎖比讀鎖具有更高的優先順序:當乙個鎖釋放時,這個鎖會優先給寫鎖佇列中等候的獲取鎖請求,然後再給讀鎖佇列中等候的獲取鎖請求。

不過需要注意的是,事務中的 mdl 鎖,在語句執行開始時申請,但是語句結束後並不會馬上釋放,而會等到整個事務提交後再釋放,因此要謹慎使用,可能導致死鎖。

因此如果有長事務需要mdl鎖,那麼由於事務一直不提交,就會一直佔著 mdl 鎖。在 mysql 的information_schema 庫的 innodb_trx 表中,你可以查到當前執行中的事務。如果你要做 ddl 變更的表剛好有長事務在執行,要考慮先暫停 ddl,或者 kill 掉這個長事務。對於熱點表,請求非常頻繁,那麼在 alter table 語句裡面設定等待時間,如果在這個指定的等待時間裡面能夠拿到 mdl 寫鎖最好,拿不到也不要阻塞後面的業務語句,先放棄。

還有補充的是,在自動加鎖的情況下,myisam 總是一次獲得 sql 語句所需要的全部鎖,所以 myisam 表不會出現死鎖。【因為沒有事件】

行鎖行鎖的開銷大,加鎖慢;會出現死鎖;鎖定粒度最小,發生鎖衝突的概率最低,併發度也最高。innodb 是支援行鎖的,myisam 引擎就不支援行鎖。

行鎖是兩階段鎖,在 innodb 事務中,行鎖是在需要的時候才加上的,但並不是不需要了就立刻釋放,而是要等到事務結束時才釋放。因此,為了盡可能提高併發度,加快處理速度,如果事務中需要鎖多個行,要把最可能造成鎖衝突、最可能影響併發度的鎖盡量往後放。

行鎖很容易引發死鎖,如下面的例子:

事務 a 在等待事務 b 釋放 id=2 的行鎖,而事務 b 在等待事務 a 釋放 id=1 的行鎖。 事務 a 和事務 b 在互相等待對方的資源釋放,就是進入了死鎖狀態.

當出現死鎖以後,有兩種策略:

第一種方案中,超時值的設定就比較麻煩,如果超時值設定太大,那麼可能導致等待時間過長,對線上服務產生非常不好的影響,如果超時值設定太小,會產生比較多的誤傷,正常的鎖等待可能會被誤判。

所以,正常情況下我們還是要採用第二種策略,即:主動死鎖檢測,而且 innodb_deadlock_detect 的預設值本身就是 on。主動死鎖檢測在發生死鎖的時候,是能夠快速發現並進行處理的,但是它也是有額外負擔的。

每當乙個事務被鎖的時候,就要看看它所依賴的執行緒有沒有被別人鎖住,如此迴圈,最後判斷是否出現了迴圈等待,也就是死鎖,因此對每個新來的被堵住的執行緒,都要判斷會不會由於自己的加入導致了死鎖,這是乙個時間複雜度是 o(n) 的操作,因此整體是o(n

2n^2

n2)的時間複雜度。

innodb 實現的兩種型別的行鎖

加鎖機制

樂觀鎖與悲觀鎖是兩種併發控制的思想,可用於解決丟失更新問題

樂觀鎖會「樂觀地」假定大概率不會發生併發更新衝突,訪問、處理資料過程中不加鎖,只在更新資料時再根據版本號或時間戳判斷是否有衝突,有則處理,無則提交事務。用資料版本(version)記錄機制實現,這是樂觀鎖最常用的一種實現方式。

悲觀鎖會「悲觀地」假定大概率會發生併發更新衝突,訪問、處理資料前就加排他鎖,在整個資料處理過程中鎖定資料,事務提交或回滾後才釋放鎖。另外與樂觀鎖相對應的,悲觀鎖是由資料庫自己實現了的,要用的時候,我們直接呼叫資料庫的相關語句就可以了。

mysql鎖機制 mysql 鎖機制

一 概述 mysql有三種鎖的級別 頁級 表級 行級。myisam和memory儲存引擎採用的是表級鎖 table level locking bdb儲存引擎採用的是頁面鎖 page level locking 但也支援表級鎖 innodb儲存引擎既支援行級鎖 row level locking 也...

mysql鎖機制 php Mysql鎖機制

表級鎖 開銷小,加鎖快 不會出現死鎖 鎖定粒度大,發生鎖衝突的概率最高,併發度最低。行級鎖 開銷大,加鎖慢 會出現死鎖 鎖定粒度最小,發生鎖衝突的概率最低,併發度也最高。共享鎖和排它鎖 頁面鎖 開銷和加鎖時間界於表鎖和行鎖之間 會出現死鎖 鎖定粒度界於表鎖和行鎖之間,併發度一般 mysql的行級鎖有...

mysql鎖機制總結 mysql鎖機制總結

1.隔離級別 1 讀不提交 read uncommited,ru 這種隔離級別下,事務間完全不隔離,會產生髒讀,可以讀取未提交的記錄,實際情況下不會使用。2 讀提交 read commited,rc 僅能讀取到已提交的記錄,這種隔離級別下,會存在幻讀現象,所謂幻讀是指在同乙個事務中,多次執行同乙個查...