Seata?AT模式前后鏡像是如何生成詳解
前言
在Seata官網(wǎng)中,我們可以知道AT模式一階段的處理流程如下:
1.解析 SQL:得到 SQL 的類型(UPDATE),表(product),條件(where name = 'TXC')等相關(guān)的信息。
2.查詢前鏡像:根據(jù)解析得到的條件信息,生成查詢語句,定位數(shù)據(jù)。
3.執(zhí)行業(yè)務(wù) SQL。
4.查詢后鏡像:根據(jù)前鏡像的結(jié)果,通過 主鍵 定位數(shù)據(jù)。
5.插入回滾日志:把前后鏡像數(shù)據(jù)以及業(yè)務(wù) SQL 相關(guān)的信息組成一條回滾日志記錄,插入到 UNDO_LOG
表中
......
前鏡像的作用是保證在分布式事務(wù)失敗時能夠成功回滾的重要依據(jù),后鏡像是在回滾前校驗是否臟寫的數(shù)據(jù)依據(jù),那么我們一階段的前后鏡像在真實的代碼實現(xiàn)中是如何生成的呢?
前后鏡像的生成
為了能夠探尋到前后鏡像的生成原理,我們需要看一看seata源碼。最終我們把入口定位到AbstractDMLBaseExecutor.executeAutoCommitFalse()
方法:
protected T executeAutoCommitFalse(Object[] args) throws Exception { // 獲取前鏡像 TableRecords beforeImage = beforeImage(); // 執(zhí)行業(yè)務(wù)SQL T result = statementCallback.execute(statementProxy.getTargetStatement(), args); // 獲取后鏡像 TableRecords afterImage = afterImage(beforeImage); // 存儲前后鏡像 prepareUndoLog(beforeImage, afterImage); return result; }
前鏡像
繼續(xù)深入探究一下到底是怎么獲取beforeImage
的,根據(jù)源碼來看,我們發(fā)現(xiàn)beforeImage()
方法有很多實現(xiàn):
也就是說,Seata會根據(jù)不同的業(yè)務(wù)SQL來生成beforeImage
,有點經(jīng)驗的小伙伴能夠看出,這里其實使用到了模版模式加上策略模式,我們挑一個DeleteExecutor
來看一下:
@Override protected TableRecords beforeImage() throws SQLException { SQLDeleteRecognizer visitor = (SQLDeleteRecognizer) sqlRecognizer; // 根據(jù)表名解析出元數(shù)據(jù) TableMeta tmeta = getTableMeta(visitor.getTableName()); ArrayList<List<Object>> paramAppenderList = new ArrayList<>(); // 生成查詢beforeImage的SQL語句 // SELECT [列名,] FROM [表名] (別名) (WHERE) (ORDER BY) (LIMIT) FOR UPDATE String selectSQL = buildBeforeImageSQL(visitor, tmeta, paramAppenderList); // 執(zhí)行SQL查詢beforeImage return buildTableRecords(tmeta, selectSQL, paramAppenderList); }
1.關(guān)鍵原理就是根據(jù)業(yè)務(wù)SQL反向查詢出被影響的數(shù)據(jù);
2.為了保證查詢到的數(shù)據(jù)不是快照數(shù)據(jù),一定要記得加上FOR UPDATE
;
另外的話,我們發(fā)現(xiàn)其實UpdateExecutor
的前鏡像生成方式和DeleteExecutor
也差不多,像普通的insert
這種SQL的前鏡像就更簡單了:
@Override protected TableRecords beforeImage() throws SQLException { return TableRecords.empty(getTableMeta()); }
因為普通insert
語句不存在任何前鏡像,所以直接返回空記錄;
后鏡像
我們再來看一下后鏡像是如何生成的,這次我們看一下UpdateExecutor
的后鏡像生成方法afterImage()
:
@Override protected TableRecords afterImage(TableRecords beforeImage) throws SQLException { // 獲取元數(shù)據(jù) TableMeta tmeta = getTableMeta(); // 沒有前鏡像,也不存在后鏡像,說明沒有數(shù)據(jù)被修改 if (beforeImage == null || beforeImage.size() == 0) { return TableRecords.empty(getTableMeta()); } // 生成查詢后鏡像SQL // SELECT [列名,] FROM [表名] (別名) WHERE 主鍵 in (前鏡像的主鍵值) // 這里面有一個配置項[client.undo.onlyCareUpdateColumns],是否只關(guān)心被修改的列名,默認(rèn)是true String selectSQL = buildAfterImageSQL(tmeta, beforeImage); ResultSet rs = null; try (PreparedStatement pst = statementProxy.getConnection().prepareStatement(selectSQL)) { SqlGenerateUtils.setParamForPk(beforeImage.pkRows(), getTableMeta().getPrimaryKeyOnlyName(), pst); // 執(zhí)行查詢后鏡像 rs = pst.executeQuery(); // 包裝查詢結(jié)果 return TableRecords.buildRecords(tmeta, rs); } finally { IOUtil.close(rs); } }
后鏡像的生成原理與前鏡像的生成原理差不多,不過還是有一些小小的區(qū)別的:
1.后鏡像的查詢條件使用的是前鏡像對應(yīng)的主鍵值,就沒有用業(yè)務(wù)SQL的查詢條件;不同的Executor處理方式不同,需要根據(jù)具體的業(yè)務(wù)SQL來區(qū)分;
2.查詢后鏡像的SQL沒有使用FOR UPDATE
加鎖,直接拿的快照數(shù)據(jù);
小結(jié)
通過對seata源碼的分析,我們現(xiàn)在已經(jīng)了解了前后鏡像的生成原理了:
1.通過業(yè)務(wù)SQL來判斷SQL語句的類型,從而選擇不同的Executor來獲取前后鏡像;
2.前鏡像是通過業(yè)務(wù)SQL的查詢條件,并加上FOR UPDATE
來查詢業(yè)務(wù)SQL執(zhí)行前的數(shù)據(jù);(不同的Executor實現(xiàn)不同)
3.后鏡像是在業(yè)務(wù)SQL執(zhí)行完畢后,根據(jù)前鏡像內(nèi)的主鍵數(shù)據(jù)來獲取的數(shù)據(jù);(不同的Executor實現(xiàn)不同)
4.通過前后鏡像的多種實現(xiàn)可以判斷出seata AT模式所支持的SQL語句的所有類型;
以上就是Seata AT模式前后鏡像是如何生成詳解的詳細(xì)內(nèi)容,更多關(guān)于Seata AT模式生成前后鏡像的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
SpringBoot中的異常處理與參數(shù)校驗的方法實現(xiàn)
這篇文章主要介紹了SpringBoot中的異常處理與參數(shù)校驗的方法實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-04-04MyBatis深入解讀動態(tài)SQL的實現(xiàn)
動態(tài) SQL 是 MyBatis 的強大特性之一。如果你使用過 JDBC 或其它類似的框架,你應(yīng)該能理解根據(jù)不同條件拼接 SQL 語句有多痛苦,例如拼接時要確保不能忘記添加必要的空格,還要注意去掉列表最后一個列名的逗號。利用動態(tài) SQL,可以徹底擺脫這種痛苦2022-04-04Java并發(fā)編程 interrupt()方法示例詳解
interrrupt()方法可以用來打斷正在運行的線程,也可以打斷sleep()、wait()、join()情況下的線程,但是這些情況下被打斷線程的打斷標(biāo)記不同,這篇文章主要介紹了Java并發(fā)編程 interrupt()方法示例詳解,需要的朋友可以參考下2023-06-06Netty分布式pipeline管道傳播事件的邏輯總結(jié)分析
這篇文章主要為大家介紹了Netty分布式pipeline管道傳播事件總結(jié)分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-03-03