客户画像分析的预计算几时休
客户画像在当前商业分析中很时髦。说白了也简单,就是给客户打上各种标签,再用这些标签定义出不同的客群分类(所谓画像),然后统计各类客群的成员数量(以及变化情况)。
逻辑上看,标签也就是维度,或者说是数据表上的字段。标签还是取值比较简单的字段,一般有两类:二值标签,也就是只有两种取值,一般用 0/1 表示,比如婚否、性别等。另一种是枚举标签,取值范围大概在几到几百之间,都可以小整数表示,比如学历、年龄段。这两种标签也可以互相转换。
画像统计也没什么特别的,就是个标准的多维分析任务,用客群(画像)对应的标签条件过滤(WHERE)后再做聚合(主要是 COUNT),有时还会有分组。
但是,当数据量特别大时(客户数特别多,这是常态,否则也不用做分析了),这个统计可能变成很慢,以致于经常不能实时计算出来。需要提前调研出各类画像标准(对应的标签条件),针对性地事先做些预计算后,才能供业务人员查询。
本来,业务经理们可能随时设计出一种画像标准来做统计,感觉有问题时马上调整画像标准再统计,这种探索式的分析才有可能得出有意义的结果。但必须预计算时,这一切就失去了意义。
那么,为什么算不快呢?按说这种简单的运算已经被业界玩得很纯熟了。
这主要有三方面原因。
标签的数量特别多,尤其是二值标签,可能会有几百个甚至几千个,还在不断增加。这个数量经常会超过数据库表的最大字段数(通常只有 256 或 512 个),在技术上就不能直接设计成表的字段,要采取变通手段。
一般有两种办法:一是设计成多个以客户 ID 为主键的表,每个表存储一部分标签,在运算时再 JOIN 起来;另一种办法是做列转行,把标签编号后,把数据结构设计成{客户 ID, 标签编号, 标签值 },计算时会涉及 GROUP BY 和 HAVING 或者 COUNT(DISTINCT)
这两种办法的性能都不好。JOIN 是数据库的老大难问题,GROUP BY 和 COUNT(DISTINCT) 也是。面对数量巨大的客户 ID,数据库的计算方法都要占用大量内存,这容易导致崩掉,要么动用缓存多次遍历,性能就会急剧下降。还有个办法是用字符串来表示所有标签取值,取值为形如“标签 1= 值 1; 标签 2= 值 2;…”的字符串,这样灵活是灵活了,但读取和解析字符串都非常耗时,计算性能经常还不如 JOIN 和 GROUP,只是一般不会跑崩罢了。
枚举标签的过滤条件通常会是一个集合 IN 运算。如果不加优化,数据库判断 IN 需要比对 N 次(IN 子集的长度),复杂度是 O(N)。即便先排序后用二分法对比,也仍然要比对 logN 次,复杂度是 O(logN)。而且二分法本身还有些固定开销,虽然复杂度降下来了,但在 IN 子集较小(画像分析中常见情况)起到的优化作用并不明显。
最关键的原因在于,画像分析时常常会针对多种画像标准同时统计。每种画像标准对应不同的过滤条件(WHERE)和分组规则(GROUP),在数据库中就要执行一条 SQL 语句,将数据表遍历一次计算出结果,有多种画像标准时就会有多条 SQL 语句,数据表就会被遍历多次。一个典型的画像分析界面上有可能同时出现几十个甚至上百个画像指标,这意味着巨大的数据表要被遍历几十次甚至上百次,这就无论如何也不可能实时计算出来了,只能预计算了。
如果这三方面都有较好的优化手段,画像分析就可以跑得快了。遗憾的是,大部分关系数据库都做不到。第一个因素是工程性的,本来应该可以处理,但由于数据库的封闭性要求统一的元数据管理,字段太多会导致元数据异常复杂,所以大部分数据库也拒绝支持;第二个因素是半理论半工程的,SQL 只能用 IN 来描述枚举值标签过滤条件,这要求数据库优化引擎识别出来并采取能避免集合成员比对的计算方案,这也是大部分数据库没想到过的。第三个因素则是理论性的,单句 SQL 无法返回多种汇总结果,优化引擎也不能假定多句 SQL 之间有什么关联,可以说所有数据库都只能被迫遍历多次了,所以前面说这是最关键的因素。
esProc SPL 都可以!
esProc SPL 严格地说并不是一个数据库,而是个专业的计算引擎。它提供了列存格式文件,并基于此提供了不依赖于数据库的计算能力,可以完全替代数据仓库的计算功能。和传统数据库不同,esProc SPL 没有再采用关系代数和 SQL,而是自创了离散数据集理论并发明了新的程序语言 SPL,这样可以不受 SQL 的限制,写出更丰富的优化逻辑。
esProc SPL 的列存文件的列数容量要大很多(可以多到上千列),它也没有元数据的概念,不会因为某个文件(表)的列数太多而导致整体管理的沉重,可以天然解决上述的因素一。而且即使由于标签太多导致列数太多而要采取分表方案,esProc SPL 针对这种情况也有高性能的有序归并算法,多表关联导致的性能损失非常小,速度和单表遍历区别不大,基本上只取决于涉及的标签数量。
特别地,对于二值标签,esProc SPL 还支持按位存储,即用一个二进制位来存储一个标签,把多个标签组合成一个 16 或 32 位以及 64 位整数,这样可以大幅减少表中列的数量以及存储容量(和常规存储方式相比能减少 16 倍 -64 倍)。在画像标准中涉及的二值标签很多时。还能有效地减少数据读取量和计算量,即一个 16 位整数中存储了多个需要计算条件的二值标签,可以只读取和计算一次 【性能优化】8.5 [多维分析] 标签位维度 。
现在也有些数据库能支持位操作,但 SQL 语法写起来还比较繁琐。esProc SPL 提供了虚表对象,可以将组合二值标签的运算透明化,程序员可以继续操作单个的标签字段,实际上会被 SPL 转换成 16 位整数的某些位 SPL 虚表的数据类型优化 。
对于枚举标签上的条件,esProc SPL 提供了布尔维序列的方法,可以将 IN 计算转换成数组取值,复杂度直接降成 O(1),即计算时间和 IN 子集的长度无关 【性能优化】8.4 [多维分析] 布尔维序列 。
对于多指标统计,esProc SPL 还有遍历复用的语法,可以在一次遍历时计算出多个统计值 【性能优化】4.2 [遍历技术] 遍历复用 ,这是解决画像分析性能问题的关键。
这里有一个基于 TPCH 100G 数据生成的宽表上的多指标计算测试(时间单位为秒,完整测试报告: SPL 计算性能系列测试:多指标统计 ) :
4C16G |
8C32G |
|||||
统计指标数 |
1 |
2 |
3 |
1 |
2 |
3 |
ClickHouse 宽表 |
77.4 |
156.0 |
249.6 |
34.7 |
69.0 |
106.4 |
Starrocks 宽表 |
135.7 |
253.6 |
402.6 |
62.2 |
104.6 |
156.2 |
esProc SPL 宽表 |
114.2 |
119.5 |
124.1 |
57.7 |
61.6 |
64.6 |
esProc SPL 关联 |
100.5 |
49.5 |
可以看出,只统计一个指标时,esProc SPL 的性能还赶不上 ClickHouse。但统计多个指标时就会反超,指标越多优势就越明显。面对画像分析时动辄几十上百个指标时,esProc SPL 的遍历复用机制将会表现出碾压式的优势。
实践中,esProc SPL 的表现也很优异,这有两个使用 SPL 解决预计算的案例:开源 SPL 提速银行用户画像客群交集计算 200+ 倍 ,用前述的二进制位表示二值维度,和布尔维序列优化枚举标签条件,性能比 MPP 数据库提升了 200 倍以上。开源 SPL 优化银行预计算固定查询成实时灵活查询 ,进一步使用了遍历复用技术,实时计算出同一页面的近 200 个指标,完全取消了预计算,改变了原有的业务形态。
esProc SPL 还有很多工程上的优势:
esProc 是个纯 Java 软件,能在任何有 JVM 的环境下运算,可以无缝地嵌入到 Java 程序中,非常轻量地将数据仓库的运算能力赋予给各种场景下的应用中。
esProc 提供了可视的开发环境,支持单步执行、设置断点、所见即所得的结果预览,开发调试要比 SQL 和存储过程方便得多。
SPL 还有完善的流程控制语句,像 for 循环,if 分支都不在话下,还支持子程序调用,拥有存储过程才有的过程化能力,可以全面取代 SQL 和存储过程。
…
最后,esProc SPL 是开源免费的。在这里https://github.com/SPLWare/esProc。
英文版