我們經常使用事務來保證資料庫層面資料的acid特性。
舉個栗子,使用者下了乙個訂單,需要修改餘額表,訂單表,流水表,於是會有類似的偽**:
start transaction;
curdtable t_account; any exception rollback;
curdtable t_order; any exceptionrollback;
curdtable t_flow; any exceptionrollback;
commit;
如果對餘額表,訂單表,流水表的sql操作全部成功,則全部提交,如果任何乙個出現問題,則全部回滾
,以保證資料的一致性。
網際網路的業務特點,資料量較大,併發量較大,經常使用拆庫的方式提公升系統的效能。如果進行了拆庫,餘額、訂單、流水可能分布在不同的資料庫上,甚至不同的資料庫例項上,此時就不能用事務來保證資料的一致性了。這種情況下如何保證資料的一致性,是今天要討論的話題。
補償事務是一種在業務端實施業務逆向操作事務,來保證業務資料一致性的方式。
舉個栗子,修改餘額表事務為
int do_accountt(uid, money)
那麼補償事務可以是:
int compensate_accountt(uid, money)
同理,訂單表操作為
do_ordert,新增乙個訂單
compensate_ordert,刪除乙個訂單
要保重餘額與訂單的一致性,可能要寫這樣的**:
// 執行第乙個事務
int flag = do_accountt();
if(flag=yes)
else
}該方案的不足是:
(1)不同的業務要寫不同的補償事務,不具備通用性
(2)沒有考慮補償事務的失敗
(3)如果業務流程很複雜,if/else會巢狀非常多層
例如,如果上面的例子加上流水表的修改,加上do_flowt和compensate_flowt,可能會變成乙個這樣的if/else:
// 執行第乙個事務
int flag = do_accountt();
if(flag=yes)
else
}else
}單庫是用這樣乙個大事務保證一致性:
start transaction;
curdtable t_account; any exception rollback;
curdtable t_order; any exceptionrollback;
curdtable t_flow; any exceptionrollback;
commit;
拆分成了多個庫,大事務會變成三個小事務:
start transaction1;
//第乙個庫事務執行
curdtable t_account; any exception rollback;
…// 第乙個庫事務提交
commit1;
start transaction2;
//第二個庫事務執行
curdtable t_order; any exceptionrollback;
…// 第二個庫事務提交
commit2;
start transaction3;
//第三個庫事務執行
curdtable t_flow; any exceptionrollback;
…// 第三個庫事務提交
commit3;
乙個事務,分成執行與提交兩個階段,執行的時間其實是很長的,而commit的執行其實是很快的
,於是整個執行過程的時間軸如下:
第乙個事務執行
200ms
,提交1ms;
第二個事務執行120ms,提交1ms;
第三個事務執行80ms,提交1ms;
那在什麼時候系統出現問題,會出現不一致呢?
回答:第乙個事務成功提交之後,最後乙個事務成功提交之前,如果出現問題(例如伺服器重啟,資料庫異常等),都可能導致資料不一致。
如果改變事務執行與提交的時序,變成事務先執行,最後一起提交,情況會變成什麼樣呢:
第乙個事務執行
200ms;
第二個事務執行120ms;
第三個事務執行80ms;
第乙個事務執行1ms;
第二個事務執行1ms;
第三個事務執行1ms;
那在什麼時候系統出現問題,會出現不一致呢?
問題的答案與之前相同
:第乙個事務成功提交之後,最後乙個事務成功提交之前,如果出現問題(例如伺服器重啟,資料庫異常等),都可能導致資料不一致。
這個變化的意義是什麼呢?
方案一總執行時間是303ms,最後202ms內出現異常都可能導致不一致;
方案二總執行時間也是303ms,但最後2ms內出現異常才會導致不一致;
雖然沒有徹底解決資料的一致性問題,但不一致出現的概率大大降低了!
事務提交後置降低了資料不一致的出現概率,會帶來什麼***呢?
回答:事務提交時會釋放資料庫的連線,第一種方案,第乙個庫事務提交,資料庫連線就釋放了,後置事務提交的方案,所有庫的連線,要等到所有事務執行完才釋放。這就意味著,資料庫連線占用的時間增長了,系統整體的吞吐量降低了。
trx1.exec();
trx1.commit();
trx2.exec();
trx2.commit();
trx3.exec();
trx3.commit();
優化為:
trx1.exec();
trx2.exec();
trx3.exec();
trx1.commit();
trx2.commit();
trx3.commit();
這個小小的改動(改動成本極低),不能徹底解決多庫分布式事務資料一致性問題,但能大大降低資料不一致的概率,帶來的***是資料庫連線占用時間會增長,吞吐量會降低。對於一致性與吞吐量的折衷,還需要業務架構師謹慎權衡折衷。
如果有收穫,幫忙隨手**喲。
架構師之路 12 多庫多事務降低資料不一致概率
我們經常使用事務來保證資料庫層面資料的acid特性。舉個栗子,使用者下了乙個訂單,需要修改餘額表,訂單表,流水表,於是會有類似的偽 start transaction curdtable t account any exception rollback curdtable t order any e...
執行緒資料不一致例項
package kkkk public class sharedata private static class sharethread1 implements runnablecatch interruptedexception e private static class sharethread...
Redis Mysql資料不一致問題
目前各個大專案中多多少少用了寫redis快取技術,通過對redis中資料的讀取來減少對db的壓力。那麼在讀 寫兩個分離的技術中就容易造成,資料庫和快取不一致的問題 當使用者在更新資料時,在第二個步驟還未執行,但是此時又通過獲取資料介面,此時快取已經刪除,但是資料庫中還是老資料,那麼就會將老資料重新寫...