回來寫博客,少年前端時間被django迷了心魄 如果轉載,請註明博文來源: www.cnblogs.com/xinysu/ ,版權歸 博客園 蘇家小蘿蔔 所有。望各位支持! 如果轉載,請註明博文來源: www.cnblogs.com/xinysu/ ,版權歸 博客園 蘇家小蘿蔔 所有。望各位支持! ...
回來寫博客,少年前端時間被django迷了心魄

如果轉載,請註明博文來源: www.cnblogs.com/xinysu/ ,版權歸 博客園 蘇家小蘿蔔 所有。望各位支持!
1 什麼是MVCC
MVCC全稱是: Multiversion concurrency control,多版本併發控制,提供併發訪問資料庫時,對事務內讀取的到的記憶體做處理,用來避免寫操作堵塞讀操作的併發問題。 舉個例子,程式員A正在讀資料庫中某些內容,而程式員B正在給這些內容做修改(假設是在一個事務內修改,大概持續10s左右),A在這10s內 則可能看到一個不一致的數據,在B沒有提交前,如何讓A能夠一直讀到的數據都是一致的呢? 有幾種處理方法,第一種: 基於鎖的併發控制,程式員B開始修改數據時,給這些數據加上鎖,程式員A這時再讀,就發現讀取不了,處於等待情況,只能等B操作完才能讀數據,這保證A不會讀到一個不一致的數據,但是這個會影響程式的運行效率。還有一種就是:MVCC,每個用戶連接資料庫時,看到的都是某一特定時刻的資料庫快照,在B的事務沒有提交之前,A始終讀到的是某一特定時刻的資料庫快照,不會讀到B事務中的數據修改情況,直到B事務提交,才會讀取B的修改內容。 一個支持MVCC的資料庫,在更新某些數據時,並非使用新數據覆蓋舊數據,而是標記舊數據是過時的,同時在其他地方新增一個數據版本。因此,同一份數據有多個版本存儲,但只有一個是最新的。 MVCC提供了 時間一致性的 處理思路,在MVCC下讀事務時,通常使用一個時間戳或者事務ID來確定訪問哪個狀態的資料庫及哪些版本的數據。讀事務跟寫事務彼此是隔離開來的,彼此之間不會影響。假設同一份數據,既有讀事務訪問,又有寫事務操作,實際上,寫事務會新建一個新的數據版本,而讀事務訪問的是舊的數據版本,直到寫事務提交,讀事務才會訪問到這個新的數據版本。 MVCC有兩種實現方式,第一種實現方式是將數據記錄的多個版本保存在資料庫中,當這些不同版本數據不再需要時,垃圾收集器回收這些記錄。這個方式被PostgreSQL和Firebird/Interbase採用,SQL Server使用的類似機制,所不同的是舊版本數據不是保存在資料庫中,而保存在不同於主資料庫的另外一個資料庫tempdb中。第二種實現方式只在資料庫保存最新版本的數據,但是會在使用undo時動態重構舊版本數據,這種方式被Oracle和MySQL/InnoDB使用。 這部分可以查閱維基百科:https://en.wikipedia.org/wiki/Multiversion_concurrency_control2 Innodb的MVCC
在Innodb db中,無論是聚簇索引,還是二級索引,每一行記錄都包含一個 DELETE bit,用於表示該記錄是否被刪除, 同時,聚簇索引還有兩個隱藏值:DATA_TRX_ID,DATA_ROLL_PTR。DATA _TRX_ID表示產生當前記錄項的事務ID,這個ID隨著事務的創建不斷增長;DATA _ROLL_PTR指向當前記錄項的undo信息。- 無論是聚簇索引,還是二級索引,只要其鍵值更新,就會產生新版本。將老版本數據deleted bti設置為1;同時插入新版本。
- 對於聚簇索引,如果更新操作沒有更新primary key,那麼更新不會產生新版本,而是在原有版本上進行更新,老版本進入undo表空間,通過記錄上的undo指針進行回滾。
- 對於二級索引,如果更新操作沒有更新其鍵值,那麼二級索引記錄保持不變。
- 對於二級索引,更新操作無論更新primary key,或者是二級索引鍵值,都會導致二級索引產生新版本數據。
- 聚簇索引設置記錄deleted bit時,會同時更新DATA_TRX_ID列。老版本DATA_TRX_ID進入undo表空間;二級索引設置deleted bit時,不寫入undo。
- select * from tbname where .... for update (加X鎖)
- select * from tbname where .... lock in share mode(加S鎖)
- insert into tbname .... (加X鎖,註意如果有unique key的情況)
- delete from tbname ... (加X鎖)
- update tbname set ... where .. (加X鎖)
3 Two Phase Locking
2-PL,也就是兩階段鎖,鎖的操作分為兩個階段:加鎖、解鎖。先加鎖,後解鎖,不相交。加鎖時,讀操作會申請並占用S鎖,寫操作會申請並占用X鎖,如果對所在記錄加鎖有衝突,那麼會處於等待狀態,知道加鎖成功才驚醒下一步操作。解鎖時,也就是事務提交或者回滾的時候,這個階段會釋放該事務中所有的加鎖情況,進行一一釋放鎖。 假設事務對記錄A和記錄B都有操作,那麼,其加鎖解鎖按照逐行加鎖解鎖順序,如下:BEGIN LOCK A READ A A:A+100 WRITE A UNLOCK A LOCK B READ B UNLOCK B COMMIT兩階段鎖還有幾種特殊情況:conservative(保守)、strict(嚴格)、strong strict(強嚴格),這三種類型在加鎖和釋放鎖的處理有些不一樣。
- conservative
- 在事務開始的時候,獲取需要的記錄的鎖,避免在操作期間逐個申請鎖可能造成的鎖等待,conservative 2PL 可以避免死鎖
- strict
- 僅在事務結束的時候(commit or rollback),才釋放所有 write lock,read lock 則正常釋放
- strong strict
- 僅在事務結束的時候(commit or rollback),才釋放所有鎖,包括write lock 跟 read lock 都是結束後才釋放。
4 數據不一致情況
4.1 臟讀
讀取未提交事務中修改的數據,稱為臟讀。 舉例,表格 A (name,age),記錄1為name='xinysu',age=188
4.2 丟失更新
多個更新操作併發執行,導致某些更新操作數據丟失。 舉例,表格 A (name,age),記錄1為name='xinysu',age=188。併發2個更新操作如下:
4.3 不可重覆讀
同個事務多次讀取同一條存在的記錄,但是讀取的結構不一致,稱之為不可重覆讀。 舉例,表格 A (name,age),記錄1為name='xinysu',age=188。操作如下:
4.4 幻讀
同個事務多次讀取某段段範圍內的數據,但是讀取到底行數不一致的情況,稱之為幻讀。 舉例,表格 A (name,age),記錄1為name='xinysu',age=188。操作如下:
5 innodb的隔離級別
5.1 隔離級別介紹
- Read Uncommited
- 簡稱 RU,讀未提交記錄,始終是讀最新記錄
- 不支持快照讀,都是當前讀
- 可能存在臟讀、不可重覆讀、幻讀等問題;
- Read Commited
- 簡稱 RC ,讀已提交記錄
- 支持快照讀,讀取版本有可能不是最新版本
- 支持當前讀,讀取到的記錄添加鎖
-
- 不存在臟讀、不可重覆讀
- 存在幻讀問題;
- Read Repeatable
- 簡稱 RR ,可重覆讀記錄
- 支持快照讀,讀取版本有可能不是最新版本
- 支持當前讀,讀取到的記錄添加鎖,並且對讀取的範圍枷鎖,保證滿足查詢條件的記錄不能夠被insert進來
- 不存在臟讀、不可重覆讀及幻讀情況;
- Read Serializable
- 簡稱 RS,序列化讀記錄
- 不支持快照讀,都是當前讀
- select數據添加S鎖,update\insert\delete數據添加X鎖
- 併發度最差,除非明確業務需求及性能影響,才使用,一般不建議在innodb中應用
5.2 隔離級別測試
測試各個隔離級別下的數據不一致情況。1.查看當前會話隔離級別 select @@tx_isolation; 2.查看系統當前隔離級別 select @@global.tx_isolation; 3.設置當前會話隔離級別 set session transaction isolation level repeatable read; 4.設置系統當前隔離級別 set global transaction isolation level repeatable read;
5.2.1 Read Uncommitted
所有事務隔離級別設置: set session transaction isolation level read Uncommited ; 該隔離級別沒有的快照讀,所有讀操作都是讀最新版本,可以讀未提交事務的數據。 測試1:update數據不提交,另起查詢 測試結果:正常select可以查詢到不提交的事務內容,屬於臟讀



