由於數據庫中有大量的併發訪問,爲了預防死鎖,通常推薦使用一次封鎖法,就是在方法的開始階段預先知道使用哪些數據,而後所有鎖住,在方法運行以後在解鎖。能夠避免死鎖。可是數據庫並不知道要用到哪些數據。數據庫
數據庫遵循兩段鎖協議,將事物分紅兩個階段。加鎖階段和解鎖階段。併發
加鎖階段:該階段能夠進行加鎖操做,在任何數據進行讀以前,都要申請並得到S鎖(共享鎖)。在寫操做以前要得到X鎖。加鎖不成功,則事物進入等待狀態,直到加鎖成功才繼續執行。性能
事務加鎖/解鎖處理spa
begin; insert into test .....加insert對應的鎖rest
update test set...加update對應的鎖索引
delete from test ....加delete對應的鎖事務
commit;it
事務提交時,同時釋放insert、update、delete對應的鎖。io
這種方式沒法避免死鎖,可是兩段鎖能夠保證事物的併發調度是串行的。table
在RC 這個隔離級別,數據的讀取都是不加鎖的,可是數據的寫入,修改,刪除是須要加鎖的。
事務A | 事務B |
---|---|
begin; | begin; |
update class_teacher set class_name='初三二班' where teacher_id=1; | update class_teacher set class_name='初三三班' where teacher_id=1; |
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction | |
commit; |
teacher_id 加了索引,鎖住的是teacher_id 這一行,若是沒有索引,鎖住是是整個表。但實際使用過程當中,MySql作了一些改進,在MySQL Server過濾條件發現不知足後,會調用unlock_row 方法把不知足條件的記錄釋放鎖 (違背了二段鎖協議的約束)。這樣作,保證了最後只會持有知足條件記錄上的鎖,可是每條記錄的加鎖操做仍是不能省略的。
RR 隔離級別
事務A | 事務B | 事務C | |||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
begin; | begin; |
begin; |
|||||||||
select id,class_name,teacher_id from class_teacher where teacher_id=1;
|
|||||||||||
update class_teacher set class_name='初三三班' where id=1; commit; |
|||||||||||
insert into class_teacher values (null,'初三三班',1);commit; | |||||||||||
select id,class_name,teacher_id from class_teacher where teacher_id=1;
|
不少人容易搞混不可重複讀和幻讀,確實這二者有些類似。但不可重複讀重點在於update和delete,而幻讀的重點在於insert。
在可重複讀中,該SQL第一次讀取到數據後就將這些數據加鎖,其它事務沒法修改這些數據,就能夠實現可重複讀了。可是沒法鎖住insert 數據。
mySql 這種成熟的數據庫出於性能考慮,使用了基於樂觀鎖的爲理論基礎的MVCC(協議)
在InnoDB中,會在每行數據後添加兩個額外的隱藏的值來實現MVCC,這兩個值一個記錄這行數據什麼時候被建立,另一個記錄這行數據什麼時候過時