T+0 查询统计的实现技术

T+0 查询是指实时数据查询,数据统计时可以查询到最新产生的数据。对应的还有 T+1、T+7 等,对应表示只能查询 1 天或 7 天以前的数据,显然这种 T+N 查询的数据是非实时的。本文将要探讨目前一些主要的 T+0 查询技术。

数据库

通过数据库实现 T+0 查询最简单的办法是直接使用单个业务数据库进行查询,所有数据都存储在一个数据库中自然能完成实时数据查询。不过,这种方式仅适用于数据量不大并发不高的场景,数据量稍大或查询的并发量较高时会导致数据库压力过大进而影响到业务使用。所以,很多系统为了保证业务的稳定性都会做读写分离,数据查询主要基于读库(查询库)进行。

主从架构

一旦数据分布到多个数据库中,要实现 T+0 查询就不那么容易了。一个还算简单的办法是使用数据库的主从架构(Master-Slave),借助数据库的数据同步机制(如 Oracle Golden Gate,MySQL 主从复制)将业务库数据复制到分析库中。不过,Master-Slave 并不能做到真正的实时,从库复制数据经常会出现延迟,除了数据库 IO 还有主库的高并发都会引起时间不等的延迟。MySQL 主从复制时从库只能串行执行 SQL 进行数据复制(主库可以并行)更会加剧这种情况,如果某个 SQL 执行时间较长或卡住,延迟则会越来越长。

此外,Master-Slave 往往要求是同一种类型的数据库,而且为了满足交易系统的需要往往是 RDB,当面向查询分析业务时无论在容量还是性能方面 RDB 就都没什么优势了。因此,Master-Slave 比较适合用在对数据实时性要求不那么高、数据量不太大的“准 T+0”查询场景。

跨库查询

还有一种做法是借助数据库自身的跨库查询能力(如 Oracle 的 DBLink,MySQL 的 FEDERATED 引擎),往期数据来自分析库,实时数据来自业务库,由于实时数据量并不大,查询对业务库也不会产生较大影响。同时,跨库查询对分析库的数据同步频率要求并不高,可以 T+N。

不过,借助数据库自身的跨库查询能力实现 T+0 查询也有很多缺点,最明显的是对异构数据源支持不好,无论是 DBLink 还是 FEDERATED 在异构源方面表现都不够稳定,甚至 FEDERATED 还要求表结构完全一样(FEDERATED 会在本地创建表定义,数据文件存在于远程数据库中)。而分析库(数据仓库)使用与业务库不同类型的数据库是非常常见的。其次这种方式会占用大量数据库资源而且性能很差。还有一些缺点,包括:数据传输不稳定、不支持大对象操作、容易出现错误稳定性差、可扩展性低等问题。

Java

从上面的讨论可以看出,只要能实现跨库(实时热数据和非实时冷数据混合)查询就可以实现 T+0 查询统计。既然数据库自身支持的不够好,一个很自然的想法就是在应用端实现。将两部分数据分别从数据库中取出,再通过应用端硬编码进行混合计算从而实现 T+0。

不过,在应用端完成计算要考虑两个问题,开发难度和运算性能。

开发效率

我们知道,目前大部分应用系统都采用 Java 开发,而 Java 由于缺少结构化计算类库并不擅长结构化数据计算,简单的分组汇总就要几十行代码,在数据运算方面远没有 SQL 方便。使用 Java 硬编码,应对一些简单的查询任务(如帐户交易查询)还问题不大,而面对复杂的统计分析场景时,Java 的实现复杂度太高,往往不具有实际的可行性了。这时还可以借助一些 Java 计算引擎来辅助完成数据处理从而简化开发难度。不过遗憾的是,现实中的计算引擎都不够成熟完善,有的不支持数据库数据源,有的无法实现关联,计算能力不足,即使具备一定计算能力在实现时编码复杂度仍然很高,总体很难降低开发难度,与 SQL 相比还有很大差距。

运算性能

为了兼顾开发效率,历史数据仍然会使用数据库存储,这样就可以利用数据库的计算能力,通过 SQL 来简化开发过程。不过大量历史数据存储在数据库中往往会导致数据库压力过大性能下降,有些复杂计算数据库执行效率也很低。另外,数据库的应用程序接口(如 JDBC)性能往往很差,结果集稍大返回时间就很长,这就导致了更高的 IO 成本,总体运算性能更差。而且,数据库写入动作很慢,业务库数据导出到分析库时间成本也不低,而 ETL 时间窗口往往有限,这对一些周期性很强的业务(如银行月末年末结算)也会产生很大影响。

总体来看,在应用端通过 Java 进行多源混算实现 T+0 并不容易,硬编码太难,借助数据库性能又不理想。

集算器 SPL

沿着跨源运算实现 T+0 的思路,使用集算器 SPL 也是一种选择。集算器是一款开源的结构化数据计算引擎,提供了丰富的计算类库可以比较简单地完成各类数据计算。集算器采用嵌入式 JDBC 的方式可以与 Java 应用无缝集成 ,从这个角度可以将其看做一款增强的 Java 计算引擎。

通过集算器实现 T+0 查询时主要借助了以下能力。

跨源运算

集算器提供了多种数据源支持,RDB、NoSQL、Json、CSV、Webservice 以及私有的数据文件格式。重要的是集算器可以基于任意多种数据源进行混合计算,从不同数据源中分别读取冷热数据混算实现 T+0。


A


1

=cold=db1.cursor(“select   * from orders where odate<?”,date(now()))

/ 冷数据从历史库中取,昨天及以前的数据

2

=hot=db2.cursor(“select   * from orders where odate>=?”,date(now()))

/ 热数据从生产库中取,今天的数据

3

=[cold,hot].conjx()


4

=A3.groups(area,customer;sum(amout):amout)


可以进行跨源计算就可以直接基于业务库和分析库实现 T+0 查询了。

敏捷语法

集算器提供了专门用于数据处理的 SPL 语法,借助丰富的计算类库实现复杂计算更简单。如根据股票记录表查询股价连续上涨超过 5 天的股票及上涨天数(股价相等记为上涨)

SPL 的写法如下:


A


1

=db.query@x("select * from stock_record order by   ddate")


2

=A1.group(code)


3

=A2.new(code,~.group@i(price<price[-1]).max(~.len())-1:maxrisedays)

计算每只股票的连续上涨天数

4

=A3.select(maxrisedays>=5)

选出符合条件的记录

同样的计算使用 SQL 要嵌套 3 层才能完成,而 Java 就更加复杂,SPL 则通过简单几步就可以完成,这是 Java 计算引擎无法比拟的优点了。

使用 SPL 实现 T+0 计算还有第三个好处:

高性能

与数据库和 Java 计算都需要强依赖数据库不同,SPL 拥有完备的计算能力可以直接基于文件计算,这样历史数据就可以存储到文件(IO 快、压缩灵活、易并行)中,再借助 SPL 的跨源计算能力,将数据库(热数据)和文件(冷数据)混合计算就可以获得更高效的 T+0 查询性能。同时,使用 SPL 自有的高性能数据格式还可以进一步提升运算性能。

不仅如此,SPL 还内置了大量高性能算法执行效率更高,相对 SQL 也更有优势。SPL 是解释执行的,支持热部署 / 热切换。基于做这些能力,集算器可以完全应对 T+0 数据查询并保证查询系统永久在线,相对数据库和 Java 实现也更简单、高效。