高并发账户记录查询

前提是你 提交于 2020-12-19 08:52:00

【摘要】 面对高并发账户记录查询问题,按照本文的介绍一步一步操作,就能有效提升性能。点击了解高并发账户记录查询

问题描述

高并发账户记录查询在银行、互联网企业、通信企业中广泛存在。例如:网上银行、手机银行、电商个人账户查询、互联网游戏账户等等。这类查询有三个共同点:

1、 数据总量非常大。用户数量本身就非常多,再加上多年的账户数据,数据量可以达到几千万甚至上亿条。

2、 访问人数众多。几百万甚至上千万人访问,属于高并发查询。

3、 不能让用户等待。手机、网页要达到秒级响应,否则严重影响用户体验。

下面以某银行账户活期明细查询为例,给出这类问题的解决办法。

某银行共一亿个活期账户,每个账户平均每月有 7 条数据,每年数据总量 84 亿条。每条数据中的机构字段,还要关联分支机构表(几千条)记录。在性能上,要求单台服务器支持一千个以上的查询,响应时间不能超过 1 秒。

有序行存

活期明细数据随着时间增长非常快,一年就有 84 亿条。如果放到内存中,需要大量内存空间,硬件投入成本太高,所以要放到硬盘上存储。分支机构表只有几千条数据,可以放在内存中存储。

在硬盘上存储,要考虑是行存还是列存。列存数据分块压缩,能减少遍历数据量。但由于账户查询是随机的,整块读取会有额外解压计算。而且每次取数都针对整个分块,复杂度较高,性能不如行存。因此,这个场景要选择行存存储,如下图:

图 1:行存和列存

具体的实现可以采用 Java、C++、SPL 等高级语言。这里我们以代码量最少的 SPL 语言为例讲解。

代码示例 1

A1:连接生产数据库,用游标读取活期存款数据,按照账户 id 排序。

A2:建立本地组表文件。

A3:建立组表,并从数据库游标读取活期存款明细数据,写入文件。

其中,A3 中的 @r 选项,就是建立行存文件。一年 84 亿条数据都导出,时间会比较长。但是这是一次性的工作,后续就只需要追加增量数据即可。增量数据的追加方法,后面会有介绍。如果按照账户排序会对生产数据库造成较大压力,可以导出之后基于文件排序。排序使用 SPL 的 sortx 函数,具体用法参见函数参考。

利用索引

要利用索引提速,先要对明细文件建立索引。由于明细数据量大,建立的索引文件也会很大。很难全部加载到内存中。可以建立多级索引,如下图:

图 2:多级缓存

还是以 SPL 为例,建立多级索引,只需要在“代码示例 1”的基础上,增加一个网格即可:

代码示例 2

A4:对行存文件建立索引文件。

加载的索引级别越多,占用存储空间越大。同时,账户 id 的跨度变小,加载到内存中后,索引效果也会变好。查询时可以根据内存大小,尽可能加载更多级别的索引,可以有效提高查询速度。

在查询之前,系统初始化或者数据变动时,要预先加载多级索引,以 SPL 代码为例:

代码示例 3

A1:判断全局变量中是否存在 detailR,如果存在,表示已经加载了索引。

B1:如果全局变量中没有 detailR,那么打开组表加载三级索引。@2 或者 @3 表示加载 2 或者 3 级索引。

B2:detailR 存入全局变量。

这段预先加载的初始化代码(代码示例 3),可以保存成 init.dfx,放入集算器主目录(main path),在节点机(unitServer)启动的时候会自动被调用,如下图:

图 3:节点机自动调用初始化代码

由于我们提前准备好的活期数据是对账户 id 物理有序的,查询时根据索引找到账户 id 后,可以在硬盘连续读取,显著减少磁盘 IO,有效提速,如下图:

图 4:索引和有序行存

查询账户 10100,先利用内存中预先加载的多级索引和索引文件,快速定位到活期明细数据文件中的位置,再连续读取到账户 id 有变化为止。因为数据是按照账户 id 物理有序的,所以文件的其他位置不可能再有 10100 账户的数据了。

利用索引查询的示例代码如下:

代码示例 4

A1:全程变量 detailR 已经缓存了三级索引,现在可以基于它利用索引,按照条件取出账户为 10100 的记录。

每个账户的数据量并不大,所以可以全部读入内存。

活期明细前端应用(网页或者 APP),要通过集算器的 JDBC 驱动调用 SPL 代码查询。调用的时候,需要将账户 id 作为参数传给 SPL 程序。例如:定义一个网格参数 countid,传入账户 id 为 10100。A1 中的代码就要改为:=detailR.icursor (;ID==countid,index_detailR_id).fetch()。

关联查询

查到指定账户数据装入内存后,可以将机构数据也读入内存。在内存中关联计算,性能可以得到保障。示例代码如下:

代码示例 5

A2:读入机构数据。

A3:账号 10100 的活期明细数据关联机构数据。

A4:将账户 id、分支机构名称和金额返回给前端调用者(网页或者 APP)。

机构数据可以在应用系统初始化的时候加载入内存,不必每次读取,查询速度更快。在上面提到的 init.dfx 中增加代码如下:

代码示例 6

预先加载机构数据之后,查询代码要在“代码示例 5”的基础上去掉加载机构数据的部分,修改之后的查询代码如下:

代码示例 7

A2:直接用预先加载的全程变量 corp 和活期明细数据关联计算,省去了每次查询加载机构数据的时间。

数据更新

活期明细存款是按账号有序的,并不是按日期有序。所以,不能在末尾追加当日新增数据。活期明细几十亿条,如果每天有序归并新数据的话,耗时太长。每月归并一次的方案比较理想。如果将活期明细数据看成是主文件,那么更新原理如下图:

图 5:数据更新

数据更新的示例代码如下:

代码示例 8

A1、B1:如果是每月 1 日,重整文件。

A2:从生产数据库中读入当日数据。

A3:将当日数据有序归并到集算器自动生成的补文件中。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!