MySQL [pom_5]> select @@global.tx_isolation;
+-----------------------+
| @@global.tx_isolation |
+-----------------------+
| REPEATABLE-READ |
+-----------------------+
如果事務隔離級別顯示REPEATABLE-READ,即是可重復讀。
事務的四種隔離級別
(引自: Innodb中的事務隔離級別和鎖的關系)
在數據庫操作中,為了有效保證並發讀取數據的正確性,提出的事務隔離級別。我們的數據庫鎖,也是為了構建這些隔離級別存在的。
隔離級別 | 臟讀(Dirty Read) | 不可重復讀(NonRepeatable Read) | 幻讀(Phantom Read) |
---|---|---|---|
未提交讀(Read uncommitted) | 可能 | 可能 | 可能 |
已提交讀(Read committed) | 不可能 | 可能 | 可能 |
可重復讀(Repeatable read) | 不可能 | 不可能 | 可能 |
可串行化(Serializable ) | 不可能 | 不可能 | 不可能 |
未提交讀(Read Uncommitted):允許臟讀,也就是可能讀取到其他會話中未提交事務修改的數據
提交讀(Read Committed):只能讀取到已經提交的數據。Oracle等多數數據庫默認都是該級別 (不重復讀)
可重復讀(Repeated Read):可重復讀。在同一個事務內的查詢都是事務開始時刻一致的,InnoDB默認級別。在SQL標准中,該隔離級別消除了不可重復讀,但是還存在幻象讀
串行讀(Serializable):完全串行化的讀,每次讀都需要獲得表級共享鎖,讀寫相互都會阻塞
可重復讀和幻讀
在可重復讀中,該sql第一次讀取到數據后,就將這些數據加鎖(悲觀鎖),其它事務無法修改這些數據,就可以實現可重復讀了。但這種方法卻無法鎖住insert的數據,所以當事務A先前讀取了數據,或者修改了全部數據,事務B還是可以insert數據提交,這時事務A就會發現莫名其妙多了一條之前沒有的數據,這就是幻讀,不能通過行鎖來避免。需要Serializable隔離級別 ,讀用讀鎖,寫用寫鎖,讀鎖和寫鎖互斥,這么做可以有效的避免幻讀、不可重復讀、臟讀等問題,但會極大的降低數據庫的並發能力。
但是MySQL、ORACLE、PostgreSQL等成熟的數據庫,出於性能考慮,都是使用了以樂觀鎖為理論基礎的MVCC(多版本並發控制)來實現。
實際測試結果
對相同db實例創建兩個連接:a和b,來測試select, select for update, update語句的生效情況
不使用select for update行級鎖
a先提交 | b先提交 | |
---|---|---|
a先開啟事務 | b的update內容 | a的update內容 |
a先開啟事務 | b的update內容 | a的update內容 |
結果是誰最后提交,誰的結果生效
a先使用select for update, update行級鎖
a使用select for update | a使用select for update | a使用update | |
---|---|---|---|
a開啟事務,b不開啟 | b里面的select for update和update被阻塞 | b不受影響 | b里面的select for update和update被阻塞 |
a不開啟事務,b開啟 | b不受影響 | b不受影響 | b不受影響 |
a,b開啟事務 | b里面的select for update和update被阻塞 | b不受影響 | b里面的select for update和update被阻塞 |
a,b不開啟事務 | b不受影響 | b不受影響 | b不受影響 |
結果是只有使用begin顯式開啟事務時,使用select for update才會對數據加上行級鎖,對其他連接的的select for update(對select沒有影響)和update造成阻塞效果。
a上鎖后修改提交,b讀取到的內容
下面是a開啟事務,然后使用a使用select for update,接着b使用select for update/select,a執行updat,最后commit,觀察整個過程中b的查詢效果。
b使用select for update | b使用select | |
---|---|---|
b開啟事務 | b里面的select for update被阻塞,a提交后,b讀到的是a修改的內容 | b不受a阻塞,讀到的仍是b事務開啟時的內容 |
b不開啟事務 | b里面的select for update被阻塞,a提交后,b讀到的是a修改的內容 | b不受a阻塞,讀到的數據在a提交后變化 |
在連接a使用begin開啟事務之后,select for update和update對連接b的阻塞效果
a先執行select for update | a先執行update | |
---|---|---|
b后執行select | 未阻塞 | 未阻塞 |
b后執行select for update | 阻塞 | 阻塞 |
b后執行update | 阻塞 | 阻塞 |