關於資料庫鎖的總結

pikalu發表於2020-11-24

前言

本文主要是在總結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.時間戳機制

其道理和版本號一樣。

悲觀鎖

實際上是一種思想,對資料被其他的事務修改持保守態度 會通過資料庫自身的鎖機制來實現,保證資料操作的排他性

對資料衝突持有悲觀的態度,則認為肯定會衝突,那麼在每次資料讀取的時候將資料鎖住;之後所有的操作讀取操作都需要等待;

資料庫層面實現,阻止一切資料庫操作。

常見的死鎖問題

必要條件:互斥,佔有且等待,不可強佔用,迴圈等待。

解決死鎖的方法

最佳方式就是防止死鎖的發生。

注意的一些細節:

  1. 不要將無關的操作放進事務裡,小事務發生死鎖的概率很低;

  2. 如果不同的程式會同時操作多個表,應儘量約定以相同的順序來訪問表,這樣事務就會形成良好的定義的查詢;

  3. 儘量按照索引去查資料,範圍查詢增加了鎖的可能性;

  4. 對於非常容易產生死鎖的業務部分,可以嘗試升級鎖的粒度;

  5. 更新表時,儘量使用主鍵更新;

  6. 設定鎖等待超時引數,通過設定 innodb_lock_wait_timeout 引數,設定合理的等待閾值;在高併發的業務中,儘量將該值設定的小一點,避免大量事務等待,佔用系統資源,造成嚴重的效能開銷;

  7. Innodb提供了wait-for graph演算法來主動進行死鎖檢測,我們可以通過innodb_deadlock_detect = on 開啟死鎖檢測。

其他

原文連結:關於資料庫鎖的總結

本作品採用《CC 協議》,轉載必須註明作者和本文連結
不積跬步,無以至千里;不積小流,無以成江海

相關文章