TP 数据库太撑咋办?上 AP 库不如上 SPL

信息系统建设之初通常只有一个数据库,TP(交易)和 AP(分析)一把抓。随着业务和数据的不断增长,原来的数据库压力变得越来越大。为了不影响交易,业界常用的办法是把数据(通常是冷数据)迁出,把 AP 业务独立采用专门的数据库用于分析,这样就可以有效减轻 TP 库的负担,保证交易业务顺利进行。

专业的 AP 数据库速度通常比较快,一定程度上确实能解决性能问题,但也会带来一些其他问题。

AP 数据库面临的问题

首先是成本问题。

目前主流的 AP 数据库主要是 MPP 架构的,这与原来的 TP 库类型不同。MPP 虽然有较好的性能,但软硬件成本都很高。MPP 的硬件资源消耗很大,需要较高的硬件成本,如果使用商用软件还需要支付昂贵的授权费用。MPP 的每个节点需要单独维护,分布式架构下数据均匀分布和一致性保证等都会增加使用复杂度,这些都会推高使用成本。

增加 AP 库还意味着更复杂的管理。原来 TP 库的管理已然复杂,元数据设计、数据要满足约束才能入库、访问权限控制等等,现在增加的 AP 库这些工作仍然要做,对于类型不同的 AP 库还需要投入更多的资源,两套系统往往需要双倍以上的运维成本,带来成本问题。

从 TP 库向 AP 库迁移也不简单,还会面临两难困境

我们上 AP 库的目标是希望将所有 AP 业务都迁移过来,但全部迁移会面临很大风险。且不论 AP 库的功能是否齐全,很多原来在一个库内实现的业务,现在分开可能需要重新设计,数据库的不同与 SQL 的兼容性差异等都会增加迁移难度。这些都会导致一次性都倒腾过来的风险太大。

所以,稳妥的办法是逐步迁移,但又会面临新的问题。

我们都有这样的经验,随着数据库使用的深入,业务的不断增加,原来正常的查询会变得越来越慢。这除了数据量的原因,还有表数量、索引、元数据、存储空间等诸多因素,对于统一管理的数据库系统来说,在负载没有达到一定程度时很难判定该数据库是否对自己的业务有效。

初期只迁移一点点业务,那肯定跑得很快,但这很难判断选型是否正确,随着迁移的逐渐进行可能会出现后迁移的业务影响前面的,这仍有很大的风险,如果后期发现 AP 库无法承担自己的业务,或者还需要大量扩容,而此时已经积累了很多工作量,这将进入很尴尬的境地。

还有更麻烦的 T+0 问题

有些原来在一个数据库跑的好的业务,分库以后就无法实现了,最典型的就是 T+0 查询。T+0 在一个 TP 库内是天然支持的,但分库后则完全不同了,同一类型的数据库有时还可以跨库查询从而变相实现 T+0,虽然通常性能不高使用效果也差,但至少能做。但对于与 TP 库类型几乎不可能相同的 AP 库来说,要实现 T+0 就变成了非常困难的事情。导致以前经常使用的 T+0 业务,现在只能提供 T+1 甚至 T+N 支持,这对业务的影响之大是不言而喻的。

事实上,T+0 本质上是跨源计算问题,如果体系开放,跨源计算其实可以很容易实现,但数据库的封闭性要求数据只有进来才能算,从而带来严重的 T+0 问题。这意味着解决了旧问题又带来新问题,需求满足程度变差。

当然,要满足 T+0 需求还可以选择 HTAP 数据库,让查询和分析在一个数据库内完成是 HTAP 要达到的主要目标(怎么感觉又回到 TP 库)。HTAP 当然是一种选择,但实际情况是绝大部分 HTAP 库主要仍然是 TP 的能力,AP 方面往往很弱,这跟使用原来的 TP 库很多时候并无太大差别。更重要的是,上 HTAP 仍会面临直接成本和迁移两难的问题。原来的 TP 库只是压力大,并不是不能用,完全弃用也是一种浪费。

