事務與MVCC

一 事務

事務是一組原子性的SQL查詢,事務裏的語句,要麼全部執行成功,要麼全部執行失敗。

例: 銀行從小明的餘額(save)中減去200元,作爲其水電費支出,記作支出(out)。

步驟:1. 查詢銀行餘額大於200       2.餘額減去200      3.支出增加200

START TRANSACTION                                                                                    //開啓事務
SELECT save FROM COUNT WHERE NAME = '小明';                                /查詢餘額/
UPDATE COUNT SET save = save - 200 WHERE NAME = '小明';                //餘額減200
UPDATE COUTN SET out = out + 200 WHERE NAME = '小明';                   //支出+200
COMMIT;                                                                                                          //提交事務

ACID原則

原子性(Atomicity):事務是最小的執行單位,不允許分割。事務的原子性確保動作要麼全部完成,要麼完全不起作用;

一致性(Consistency):事務必須使數據庫從一個一致性狀態變換到另一個一致性狀態,也就是說一個事務執行之前和執行之後都必須處於一致性狀態。就像小明餘額減去200與支出增加200,相加與之前是一樣的。

隔離性(Isolation):併發訪問數據庫時,一個用戶的事務不被其他事務所幹擾,各併發事務之間數據庫是獨立的;

持久性(Durability):一個事務被提交之後。它對數據庫中數據的改變是持久的,即使數據庫發生故障也不應該對其有任何影響。

 

髒讀 : 一個事務讀取到另外一個事務未提交的數據
       解決:就是把釋放鎖的位置調整到事務提交之後,此時在事務提交前,其他進程是無法對該行數據進行讀取的,包括任何操作


不可重複讀:一個事務讀取到另外一個事務已經提交的數據,也就是說一個事務可以看到其他事務所做的修改
         在一個事務內,多次讀同一個數據。在這個事務還沒有結束時,另一個事務也訪問該同一數據。那麼,在第一個事務的兩次讀數據之間。由於第二個事務的修改,那麼第一個事務讀到的數據可能不一樣,這樣就發生了在一個事務內兩次讀到的數據是不一樣的,因此稱爲不可重複讀,即原始讀取不可重複。


虛讀(幻讀):是指在一個事務內讀取到了別的事務插入的數據,導致前後讀取不一致。
                    和不可重複讀類似,但虛讀(幻讀)會讀到其他事務的插入的數據,導致前後讀取不一致
如第一個事務對一個表中的數據進行了修改,比如這種修改涉及到表中的“全部數據行”。同時,第二個事務也修改這個表中的數據,這種修改是向表中插入“一行 新數據”。那麼,以後就會發生操作第一個事務的用戶發現表中還存在沒有修改的數據行,就好象發生了幻覺一樣.一般解決幻讀的方法是增加範圍鎖 RangeS,鎖定檢索範圍爲只讀,這樣就避免了幻讀。

 

事務隔離級別有4種:

Read uncommitted(讀未提交): 會出現髒讀,不可重複讀,幻讀
Read committed(讀已提交):會出現不可重複讀,幻讀
Repeatable read(可重複讀):會出現幻讀(但在Mysql實現的Repeatable read配合間隙鎖不會出現幻讀!)
Serializable(串行):避免以上的情況!

設置事務隔離級別: set  [glogal | session]  transaction isolation level  (Read uncommitted);


死鎖:兩個或多個以上的事務在同一資源上相互佔用,並請求鎖定對方佔用的資源,從而導致惡性循環。
解決:將持有最少行級排他鎖的事務進行回滾。

事務日誌:可以提高事務的效率。在修改表時只需要修改其內存拷貝,再把該修改行爲記錄到持久在硬盤上的事務日誌中。事務日誌被持久以後,內存中被修改的數據在後臺可以慢慢的刷回磁盤。

 

二 MVCC

定義:MVCC(Multi-Version Concurrency Control)即多版本併發控制。

優點:MVCC在大多數情況下代替了行鎖,實現了對讀的非阻塞,讀不加鎖,讀寫不衝突。

缺點:每行記錄都需要額外的存儲空間,需要做更多的行維護和檢查工作。

