今天遇到一个非常常见的业务场景,即“通过接口同步数据并保证数据的一致性”,因为业务场景太常见导致我忽略了很多问题,最终也引发了无数次反省。在反省无果的情况下找老司机求助才得以解决,具体场景如下:
需求:
- a有一张表存储有5千万用户的账号信息,其中包括邮箱,电话等等。
- b开发了一个接口,要求a实时同步用户的电话和邮箱等信息到b这里。
就是这样一个非常简单的需求,b的建议是为保证实时性要求a每次在用户添加或修改电话与邮箱时调用他的接口,这样计划任务都不用起,直接果断的同步数据。都说的这么好了,那么我们开始编码吧!在a这边用户信息发生改变时就调用一次b的接口,很快就开发完回家洗洗睡了多好…
出现的问题:
虽然我们能保证功能上线后信息都可以实时同步过去,但是历史5千万数据怎么同步过去?当然同步历史数据不难,写个脚本去跑,个把小时就搞定。更简单直接把sql备份给b让他自己去同步等等方法。
但是,我们同步的历史数据其实是数据表某个时间的快照。比如23点开始同步,其实是同步这张表23点时的快照。如果23:01用户修改了邮箱,我们也成功把这条数据同步到了b。b这边23:10分又被历史数据覆盖了。忙了半天才发现日了狗了……之后想想,发现还是有点小复杂啊。
优化方案:
最简单成本最低的办法就是发停服公告,告诉用户凌晨2点到4点为系统维护时间,不提供电话和邮箱等的修改绑定服务。
这个方案既节约人力又节约成本!2点开始同步历史数据,4点前同步完。然后再开放服务,数据一致性百分百能保证。不过这种解决办法“有种坏味道”,作为程序员我是很难接受的。特别是要我凌晨为这点事去加班更不能接受。和b协商,要b在同步的表中加个时间字段。这样我在做历史数据导入的时候就可以先对比时间。
比如我们23点开始导入数据,如果其中一条数据23:10分被修改了。通过判断时间就知道这条记录不用更新。这个办法相比1更优美,但是这样给b增加了大量工作,别人的数据表中可能就没有时间这个字段。就算有,还得去重新修改接口加时间参数,另外文档也要改……在负责任的程序员这么少的年代,给别人增加麻烦多半会被暗地里骂成狗,所以提醒同行朋友们此法慎用。另外如果你的角色是b那么请你提供接口的时候考虑到时间参数,因为同步的数据毕竟有延时,而且灾难恢复的时候你也很轻松。自己新建一张表,记录数据表的增量。比如新建一张表C,每次修改用户电话和邮箱的时候不直接调用接口,而是按一定的格式写入表C中。最后定时通过表C中记录的增量去调用接口。
如果既想装逼又不想被暗地里骂,那我们最好还是选择这个方案。这个方案实现也非常简单,开始同步历史数据的时候我们就开启增量写入C表中。当历史数据同步完后,我们开起计划任务去执行C中的增量数据(执行一条删除一条)。最后当C表中数据为空时我们就开启接口写入。所有这些都能通过程序控制,不用加班不用被吐槽多爽!
PS:最后想说的是,程序编的再好也是一个码农,真正的高手都是那种会用优美的方法解决复杂的问题!