MySql(三) MySql中的鎖機制

前面兩篇博客中簡單的聊了下mysql中的索引,今天聊聊mysql(InnoDB引擎)中的鎖以及事務的實現

MySql(一) 淺析MySql索引
html

MySQL(二) MySql經常使用優化
mysql


講到鎖你們應該都不陌生。像是Java中常見的採用CAS算法實現的樂觀鎖,典型的例子就是原子類,經過CAS自旋實現原子操做的更新,悲觀鎖一般都是 SynchronizedLock實現。

樂觀鎖與悲觀鎖

  • 樂觀鎖:每次讀數據的時候都認爲其餘人不會修改,因此不會上鎖,而是在更新的時候去判斷在此期間有沒有其餘人更新了數據,可使用版本號機制。在數據庫中能夠經過爲數據表增長一個版本號字段實現。讀取數據時將版本號一同讀出,數據每次更新時對版本號加一。當咱們更新的時候,判斷數據庫表對應記錄的當前版本號與第一次取出來的版本號值進行比對,若是值相等,則予以更新,不然認爲是過時數據。樂觀鎖適用於多讀的應用類型,能夠提升吞吐量。
  • 悲觀鎖:每次讀數據的時候都認爲別人會修改,因此每次在讀數據的時候都會上鎖,這樣別人想讀這個數據時就會被阻塞。MySQL中就用到了不少這種鎖機制,好比行鎖,表鎖等,讀鎖,寫鎖等,都是在操做以前先上鎖。


共享鎖與排他鎖

  • 共享鎖:共享鎖又叫作讀鎖或S鎖,加上共享鎖後在事務結束以前其餘事務只能再加共享鎖、只能對其進行讀操做不能寫操做,除此以外其餘任何類型的鎖都不能再加了。

# 加上lock in share mode
SELECT description FROM book_book lock in share mode;複製代碼

  • 排他鎖:排他鎖又叫寫鎖或X鎖,某個事務對數據加上排他鎖後,只能這個事務對其進行讀寫,在此事務結束以前,其餘事務不能對其加任何鎖,能夠讀取,不能進行寫操做,需等待其釋放。

# 加上for update
SELECT description FROM book_book for update; 
複製代碼


行鎖與表鎖

行鎖與表鎖區別在於鎖的粒度,在Innodb引擎中既支持行鎖也支持表鎖(MyISAM引擎只支持表鎖),只有經過索引條件檢索數據InnoDB才使用行級鎖,不然,InnoDB將使用表鎖。
  • 表鎖:開銷小,加鎖快;不會出現死鎖;鎖定粒度大,發生鎖衝突機率高,併發度最低
  • 行鎖:開銷大,加鎖慢;會出現死鎖;鎖定粒度小,發生鎖衝突的機率低,併發度高

這裏有個比較疑惑的地方,爲何表鎖不會出現死鎖?在MyISAM中因爲沒有事務,一條SQL執行完鎖就釋放了,不會循環等待,因此只會出現阻塞而不會發生死鎖。可是在InnoDB中有事務就比較疑惑了,但願有了解的小夥伴指點指點@-@算法

下面舉兩個例子說明上面幾種鎖:

# 事務1
BEGIN;
SELECT description FROM book_book where name = 'JAVA編程思想' lock in share mode;

# 事務2
BEGIN;
UPDATE book_book SET name = 'new book' WHERE name = 'new';

# 查看事務狀態
SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX;

trx_id  trx_state       trx_started           trx_tables_locked    trx_rows_locked
39452	LOCK WAIT	2018-09-08 19:01:39	    1	                1	
282907511143936	RUNNING	2018-09-08 18:58:47	    1	                38	
複製代碼

事務1給book表加上了共享鎖,事務2嘗試修改book表發生了阻塞,查看事務狀態能夠知道事務一因爲沒有走索引使用了表鎖。
sql

# 事務1
BEGIN;
SELECT description FROM book_book WHERE id = 2 lock in share mode;

# 事務2
BEGIN;
UPDATE book_book SET name = 'new book' WHERE id = 1; 

# 查看事務狀態
SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX;

trx_id          trx_state   trx_started     trx_tables_locked    trx_rows_locked
39454	        RUNNING	2018-09-08 19:10:44	1	                1	
282907511143936	RUNNING	2018-09-08 19:10:35	1	                1	
複製代碼

事務1給book表加上了共享鎖,事務2嘗試修改book表並無發生阻塞。這是因爲事務一和事務二都走了索引,因此使用的是行鎖,並不會發生阻塞。
數據庫


意向鎖(InnoDB特有)