5.2.2 Read Committed
所有事務隔離級別設置: set session transaction isolation level read committed ; 由於該隔離級別支持快照讀,不添加for update跟lock in share mode的select 查詢語句,使用的是快照讀,讀取已提交記錄,不添加鎖。所以測試使用當前讀的模式測試,添加lock in share mode,添加S鎖。 測試1:update數據不提交,另起查詢 測試結果:由於當前讀持有S鎖,導致update申請X鎖處於等待情況,無法更新,同個事務內的多次查詢結果一致,無臟讀及不可重覆讀情況。
測試2:INSERT數據不提交,另起事務多次查詢 測試結果:同個事務多次讀取相同範圍的數據,但是行數不一樣,屬於幻讀(這裡註意,如果insert 分為beigin;commit,一直不commit的話,3的查詢會處於等待情況,因為它需要申請的S鎖被 insert的X鎖所堵塞)

測試3:快照讀測試 測試結果:同個事務多次讀取相同記錄,讀取的都是已提交記錄,不存在臟讀及丟失更新情況,但是存在不可重覆讀及幻讀。

總結:支持快照讀,快照讀 不存在臟讀及丟失更新情況,但是存在不可重覆讀及幻讀;而當前讀不存在臟讀、不可重覆讀問題,存在幻讀問題。
5.2.3 Read Repeatable
所有事務隔離級別設置: set session transaction isolation level repeatable read ; 由於該隔離級別支持快照讀,不添加for update跟lock in share mode的select 查詢語句,使用的是快照讀,不添加鎖。所以測試使用當前讀的模式測試,添加lock in share mode,添加S鎖。 測試1:update數據不提交,另起查詢 測試結果:由於當前讀持有S鎖,導致update申請X鎖處於等待情況,無法更新,同個事務內的多次查詢結果一致,無臟讀及不可重覆讀情況。.png)


