一招解决数据库中报表查询慢的痛点

作者:日休
一、背景
常见的 CRM, ERP 等信息管理系统都基于数据库构建。它们都是常见的 TP 系统,强调一致性、高并发的在线事务处理(OLTP)系统。这类系统里面有日常的数据增删改查的事务需求,也有周期性的报表需求。TP 数据库如 PostgreSQL 在解决事务型需求时性能卓越,但在处理复杂报表的分析型需求时则表现欠佳,难以高效支持多维度聚合、大规模表关联等操作。传统方案需通过 ETL 将数据迁移至 ClickHouse、Snowflake 等专用 OLAP 系统,导致架构割裂、运维成本陡增。
为了提升 TP 系统的分析性能,RDS PosgreSQL 引入 DuckDB,推出了 rds_duckdb 加速引擎。该引擎利用 PostgreSQL 扩展插件接口,深度集成 DuckDB,实现了一体化的实时事务处理和实时数据分析的能力,一站式满足业务的 OLTP 及 OLAP 需求。通过使用 rds_duckdb 可以获得与原生 DuckDB 几乎相同的性能,TPCH 性能有 10-100 倍的提升。我们可以将 RDS PostgreSQL 中的本地表、视图、物化视图等导出为列存表,同时支持行存到列存数据的自动同步。启用分析型查询加速功能后,查询会被在 duckdb 中执行,非常适合实时报表等复杂分析场景。
二、什么是 DuckDB?
DuckDB【1】【2】是一款面向嵌入式分析场景的高性能开源列式关系型数据库,专注于复杂 SQL 查询的加速与大规模数据分析(OLAP)。该项目由荷兰国家数学与计算机科学研究院(CWI)的 Mark Raasveldt 和 Hannes Mühleisen 主导开发,于 2019 年正式开源,凭借其卓越的 AP 性能与轻量级设计,迅速成为数据科学和边缘计算领域的热门选择,月下载量超百万次。
其核心优势在于以单机进程内(in-process)架构高效处理百列级宽表及十亿行级数据量的复杂分析,例如多表关联聚合、窗口函数计算等场景。与 SQLite 等传统嵌入式数据库的 OLTP 定位不同,DuckDB 深度优化列式存储、向量化执行引擎及并行计算,特别适配 Python/R 生态的数据分析、BI 工具嵌入式加速等场景。截至 2024 年,其 GitHub Star 数已突破 24.7k,社区贡献者超 500 人,版本迭代速度与性能优化持续领跑开源 OLAP 领域。
三、DuckDB 为什么这么快?
DuckDB 从优化器,执行器,操作符,和存储等各个环节都利用了业界最新的技术,并凭借强大的工程能力,实现了单机复杂查询的极致性能,超过了同类分析型数据库。这里仅对 DuckDB 的优化点做一个简单的概述,具体的实现细节可以参考引用文档。
3.1 优化器
在很少甚至没有统计信息的情况下,连接顺序优化器(join order optimzier)【3】通过减少连接过程中处理的中间元组数量,显著提升了多表 join 查询性能。具体优化点如下:
通过减少多表连接时的中间结果规模,提升复杂查询的执行效率;
在 Join Order 枚举过程中,采用高效的搜索策略,平衡探索空间与计算开销,避免传统动态规划方法的高复杂度问题;
轻量化统计依赖:结合轻量级信息生成优化策略,降低对传统统计信息的依赖,适应实时分析场景。
3.2 执行器
列向量化执行(columnar-vectorized query execution engine)
DuckDB 采用列向量化执行提升查询的性能。

[4]
向量化查询执行指的是数据库引擎中的一种方法,它通过批量处理数据而不是逐行处理数据来增强查询性能。这种方法通过利用现代 CPU 架构及其执行单指令、多数据 (SIMD) 操作的能力,提高了 CPU 的数据处理效率。
向量化查询执行通过将称为向量的数据块加载到 CPU 缓存中,并在这些数据上执行批量操作来运行。其主要特点包括:
批量处理:以大块数据处理,减少与处理单个数据点相关的开销。
单指令多数据 (SIMD) 优化:通过同时对多个数据点执行相同的操作来最大限度地提高效率。
列式读写:仅处理查询相关的列,而不是整个数据集,从而简化操作。
基于推送的执行(push based execution)
DuckDB 采用推送的执行模型提高并行度从而提升查询的性能。
基于推送的处理是一种查询处理模型,数据从底层的操作符推送到上层的操作符,整体执行的控制流是自底向上。

[5]
数据其中系统中的每个操作符自行决定是否并行执行,而不是依赖于集中执行器。DuckDB 采用这一模型,是因为原来的基于拉取(pull based execution)的向量化查询处理在添加额外操作符时遇到了挑战。基于推送的处理模型允许更灵活和高效地同时执行多个管道,提高了系统处理复杂并行性和高效操作的能力。
拉取模型往往需要在计划生成阶段就决定好并行度,DuckDB 的推送模型实现了 Morsel-Driven Parallelism:
查询被划分为多个 pipeline,即执行计划可以划分为多个部分并行执行;
每个操作符自行决定是否并行执行,同时操作符间的并行互相能感知到。
下图是一个三张表 join 的例子,左边为关系代数表达式,右边为并行化执行的过程。

