CMU15445-2024fall-project4踩坑經歷

project4目錄

  • Task1
  • Task2
    • Reconstruct
    • SeqScan
  • Task3
    • Insert
    • Commit
    • TxnMgrDbg
    • GenerateNewUndoLog And GenerateUpdateUndoLog
    • Update And Delete
    • 垃圾回收
  • Task4
    • Index Insert
      • 并發控制
    • Index Scan
    • Delete、Update并發控制
    • 主鍵更新
  • Bonus 1
  • Bonus 2
    • 處理寫傾斜

感謝CMU的教授們給我們分享了如此精彩的一門課程, 希望您能尊重教授們和TAs的勞動成果!

本篇文章記錄本人對實驗中各個板塊的理解以及踩坑, 如果您發現我過多的涉及到了實驗的內容, 有違學術誠信, 請告訴我!

正文

project4整體實現了一個OCC版本的MVCC, 即樂觀版本的多版本并發控制為事務提供支持。

  • Task1要求實現時間戳和水印;
  • Task2要求元組重構方法和撤銷日志收集方法, 以及重構project3中實現的SeqScan執行器。
  • Task3要求實現MVCC執行器, 分成一個個小任務就是:重構insert、delete、Update執行器, 實現Commit提交操作, Undolog生成和更新函數、 垃圾回收機制。
  • Task4引入主鍵, 并實現主鍵更新和插入
  • Bonus1為abort事務回滾, Bonus2為Serializable Verification序列化驗證。

Task1

首先要看一下讀取時間戳到底怎么工作。 對于事務來說, 事務有一個事務id, 一個讀取時間戳, 一個提交時間戳。 事務被創建時, 會被事務管理器分配一個遞增的事務id,用來唯一標記事務, 這個事務管理器就是DBMS里面管理所有事物的對象。 事務啟動時, 也會被分配一個讀取時間戳,這個讀取時間戳是整個全局中, 上一個事務的提交時間戳。 而提交時間戳, 同樣是由事務管理器分配, 并且每次事務提交都會遞增, 保證全局唯一。

在這里插入圖片描述

如上圖, 每一次有事務提交, 那么last_commits_ts_就會加一, 同時一個事務啟動, next_txn_id也會加一。 然后讀取時間戳就可以在啟動的時候直接賦值last_commit_ts即可。

Task1中需要理解的是watermark水位線。水位線用來判斷DBMS中有哪些數據版本不再被所有活躍事務需要。其中current_reads里面包括了當前活躍事務中,讀取時間戳與讀取時間戳個數的對應關系。如果獲取水位線, 那么就返回current_reads中的最小值, 如果current_reads中沒有數據, 直接返回。

我需要做的就是, 在Commit函數中, 提交時間戳加一,并將事務的提交時間戳設置為該時間戳。然后更新水位線中的commits設置為提交時間戳。最后要移除水位線中的該事務read_ds計數。在Begin函數中設置read_ds即可。

Task2

Task2的主要任務有兩個, 一個是元組重構方法Reconstruct、一個是重構順序掃描方法SeqScan。

Reconstruct

理解元組重構就要先理解undolog。undolog就是將元組改變前和改變后的“差異“保存下來, 比如當T1將A的四列元組改變了兩列, 則將:改變了哪些列、改變之前該列的值、該槽位的版本鏈、當前事務的事務ID保存為一個undolog(撤銷日志), 然后將這個撤銷日志保存到當前的事務里面, 并將該撤銷日志添加到對應槽位的版本鏈中。注意該版本連由當前DBMS的事務管理器維護。就是下圖中的version_map:

在這里插入圖片描述

Reconstruct就是拿到一組撤銷日志(undolog), 根據undolog中的差異, 將basetuple恢復成舊元組并返回。

具體實現細節就是:

  • 如果undolog的is_delete標記為true, 說明舊元組為空,那么就構建一個空tuple。
  • 否則依次根據拿到undolog中的差異列的值, 和無差異列的值。 構建出一個舊元組。 最后遍歷整個”undo_logs", 直到遍歷完為止。

SeqScan

