最近在看mysql相關的書籍.實驗了一些內容.分享一下,主要是關於事務隔離級別(read-committed和repeatable-read)和鎖相關的.mysql
不少網上文章上都能搜索到 read-committed能夠防止髒數據.可是不能防止 不可重複讀.sql
而repeatable-read能夠防止 不可重複讀.可是不能防止幻讀.session
如今我想分享下具體是怎麼操做的. 日誌
read-committed和repeatable-read的區別究竟是什麼?索引
我以爲在 不可重複讀 這個問題上並無涉及鎖的問題.而是涉及一致性非鎖定讀的問題事務
假設有一張user,主鍵id int, name varchar, i varchar.it
在i這一列上有索引,非惟一.io
數據以下:table
id name idate
1 v1 i1
5 v5 i5
9 v9 i9
全部session的 autocommit=0 不會自動提交.
session A:
update user set name = 'v5-new' where id = 5;
這個時候鎖定了id=5這一列.沒有commit
Session B:
select name from user where id = 5
查詢出來是v5.這是由於RR和RC都不會讀到髒數據
而後session A:
commited;
這個時候行最新的name已是v5-new了
Session B:
select name from user where id = 5
這個時候的select就有區別了.
RC讀出來是v5-new, RR仍然是v5
可是要注意的是Session B在這裏始終都只是通常的select,因此沒有加鎖.因此這裏不可重複和鎖我以爲沒有關係(可是和session A在記錄上加的X鎖有點關係,由於這條記錄被鎖定了).
那爲何2種隔離級別會有區別呢? 由於一致性非鎖定讀的緣由
session B第一次讀取發現記錄ID=5被鎖定(有X鎖).那讀的時候就會從undo日誌裏找到原始的值.也就是v5.
第二次讀的時候RC和RR有區別.RC由於session A已經committed,會讀最新的行數據,也就是v5-new
而RR仍然會讀取以前讀到的那個snap-shot版本,也就是v5.因此會有區別..
RR模式下加上鎖能夠解決幻讀的問題(還原以前的數據).
實驗以下:
Session A:
update user set name = 'v55' where i = 'i5';
沒有committed
Session B:
insert into user values(6, 'v6', 'i5');
這個時候若是B能夠committed,那就會產生幻讀的問題.由於A明明修改了全部i=i5的數據.
可是卻沒有修改B插入的那條.
可是實際上Session B執行這個insert的時候回等待.
由於A鎖住了i = i5的數據.因此B是插入不了的.
那若是B執行的是
insert into user values(6, 'v6', 'i6'); 能夠嗎?
事實上也會等待.由於A會鎖定i=i5的數據和它到上下兩條相鄰數據的區間.
也就是說 i1-i5, i5, i5-i9的數據都被鎖住了.都是不能插入的.
insert into user values(10, 'v10', 'ia');
這條記錄就能夠插入.由於ia在i9以後(字母大於數字).
mysql對主鍵會鎖單條記錄.可是對非惟一的通常的索引,會鎖單條記錄加上上下範圍