Google關於BigTable的論文中分布式事務的實現
Google在BigTable相關的論文中詳細的解釋了Percolator分布式事務的實現方式, 而且用簡潔的偽代碼示例怎么實現分布式事務;
Percolator算法在分布式數據庫中運用廣泛, 國內著名的開源分布式數據庫TiDB的事務實現來源於Percolator, 騰訊TBase的分布式事務實現也來自於Percolator;
在講Percolator之前, 我們先看幾個問題:
1, 假設一個事務開始的時間戳是 T2 , 這個事務讀取數據的原則是什么, 是讀取最新的數據還是只能讀取截止到 T2 的數據?
2, 假設一個事務里面需要訪問很多表的記錄, 而且都是截止到 T2 時間戳的快照, 怎么能獲取到這些快照?
3, 假設一個事務操作很多記錄, 這些記錄落在多台服務器上, 怎么能保證多台服務器上的記錄操作都是在原子鎖的情況下進行?
4, 假如有多個 Worker 操作多個服務器上的多條記錄上的鎖, 這些記錄和鎖屬於一個事務, 怎么能保證這些記錄全部提交, 或者這些記錄全部回滾?
我們直接摘抄Google的論文, 看看 Percolator 的實現:
假設有一個表, 有2個用戶, Bob和Joe, Bob的賬戶余額是$10, Joe的賬戶余額是$2

下面開始一個事務, Bob轉 7美金給Joe:
假設這個事務開始的時候, 時間戳是 7, 這個時間戳是事務開始時間戳;
第 1 步, 加主鎖, 主鎖只有一個;

在Bob這條記錄(row)的bal列(余額列)的data列, lock列, 都寫入帶有時間戳7的記錄, row上的操作是行級別的事務;
第 2 步, 加從鎖, 從鎖關聯主鎖, 從鎖可以有多個;

在Joe這條記錄(row)的bal列(余額列)的data列, lock列, 都寫入帶有時間戳7的記錄, row上的操作是行級別的事務;
上述流程是第1階段, 預寫入(Prewrite);
Prewrite之后, 進入第 2 個階段, 提交階段(Commit階段);
提交階段會獲取第2個時間戳---提交時間戳, 這里假設是8;

一旦清除主鎖, 並寫入write列, 則事務一定要完成; 從鎖可以保證即使發生異常, 事務也能進行前向滾動從而完成整個事務; 而主鎖可以確保事務加鎖的原子性;
注意1: 需要說明一點, 因為對每一個記錄的操作都會涉及到多個列, 所以會使用 row 事務, 保證對同一row的多個列的操作是原子性的;

上面就是Commit流程, 先處理主鎖, 然后依次處理從鎖, 主鎖是關鍵, 是原子級的鎖, 一旦主鎖提交(這是一個row事務, 更新lock列和write列), 這個事務必須要提交;
如果主鎖沒有提交, 則這個事務是可以回滾的, 回滾時也必須先操作主鎖, 確保對多個鎖的原子性操作, 然后依次處理從鎖, 事務提交者在Commit階段發現主鎖已經失效, 說明事務被其他worker回滾掉了, 不能進行提交;
可以看到, Percolator包括:
1. 兩階段提交, Prewrite階段, Commit階段;
2. 主從鎖, 主鎖作為多個鎖當中的負責原子級操作的鎖;
3. 多列(data, lock, write);
4. 每一個維度的列都帶時間戳;
5. 數據多版本(MVCC);
我們看看Google論文里面的偽代碼示例:
Get函數偽代碼:

一個讀操作需要等待lock列的鎖, 范圍是早於讀取的時間戳, 一個前次的事務有可能還在Commit階段, 而且Commit的時間戳也是小於讀取操作的時間戳的;
Prewrite函數, 注意, 這只是Prewrite函數不是Prewrite階段

Commit函數, 里面實現了兩階段提交

一個事務的關鍵節點是--清除lock上的主鎖, 並且在write列寫入---這個操作是原子性的, 基於row事務---一旦這個操作完成, 事務必須完成, 即使事務的worker掛掉, 其他worker也有義務幫助這個事務完成前向滾動(rolled farward), 您可以把他看成是mysql的redo操作; 如果這個操作沒有完成, 其他worker可以回滾掉這個事務, 一般是這個事務的worker產生了異常, 例如事務超過了一定的時限; 即使沒有超過時限, 回滾也是安全的, 不違反一致性;
現在回到我們開始的問題;
1, 假設一個事務開始的時間戳是 T2 , 這個事務讀取數據的原則是什么, 是讀取最新的數據還是只能讀取截止到 T2 的數據;
只能讀取T2時間戳的數據, 依據write列的時間戳, 這樣是為了在事務里面獲得一致性的快照(snapshot); 一般叫做可重復讀(Read Repeatable)
2, 假設一個事務里面需要訪問很多表的記錄, 而且都是截止到 T2 時間戳的快照, 怎么能獲取到這些快照?
根據write列的時間戳來獲取T2時間戳的快照;
3, 假設一個事務操作很多記錄, 這些記錄落在多台服務器上, 怎么能保證多台服務器上的記錄操作都是在原子鎖的情況下進行?
主鎖(Primary lock)和write列是關鍵;
4, 假如有多個 Worker 操作多個服務器上的多條記錄上的鎖, 這些記錄和鎖屬於一個事務, 怎么能保證這些記錄全部提交, 或者這些記錄全部回滾?
主鎖和write列是判斷事務成功提交的關鍵, 主鎖和write列操作成功, 事務一定要提交, 如果提交事務的worker掛了, 其他的worker根據從鎖(Secondary lock)幫助提交(rolled forward); 否則, 可以回滾(roll back), 回滾依賴於事務的超時時間和事務負責的worker的存活狀態;
下篇博客, 我們一起看下TiDB里面分布式事務的實現代碼;