我認為SeqScan是所有的算子的基礎, 因為除了Insert, 其他的算子的最底層都是SeqScan或者IndexScan。 大部分算子都是從SeqScan拉取數據。 SeqScan中要處理:哪些元組看的見、哪些元組看不見。

因為我之前p3實現的是無MVCC的掃描, 掃描每一個槽位時, 該槽位是什么拿到什么就可以了, 如果為空, 則繼續遍歷下一個槽位即可。 但是對于MVCC控制的SeqScan, 同樣是遍歷每一個槽位, 但是每個槽位還要深入遍歷它的版本鏈, 即undolog鏈。

具體實現細節如下:

  • 對于每一個槽位, 先看當前tuple能否看到

    • 能看到并且不為空, 就返回; 能看到但是為空, 檢查下一個槽位。
  • 如果看不到, 那么就檢查undolog。

  • 一邊回溯版本鏈, 一遍收集undolog, 直到當前undolog能夠看到。

    • 如果最舊的undolog也看不到, 那么就檢查下一個元組。
  • 利用收集到的undolog構建舊元組, 返回即可。

這里要注意的是, 文檔中說解釋了CollectUndolog,并要求實現了。

在這里插入圖片描述

所以我可以實現該函數,方便SeqScan的編寫。

實現流程就是

  • 從DBMS的事務管理器中拿到整個該槽位的版本鏈。
  • 遍歷整個版本鏈, 如果看不到, 則加入到“return_undologs(返回undolog數組)”中, 如果能看到,則返回該數組就可。

Task3

Task3要求實現MVCC版本的執行器, 前面實現的SeqScan也是MVCC版本的執行器的一部分, Task3中要求實現Insert、Update、Delete的MVCC版本執行器。 并且要實現一下垃圾回收機制以及提交。

Insert

Insert不需要構建undolog, 因為Insert只是在一個新的槽位插入新元組。 與p3不同的是, 要注意設置該槽位的時間戳為事務ID, 并且要把該槽位的修改記錄添加到事務中。

這里不需要注意并發, 因為InsertTuple是在一個新槽位插入數據。 并發沖突只存在一種情況: 多個事務同時向內部插入數據。而InsertTuple本身為原子的, 所以無需注意并發控制。

Commit

關于Commit, 要注意一點就是要先更新事務以及更新元組的提交實踐戳。 然后才能更新全局的提交時間戳以及watermark里面的最新提交時間戳commit_ts。 因為對于一個事務,如果先更新了全局提交時間戳, 那么此時另一個事務就會獲得這個全局提交時間戳作為read_ts。

另外,Commit一定是原子的, 并且更新元組的提交時間戳一定是原子的。當前一個事務修改了元組后,假如一共修改了幾個元組, 但是只更新了一個元組的提交時間戳。 那么另一個新事物, 就永遠不能讀取這些沒有更新提交時間戳的元組了。所以要么Commit完成, 要么沒有Commit, 不存在Commit一半的情況。

TxnMgrDbg

TxnMgrDbg就是回溯整個版本鏈, 對于版本連上的每一個版本, 都要打印出對應的元組。 一直到版本鏈結束。該函數為輔助調試函數,可以不實現, 但是建議實現, 方便調試。

GenerateNewUndoLog And GenerateUpdateUndoLog

GenerateNewUndoLog和GeenerateUpdateUndoLog用來當Update和delete對元組進行改變時, 保存舊元組與新元組的差異。 保存的方法就是創建undolog, 并將undolog連接到版本鏈中。

GenerateNewUndolog的參數為basetuple和targettuple, 根據兩者構建一個undolog。

GenerateUpdateUndoLog的參數為basetuple、targettuple、undolog。 需要先利用undolog和basetuple構建出舊元組, 然后根據舊元組和targettuple生成新undolog并返回。

具體實現細節如下:

  • GenerateNewUndoLog

    • 如果basetuple為空, 那么創建一個undolog的is_delete為true, 并且modifys_filed為schema.columns.size()。 并且每個列對應的modify_value為空。鏈接版本鏈后返回即可。
    • 如果targettuple為空, 那么創建undolog的is_delete為false, 并且modify_filed為schema.columns.size()。 并且每個對應的modify_value為basetuple對應列的值, 鏈接版本鏈后返回。
    • 如果兩者都不為空, 則遍歷兩tuple的每一列,標記每一列是否有差異, 并將有差異的列的basetuple對應的列收集起來,構造undolog。
  • GenerateUpdateUndoLog

    • 先構造舊元組。

    • 然后調用重復一遍GenerateNewUndoLog的邏輯即可。

