分库分表带来的完整性和一致性问题分库分表带来的完整性和一致性问题
在最近做的一个项目中,由于每天核算的数据量过于庞大,需要把数据库进行分库保存。当数据分散到各个库之后,带来的数
据更新操作就会存在一个一致性和完整性的问题。下面是一个典型的场景
假设目前存在三个物理库,现在有一个文件,里面有1W条数据,根据分库的规则,可以把文件里面的数据分到三个库中,现
在需要保证这1W条数据要要完整的保存到这三个库里面,并且数据是一致性的,也就是说 三个库里面已导入的数据完全和文
件里面的数据一致。
正常情况下,我们先把文件里面的数据按照所属的数据库分成三份,然后针对每一份数据库进行保存,在单库的情况下,可以
保证单库的数据完整性。但是三个库要保证一致性,就是非常复杂的一项工作,很有可能第一个库的数据保存成功了,但是后
面三个库的数据保存失败了,导致整个文件的里面的数据在数据库里面不完整。
如何解决这种问题,目前想到的有几个办法:
方案1
使用类似JTA提供的分布式事物机制,也就是说需要相关的数据库提供支持XA的驱动。( XA 是指由 X/Open 组织提出的分布
式交易处理的规范)。这个需要依赖特定的数据库厂商,也是比较简单的方案。毕竟复杂的事务管理都可以通过提供JTA服务
的厂商和提供XA驱动的数据库厂商来完成。目前大多数实现了JTA的服务器厂商比较多,比如JBOSS,或者开源的
JOTM(Java Open Transaction Manager)——ObjectWeb的一个开源JTA实现。但是引入支持XA的数据库驱动会带来很多潜在
的问题,在 《java事务设计策略》里面:在Java事务管理中,常常令人困惑的一个问题是什么时候应该使用XA,什么时候不
应使用XA。由于大多数商业应用服务器执行单阶段提交(one-phase commit)操作,性能下降并非一个值得考虑的问题。然
而,非必要性的在您的应用中引入XA数据库驱动,会导致不可预料的后果与错误,特别是在使用本地事务模型(Local
Transaction Model)时。因此,一般来说在您不需要XA的时候,应该尽量避免使用它。” 所以这个是一个可选的方案,也是
最简单的一个方案
方案2
建立一张文件批次表(放在一个独立的数据库里面),保存待处理的文件批次信息(不是明细数据,简单说的就是要处理的文
件名和所在路径),在每次处理文件数据的时候,先往表里面插入一条文件批次信息,并且设置文件的状态为初始状态,在文
件中的数据全部成功的保存到三个分库里面之后,在更新文件的批次状态为成功。如果保存到分库的过程中出现异常,文件批
次的状态还是初始状态。而后台启动一个定时机制,定时去扫描文件批次状态,如果发现是初始状态,就重新执行文件的导入
操作,直到文件完全导入成功。这个方案看起来没有问题,但是可能存在重复导入的情况,比如批次导入到第一个分库成功
了,后面两个库失败了,重新导入的话,可能会重复把数据重复导入第一个分库。我们可以在导入之间进行判断,如果导入
过,就不进行导入,但是极端的情况,我们无法判断数据是否导入过,也是一个有缺陷的方案,并且如果每次导入之前,都进
行数据是否导入的操作,性能会有一些影响。我们也可以通过异常恢复机制来进行,如果发现文件导入失败了,我们删除已经
导入入库的流水,但是这也引入了错误处理带来的一致性问题,比如我们已经导入成功2个分库的数据,在导入第三个分库失
败的情况下,要删除掉前面两个分库的数据,这也没有办法保证是一致的。
在这个方案里面,我们可以在进行一定的优化,让它看起来运作起来是没有问题的。首先再建立一张子批次表(和文件批次表
放在同一个库),在进行处理的时候,我们把大的文件的数据按照分库规则拆成三个子文件,每一个子文件里面的数据对应一
个分库。这样就产生三条子批次信息,由于文件批次信息和子批次信息 在同一个库里面,可以保证一致性。这样每个待处理
的文件就分成了四条记录,一条主文件批次信息,三条子批次信息,在导入数据之前,这些批次的信息的状态都是初始状态。
这样一个文件的导入就分解为三个子文件,分别导入到对应库里面去。对于每个子文件批次,我们可以保证子文件数据的都是
在同一个库里面,保证每个子文件里面数据的一致性和完整性,然后导入成功之后,在更新子批次的状态为成功,如果所有的
子文件的批次状态都为成功,那么对应的文件批次状态就更新为成功。这样看起来非常完美,解决了问题。但是仔细考虑一
下,有一个小的细节问题:子批次信息和一个独立的库,要导入的数据是和子批次信息可能不再一个库,没有办法保证这两个
操作是一致性的,也就是说 子文件里面的数据成功的导入到分库,但是可能子批次信息状态没有更新。那子批次信息能不能
放在每个分库里面了,这样的话,又回到刚开始提出的问题了(这里面就不解释,可以去自己去想想)。
下面一副图简单的演示的设计思想:
评论0
最新资源