我们需要找到一种相对轻量的 AP 解决方案,既不会带来很高的成本,最好还可以逐步迁移,同时能解决 T+0 问题。

这时可以采用 SPL 来承担 AP 类任务,为 TP 库减负。

开放的 SPL 解决 AP 迁移各类问题

SPL 作为专门用于 AP 业务的开源计算引擎,具备简单轻量的特点,使用成本很低;而开放的计算能力和文件存储支持业务逐步迁移,且前后不会产生影响;多源混合计算能力则天然支持 T+0,使得剥离 AP 业务后仍能轻松满足所有业务需求。

SPL 应用架构

轻量低成本

SPL 与数据库的主要差别之一在于简单轻量,从而带来更低成本。

SPL 对硬件的要求很低,整体表现很轻。只要有 JDK1.8 及以上的 JVM 环境的任何操作系统都可以运行,包括常见的 VM 和 Container,安装后空间不到 1G。同时由于提供了诸多高性能机制,经常使用单机就能实现原来 MPP 集群的效果,这将直接降低软硬件的使用成本(关于高性能后面还会更详细说明)。

更特别的是,SPL 除了独立部署外,还可以与应用嵌入集成,在应用内提供强计算能力。这样应用不必依赖数据库就能获得强计算能力。TP 向 AP 迁移时可以在应用内发起,同时可以充当本应用的数据集市 / 前置计算引擎使用。

SPL 的敏捷语法在实现复杂计算时也有优势,我们在迁移之初通常会选择那些性能低资源消耗大的业务,这类业务往往很复杂,用 SPL 改造往往比改造 SQL 更简单。这会带来更低的开发调试成本(后面也会更详细说明)。

SPL 的轻量还表现在存储方式上。

相对数据库的封闭存储,SPL 直接采用文件存储数据。事实上,SPL 并不绑定存储,用户可以采用任何介质存储数据,但相对其他形式,文件具备很多不可比拟的优势。文件直接存放在文件系统上,本地或网络(云上)都可以。采用文件存储不必担心容量问题,存储很便宜,我们可以随便冗余数据,无非多几个文件而已。为了安全性进行备份也是一样的道理,文件存储几乎没有上限。

不过,很多开放格式的文件使用性能并不高,SPL 为此提供了专门的高性能文件格式。用户直接把源数据转存成 SPL 数据文件,使用过程中根据性能要求还可以随意进行数据冗余。

文件存储另外一个优点是可以灵活组织数据。有时我们把数据按照计算目标组织后往往可以使用不同的算法从而获得更高性能。相对数据库无法干预存储,文件要灵活很多,数据不仅可以冗余存多份,同一份还可以设计不同的组织形式(如按不同字段有序),以适应不同的计算场景。

文件在文件系统中采用多级目录进行管理,我们可以针对不同业务或不同模块设置不同的目录,某个目录及子目录专门为单一业务服务,彼此之前不存在任何耦合,数据修改不会影响其他业务。如果某个业务下线,该业务对应的数据(目录)可以放心删除,整体数据管理会十分清爽。

逐步迁移

有了文件存储和开放性的支持,我们就可以实施逐步迁移

前面说过,之所以想把 AP 业务独立出来是因为和 TP 混在一起数据库的压力太大,不过数据库虽然沉重但并不是不能用了,只要把压力降低原来的数据库还可以很好工作。而一次性全部迁移风险太大,最好采用逐步迁移的方式。逐步迁移不仅可以降低风险,还符合 SPL 的特点,初期先挑一些性能较低的统计查询场景迁移,这类业务会比较吃资源,迁移以后数据库减负效果明显。

更重要的是,使用 SPL 进行文件迁移不会出现后迁移的业务影响已迁移的情况,采用文件存储,文件之间不存在任何关联,增加文件也不会影响已有文件的使用。原来数据库在复杂封闭的体系内出现的问题,在 SPL 体系内完全不存在,可以放心进行迁移。

这样一点点迁移,风险很低,而且性能可控,没必要一股脑把所有 AP 业务一次性都迁移出来。

T+0 解决