Update And Delete

update和delete在執行主邏輯之前, 我們要檢查一下 寫寫 沖突。

在這里插入圖片描述

判斷條件就是當前要修改的元組的提交時間戳大于自己的讀取時間戳, 并且提交時間戳不為自己的事務ID。 那么就說明這個元組要么正在被其他事務修改, 要么已經被其他事務修改并且事務已經提交了。 發生了寫寫沖突。

  • Delete: 當槽位處的提交時間戳為當前的事務ID, 那么更新undolog,直接更新槽位處的元組即可。如果槽位處的提交時間戳為某個已經提交的事務的提交時間戳, 那么就以target為nullptr, 原元組為basetuple構建undolog, 注意要添加寫集合與將undolog添加到當前事務中。最后更新當前槽位處元組。
  • Update:當槽位處的提交時間戳為當前的事務ID, 那么更新undolog, 并且更新槽位處的元組即可。 如果槽位處的提交時間戳為某個已經提交的事務的提交時間戳, 那么以target為newtuple, 原元組為basetuple創建undolog, 并且更新undolog。然后更新當前槽位元組即可。

需要注意的是更新元組的函數中Check參數填成nullptr即可。 OCC的并發控制不需要加鎖, 只需要在需要并發控制時添加一個Check函數即可。

垃圾回收

對于垃圾回收就是回收某個不需要的事務。 如果該事務內部所有的undolog都不會被當前活躍事務看到, 那么該事務以及內部所有的undolog就可以被回收了。

實現策略為:

  • 遍歷DBMS中所有的已經提交事務, 并將事務修改的table和rid保存下來。
  • 遍歷所有table以及rid,拿到對應槽位的版本連上面的提交時間戳ts。 查看watermark, 如果存在watermark > ts, 則該時間戳對應的已提交事務不應被回收, 進行標記。

最后回收所有未被標記為不可回收的提交事務。

Task4

Task4引入主鍵, 并且引入并發控制。

Index Insert

如果存在主鍵,此時進行插入要考慮插入數據的主鍵列所對應的Value(以后叫作Key)是否已經存在索引中。 如果已經存在索引中, 則要拿到索引中Key指向的槽位,看看是否槽位處為delete, 如果為delete,則更新該槽位的數據和元數據即可。同時要注意并發控制。如果不為delete, 說明有數據, 那么就是 寫寫 沖突。

如果不在索引中, 則類似于無主鍵索引的插入。

并發控制

OCC的并發控制是在提交階段之前進行驗證,驗證是否與之前已經提交的事務有 寫寫沖突。這里要做的就是先拿到該槽位原本的元數據, 將這個元數據作為Check的固定參數。 Check函數就是檢查原本的元數據是否與新拿到的元數據是否相同。

Index Scan

Index Scan和SeqScan實現的邏輯相同, 對于每一個元組要檢查版本鏈。

  • 如果版本可見并且不為delete, 則返回, 如果版本可見但是delete, 檢查下一個槽位。
  • 如果版本不可見, 遍歷到版本鏈的下一個位置, 當版本連為末位, 則檢查下一個槽位。

Delete、Update并發控制

Task4中我需要重構Delete、Update, 將兩者設計為支持并發控制的版本。 我需要改動的地方很少, 只需要類似于Insert設計一個綁定了元組元數據的可調用對象作為Check即可。 并且Check內部邏輯為比較新的元數據是否與舊的元數據是否相同。

我這里理解的意思就是, 當前事務在修改該槽位的過程中, 如果其他事務已經對該槽位進行了修改, 即便沒有提交。此時就類似于當前事務正在修改一個已經被修改的元組,該元組不應該被當前事務可見, 相當于剛剛進入函數時寫寫沖突的判斷, 直接設置abort退出即可。

主鍵更新