undo-log

  • Undo log是InnoDB MVCC事務特性的重要組成部分。當我們對記錄做了變更操作時就會產生undo記錄,Undo記錄默認被記錄到系統表空間(ibdata)中,但從5.6開始,也可以使用獨立的Undo 表空間。
  • Undo記錄中存儲的是老版本數據,當一箇舊的事務需要讀取數據時,爲了能讀取到老版本的數據,需要順着undo鏈找到滿足其可見性的記錄。當版本鏈很長時,通常可以認爲這是個比較耗時的操作(例如bug#69812)。
  • 大多數對數據的變更操作包括INSERT/DELETE/UPDATE,其中INSERT操作在事務提交前只對當前事務可見,因此產生的Undo 日誌可以在事務提交後直接刪除(誰會對剛插入的數據有可見性需求呢!!),而對於UPDATE/DELETE則需要維護多版本信息,在InnoDB 裏,UPDATE和DELETE操作產生的Undo日誌被歸成一類,即update_undo
  • 另外, 在回滾段中的undo logs分爲: insert undo logupdate undo log

    • insert undo log : 事務對insert新記錄時產生的undolog, 只在事務回滾時需要, 並且在事務提交後就可以立即丟棄。
    • update undo log : 事務對記錄進行delete和update操作時產生的undo log, 不僅在事務回滾時需要, 一致性讀也需要,所以不能隨便刪除,只有當數據庫所使用的快照中不涉及該日誌記錄,對應的回滾日誌纔會被purge線程刪除。

工作過程:

序號 動作
1 開始事務
2 記錄數據行數據快照到undo log
3 更新數據
4 將undo log寫到磁盤
5 將數據寫到磁盤
6 提交事務

1)爲了保證數據的持久性數據要在事務提交之前持久化
2)undo log的持久化必須在在數據持久化之前,這樣才能保證系統崩潰時,可以用undo log來回滾事務

 

InnoDB實現MVCC的方式是:

  • 事務以排他鎖的形式修改原始數據
  • 把修改前的數據存放於undo log,通過回滾指針與主數據關聯
  • 修改成功(commit)啥都不做,失敗則恢復undo log中的數據(rollback)

 

Innodb中的隱藏列

Innodb通過undo log保存了已更改行的舊版本的信息的快照。
InnoDB的內部實現中爲每一行數據增加了三個隱藏列用於實現MVCC。

列名 長度(字節) 作用
DB_TRX_ID 6 插入或更新行的最後一個事務的事務標識符。(刪除視爲更新,將其標記爲已刪除)
DB_ROLL_PTR 7 寫入回滾段的撤消日誌記錄(若行已更新,則撤消日誌記錄包含在更新行之前重建行內容所需的信息)
DB_ROW_ID 6 行標識(隱藏單調自增id)

 

案例分析

 

 

 

MVCC工作過程

MVCC只在READ COMMITED 和 REPEATABLE READ 兩個隔離級別下工作。READ UNCOMMITTED總是讀取最新的數據行,而不是符合當前事務版本的數據行。而SERIALIZABLE 則會對所有讀取的行都加鎖

SELECT

InnoDB 會根據兩個條件來檢查每行記錄:

  • InnoDB只查找版本(DB_TRX_ID)早於當前事務版本的數據行(行的系統版本號<=事務的系統版本號,這樣可以確保數據行要麼是在開始之前已經存在了,要麼是事務自身插入或修改過的)
  • 行的刪除版本號(DB_ROLL_PTR)要麼未定義(未更新過),要麼大於當前事務版本號(在當前事務開始之後更新的)。這樣可以確保事務讀取到的行,在事務開始之前未被刪除。

INSERT

InnoDB爲新插入的每一行保存當前系統版本號作爲行版本號

DELETE

InnoDB爲刪除的每一行保存當前的系統版本號作爲行刪除標識

UPDATE

InnoDB爲插入一行新記錄,保存當前系統版本號作爲行版本號,同時保存當前系統版本號到原來的行作爲行刪除標識

 

 

案例分析:

1、在插入操作時 : 記錄的創建版本號就是事務版本號。 

比如我插入一條記錄, 事務id 假設是1 ,那麼記錄如下:也就是說,創建版本號就是事務版本號。

id   name   create version   delete version  
1 test   1  

2、在更新操作的時候,採用的是先標記舊的那行記錄爲已刪除,並且刪除版本號是事務版本號,然後插入一行新的記錄的方式。 

比如,針對上面那行記錄,事務Id爲2 要把name字段更新

update table set name= 'new_value' where id=1;

id     name   create version   delete version  
1   test   1 2        
1   new_value   2  

 

3、刪除操作的時候,就把事務版本號作爲刪除版本號。比如

delete from table where id=1; 

 

id   name   create version   delete version  
1 new_value 2 3  

 

 

4、查詢操作: 

從上面的描述可以看到,在查詢時要符合以下兩個條件的記錄才能被事務查詢出來: 

1) 刪除版本號 大於 當前事務版本號,就是說刪除操作是在當前事務啓動之後做的。 

2) 創建版本號 小於或者等於 當前事務版本號 ,就是說記錄創建是在事務中(等於的情況)或者事務啓動之前。

這樣就保證了各個事務互不影響。從這裏也可以體會到一種提高系統性能的思路,就是: 

通過版本號來減少鎖的爭用。

 

 

 


轉載:

https://segmentfault.com/a/1190000012650596(詳細)

https://www.cnblogs.com/dongqingswt/p/3460440.html(案例實現)

https://www.jianshu.com/p/a3d49f7507ff

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章