> 拋磚引玉:多個查詢需要在同一時刻進行數據的修改,就會產生併發控制的問題。我們需要如何避免寫個問題從而保證我們的資料庫數據不會被破壞。 ## 鎖的概念 讀鎖是共用的互相不阻塞的。多個事務在聽一時刻可以同時讀取同一資源,而相互不幹擾。 寫鎖的排他的。一個寫鎖會阻塞其他寫鎖或讀鎖。出於安全考慮只有這樣 ...
拋磚引玉:多個查詢需要在同一時刻進行數據的修改,就會產生併發控制的問題。我們需要如何避免寫個問題從而保證我們的資料庫數據不會被破壞。
鎖的概念
讀鎖是共用的互相不阻塞的。多個事務在聽一時刻可以同時讀取同一資源,而相互不幹擾。
寫鎖的排他的。一個寫鎖會阻塞其他寫鎖或讀鎖。出於安全考慮只有這樣才能保證在給定的時間里只有一個事務能夠執行寫入,並防止其他事務讀取正寫入的同一資源。
鎖帶來的問題
通過鎖定機制可以實現事務的隔離性要求,使得事務可以併發的工作,同時也帶來了三個問題:臟讀,不可重覆讀和丟失更新。
臟讀
臟數據:未提交的數據
如果讀到了臟數據即一個事務可以讀取到另一個事務中未提交的數據那就違背了事務的隔離性。
所以臟讀是指在不同的事務下,當前事務可以讀取到另外事務的未提交的數據,簡單來說就是可以讀取到臟數據。
演示:
初始狀態:
將會話A,B設置隔離級別為RU
set session transaction isolation level READ UNCOMMITTED;
會話A插入一條數據
這時候事務B在此執行查詢操作,會發現事務B讀取到了事務A新增的數據。註意:此時事務A沒有提交。
不可重覆讀
在一個事務中兩次讀取到的數據是不一樣的,這中情況被稱為不可重覆讀。
與臟讀的區別:臟讀是讀取到了未提交的數據,而不可重覆讀是讀取到的卻是已經提交的數據,但是違反了資料庫事務一致性的要求。
演示:
事務B插入一條數據並且提交
事務A在此執行select語句,事務A讀取到了事務B提交的數據
一般來說不可重覆讀問題是可以接受的,因為讀取到的是已經提交的數據,本身不會帶來什麼問題。例如Oracle 和 SQL Server的預設的事務隔離級別就是RC。 MySQL預設的事務隔離級別是RR。
在MySQL InnoDB中通過使用 Next-key lock 演算法來避免不可重覆讀問題,並且將不可重覆讀問題定義為幻讀(Phantom problem)
丟失更新
一個數據的更新會被另一個事務的更新操作所覆蓋,從而導致數據的不一致性。
第一種丟失:
A事務撤銷時,把已經提交的B事務的更新數據覆蓋了。這種錯誤可能造成很嚴重的問題,通過下麵的賬戶取款轉賬就可以看出來:
時間 | 取款事務A | 轉賬事務B |
---|---|---|
T1 | 開始事務 | |
T2 | 開始事務 | |
T3 | 查詢賬戶餘額為1000元 | |
T4 | 查詢賬戶餘額為1000元 | |
T5 | 匯入100元把餘額改為1100元 | |
T6 | 提交事務 | |
T7 | 取出100元把餘額改為900元 | |
T8 | 撤銷事務 | |
T9 | 餘額恢復為1000 元(丟失更新) |
第二類丟失更新
A事務覆蓋B事務已經提交的數據,造成B事務所做操作丟失:
時間 | 轉賬事務A | 取款事務B |
---|---|---|
T1 | 開始事務 | |
T2 | 開始事務 | |
T3 | 查詢賬戶餘額為1000元 | |
T4 | 查詢賬戶餘額為1000元 | |
T5 | 取出100元把餘額改為900元 | |
T6 | 提交事務 | |
T7 | 匯入100元 | |
T8 | 提交事務 | |
T9 | 把餘額改為1100 元(丟失更新) |
要避免丟失更新的發生,需要讓事務在這種情況的操作變成串列化,而不是並行操作。即上面的select操作中加上排他鎖。
MySQL鎖的分類:
按照鎖的粒度來說
MySQL主要包含三種類型(級別)的鎖定機制:
全局鎖:鎖的是整個database。
表級鎖:鎖的是某個table。 (表排他鎖,表共用鎖,元數據鎖,自增鎖)
行級鎖:鎖的是某行數據,也可能鎖定行之間的間隙。由某些存儲引擎實現,比如InnoDB。
InnoDB的行級鎖,按照鎖定範圍來說
分為四種:
1.記錄鎖(Record Locks):鎖定索引中一條記錄。
2.間隙鎖(Gap Locks):要麼鎖住索引記錄中間的值,要麼鎖住第一個索引記錄前面的值或者最後一個索
引記錄後面的值。
3.臨鍵鎖(Next-Key Locks):是索引記錄上的記錄鎖和在索引記錄之前的間隙鎖的組合(間隙鎖+記錄
鎖)。
4.插入意向鎖(Insert Intention Locks):做insert操作時添加的對記錄id的鎖。
InnoDB的行級鎖,按照功能來說
分為兩種:
1.共用鎖(S):允許一個事務去讀一行,阻止其他事務獲得相同數據集的排他鎖。
2.排他鎖(X):允許獲得排他鎖的事務更新數據,阻止其他事務取得相同數據集的共用讀鎖和排他寫鎖。
InnoDB行鎖是通過給索引上的索引項加鎖來實現的,因此InnoDB這種行鎖實現特點意味著:只有通過
索引條件檢索的數據,InnoDB才使用行級鎖,否則,InnoDB將使用表鎖!
註意:插入意向鎖
(1)插入意向鎖是一種Gap鎖,不是意向鎖,在insert操作時產生。
(2)在多事務同時寫入不同數據至同一索引間隙的時候,並不需要等待其他事務完成,不會發生鎖等 待。
(3)假設有一個記錄索引包含鍵值4和7,不同的事務分別插入5和6,每個事務都會產生一個加在4-7之 間的插入意向鎖,獲取在插入行上的排它鎖,但是不會被互相鎖住,因為數據行並不衝突。
(4)插入意向鎖不會阻止任何鎖,對於插入的記錄會持有一個記錄鎖。