測試3:快照讀測試 測試結果:同個事務多次讀取相同記錄,不存在臟讀及丟失更新、不可重覆讀及幻讀等情況。

總結:支持快照讀,快照讀跟當前讀不存在臟讀、不可重覆讀問題,存在幻讀問題。
5.2.4 Read Serializable
所有事務隔離級別設置: set session transaction isolation level Serializable ; 該隔離級別不支持快照讀,所有SELECT查詢都是當前讀,並且持有S鎖. 測試1:update數據不提交,另起查詢;INSERT數據不提交,另起事務多次查詢 測試結果:該隔離級別下所有select語句持有S鎖,導致update申請X鎖處於等待情況,INSERT申請X也被堵塞,同個事務內的多次查詢結果一致,不存在臟讀、不可重覆讀及幻讀情況。
以為沒了,not,還有一個概念這裡沒有提交,這裡補充介紹下:semi-consistent read
PS: semi-consistent read
在read committed或者read uncommitted 隔離級別下,有這樣的測試現象: 測試表格及數據 CREATE TABLE `tblock` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(10) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; insert into tblock(name) select 'su'; insert into tblock(name) select 'xin'; 測試1:兩個update事務併發,分別update不同行,update條件列無索引 測試結果:兩條update互不幹擾,正常執行。
測試2:update語句不提交,另起事務當前讀操作 測試結果:當前讀被堵塞,無法正常加X鎖
.png)

- update語句
- 執行計劃時scan,range scan or table scan,不能時unique scan
- 表格為聚集索引