意向鎖的意義在於方便檢測表鎖和行鎖之間的衝突
  • 意向鎖:意向鎖是一種表級鎖,表明要對某行記錄進行操做。分爲意向共享鎖(IS)和意向排他鎖(IX)。
  • 行鎖和表鎖之間的衝突:事務A給表中的某一行加了共享鎖,讓這一行只能讀不能寫。以後事務B申請整個表的排他鎖。若是事務B申請成功,那麼它就能修改表中的任意一行,這與A持有的行鎖是衝突的。InnoDB引入了意向鎖來判斷它們之間的衝突。
    • 沒有意向鎖的狀況:一、判斷表是否已被其餘事務用表鎖鎖表。二、判斷表中的每一行是否已被行鎖鎖住,這樣要遍歷整個表,效率很低。
    • 意向鎖存在的狀況:一、判斷表是否已被其餘事務用表鎖鎖表。二、判斷表上是否有意向鎖
  • 意向鎖存在時申請鎖:申請意向鎖的動做是數據庫完成的,上述例子中事務A申請一行的行鎖的時候,數據庫會自動先開始申請表的意向鎖,當事務B申請表的排他鎖時檢測到存在乎向鎖則會阻塞。
  • 意向鎖會不會存在衝突: 意向鎖之間不會衝突, 由於意向鎖只是表明要對某行記錄進行操做。


各類鎖之間的共存狀況

IX     IS       X      S
IX    兼容    兼容    衝突    衝突
IS    兼容    兼容    衝突    兼容
X     衝突    衝突    衝突    衝突
S     衝突    兼容    衝突    兼容
複製代碼


死鎖

  • 概念:兩個或兩個以上的事務在執行過程當中,因爭奪資源而形成的一種互相等待的現象。
  • 存在條件:一、 互斥條件:一個資源每次只能被一個事務使用。二、 請求與保持條件:一個事務因請求資源而阻塞時,對已得到的資源保持不放。三、不剝奪條件:已得到的資源,在末使用完以前不能強行剝奪。四、循環等待條件:造成一種頭尾相接的循環等待關係
  • 解除正在死鎖的狀態:撤銷其中一個事務


MVCC(多版本併發控制)

MVCC使得InnoDB更好的實現事務隔離級別中的REPEATABLE READ
  •  它使得InnoDB再也不單純的使用行鎖來進行數據庫的併發控制,取而代之的是把數據庫的行鎖與行的多個版本結合起來,只須要很小的開銷,就能夠實現非鎖定讀,從而大大提升數據庫系統的併發性能。
  • 實現:InnoDB實現MVCC的方法是它爲每一行存儲三個額外的隱藏字段
    • 1.DB_TRX_ID:一個6byte的標識,每處理一個事務,其值自動+1 ,能夠經過語句「show engine innodb status」來查找
    • 2.DB_ROLL_PTR: 大小是7byte,指向寫到rollback segment(回滾段)的一條undo log記錄 
    • 3.DB_ROW_ID: 大小是6byte,該值隨新行插入單調增長。
  • SELECT:返回的行數據須要知足的條件:  一、數據行的建立版本號必須小於等於事務的版本二、行的刪除版本號(行中的特殊位被設置爲將其標記爲已刪除)必定是未定義的或者大於當前事務的版本號,肯定了當前事務開始以前行沒有被刪除。
  • INSERT:InnoDB爲每一個新增行記錄當前系統版本號做爲建立版本號。
  • DELETE:InnoDB爲每一個刪除行的記錄當前系統版本號做爲行的刪除版本號。
  • UPDATE:InnoDB複製了一條數據。這條數據的版本號使用了系統版本號。它也把系統版本號做爲老數據的刪除號。
  • 說明:這裏的讀是不加鎖的select等,MVCC實現可重複讀使用的是讀取undo中的已經提交的數據,是非阻塞的。insert操做時"建立時間"=DB_ROW_ID,這時"刪除時間"是未定義的;update時,複製新增行的"建立時間"=DB_ROW_ID,刪除時間未定義,舊數據行"建立時間"不變,刪除時間=該事務的DB_ROW_ID; delete操做,相應數據行的"建立時間"不變,刪除時間=該事務的DB_ROW_ID;


間隙鎖(Next-Key鎖)

間隙鎖使得InnoDB解決幻讀問題,加上MVCC使得InnoDB的RR隔離級別實現了串行化級別的效果,而且保留了比較好的併發性能。

定義:當咱們用範圍條件檢索數據時請求共享或排他鎖時,InnoDB會給符合條件的已有數據的索引加鎖;對於鍵值在條件範圍內但並不存在的記錄,叫作間隙(GAP),InnoDB也會對這個"間隙"加鎖,這種鎖機制就是間隙鎖。
編程

例如:book表中存在bookId 1-80,90-99的記錄。SELECT * FROM book WHERE bookId < 100 FOR UPDATE。InnoDB不只會對bookId值爲1-80,90-99的記錄加鎖,也會對bookId在81-89之間(這些記錄並不存在)的間隙加鎖。這樣就能避免事務隔離級別可重複讀下的幻讀。bash






有問題的同窗能夠指出相互探討,如需轉載請註明出處。
參考文獻:
https://dev.mysql.com/doc/refman/5.7/en/innodb-multi-versioning.htmlhttps://www.cnblogs.com/chenpingzhao/p/5065316.html
相關文章
相關標籤/搜索