SPL 具备很强的开放性,除了文件存储,SPL 还提供了多种数据源支持,任何数据源对于 SPL 是逻辑等同的。SPL 不仅可以对接多种数据源,还可以进行混合计算,具备了这个能力后就可以轻松实现 T+0 查询。

由于具备开放的计算能力,SPL 可以分别从不同的数据库取数计算,因此可以很好适应异构数据库的情况,冷数据在文件系统(AP 库),热数据在 TP 库,SPL 基于二者混合计算实现 T+0。在计算实现上,SPL 的敏捷语法与过程计算可以大大简化 T+0 查询中的复杂计算,提升开发效率,SPL 解释执行支持热部署。

具体实现上,使用 SPL 文件存储历史冷数据与生产库热数据混合查询:


A

1

=connect("oracle")

2

=A1.query@x("select sellerid, sum(amount) totalamount, count(amount) countamount,max(amount) maxamount,min(amount) minamount from sales group by sellerid")

3

=file(“his_sales.btx”).cursor@b()

4

=A3.groups(sellerid;sum(amount):totalamount,count(amount):countamount,max(amount):maxamount,min(amount):minamount)

5

=[A3,A4].conj().groups(sellerid;sum(totalamount):totalamount,sum(countamount):countamount,max(maxamount):maxamount,min(minamount):minamount)

通过简单几行脚本就能实现原来分库后很难实现的 T+0 查询,从而彻底解决分库后面临的所有问题。

目前从 TP 向 SPL 迁移已经有诸多实践,我们将常见的情况总结成实践例程: SPL 实践:向数据库外迁移计算任务 ,对照着例程就可以完成迁移工作。

更高性能

在工程上,SPL 也采用了当前专业 AP 数据库采用的工程手段,压缩、列存、索引、向量计算等,保证在工程方面做到足够优秀。前面说过,SPL 设计了支持这些机制的高性能文件存储,不需要封闭的数据库管理系统,文件存储可以直接分布在任何的文件系统之上,表现更加开放。

更重要的是,由于 SQL 的先天缺陷,SPL 没有再基于 SQL 体系,而是采用了独立的程序语言,即 Structured Process Language。在 SPL 中,提供了更多数据类型和运算,从根本上做了革新(要知道,理论上的缺陷很难在工程上弥补)。我们知道,软件改变不了硬件速度,但如果能使用更低复杂度的算法就可以让硬件少执行一些运算,这样性能就更高了。SPL 提供了大量这样的高性能算法,像前面提到的复杂多步有序运算用 SPL 就很容易实现,写得简单的同时跑得也快。

有了这些性能保障机制,SPL 需要的硬件资源就更少,因此能实现前面那些单机顶集群的效果。更进一步,SPL 也提供了多线程并行和分布式计算机制,具备很强的扩展性来进一步保障性能。

比如在这些案例中 SPL 都实现了单机替代集群的效果:
SPL 提速天体聚类任务 2000 倍
开源 SPL 将银行手机账户查询的预先关联变成实时关联
开源 SPL 提速保险公司团保明细单查询 2000+ 倍
开源 SPL 提升银行自助分析从 5 并发到 100 并发
开源 SPL 提速银行用户画像客群交集计算 200+ 倍

更低开发成本

SPL 由于没有采用 SQL 体系,在使用之前会有个学习成本。很多熟悉 SQL 的人可能会认为使用 SPL 的迁移成本会更高。

其实并不会,SPL 的长远开发成本反而更低!

SPL 不兼容 SQL,迁移 SQL 时确实需要改写,有一定的改造成本。但 SPL 很容易学,加上语法的简洁性,改造 SQL 的成本并不算很高。相比之下,AP 库虽然同样用 SQL,但数据库类型不同,从 TP 库向 AP 库迁移时也有不少 SQL 改造的工作,而且由于 AP 相关的计算逻辑普遍比较复杂,在改造过程中往往需要重写。加之 SQL 本身的开发调试并不容易,改造工作量也不低,远没有像厂商宣传的“无缝迁移”那么简单。综合来看,应用 SPL 的改造成本不会比 AP 库高出太多,是可以接受的。

