《mysql》之undolog


還不懂mysql的undo log和mvcc?算我輸! 

 

undo log有兩個作用:提供回滾和MVCC。

undo log是邏輯日志。

undo log存在於一個特殊的段中,存在於表空間中,和主鍵id組織的數據存在一個文件中,畢竟每行數據都有個指向undo log的指針。

 

 

當執行rollback時,就可以從undo log中的邏輯記錄讀取到相應的內容並進行回滾。

有時候應用到行版本控制的時候,也是通過undo log來實現的:當讀取的某一行被其他事務鎖定時

它可以從undo log中分析出該行記錄以前的數據是什么,從而提供該行版本信息,讓用戶實現非鎖定一致性讀取。

 

undo log的存儲方式

innodb存儲引擎對undo的管理采用段的方式。rollback segment稱為回滾段,每個回滾段中有1024個undo log segment。

在以前老版本,只支持1個rollback segmentMySQL5.5可以支持128個rollback segment,即支持128*1024個undo操作,還可以通過變量 innodb_undo_logs自定義多少個rollback segment,默認值為128。

undo log默認存放在共享表空間中。

默認rollback segment全部寫在一個文件中,但可以通過設置變量 innodb_undo_tablespaces 平均分配到多少個文件中。

 

 delete/update操作的內部機制

當事務提交的時候,innodb不會立即刪除undo log,因為后續還可能會用到undo log,如隔離級別為repeatable read時,事務讀取的都是開啟事務時的最新提交行版本,只要該事務不結束,該行版本就不能刪除,即undo log不能刪除。

但是在事務提交的時候,會將該事務對應的undo log放入到刪除列表中,未來通過purge來刪除。

並且提交事務時,還會判斷undo log分配的頁是否可以重用,如果可以重用,則會分配給后面來的事務,避免為每個獨立的事務分配獨立的undo log頁而浪費存儲空間和性能。

 

通過undo log記錄delete和update操作的結果發現:

delete操作實際上不會直接刪除,而是將delete對象打上delete flag,標記為刪除,最終的刪除操作是purge線程完成的。

update分為兩種情況:update的列是否是主鍵列。

  • 如果不是主鍵列,在undo log中直接反向記錄是如何update的。即update是直接進行的。
  • 如果是主鍵列,update分兩部執行:先刪除該行,再插入一行目標行。

 

insert undo log和update undo log為啥要分開,為啥提交之后insert undo log可以直接刪除了,update undo log需要等待purge?

如果某個事務ID=100新增了一條記錄,那么在這個事務版本之前這個記錄是不存在的

  • 這條數據要么是事務100提交的,然后就存在這條數據了
  • 事務100沒有提交,這條數據是nul

這條數據本身就是一個版本,要么是不存在,讀取不到,要么就是存在,可以讀取

數據是否存在,在RC和RR級別看事務有沒有提交。

 

undo log 分成兩種格式

一種給insert操作。記錄中不含回滾指針,不含舊值。insert之前是沒有的。

一種給update/delete操作。有回滾指針,有舊值。

 

對於INSERT_UNDO,調用函數trx_undo_page_report_insert進行插入,記錄格式大致如下圖所示: 

pastedGraphic_3.png

 

對於UPDATE_UNDO,調用函數trx_undo_page_report_modify進行插入,UPDATE UNDO的記錄格式大概如下圖所示: pastedGraphic_4.png

 

 

 

 

【MySQL筆記】正確的理解MySQL的MVCC及實現原理

 

什么是MVCC

MVCC在MySQL InnoDB中的實現主要是為了提高數據庫並發性能,用更好的方式去處理讀-寫沖突

做到即使有讀寫沖突時,也能做到不加鎖,非阻塞並發讀

准確的說,MVCC多版本並發控制指的是 “維持一個數據的多個版本,使得讀寫操作沒有沖突” 這么一個概念。僅僅是一個理想概念


當前讀和快照讀

當前讀

像select lock in share mode(共享鎖), select for update ; update, insert ,delete(排他鎖)這些操作都是一種當前讀

它讀取的是記錄的最新版本,讀取時還要保證其他並發事務不能修改當前記錄,會對讀取的記錄進行加鎖

快照讀
像不加鎖的select操作就是快照讀,即不加鎖的非阻塞讀

快照讀的前提是隔離級別不是串行級別,串行級別下的快照讀會退化成當前讀;

提高並發性能,快照讀是基於多版本並發控制,即MVCC,在很多情況下,避免了加鎖操作,降低了開銷;

既然是基於多版本,即快照讀讀到的不一定是數據的最新版本,而有可能是之前的歷史版本

 

MVCC就是為了實現讀-寫沖突不加鎖,而這個讀指的就是快照讀, 而非當前讀

當前讀實際上是一種加鎖的操作,是悲觀鎖的實現


當前讀,快照讀和MVCC的關系

(1)MVCC多版本並發控制指的是 “維持一個數據的多個版本,使得讀寫操作沒有沖突” 這么一個概念。僅僅是一個理想概念,

而在MySQL中,實現這么一個MVCC理想概念,我們就需要MySQL提供具體的功能去實現它

(2)快照讀就是MySQL實現MVCC的其中一個具體非阻塞讀功能。

(3)當前讀就是悲觀鎖的具體功能實現

要說的再細致一些,快照讀本身也是一個抽象概念,再深入研究。MVCC模型在MySQL中的具體實現則是由 3個隱式字段undo日志 ,Read View 等去完成的,具體可以看下面的MVCC實現原理


MVCC能解決什么問題

