有必要选用专业的内存数据库吗?
在报表、BI、跑批等数据分析业务中,性能不足时经常会想到内存数据库。所谓内存数据库是指全量数据永驻内存,计算时不需要从外存(硬盘)读取,避免磁盘 IO,可以有效提升数据处理性能。
内存数据库被宣传的特点是性能高,可以解决很多数据分析业务性能不达标的问题。内存数据库之所以快,除了前面提到的没有磁盘 IO 成本,数据全内存后还可以采用一些专门的内存优化技术。比如,由于内存支持随机访问,具备很强的并行能力,以及使用预加载、预索引等来提升计算性能等。
不过,这些技术并不是内存数据库独有的,因为所有计算事实上都是在内存中进行的,毕竟 CPU 只能计算内存的数据,外存的数据也要先读入内存再处理。作为通用内存优化技术,很多数据库和计算引擎也会采用,把内存优化技术做好了,性能也就跟着上来了。从这个意义上来讲,只要内存足够大能装下全量数据,所有数据库都可以说是内存数据库。现在所谓的 “专业内存数据库”的说法,其实往往是因为这些数据库只优化了全内存计算,超出内存(涉及外存)的计算就做不好甚至做不了(外存计算难度大很多),厂商为了突出优点掩盖缺点,就把这种数据库称为内存数据库,用以跟普通数据库区分。事实上,专业内存数据库的能力反而是减弱了。
所以说,并没有什么专业的内存数据库,只有专门的内存数据技术。我们不必纠结于某个产品是不是在喊内存数据库,重要的是考察该产品内存技术是否过硬,即测试比较大内存下的计算性能。
搞清楚内存数据库是怎么回事以后,下面再进一步讨论内存技术与计算性能。
我们知道,采用 SQL 语言的关系数据库仍然是数据库的主流,无论是不是所谓的内存数据库都是如此。不过,遗憾的是,SQL 其实并不能充分利用内存的特性,性能还有很大提升空间!虽然压缩、列存、索引、并行以及向量计算等优化技术基于 SQL 都能很好实施利用,但还有些对进一步提升计算性能大有裨益的技术在 SQL 体系下并不好做。这主要是由 SQL 本身的限制决定的。
SQL 缺乏必要的数据类型来表示记录,SELECT 的结果(记录)是一个与原表无关的新数据表,即使结果只有一条。这会导致计算时大量复制数据(开辟新的内存空间),无论是在空间占用,还是时间开销都会变大。如果有显式的记录类型,我们就可以直接使用记录来构成数据表,记录中存储的是原表数据的内存地址,这样在计算过程中就不会频繁 / 大量复制数据,有效复用内存,性能就会更高。
在进行多表关联计算时,如果能事先将事实表的外键转换成维表记录的内存地址(预关联),那在使用时就可以不必再关联,从而获得与单表查询同样高的性能。遗憾的是,SQL 不能利用这种特性。
SQL 对有序运算的支持也不好,SQL 是基于无序集合设计的,集合成员没有序号的概念,更没有提供定位计算以及相邻引用的机制,难以利用有序的特点实施更高效的算法。比如,如果数据在内存中有序,查找时就可以使用二分法来提速,对于数据规模较大的场景性能提升会很明显。与之相近的序号定位则是另一种更高效的手段,借助内存的高速随机访问特性,可以快速从内存中按指定序号取出数据。这些 SQL 都很难实现。
充分利用内存还表现在数据结构描述方面,关系模型(SQL)依靠二维表来描述关系,更复杂的数据结构(如多层 JSON 格式)则很难描述和使用,而这些复杂数据结构非常适合内存存储,在空间利用和使用效率上都有优势。事实上,不支持复杂的数据结构就不能实现真正的分组,SQL 要求分组必须聚合,而有时我们关心的是分组成员(如每个分组的),SQL 要达到这个目标就要借助子查询先造分组序号才能实现,表达繁琐,重复查询,效率也低。
SQL 无法有效利用内存是理论导致的,SQL(关系模型)诞生的年代内存还很小,不能很好适应当今的大内存时代也可以理解。当然,现代数据库在工程上做了很多优化,可以从一定程度上改善上面的状况,但情况稍复杂一些优化引擎可能就不管用了,毕竟理论的限制很难通过工程来弥补。优化引擎的好坏及适应范围要进行认真测试评估才能确认,但一般的测试都限定在 SQL 易于描述的范围内,并不涉及这些复杂内容,所以导致选型很容易失败,风险很大。
SPL 的出现解决了这些问题。
SPL(Structured Process Language)是一个开源计算引擎,主要面向结构化和半结构化数据计算。为了彻底解决上面提到那些问题,SPL 没有再延用 SQL 的关系代数理论,而是基于全新的模型进行了重新设计。SPL 同时支持内外存两种计算方式,特别提供了针对内存的专门优化,在大内存环境下可以享受甚至超越内存数据库的计算性能。在数据量超出内存容量时还可以进行外存(分批加载到内存)计算,在某些计算场景下可以获得与全内存接近的计算性能,让小内存也可以实现高性能计算。更加与众不同的是,SPL 还支持与应用集成,可以嵌入应用内部提供高效计算能力。
对于内存数据库采用的一些成熟的工程优化策略,SPL 同样也提供。像前面的提到的列式向量化计算、预加载、预索引等技术,这些通用的工程手段当然很有意义。但只有这些还不够,这只能做到跟内存数据库一样的性能,但如何更好发挥内存的特性才是 SPL 的优势所在。
前面说过,有些复杂的数据结构和计算可以利用内存和计算的特性进行更高性能的计算,而 SQL 的限制导致内存数据库无法发挥这样的优势。
但 SPL 可以。
与 SQL 在 SELECT 时会复制数据导致数据变大、效率变低不同,SPL 在选出时只会保留原记录的引用,即内存地址,并不会真地复制数据,无论是占用空间还是效率上都很有优势。能实现这样的效果是因为 SPL 有专门的记录类型,记录中保存的就是原数据的内存地址(引用),而 SQL 没有记录类型,单记录实际上是只有一行的数据表,不同数据表中的记录也不能共享,过滤时会复制出新记录来构成新数据表,空间和时间成本都不理想。
我们知道 CPU 是通过地址读取内存中的数据,如果能事先保存某些数据的地址再访问会十分高效。比如在关联计算中,如果将外键表(维表)记录的地址都保存在事实表中,再使用两表数据时就不需要再关联(HASH 计算),这样就可以获得与访问单表类似的性能。这种预关联的方式在数据重复使用以及多表关联(维表较多)时优势十分明显。SPL 提供了这种机制,允许一次性将事实表和多个维表进行预关联(外键字段保存维表记录的内存地址)并保存在内存中,直接使用内存地址来加速运算。更多参考:外键地址化
针对 SQL 的无序集合导致很多高性能算法无法使用,SPL 直接提供了有序集合,可以充分利用有序来发挥优势。比如在十分复杂的天体对比计算场景,如果事先利用有序下的二分法进行初步筛选就可以大幅降低计算量,为后续计算提供基础。但 SQL 无法描述这样的计算,也就没法利用有序这个特性了,因此实际使用中会比 SPL 慢 3 个数量级不止。详细参考: SPL 计算性能系列测试:位置关联
SPL 还可以充分利用序号进行高效访问。在查找任务中,如果被查找键的取值正好是目标值在序表中的序号,或者很容易通过查找值计算出目标值的序号,就可以使用序号定位的方法避免比对从而在常数时间内完成计算。
更进一步,序号的使用还可以应用到大事实表(外存计算)的关联计算。事实表大到内存装不下时就不能再使用前面提到的地址化方式,这时可以将事实表的外键转换对应维表记录的位置,进行序号化。序号化以后的事实表再与维表关联时就可以采用效率更高的序号定位方法进行查找,无需再进行比对,这样可以获得与内存地址化相近的关联性能。在 SPL 中,序号使用是一种重要的性能优化手段。
所以我们可以看到,SPL 在设计时会综合考虑各种情况,并非单一只针对内存或外存,二者有机集合才能很好解决各类场景面对的问题。更多参考:外键序号化
有时计算目标有明确的顺序要求,比如在证券业务中的连涨连跌计算就需要相邻的数据进行比对。SQL 支持较好的数据库可以使用窗口函数完成这类计算,但这种迂回的方式即使实现某些看似简单的计算时也很难表达,运算效率更低。
比如我们要计算某只股票最长的连涨天数,即使有窗口函数 SQL 也要嵌套 3 层才能写出来,最后的执行效率也很低(数据库优化策略在复杂场景下会失效)。但支持有序计算(有序分组)的 SPL 表达起来很简单:
stock.sort(date).group@i(price>price[-1]).max(~.len())
通过相对位置就能引用前后记录。
在计算电商用户流式率的计算中,SPL 有序计算的效果表现得更加突出,详情参考: SPL 计算性能系列测试:漏斗分析
SPL 的充分利用内存,还表现在描述多层 JSON 格式这类的复杂数据结构方面。SPL 直接支持这种多层数据结构,在泛型的支持下,SPL 允许集合作为序列的成员,集合的成员还可以是集合,这样就可以天然描述 JSON/XML 这种多层数据结构。有了这个特性后,在进行分组计算时还可以保留分组子集(集合的集合),以便针对各个分组进行再次计算,这样可以有效避免 SQL 式的迂回嵌套方式,性能更高。
SPL 提供了这些充分利用内存的手段以后,就可以获得超越内存数据库的计算性能,同时在计算表达上也更简洁。
那么,有了 SPL 后,专业的内存数据库还有多大必要呢?
确实没什么必要了,虽然不喊内存数据库的概念,但事实上 SPL 已经拥有了更强于内存数据库的内存计算技术。
不仅如此,SPL 还提供了超出内存容量的外存计算,从而覆盖更多的场景,将内外存有效结合发挥更大的价值,像前面提到的漏斗计算就是内外存结合的典范。宣称专业内存数据库的产品往往不具备外存计算能力,这会大大限制使用场景,SPL 则具有更加宽泛的使用范围。
SPL 还可以作为嵌入式计算引擎与应用结合使用,通过 jar 包的方式与应用集成到一起,跟随应用一起部署,在应用内部直接提供内存数据库式的高性能计算,同时也支持内外存结合的使用方式。
有了高效的内存计算能力,SPL 就可以完全替代专业内存数据库使用。再加上外存计算和灵活的集成性,SPL 还拥有更宽泛的适用范围。所以,我们在考察高性能计算时,不必拘泥于是否叫内存数据库,只要内存计算技术过硬,还能适应更广泛的应用场景,就是好产品,SPL 就是个理想的选择。
英文版