当 PostgreSQL 面临性能瓶颈:80TB 电商业务迁移至 Apache Doris 的实践思考 在与客户交流过程中我们曾遇到一家电商企业其分析数据规模已达到80TB。为了支撑不同业务场景团队分别部署了仪表盘、商品搜索和向量检索三套独立系统。随着业务发展系统间的数据同步、运维和故障排查成本持续增加维护多套系统逐渐成为新的挑战。最终该团队基于 Apache Doris 完成了相关能力的统一建设在满足业务需求的同时也显著降低了系统复杂度。类似的情况在企业数字化建设过程中并不少见。当分析业务不断演进、系统持续增加时团队往往需要重新评估现有架构是否仍然适合业务发展。在着手重构之前更值得思考的是当前面临的问题是否已经达到需要引入新技术栈、投入额外资源解决的程度。如果答案是肯定的希望本文能够为相关技术选型和架构演进提供一些参考。架构设计有一个重要原则技术栈的复杂度应与团队的运维能力和业务规模保持匹配。每引入一套新的系统都意味着额外的部署、监控、运维、故障排查以及人才投入成本。因此在满足业务需求的前提下构建适配当前发展阶段的最小可行基础设施Minimum Viable InfrastructureMVI通常是更具长期价值的选择。如何判断 PostgreSQL 是否面临性能瓶颈扩展性问题通常不会突然出现而是随着业务规模和数据量增长逐步显现。随着分析负载不断增加团队往往会优先采用成本较低、改动较小的优化手段例如增加索引、表分区、反规范化设计、物化视图、只读副本以及 Citus 等分片方案。这些措施通常能够在一定程度上缓解当前问题但随着优化手段不断叠加系统架构也会逐渐复杂后续维护和扩展成本随之增加。实践中很多团队最终会发现真正的瓶颈并不在于缺少更多优化手段而是在于 PostgreSQL 主要面向单机架构设计对于持续增长的分布式分析负载其扩展能力存在一定局限。当然在引入新的技术方案之前更重要的是确认当前是否已经出现了典型的分析型性能瓶颈。可参考下表自查同时建议在 PostgreSQL 的只读副本上执行以下查询用于分析当前表扫描情况SELECT schemaname, relname, seq_scan, seq_tup_read, idx_scan, idx_tup_fetch, CASE WHEN seq_scan 0 THEN seq_tup_read / seq_scan ELSE 0 END AS avg_rows_per_seq_scan FROM pg_stat_user_tables WHERE seq_scan 100 AND seq_tup_read / GREATEST(seq_scan, 1) 100000 ORDER BY seq_tup_read DESC LIMIT 10;如果查询结果显示部分表在顺序扫描时平均需要读取超过10 万行数据通常意味着分析型查询已经大量依赖全表扫描。对于这类场景列式存储数据库通常能够获得数量级的性能提升因为其存储和执行模型针对大规模扫描进行了专门优化。很多团队在发现这一现象后第一反应往往是继续增加 PostgreSQL 只读副本希望通过横向扩展缓解压力。为什么只读副本无法解决这一问题而只读副本能够提升系统的查询并发能力但通常无法显著改善单条分析查询的执行性能。这种差异本质上源于 OLTP 数据库与 OLAP 数据库在架构设计上的不同差异总览如下。行存储与列存储的对比PostgreSQL 采用行存储Row Store架构其设计目标是高效读取完整记录因此非常适合事务处理场景。例如当一个包含 50 个字段的数据表仅需要查询其中 2 个字段用于报表分析时PostgreSQL 仍然需要从磁盘读取完整的数据行再丢弃未使用的字段这意味着实际读取的数据量远高于查询所需的数据量。列式数据库则采用完全不同的存储方式每一列独立存储仅加载查询涉及的列因此能够显著减少分析查询的 I/O 开销。此外列存储通常具有更高的数据压缩率。由于同一列中的数据类型一致、数据分布更加集中列式数据库通常能够实现510 倍的数据压缩而传统行存储的压缩率通常约为23 倍。近年来Citus Columnar、Mooncake 等 PostgreSQL 扩展也引入了列存储能力用于改善分析场景下的查询性能。但这些能力仍然建立在 PostgreSQL 行存储架构之上而不是数据库的默认存储模型。逐行执行与向量化执行的区别除了存储方式之外查询执行模型也是影响分析性能的重要因素。PostgreSQL 采用逐行执行Row-based Execution模式每一行数据都需要独立完成表达式计算、条件过滤以及聚合等操作。现代分析型数据库普遍采用向量化执行Vectorized Execution一次处理数千条数据并利用 CPU SIMD 指令集批量完成表达式计算。对于千万级数据扫描任务向量化执行能够显著减少函数调用和 CPU 开销在实际分析场景中通常能够带来数倍甚至数量级的性能提升。单节点规划与分布式规划的差异PostgreSQL 的查询优化器主要针对单机架构设计一条 SQL 查询只能在单个实例上执行。即使部署了多个只读副本也无法将同一条查询拆分到多个副本上并行计算。因此增加只读副本能够提升的是同时处理更多查询的能力而不是提升单条查询的执行速度。分析型数据库则采用分布式查询规划器能够自动将一条大型查询拆分到多个计算节点并行执行。例如对于一条需要扫描1TB数据的查询如果集群拥有10个计算节点那么每个节点可以并行处理约100GB的数据最终由协调节点汇总计算结果。在数据分布均衡的情况下随着节点数量增加单条查询的执行时间也能够相应缩短。综上所述单纯增加只读副本通常只能提升查询并发能力难以解决单条大规模分析查询的执行效率问题。当分析负载逐渐成为系统的主要压力来源时引入专门面向分析场景设计的数据库通常是更合适的选择。不过实践中我们也发现不少团队在数据库选型时过于关注基准测试成绩而忽略了业务实际依赖的数据能力。迁移完成后才发现新系统无法覆盖 PostgreSQL 开箱即用提供的一些能力。明确业务所依赖的 PostgreSQL 能力虽然 PostgreSQL 通常被定位为 OLTP 数据库但在实际业务中它往往也承担了部分实时分析任务。数据库系统通常可以分为两类一类面向 OLTP强调高并发事务处理和低延迟响应另一类面向离线分析强调大规模数据处理和分析吞吐能力。而越来越多企业需要的是介于两者之间的实时分析能力即支持实时数据写入并能够针对最新数据进行低延迟分析。正因如此很多团队虽然将 PostgreSQL 作为业务数据库使用但实际上已经依赖了它提供的一系列实时分析能力。亚秒级数据新鲜度 PostgreSQL 主从复制延迟通常控制在秒级以内BI 仪表盘和运营后台展示的通常是实时数据而非 10 分钟之前的历史数据。实时更新当业务数据发生更新时例如订单状态由待处理变为已发货变更能够立即同步至分析查询无需等待批处理任务或下一轮 ETL。支持事务的轻量 ETL执行INSERT INTO summary_table SELECT ... FROM orders GROUP BY ...语句要么完全执行成功要么完全执行失败。部分写入不会导致聚合数据损坏。多表关联对于订单、客户、商品、物流等业务数据开发人员通常只需编写一条 SQL查询优化器即可完成关联规划无需额外考虑数据关联方式。全文检索借助tsvector和tsqueryPostgreSQL 可以支持日志、商品描述、用户评论等文本数据的全文检索无需额外部署独立搜索系统。向量检索借助pgvector可以运行语义搜索和 AI 驱动的功能。向量数据能够与关系型数据统一存储和管理无需引入独立的向量数据库。不少团队直到完成迁移后才意识到部分业务已经建立在这些能力之上。因此如果前期缺乏充分评估迁移过程中往往需要调整数据模型甚至重构数据处理流程从而增加迁移成本和项目周期。不同分析型数据库在查询性能、实时更新、事务支持、复杂关联等方面各有侧重。在数据库选型过程中要明确业务真正依赖哪些 PostgreSQL 能力并评估目标系统是否能够满足这些需求。为什么选择 Apache Doris基于上述能力要求下表对几类主流方案进行对比。该表用于说明不同产品类型的典型设计侧重具体能力可能因产品版本、部署模式和使用方式而有所差异。从这个角度来看不同类型数据库都有各自擅长的场景。而当业务既要求亚秒级数据新鲜度又依赖实时更新、事务以及复杂关联查询时单独采用上述两类方案往往都存在一定局限。Apache Doris 是一款能够同时兼顾实时分析和丰富数据能力的统一分析平台。Apache Doris 的设计目标之一就是在提供高性能实时分析能力的同时尽可能保留企业已经习惯使用的数据能力降低数据库迁移和架构演进成本。对于前文提到的电商团队迁移至 Apache Doris 后原有多套分析系统得以统一整体架构和运维链路随之简化。与此同时在 PostgreSQL 中需要大量人工规划的部分能力例如数据分区、分布式扩展、存储管理以及高可用机制也能够通过 Apache Doris 的原生分布式架构统一实现。当然Apache Doris 也并非适用于所有场景。首先作为一款分布式分析型数据库Apache Doris 相比单机数据库具有更高的运维复杂度。如果团队此前没有分布式数据库的运维经验需要预留一定的学习和实践成本包括集群部署、资源规划、性能调优以及日常运维等工作。对于希望兼顾实时分析能力和托管体验的团队也可以考虑使用 SelectDB等托管版 Apache Doris 服务。其次对于数据规模较小、分析负载有限的业务场景PostgreSQL 配合索引优化和只读副本通常已经能够满足需求。此时引入分布式分析数据库反而可能增加系统复杂度和运维成本。推荐的迁移架构对于大多数企业而言更推荐采用 OLTP 与 OLAP 分层的架构。┌─────────────────┐ ┌─────────────────┐ │ PostgreSQL │ CDC │ Apache Doris │ │ OLTP │ ────── │ OLAP │ └─────────────────┘ └─────────────────┘ │ │ 业务事务写入 BI 分析 在线业务 即席查询 全文检索 向量检索在这一架构中PostgreSQL 继续承担事务处理和业务写入Apache Doris 负责分析型查询两者通过 CDC 保持数据实时同步实现业务与分析解耦。对于希望快速完成 PostgreSQL 分析负载迁移的团队Doris 内置 PostgreSQL CDC 提供了更加简化的一站式同步方案。用户可以直接通过 SQL 创建持续同步任务将 PostgreSQL 中的全量数据及后续 WAL 变更同步至 Doris无需额外部署 Kafka、Flink 或 Spark 等外部组件。渐进式架构演进路径那么随着数据规模增长架构如何演进不同阶段对数据库能力的要求并不相同建议根据业务规模逐步演进而不是一次性引入复杂架构。低于 10 TB推荐 PostgreSQL1 套系统这一阶段PostgreSQL 配合索引优化、分区以及只读副本通常已经能够满足业务需求无需额外部署分析系统。10 TB 至 1 PBPostgreSQL Apache Doris两套系统PostgreSQL 负责事务处理Apache Doris 统一承担分析型工作负载包括 BI 仪表盘、即席分析、全文检索、向量检索以及轻量级 ETL。对于大多数企业而言两套系统即可覆盖绝大多数业务需求。超过 1 PBPostgreSQL Apache Doris 批处理层共 3 个系统随着数据规模进一步增长实时分析与离线计算通常会逐渐分层Apache Doris负责实时分析、仪表盘、交互式查询以及低延迟数据服务。Spark、Snowflake、Databricks等平台负责大规模 ETL、历史数据重处理以及复杂离线计算。此时引入第三套系统是为了满足新的业务需求而不是过早增加架构复杂度。总结PostgreSQL 仍然是一款优秀的 OLTP 数据库。对于分析负载有限的业务通过索引优化、分区以及只读副本通常已经能够满足需求没有必要过早引入新的技术栈。当数据规模和分析需求持续增长仅依靠 PostgreSQL 难以兼顾事务处理与实时分析时采用PostgreSQL Apache Doris的 OLTP 与 OLAP 分层架构能够在保留现有业务系统的基础上构建面向实时分析的数据平台。数据库迁移并不是简单地替换一款产品。相比关注基准测试中的性能数字更重要的是结合真实业务负载验证目标系统是否能够满足实时更新、多表关联、全文检索、向量检索等核心需求。更多关于 Apache Doris 的产品能力、部署实践和最佳实践可参考 Apache Doris 官方文档并参与社区交流如果希望进一步体验云原生托管版本也可以了解 SelectDB 提供的相关服务。