前言
本文主要是在總結MySQL資料庫鎖時,記錄的一些筆記。
按鎖的粒度劃分
分為:行鎖、頁鎖、表鎖
鎖定的粒度越小,發生鎖衝突的概率越低,可以實現的併發度越高 ;但是對於鎖的開銷比較大,加鎖會比較慢,容易出現死鎖。
行鎖的三種型別
Record Lock 單個記錄上的鎖
對索引項加鎖,如果innodb引擎的表在建立的時候 沒有設定任何索引 那麼這時候對InnoDB儲存引擎會用隱性的主鍵來進行鎖定。
Gap Lock 間隙鎖
對索引項之間的間隙加鎖,設計目的:為了解決幻讀,利用這種鎖技術,鎖定的不是單個值,而是一個範圍。
Next-key lock
則是前面兩種的組合,對索引項以其之間的間隙加鎖。
只有在可重複讀或以上的隔離級別下的特定操作才會取得gap lock或者 next-key lock,在select update delete時,除了基於唯一索引的查詢之外,其他索引都會獲取gap lock 或者 next-key lock,即鎖住掃描的範圍。
從資料庫的管理角度區分
共享鎖和排他鎖 ,即讀鎖和寫鎖
共享鎖
也叫讀鎖或者S鎖。
共享鎖的資源可以被其他使用者讀取,但是不能修改;在select時,會將物件進行共享鎖鎖定;讀取完畢時,就會釋放鎖,就可以保證資料在讀取時不會被修改。
lock table xxx read;
unlock table;
共享鎖出現死鎖的原因:多個事務對同一資料獲得讀鎖的時候,可能會出現死鎖。
排他鎖
也叫獨佔鎖,寫鎖或者X鎖。
排它鎖,鎖定的資料只允許進行鎖定的事務使用;其他事務無法對已鎖定的資料進行查詢和操作。
lock table xxx write;
unlock table;
從程式設計師的角度區分
可以將鎖劃分為樂觀鎖和悲觀鎖。
樂觀鎖
樂觀鎖認為對同一資料的併發操作不會總髮生,持樂觀態度,屬於小概率事件;
不需要每次都對資料上鎖,也就是不採用資料庫自身的鎖機制,而是通過程式來實現;
在程式上,我們可以採用版本號機制或者時間戳機制來實現; 其實就是程式層面控制
不會存在死鎖的問題,但是阻止不了程式之外的資料庫操作。
主要的實現方式:
1.版本號機制
新增version欄位,第一次讀取的時候會獲取version的值,然後對資料進行更新和刪除操作時,會進行版本號的比對;
如果版本號一致,則修改資料,並將版本號+1;如果版本號不一致 則修改失敗。
2.時間戳機制
其道理和版本號一樣。
悲觀鎖
實際上是一種思想,對資料被其他的事務修改持保守態度 會通過資料庫自身的鎖機制來實現,保證資料操作的排他性
對資料衝突持有悲觀的態度,則認為肯定會衝突,那麼在每次資料讀取的時候將資料鎖住;之後所有的操作讀取操作都需要等待;
資料庫層面實現,阻止一切資料庫操作。
常見的死鎖問題
必要條件:互斥,佔有且等待,不可強佔用,迴圈等待。
解決死鎖的方法
最佳方式就是防止死鎖的發生。
注意的一些細節:
不要將無關的操作放進事務裡,小事務發生死鎖的概率很低;
如果不同的程式會同時操作多個表,應儘量約定以相同的順序來訪問表,這樣事務就會形成良好的定義的查詢;
儘量按照索引去查資料,範圍查詢增加了鎖的可能性;
對於非常容易產生死鎖的業務部分,可以嘗試升級鎖的粒度;
更新表時,儘量使用主鍵更新;
設定鎖等待超時引數,通過設定 innodb_lock_wait_timeout 引數,設定合理的等待閾值;在高併發的業務中,儘量將該值設定的小一點,避免大量事務等待,佔用系統資源,造成嚴重的效能開銷;
Innodb提供了wait-for graph演算法來主動進行死鎖檢測,我們可以通過innodb_deadlock_detect = on 開啟死鎖檢測。
其他
原文連結:關於資料庫鎖的總結
本作品採用《CC 協議》,轉載必須註明作者和本文連結