數據庫並發場景有三種,分別為:

  • 讀-讀:不存在任何問題,也不需要並發控制
  • 讀-寫:有線程安全問題,可能會造成事務隔離性問題,可能遇到臟讀,幻讀,不可重復讀
  • 寫-寫:有線程安全問題,可能會存在更新丟失問題,比如第一類更新丟失,第二類更新丟失

MVCC帶來的好處

用來解決讀-寫沖突的無鎖並發控制,為每個修改保存一個版本,讀操作只讀事務開始前的的快照

(1)在並發讀寫數據庫時,可以做到在讀操作時不用阻塞寫操作,寫操作也不用阻塞讀操作,提高了數據庫並發讀寫的性能

(2)還可以解決臟讀,幻讀,不可重復讀等事務隔離問題,但不能解決更新丟失問題


不滿意只讓數據庫采用悲觀鎖這樣性能不佳的形式去解決讀-寫沖突問題,而提出了MVCC,所以我們可以形成兩個組合:

MVCC + 悲觀鎖
MVCC解決讀寫沖突,悲觀鎖解決寫寫沖突

MVCC + 樂觀鎖
MVCC解決讀寫沖突,樂觀鎖解決寫寫沖突

這種組合的方式就可以最大程度的提高數據庫並發性能,並解決讀寫沖突,和寫寫沖突導致的問題

 


 

MVCC的實現原理

MVCC的實現依賴記錄中的 3個隱式字段undo日志 ,Read View 

取出DB_TRX_ID(即當前事務ID),與系統當前其他活躍事務的ID去對比(由Read View維護),

不符合可見性,那就通過DB_ROLL_PTR回滾指針去取出Undo Log中的DB_TRX_ID再比較,直到找到滿足特定條件的DB_TRX_ID,

那么這個DB_TRX_ID所在的舊記錄就是當前事務能看見的最新版本


 

隱式字段

每行記錄除了我們自定義的字段外,還有數據庫隱式定義的DB_TRX_ID,DB_ROLL_PTR,DB_ROW_ID等字段

DB_TRX_ID

6byte,記錄創建這條記錄 或者 最后一次修改該記錄的事務ID

DB_ROLL_PTR

7byte,回滾指針,用於配合undo日志,指向這條記錄的上一個版本(存儲於rollback segment里)用於配合undo日志

DB_ROW_ID

6byte,隱含的自增ID(隱藏主鍵),如果數據表沒有主鍵,InnoDB會自動以DB_ROW_ID產生一個聚簇索引

實際還有一個刪除flag隱藏字段, 既記錄被更新或刪除並不代表真的刪除,而是刪除flag變了

 


undo日志

undo log主要分為兩種:

insert undo log
代表事務在insert新記錄時產生的undo log, 只在事務回滾時需要,並且在事務提交后可以被立即丟棄

update undo log
事務在進行updatedelete時產生的undo log; 不僅在事務回滾時需要,在快照讀時也需要;所以不能隨便刪除,只有在快速讀或事務回滾不涉及該日志時,對應的日志才會被purge線程統一清除

purge

  • 從前面的分析可以看出,為了實現InnoDB的MVCC機制,更新或者刪除操作都只是設置一下老記錄的deleted_bit,並不真正將過時的記錄刪除。

  • 為了節省磁盤空間,InnoDB有專門的purge線程來清理deleted_bit為true的記錄。為了不影響MVCC的正常工作,purge線程自己也維護了一個read view(這個read view相當於系統中最老活躍事務的read view);如果某個記錄的deleted_bit為true,並且DB_TRX_ID相對於purge線程的read view可見,那么這條記錄一定是可以被安全清除的。


Read View(讀視圖)

事務進行快照讀的讀視圖(Read View),用來做可見性判斷

(1)當執行快照讀的時候,對該記錄創建一個Read View讀視圖,記錄當前活躍事務的ID。

(2)用來判斷當前事務能夠看到哪個版本的數據,可能是當前最新的數據,也可能是該行記錄的undo log里面的數據。

實現

當每個事務開啟時,都會被分配一個ID, 這個ID是遞增的,最新的事務,ID值越大

trx_list:列表,記錄Read View生成時刻系統正活躍的事務ID

up_limit_id:記錄trx_list列表中事務ID最小的ID

low_limit_id:系統尚未分配的下一個事務ID,也就是目前已出現過的事務ID的最大值+1

(1)首先判斷   DB_TRX_ID < up_limit_id,如果小於,則當前事務能看到DB_TRX_ID 所在的記錄,如果大於等於進入下一個判斷

(2)然后判斷 DB_TRX_ID >= low_limit_id ,如果大於等於,則DB_TRX_ID 所在的記錄在Read View生成后才出現的,那對當前事務肯定不可見,如果小於則進入下一個判斷

(3)判斷DB_TRX_ID 是否在活躍事務之中trx_list.contains(DB_TRX_ID)

  • 如果在,則代表Read View生成時刻,這個事務還在活躍,還沒有Commit,修改的數據對當前事務不可見;
  • 如果不在,這個事務在Read View生成之前就已經Commit了,修改的結果對當前事務可見

 


 

select count(*) 總行數

在 MyISAM 存儲引擎中,把表的總行數存儲在磁盤上,直接返回總數據。

在 InnoDB 存儲引擎中,沒有將總行數存儲在磁盤上,會先把數據讀出來,一行一行的累加,最后返回總數量。

在默認隔離級別可重復讀的情況下,通過多版本並發控制(MVCC)來實現,每一行記錄都需要判斷自己是否對這個會話可見,因此在統

計總數量時,InnoDB 只好把數據一行一行的讀取出來判斷,只有當前會話可見的才納入統計中。

所以同一時刻不同會話查詢到的數量就不一樣。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM