由彼观彼,而不是由己观彼。html
开始前,扯些许的题外话。咱们常常会看到一些相似于初、中、高级软件工程师的区别的文章,以为高级会如何,初中级又会如何,咱们这次就不区分title,而是经过经验丰富与否、考虑问题是否全面、作事是否稳重来展开。spring
有这样一个需求,咱们须要在国内WEB端新增的事件数据,不只要写入国内的RDS,同时也要同步到国外的三个不一样地点的RDS中(不可采用@scheduled方式进行定时同步)。markdown
@Transactional(rollbackFor = Exception.class) void newEvent(event) throws Exception { insertEventIntoInlandRds(event); asyncEventToOtherRds(event); // 异步同步数据 } 复制代码
class CustomAsyncExceptionHandler implements AsyncUncaughtExceptionHandler { public void handleUncaughtException(Throwable throwable, Method method, Object... objects) { retry 5 times label = succeed after retring 5 times if label: insert async table successful label else: insert async table failed label and data } } 复制代码
经过上述方式,想必有一批直接使用Spring Boot作项目开发的工程师,都自觉得标注上@Transactional
就能够了,就会失败回滚,也不须要什么补救措施,那你就大错特错了。@Transactional
支持的是单体应用,远程IO已经脱离了它的控制。异步
其次,同步失败,重试5次没问题,那同步概况记录表应该放在哪里呢?那些分别放到不一样地域分别创建同步概况表的就不想说什么了,retry都失败了,你写入同步概况到国外RDS就必定能成功吗?答案是不能!因此记录同步概况的表固然须要放在主RDS里面(这里主从是相对概念,从A同步到B,则A是主;若是从C同步到A,则C是主),也就是国内RDS。async
再次,若是asyncEventToOtherRds
调用失败,致使newEvent
事务回滚,因此insertEventIntoInlandRds
也回滚,也就是国内RDS没有event这条记录了,可是海外还在不断retry,海外可能的结果是失败 or 成功。此时,会存在两种不一样状况:1)海外同步失败,则国内外数据是一致的;2)海外同步成功,则国内外数据就是不一致的。因此这里存在的问题就是事务回滚却没有数据探测以及补偿措施。ide
而后,若是retry了5次仍然失败了,那该如何处理呢?这里一样须要有必定的补偿措施,而不是简单的记录一下失败同步记录和数据就完事大吉,久而久之,累积同步失败记录多了必定会出大问题的。spring-boot
最后,就是这样的设计明显是没有彻底理解业务的主次,没有作好责任划分和事务隔离。须要在保证国内RDS必定写入成功,提交事务后,再进行海外RDS数据同步,这样咱们就能够针对性对症下药处理便可,而不用一边考虑国内失败如何,海外又失败如何,尽可能责任单一。oop
其实,这里写结论的时候,是很不想写的。为何呢,由于其实这个无关乎title,真的只是关乎经验、考虑问题是否全面、是否有思辨思惟。牵扯到数据方面的问题的时候,必定要打起12分的精神,否则数据都不许,谁还敢用大家的产品呢?学习
可是,到此,仍是想说上两句:1)技术必定要扎实,不要对一门技术只知其一;不知其二就以为彻底OK。好比上面的@Transactional
注解的使用就是个很好的例子。2)必定要有思辨思惟去看到每一件事情,凡事都是双面性的。ui
因此,这里的结论就是不作结论。但愿本身思考,本身是否属于上述顾头不顾尾的往前跑,却无论产生的影响。
以前,耗子叔曾说过,“学习原本就是件逆人性的事情,若是要成长,必须逼本身一把”。话没错,正式由于停留在技术的肤浅层面,最终才会出现上述问题。
也有人说,“失败是成功之母”。不,失败不会使你成功,总结失败才会助你成功。若是只是一味地不断重复错误,那你注定只能原地踏步。但愿咱们可以剥离这种潜意识!擅于总结问题,发现问题,积累经验,不断成长。
写完文章后,发现仍是有必要贴上一些有助于理解上述问题的文章连接。就在网上找了下最佳解读的: