下载
下载交易数据的文件。
步骤
1.下载
2.解析
3.入库
数据量
不同数据量,采用不同数据库存储。//这里的数据,是远程数据。使用什么数据库存储远程数据?
1.百万级 千万级 //关系数据库
2.亿级 //nosql
3.海量数据 //大数据Hive
另外,在进行账单数据存储时为了提高效率,需要将标准账单文件格式设计得与表结构一致,这样在完成数据转换后可以直接将文件load/copy到数据库中,这样速度会快很多;而考虑数据规模会增长得超级大,这张表也可以存储在Hive上,只是对于大部分公司的交易量来说,这么做会有一些技术实现上的成本,目前作者采用的是Postgresql数据库(版本为9.5.4)作为账单存储库,数据规模大概已在10亿条左右,表示暂无压力。
下载去重
此外,对于账单的下载逻辑也需要考虑防重逻辑的,即同一个渠道账号的同一天的账单数据不能重复下载和入库,所以除了存储具体的账单数据外,也需要设计一张账单下载记录表,用于存储那个渠道账号哪一天的下载情况,并在账单下载任务启动起根据该表进行防重复下载逻辑判断。
下载文件存储
这里还需要说明下,在下载原始账单和转化标准账单时由于账单文件读写都是本地磁盘,为了统一集中管理这些账单文件、也为了数据安全需要采用统一文件存储服务,如可以采用腾讯云的CFS文件存储,或者自己搭建一个文件夹共享服务。
账单下载记录中也需要存储原始账单文件及标准账单文件的下载位置,平时基本上不会用到,只是为方便日后用于数据问题排查,需要检索原始文件时,方便查找及数据重新加载。
数据来源
1.远程数据 //下载
2.本地数据 //订单表
对账
校验数据。
表
对账数据表
1.远程数据中间表
2.本地数据中间表
对账结果表
1.对账成功记录表
2.对账失败记录表
中间表
为什么要是有中间表?
1.原始数据一直在变,所以,一般会弄一个原始数据的中间表,即把原始数据转移到中间表。处理完一批,再转移一批。
2.直接对原始数据操作,影响本来的已有的性能。因为这些操作,量都很大。
中间表的状态?
待处理——》处理成功。
中间表数据何时清除?
因为远程数据和本地数据,实际上,都已经有数据库存储这两种数据。所以,中间表的数据,是可以删除的。
什么时候删除?第二天,或者过几天。
对账结果表
对账结果表,非常重要。因为这是唯一准确的数据。
如何存储?
1.Hive
2.分布式关系数据库TiDB //优点是实时查询
考虑完成对账、以及后面会涉及的处理差错,其结果都是产生对账明细数据,而对账明细数据是渠道vs平台后最为准确的资金数据,对于后续的商户清分、资金清算、结算都具有重大意义,所以复杂查询的频率会比较高,并且数据的使用时间范围也比较大,对于交易量比较大的公司一年的支付数据量可能达到数十亿规模,在数据存储方面,可以考虑采用TIDB这类分布式关系型数据库来存储对账结果相关的数据,这样后续的数据处理逻辑效率会提高很多。可能有人会问为什么不直接使用Hive进行查询,这是因为Hive的单条查询和批量查询的效率是一样的,所以并不太适合实时查询,而如果需要将对账、结算等数据通过管理系统进行管理,涉及的查询场景比较多,所以综合考虑使用分布式关系型数据库会更合适一些。
代码
这个是数据量比较大的情况下。
主要思路是
1.定时任务
2.一次取多条数据(万级别)
3.多线程-线程池,处理数据
在代码层面通过A表 full join B表后,会得到一个结果集,如果这个结果集数据比较大,系统没有采用Spark+Hive这种方式话,通过传统编程方式则需要对查询进行分页,考虑到数据逐条对账处理速度较慢,可以一页获取数据条数稍多一些,例如一次取5W条,然后在系统内部采用多线程方式对数据集分割后并行处理,每个线程按照特定的对账逻辑执行,得到对账明细结果集或差错结果集后,批量存入对账数据库。
核心参考代码如下:
public boolean execute(ShardingContext shardingContext) {
boolean exeResult = true;
long startTime = System.currentTimeMillis();
String paramValue = shardingContext.getJobParameter();
CheckRequest checkRequest = (CheckRequest) JsonUtils.json2Object(paramValue, MbkCheckRequest.class);//对账批次请求信息
Map<String, Object> paramMap = new HashMap<String, Object>();
paramMap.put("batchNo", checkRequest.getBatchNo());//批次号
paramMap.put("channel", checkRequest.getChannel());//渠道
paramMap.put("tradeType", checkRequest.getTradeType());//交易类型
//关闭PG执行计划解释(PG9问题)
unionCheckOrderMapper.enableNestloopToOff();
int totalCount = unionCheckOrderMapper.countByMap(paramMap);//总数
int pageNum = 50000;//pageSize,每页5W条数据
int fistPage = 1;
int offset = 2;
PostgreSQLPageModel pm = PostgreSQLPageModel.newPageModel(pageNum, fistPage, totalCount);
int page = pm.getTotalPage();
BlockPoolExecutor exec = new BlockPoolExecutor();
exec.init(); //初始化线程池
ExecutorService pool = exec.getMbkBlockPoolExecutor();
List<Future<?>> results = Collections.synchronizedList(new ArrayList<Future<?>>());// 并行计算结果数据类型定义
for (int i = 1; i <= page; i++) {//分页进行数据Fetch
long startTime2 = System.currentTimeMillis();
pm.setCurrentPage(i); //设置当前页码
offset = pm.getOffset();
paramMap.put("pageSize", pageNum);
paramMap.put("offset", offset);
List<UnionCheckOrder> outReconList = unionCheckOrderMapper.selectByMap(paramMap);
Map<String, List<?>> entityMap = groupListByAvg(outReconList, 1000);
CountDownLatch latch = new CountDownLatch(entityMap.size());
OutReconProcessTask[] outReconProcessTask = new OutReconProcessTask[entityMap.size()];
Iterator<Map.Entry<String, List<?>>> it = entityMap.entrySet().iterator();
try {
int j = 0;
while (it.hasNext()) {
List<UnionCheckOrder> uList = (List<UnionCheckOrder>) it.next().getValue();
outReconProcessTask[j] = new OutReconProcessTask(latch, uList);
results.add(pool.submit(outReconProcessTask[j]));
j++;
}
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
long endTime2 = System.currentTimeMillis();
}
unionCheckOrderMapper.enableNestloopToOn();//开启PG执行计划解释
exec.destory();
long endTime = System.currentTimeMillis();
return exeResult;
}
总结
系统演进化方向
对于对账系统的演变主要需要从考虑数据的增长、任务资源的合理配置以及系统监控这几个方向去考虑。
如果数据量持续增长到传统方式已无法处理,可以采用Spark Streming+Hive+Tidb等组合技术方案进行改进。//哪个数据?用什么数据库存储?基于数据量的大小。基于数据来源,即是远程数据还是本地数据。
此外对账系统是一个以定时任务为主的系统,对于定时任务处理框架的选择可以采用分布式任务框架(推荐elasticjob/saturn)+自定义任务逻辑的方式综合处理(如有些任务存在先后顺序,如果框架本身不提供这类处理功能,则需要通过业务规则限制)。// 定时任务1.spring 2.分布式定时任务
而从系统监控角度,由于任务系统不同于实时交易流程,具有执行时间长、数据操作范围广泛的特点,除了进行正常的进程级别的监控外,对于各个任务的执行情况,也需要进行比较细致的监控,这部分可以通过监控打点等方式综合解决;而对于业务异常日志的监控则可以通过Sentry等日志监控工具进行监控。//监控定时任务处理情况