主鍵更新分為三種情況, insert、delete、update。

  • insert時如果新元組的索引列對應的Value不存在主鍵索引中,還需要將Value添加到索引中。 并指向新的RID。
  • delete時對于主鍵列不進行刪除。
  • update時, 如果主鍵更改, 那么要先將所有更改的元組刪除, 然后再將所有新元組添加。這里刪一個插入一個的話, 會導致新插入的元組與已經存在的元組索引列沖突, 出現邏輯錯誤。

Bonus 1

發生寫寫沖突后, 調用abort函數回滾。

遍歷該事務的寫集合, 拿到每個槽位。 拿到槽位對應的元組。 此時會有三種情況:

  • 如果槽位沒有版本鏈, 說明該事務在該槽位進行了插入, 則將該位置設置為delete。

    • 否則為delete或者update。 拿到undolog, 重構tuple并進行滾滾即可。 即利用tuple更新槽位即可。

Bonus 2

檢查每一個槽位, 以及槽位對應的版本鏈; 對于序列化來說, 要保證事務讀取元組的順序和事務按照啟動順序序列化執行結果相同。快照隔離的實現中已經處理 寫 - 寫沖突 。 所以要保證序列化, 就要處理 寫傾斜

所以要認識什么是寫傾斜, 下面是關于寫傾斜的理解, 對比 寫寫沖突 來理解:

  • 寫 - 寫沖突 :事務T1正在修改表1中的第三行, 事務T2稍后啟動, 也想要修改表1中的第三行。 此時T1尚未提交, 或者已經提交但T1.commit_ts > T2.read_ts, T2直接abort。

  • 寫傾斜 :寫寫沖突是針對同一行, 寫傾斜則是針對不同行。 假如一個表中有四行數據:

    •     col1  | col2  |   1   |   0   ||   1   |   0   |  |   0   |   0   ||   0   |   0   |
      
    • 現在T1想要將col1全部設置為0, T2想要將col!全部設置為1。 T1先執行 -> "update t1 set col1 = 0 where col1 != 0; 然后底層seq_scan執行器通過遍歷 + 過濾讀取到了第一二行元組。元組通過管道回傳給上層 update執行器。然后執行器更新了表中的第一二行。 然后T2執行 -> “update t1 set col1 = 1 where col1 != 1” , 由于T1未提交, 所以表中的一二行被鎖定,T2的seq_scan執行器只能讀取到第三四行元組并且交給update執行器, 然后update執行器更新三四行。 最后的表中的結果就如下:
    •     col1  | col2  |   0   |   0   | uncommited  ->  modified by T1|   0   |   0   | uncommited  ->  modified by T1  |   1   |   0   | uncommited  ->  modified by T2|   1   |   0   | uncommited  ->  modified by T2
      
    • 但是我們想要的是什么呢? 我們想要的是先執行T1, 然后T1把表中所有數據修改為0, 然后執行T2, T2把表中所有數據修改為1, 最后表中的數據應該全都為1:
    •     col1  | col2  |   1   |   0   ||   1   |   0   ||   1   |   0   ||   1   |   0   |
      

處理寫傾斜

了解了實現序列化要解決寫傾斜的問題。 然后下面具體談如何處理寫傾斜。

我們從上面的例子中就能看到, 其實寫傾斜發生的條件就是在T1的修改的元組里面, 有些元組其實滿足了T2的過濾條件, 但是由于uncommited, 所以無法被T2修改。 此時T1先執行,能夠正確修改, 但是T2因為uncommited不能正確修改, 有寫傾斜。 所以如何判斷寫傾斜? 就是判斷可能沖突的事務修改的元組里面, 如果有能夠被我們正在判斷的事務所成功過濾的, 但是又因為uncommited, 所以沒有讀取到的這些元組。 那么就是發生了寫傾斜。

我們處理的驗證邏輯就是, 在提交的時候判斷是否與序列化的結果一致。 因為如果沖突事務T2修改過某個元組, 并且這個元組符合檢查事務T1的過濾條件, 又uncommited無法被檢查事務T1讀取, 那么就不符合序列化。

  • 所以在提交階段, 先拿到所有的沖突事務。即uncommit的事務。
  • 判斷每一個沖突事務的修改過的元組(寫集合), 是否滿足檢查事務的過濾條件。
  • 如果滿足, 說明發生寫傾斜,事務中止。 如果所有的元組都不滿足, 則說明沒有發生寫傾斜, 可以正常提交。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/916257.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/916257.shtml