更重要的是,SPL 会带来后续更长远的好处。

由于 SQL 的语言能力并不完善,有些复杂的数据处理很难独立完成。比如股票连涨问题以及更复杂一些的电商漏斗运算(这些并不是多奇怪的需求,业务中经常会碰到),用 SQL 实现就非常非常困难,通常要借助 Python 或 Java 来实施。这会带来技术栈的复杂,使用和运维都不方便,这个问题无论在 TP 还是 AP 亦或 HTAP 库中都存在。

相比之下,SPL 提供了更丰富的数据类型和完善的计算类库,相比 SQL 实现困难甚至实现不了的场景,使用 SPL 却很简单。

比如计算电商流失率的漏斗分析,用 SQL 写起来十分复杂,而且不同数据库的 SQL 写法差异很大,没有移植性改造成本也高。像 Oracle 实现三步漏斗分析是这样的:

with e1 as (
 select uid,1 as step1,min(etime) as t1
 from event
 where etime>= to_date('2021-01-10') and etime<to_date('2021-01-25')
 and eventtype='eventtype1' and …
 group by 1),
e2 as (
 select uid,1 as step2,min(e1.t1) as t1,min(e2.etime) as t2
 from event as e2
 inner join e1 on e2.uid = e1.uid
 where e2.etime>= to_date('2021-01-10') and e2.etime<to_date('2021-01-25')
 and e2.etime > t1 and e2.etime < t1 + 7
 and eventtype='eventtype2' and …
 group by 1),
e3 as (
 select uid,1 as step3,min(e2.t1) as t1,min(e3.etime) as t3
 from event as e3
 inner join e2 on e3.uid = e2.uid
 where e3.etime>= to_date('2021-01-10') and e3.etime<to_date('2021-01-25')
 and e3.etime > t2 and e3.etime < t1 + 7
 and eventtype='eventtype3' and …
 group by 1)
select
 sum(step1) as step1,
 sum(step2) as step2,
 sum(step3) as step3
from
 e1
 left join e2 on e1.uid = e2.uid
 left join e3 on e2.uid = e3.uid

SPL 实现:


A

1

=["etype1","etype2","etype3"]

2

=file("event.ctx").open()

3

=A2.cursor(id,etime,etype;etime>=date("2021-01-10") && etime<date("2021-01-25") && A1.contain(etype) && …)

4

=A3.group(uid).(~.sort(etime))

5

=A4.new(~.select@1(etype==A1(1)):first,~:all).select(first)

6

=A5.(A1.(t=if(#==1,t1=first.etime,if(t,all.select@1(etype==A1.~ && etime>t && etime<t1+7).etime, null))))

7

=A6.groups(;count(~(1)):STEP1,count(~(2)):STEP2,count(~(3)):STEP3)

明显要更简洁。事实上 SPL 的写法也更有通用性(多步漏斗增加参数就行,而不必像 SQL 增加子查询),实际运行性能也更高。

除了常规的结构化计算类库,SPL 还提供了 SQL 支持不好的有序计算,以及保留分组集合的分组运算,还有不同的关联方式等等。另外,SPL 语法还设计了独特的选项语法、层次参数以及增强的 Lambda 语法,使得复杂计算实现更为简单。

语法简洁、能力完善带来的直接结果是开发高效,不需要再借助其他技术让技术栈更为简单,在一个体系内就能完成所有事情,使用和运维自然简单方便。所以说,对于移植 AP 类业务,尤其是碰到复杂一点的计算,用 SPL 大概率会比 SQL 更高效、开发成本更低。

TP 数据库太撑就要减负,但减负没必要一股脑替换,只要有效降低压力目标就可以达成。逐步迁移 AP 类业务是个好办法,但数据库在完成这个任务时总会存在这样那样的问题,迁移成本高、管理困难、T+0 问题等等。而 SPL 的开放性和高性能可以很好解决这些问题,还能附带提供完善、轻量、高性能的计算能力,以及一致的技术栈。由此观之,用 SPL 来替换 AP 库实现 TP 库的减负不失为一个明智的选择。