[6]
推送模型还开辟了额外优化和更细粒度控制系统的可能性。其中包括使用向量缓存(Vector Cache)在操作符之间缓冲结果,直到填满向量。此外,扫描共享(Scan Sharing)涉及在有向无环图(DAG)计划中将一个子操作符的结果推送给多个父操作符。在中央位置存储状态还支持反压(Backpressure)/异步 IO(Async IO),即在缓冲区满时或等待远程 IO 时暂停操作符执行。这种细粒度控制使得在数据库系统内能够进行更高效和优化的查询处理。
3.3 操作符(operator)优化
DuckDB 针对执行器中的核心算子(包括 sort,aggregationg 等等)做了大量优化,以此来提升查询性能。
sort【7】【8】
DuckDB 针对列存排序做了多项优化,包括利用了索引避免排序,内存与磁盘排序的切换,并行化排序,和延迟物化等等技术手段,实现了极致的排序性能。

hash aggregation【9】【10】
针对 hash aggregation 操作符,采用分区和并行化的优化,同时考虑内存和磁盘的交换情况,提升执行性能。

四、RDS DuckDB 架构 &性能

从以上架构图可以看到:
数据同步:PG 中全量行存数据导入 rds_duckdb 插件中,转换为列存数据,并开启增量数据同步。其中增量同步基于 PG 原生逻辑复制实现。
查询处理:分析查询路由到 rds_duckdb 插件中,经过向量化并行执行算子产生查询结果,然后经过类型转换返回给客户端。
4.1 核心优势
HTAP 性能优异:PG 在 OLTP 方面本身具备良好性能,同时 DuckDB 在 OLAP 上性能优势明显。
语法高度兼容:由于 DuckDB 语法解析使用了 PG 的语法解析器,因此 rds_duckdb 插件实现了对 PG 语法的高度兼容。
4.2 适用场景
特别适合对于业务数据已经在 RDS PG 中,对分析数据的时效性有要求的场景。
离线分析:定时数据批量导入,离线报表业务
实时分析:实时数据分析需求的场景,如实时报表。
4.3 性能对比
测试数据量:TPCH 100X。测试过程可以参考 rds_duckdb 官网文档中的 TPC-H 示例。
rds_duckdb 对比 RDS PG

在开启 rds_duckdb AP 加速后,相较于 RDS PG,查询性能有了极大幅度的提升,下图展示了 TPC-H 的 22 条查询语句执行时间对比,rds_duckdb 基本都在 3s 内完成,其中 Q2、Q15、Q17、Q18、Q20、Q21 在 RDS PG 中执行超过 10min(测试中设置单 SQL 超时时间为 10min)。
rds_duckdb 对比 clickhouse

图中共 16 条 SQL,Q8、Q9 因为机器内存不足运行有问题,Q19 超过 10min 未运行出结果,Q20~Q22 目前并不支持(CK TPC-H 官网文档)
4.4 压缩对比

下图对比了 TPC-H 100X 测试数据中一些表,分别在 rds_duckdb、RDS PG 以及开源版本 ClickHouse 中的大小。其中 rds_duckdb 导出的列存表具有最高的压缩比,可以有效节省空间。
4.5 总结
通过上面的性能和压缩数据,可以看到 rds_duckdb 的特点:
性能优异:在复杂的查询中加速效果明显,甚至可以达到百倍、千倍。
资源友好:相较于 CK 可以使用更少的内存完成查询。
压缩比高:导出列存文件有很高的压缩比,有效节省空间。
抢先体验,参见 RDS PG 官网文档:AP 加速引擎(rds_duckdb)
更多咨询,可以加入钉钉 RDS PG 插件交流群了解:103525002795 ;
参考链接
DuckDB: an Embeddable Analytical Database (SIGMOD 2019 Demo)https://duckdb.org/pdf/SIGMOD2019-demo-duckdb.pdf
Why DuckDB
Join Order Optimization with (Almost) No Statistics (Master thesis, 2022)https://blobs.duckdb.org/papers/tom-ebergen-msc-thesis-join-order-optimization-with-almost-no-statistics.pdf
In-Process Analytical Data Management with DuckDBhttps://www.infoq.com/articles/analytical-data-management-duckdb/
Push versus pull-based loop fusion in query engineshttps://www.cambridge.org/core/services/aop-cambridge-core/content/view/D67AE4899E87F4B5102F859B0FC02045/S0956796818000102a.pdf/push_versus_pullbased_loop_fusion_in_query_engines.pdf
Paper Reading: Morsel-Driven Parallelismhttps://frankma.me/posts/papers/morsel-driven-parallelism-numa-aware-query-evaluation/
These Rows Are Made for Sorting and That's Just What We'll Do (ICDE 2023):https://duckdb.org/pdf/ICDE2023-kuiper-muehleisen-sorting.pdf
Fastest Table Sort in the West – Redesigning DuckDB’s Sort:https://duckdb.org/2021/08/27/external-sorting.html
Robust External Hash Aggregation in the Solid State Age (ICDE 2024):https://duckdb.org/pdf/ICDE2024-kuiper-boncz-muehleisen-out-of-core.pdf
Parallel Grouped Aggregation in DuckDB:https://duckdb.org/2022/03/07/aggregate-hashtable.html
评论