MySQL和MongoDB事務(wù)同步的一種嘗試
現(xiàn)象
最近線上的一條數(shù)據(jù)狀態(tài)不對,但是日志又記錄上了。 查看了這條數(shù)據(jù)的更新邏輯
public Boolean autoReject(AutoRejectParam param) {
OperationLog log = createOperationLog(param);
// 保存操作日志到mysql
operationLogMapper.insertSelective(log);
Query query = new Query();
Criteria criteria = new Criteria();
criteria.and("requestId").is(param.getRequestId());
query.addCriteria(criteria);
Update update = new Update();
update.set("status", CvBusinessStatusEnum.Rejected.getCode())
.set("updateTime", new Date())
.set("taskId", "");
mongoTemplate.updateFirst(query, update, JSONObject.class, collectionName);
return true;
}從代碼可以看出這里分別保存了日志到mysql,然后更新了mongodb中的數(shù)據(jù)狀態(tài)。
很明顯保存mysql成功了,但是更新mongodb的數(shù)據(jù)失敗了,那為什么保存mongodb的數(shù)據(jù)失敗了呢? 然后根據(jù)日志發(fā)現(xiàn),當(dāng)時服務(wù)器和mongodb連接出現(xiàn)了問題,于是就導(dǎo)致了保存mysql成功,保存到mongodb失敗了。
如何解決?
問題既然產(chǎn)生了,那么有什么辦法能夠保證要成功就都成功呢? 第一個想到的是事務(wù),我們需要保證兩個數(shù)據(jù)庫操作的事務(wù)一致性就可以避免這個問題了。使用單一的事務(wù)管理器肯定是不行的,需要使用鏈?zhǔn)绞聞?wù)。
我們可以使用spring中的ChainedTransactionManager來實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用
@Configuration
public class TransactionConfig {
@Bean
public PlatformTransactionManager mongoTransactionManager(MongoTemplate mongoTemplate) {
return new MongoTransactionManager(mongoTemplate.getMongoDbFactory());
}
@Bean
public PlatformTransactionManager jpaTransactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean
public ChainedTransactionManager chainedTransactionManager(
PlatformTransactionManager mongoTransactionManager,
PlatformTransactionManager jpaTransactionManager) {
return new ChainedTransactionManager(mongoTransactionManager, jpaTransactionManager);
}
}
@Transactional("chainedTransactionManager")
public Boolean autoReject(AutoRejectParam param) {
//省略其他代碼
// 保存操作日志到mysql
operationLogMapper.insertSelective(log);
// 更新mongodb
mongoTemplate.updateFirst(query, update, JSONObject.class, collectionName);
return true;
}這種方法使用 ChainedTransactionManager 來管理多個事務(wù)管理器。當(dāng)方法執(zhí)行時,它會按順序開啟所有事務(wù),如果在執(zhí)行過程中出現(xiàn)異常,它會按相反的順序回滾所有事務(wù)。
需要注意的是,這種方法并不能保證 100% 的事務(wù)一致性,因?yàn)樗鼘?shí)際上是在應(yīng)用層面模擬的分布式事務(wù)。在某些極端情況下(比如網(wǎng)絡(luò)故障或服務(wù)器崩潰),可能會出現(xiàn)部分提交的情況。
比如我們是現(xiàn)在這樣的執(zhí)行流程
transaction1 begin
transaction2 begin
transaction2 commit -> error rollbacks, rollbacks transction1 too
transaction1 commit -> error, only rollbacks transaction1比如上面這種情況,在最后提交transaction1的時候如果由于網(wǎng)絡(luò)原因提交失敗了,就會導(dǎo)致事務(wù)2成功,事務(wù)1失敗,還是部分提交了。
當(dāng)然如果業(yè)務(wù)要求對于這種不一致是可以接受的,或者說我們可以進(jìn)行手動補(bǔ)償方式達(dá)到最終一致性,那這種方案也是可以接受的。
對于要求更高事務(wù)一致性的場景,可能需要考慮使用專門的分布式事務(wù)解決方案,如 XA 協(xié)議或 TCC (Try-Confirm-Cancel) 模式。 比如JTA就屬于XA協(xié)議, 我們可以使用開源實(shí)現(xiàn)atomikos。




