英文地址,請注明出處:http://en.pswp.cn/news/916257.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

C++20 協程

摘要:C20 引入的協程機制為異步編程提供了輕量級解決方案,其核心優勢在于通過用戶態調度實現高效的上下文切換,適用于 I/O 密集型任務、生成器模式等場景。本文系統闡述 C20 協程的底層原理與實踐要點,首先解析協程的基本結構&…

《計算機組成原理與匯編語言程序設計》實驗報告二 基本數字邏輯及漢字顯示

目 錄 一、實驗學時 二、實驗目的 三、實驗要求 四、實驗內容 五、實驗步驟 1、打開Logisim軟件,列出并行四位二進制全加器邏輯電路真值表,并使用與、或、非、異或等基本原件實現并行四位二進制全加器邏輯電路,鋪設完成后進行測試進而…

問卷調查小程序的設計與實現

問卷調查小程序的設計與實現:技術與功能全解析在數字化時代,問卷調查成為數據收集的重要工具。一款高效、易用的問卷調查小程序能夠顯著提升用戶體驗和數據質量。本文將深入探討基于現代技術棧的問卷調查小程序的設計與實現,涵蓋核心功能、技…

STM32項目實戰:正弦波

波形發生器對我的錢包不怎么友好,手里面有stm32f103c8t6,于是就想,放在哪兒吃灰也是吃灰,不如做個正弦波發生器。方案 dac沒怎么用過,所以打算使用輸出模擬正弦波。我們決定采用以下方案:1.使用TIM2_CH3(PA…

Unity 時間抗鋸齒(Temporal Antialiasing, TAA)技術解析

時間抗鋸齒(Temporal Antialiasing, TAA)技術解析 一、什么是時間抗鋸齒(TAA)? 時間抗鋸齒(TAA)是一種先進的實時抗鋸齒技術,通過在時間維度上積累多幀渲染數據,實現比傳…

uniapp打開導航軟件并定位到目標位置的實現

/*** 打開導航* param location 經緯度(例如:"117.214713,39.116884")* param address 地址*/ export function mapNavigation(location, address) {const locationArr location.split(,)const longitude locationArr[0]const latitude locationArr[1]…

Mysql實現高可用(主從、集群)

一、背景 需求:客戶要實現Mysql8.0高可用,出現故障時,需要實現自動切換。 分析:實現切換有兩種方式,一種數據庫自動切換,一種代碼實現。 本著麻煩別人,別麻煩自己的原則,得給客戶的D…

AI編程革命:自動化代碼生成、低代碼開發與智能優化實踐全景解析

AI編程革命:自動化代碼生成、低代碼開發與智能優化實踐全景解析 人工智能正在重塑軟件開發的基本范式,從自動化代碼生成到低代碼開發平臺,再到算法智能優化,AI編程技術正以指數級速度改變開發者工作方式。 一、自動化代碼生成技術…

[實戰] 用1 PPS 馴服本地恒溫晶振(OCXO/TCXO)

1 PPS 馴服本地恒溫晶振(OCXO/TCXO) “讓任何工程師都能在一周內做出自己的 GPSDO” 文章目錄1 PPS 馴服本地恒溫晶振(OCXO/TCXO)前言1 系統總體框圖2 硬件清單與連接3 時間間隔測量(TDC)原理4 數字鎖相環算…

基于RK3576+FPGA的無人機飛控系統解決方案

基于RK3576FPGA的無人機飛控系統解決方案設計如下&#xff1a;一、硬件架構設計?異構計算核心模塊?主控采用RK3576處理器&#xff0c;四核Cortex-A72&#xff08;2.3GHz&#xff09;運行路徑規劃算法&#xff08;A*、RRT*&#xff09;&#xff0c;支持動態避障響應時間<50…

2025暑期—05神經網絡-卷積神經網絡

1. 卷積神經網絡實際上就是避免過擬合&#xff0c;就是“特征”神經網絡。這個特征和卷積核相關。卷積和相關類似&#xff0c;有點是本質屬性和輸出屬性的感覺。著重注意三通道卷積padding 上下左右、前后都要加邊池化就是解決特征過多問題&#xff0c;縮小1倍較多。均值池化&a…

Elasticsearch部署全攻略:編譯安裝與 Yum 安裝實踐,日志切割及報錯處理

前言&#xff1a;在 Linux 運維工作中&#xff0c;Elasticsearch 的部署與運維是高頻需求——無論是快速搭建測試環境還是構建生產級集群&#xff0c;選擇合適的安裝方式&#xff08;編譯安裝或 Yum 安裝&#xff09;直接影響后續維護效率。同時&#xff0c;日志的合理切割能避…

TypeScript 配置參數詳解 (tsconfig.json)

文章目錄前言核心配置基本選項嚴格類型檢查選項模塊解析選項Source Map 選項實驗性選項其他選項文件包含/排除選項詳細用法示例最佳實踐建議前言 tsconfig.json 是 TypeScript 項目的配置文件&#xff0c;用于指定編譯選項和文件包含規則。以下是所有主要配置參數的中文詳解&a…

Java經典筆試題

1.計算機網絡傳輸層有哪些協議&#xff1f;分別適用于什么場景&#xff1f; TCP&#xff1a;面向連接、可靠傳輸&#xff08;重傳機制&#xff09;&#xff0c;適用于對數據完整性要求高的場景&#xff0c;如文件傳輸 &#xff08;FTP&#xff09;、HTTP 通信、郵件發送&…

如何序列化和反序列化動態 XmlElement ?

有如下XML&#xff0c;Index_0-Index_N為動態的&#xff0c;N為可變數&#xff0c;如何將對象NoneXML序列化為XML&#xff0c;以及如何將XML內容反序列化為對象NoneXML&#xff1f;1、XML內容<NoneXML><Index_0><UseInSummary>0</UseInSummary><Beg…

分布式高可用ELK平臺搭建及使用保姆級教程指南 (附安裝包網盤免費下載)

1 ELK 簡介 1.1 ELK 是什么 ? ELK是一套開源免費且功能強大的日志分析管理系統,由 Elasticsearch、Logstash、Kibana 三部分組成,是三個軟件產品的首字母縮寫,簡稱ELK。這三款軟件都是開源軟件,通常是配合使用,且歸于 Elastic.co 公司名下,所以被簡稱為 ELK。 ? ELK…

MySQL索引背后的B+樹奧秘

MySQL 索引實現機制深度解析 MySQL 索引的核心數據結構是 B樹。這種設計是數據庫領域數十年優化的結果&#xff0c;完美平衡了磁盤 I/O 效率、范圍查詢性能和存儲利用率。以下是關鍵要點&#xff1a;一、為什么選擇 B樹而非其他結構&#xff1f;數據結構劣勢B樹優勢二叉搜索樹深…

k8s通過NUMA親和分配GPU和VF接口

問題 一般情況下&#xff0c;sriov插件和gpu分配插件是單獨工作的&#xff0c;網卡和GPU沒有根據連接關系分配 如果一個節點起了多個容器&#xff0c;會造成GPU和網卡的通信瓶頸 修改 如果一個點起兩個容器&#xff0c;可以按照NUMA親和來分配 修改kubelet配置文件/var/lib/kub…

qemu-img 擴容虛擬機磁盤后擴容文件系統

在給磁盤映像擴容前需要關閉虛擬機1. 關閉虛擬機 [rootkvm1 opt]# virsh shutdown centos7.9 [rootkvm1 opt]# virsh list --allId Name State ----------------------------- centos7.9 shut off[rootkvm1 opt]# qemu-img info /var/lib/libvirt/images/centos…

Winwos上編譯opencv的GPU版本推理yolov8

1.工具 VS2019 opencv4.7.0 opencv_contrib4.7.0 Cmake3.27.0 cudnn-windows-x86_64-8.5.0.96_cuda11-archive 2.具體流程 1.配置路徑和編譯器后點擊configure 2.提前下載相關的包&#xff0c;如下圖所示 3.第一次configure完成后&#xff0c;需要再配置編譯選項 在編譯…