1 背景
分佈式環境下,無法採用單機事務(JDBC事務,Spring管理事務),在多步驟操作時,由於無法回滾(Rollback),容易導致產生髒數據。
2 總體方案
目標: 基於互聯網行業成熟經驗,和BASE理論,分佈式事務目標是柔性事務和最終一致性。
基本策略:基於數據版本+事務隔離+異步兩階段提交+事務補償機制。
3 具體方案
3.1 事務字段:
在庫表結構設計上為每個業務表增加事務表,表結構一致,並增加幾個事務字段,如:txid 等。
數據格式: 基本字段, ...., txid, txtime,txstate, version
需要記錄事務序號,事務時間,事務狀態,
每條update語句需要對version進行自增:
create table if not exists tx_item like item; //創建主表對應的事務表,表結構一致
alter table tx_item add txid int;
alter table tx_item add txid int;
update tx_item set ....... , txid=#{txid}, txtime=#{txtime}, txstate=#{txstate}, version = version + 1
where item_id = #{itemId}
其中, #{txid}, #{txtime}, txstate=#{txstate} 由應用程序傳入。
3.2 多步操作處理流程
1) 開啟事務,生成事務流水號和時間戳,
tx = new Dtransaction();
txid = tx.getTxid(); //流水號,可以用 txtime + 6位隨機號代替
txtime = tx.getTxtime(); //當前時間,精確到納秒
2) 執行操作1
變更數據時帶上 tx信息。例如: 更新賬戶餘額表 Balance , 減掉商品總價, 增加 修改日誌
create table if not exists tx_balance like balance; //創建主表對應的事務表,表結構一致
update balance set balance = balance - #{totalPrice} , txid=#{txid}, txtime=#{txtime}, txstate=#{txstate}, version = version + 1
where member_id = #{member_id} and balance - #{totalPrice} > 0
3) 執行操作2
變更數據時帶上 tx信息。例如: 更新產品 item, 減少庫存
create table if not exists tx_item like item; //創建主表對應的事務表,表結構一致
update item set stock = stock - #{quantity} , txid=#{txid}, txtime=#{txtime}, txstate=#{txstate}, version = version + 1
where item_id = #{itemId}
4.1) 假如 操作2也執行成功,則提交事務
tx.commit();
後臺操作是, 在數據庫事務表中,增加一條事務記錄,
insert into txlog(...) values(txid, txtime, txstate,version)
其中 txstate = 1 , 表示事務已提交, 待最終處理
4.2) 假如 操作2失敗, 則回滾事務
tx.rollback();
後臺操作是, 在數據庫事務表中,增加一條事務記錄,
insert into txlog(...) values(txid, txtime, txstate,version)
其中 txstate = -1, 表示事務失敗
5) 同時,事務協調任務根據事務表信息處理未提交事務數據 ( 通過線程方式,或MQ方式 )
獲取 txstate = 1 狀態為“已提交” 的事務
將txstate 改為 2. 事務狀態為完成, 並將事務記錄拷貝到主表
update item inner join tx_item on item.item_id = tx_item.item_id
set xxx = xxx
where txid = #{txid}
6) 失敗事務處理任務定期清理失敗事務( 例如:timeout = 30分鐘一次)
刪除 txstate = -1, txtime 超過 30分鐘的事務記錄。
delete from tx_item where currenttime - txtime > 30分鐘
3.3 事務隔離處理
事務隔離包括兩個場景含義:
一指當前事務的中間修改狀態(即未提交狀態)不應該被其他正在進行的事務訪問到(包括:查詢,修改,刪除)
二指當前事務中的查詢,修改,刪除不應該訪問到其他事務未提交的中間數據。
3.3.1 對他隔離
對他隔離主要防範事務隔離四個基本特性中的髒讀,讀未提交
對他隔離採用基本的 查詢邏輯就可以滿足需求。 因為其他事務訪問的是主表數據,而事務表的中間數據並未提交合併到主表。
3.3.1 對己隔離
二指當前事務中的查詢,修改,刪除不應該訪問到其他事務未提交的中間數據。
對己主要滿足事務四個基本特性的可重複讀。 可重複讀要求,在自己的事務中的中途修改能夠在事務提交前的讀取中反映出來 。
例如: 一次事務中, 未完成訂單為10條, 然後將其中2條修改為已完成, 則接下來的查詢未完成訂單應該是8條。
可重複讀的查詢主要是:
主表query結果 union 事務表查詢結果 where txid = #{txid}
其中 #{txid} 為當前事務id
3.3.3 防範幻讀
防幻讀指,在一個事務中,前後兩次查詢的數據結果是一致的, 其他事務刪除或增加的數據即便符合當前事務的數據查詢範圍,也不影響當前事務的第二次查詢結果。
策略。其他事務的新增數據先保存到 事務表 就可以實現 防幻讀。
其他事務的刪除數據以日誌形式先保存到事務表,從而防範幻讀。
閱讀更多 Hello123world 的文章