业务场景
原来支持导出2000条,导出修改文件的部分字段后再导入这2000条。
后来因为业务需要,改成支持导入导出1万条。
再到后来又需要支持导入导出支持到5万条甚至更多……
分析
每条字段不多的情况下,原来的导入导出2000条,实现方式是直接放一个事务里循环读写、内容都放内存里,然后一起提交。、
支持导入导出从2000条到1万条,如果都放内存里,内容多起来会内存溢出,需要考虑分批提交。
支持从1万条到5万条,虽然分批提交的话没有对系统压力影响不大,但会存在另一个问题,实时导出导入会超时,处理时间超过1分钟就没办法实时返回文件或者状态了,这时候需要考虑异步延时导入导出。
需要考虑新建一个延时导入导出的文件列表,显示每个文件操作的提交时间、条数、执行状态等。用定时任务调度执行。
因为不同的对文件的操作可能操作的是同一条数据,所以需要考虑延时操作的互斥性,这时候可以用同一个定时任务,每次只取一条文件操作来执行,来实现排队。
另外,还需要考虑实时操作和延时操作是否会互相影响数据准确性。例如,我正在延时导出未发货的订单,已经导出了第一批数据写进文件中,此时商户操作了单个发货,发货的订单正好在第一批数据中,那么延时导出的未发货订单文件就不准确了。所以实时操作和延时操作其实也有互斥性。
这时需要和业务商量实现方式。
第一种,是简单粗暴的把导入导出全改成延时的;
第二种,判断导出条数是否大于1万条并且当前是否有待执行或正在执行的任务(待执行也需要是因为并不知道是否会在操作实时的时候待执行被调度到了),就把该任务变为延时导入导出的任务或者直接拒绝该操作,否则可以实时处理返回。
判断当前是否有待执行或正在执行的任务,可以查任务列表的状态有没有未完成的。
第三种,判断导出条数是否大于1万条并且当前是否有正在执行的任务,就变为延时任务或直接拒绝,否则实时处理。
这里可以用一个原子级的参数判断,有点像应用级的锁,无论是实时还是延时,有在对表操作都拿着这个锁,等到自己整个任务完成再释放。如果有实时操作,锁未被释放,延时调度到也无法操作,return掉等待下一个调度周期;如果有延时在操作,等所有批次执行完才释放锁,期间实时任务无法获得锁资源无法开始。这种应用级的锁,用单例模式只能解决的是某台机子的锁,但现在生产都是集群的,不适用。需要用像数据库、redis、memcached、zookeeper等这种应用层面的锁。
//TODO redis和memcached的区别
//TODO zookeeper注册中心
结论
所以业务的一句话,感觉只是简单改一下条数,技术需要发现里面的工作量。
从一个事务处理,到分批处理,再到延时排队分批处理。