扩展我们的分析处理服务(Smartly.io):使用 Citus 对 PostgreSQL 数据库进行分片

本文涉及的产品
云原生数据库 PolarDB MySQL 版,通用型 2核4GB 50GB
云原生数据库 PolarDB PostgreSQL 版,标准版 2核4GB 50GB
简介: 扩展我们的分析处理服务(Smartly.io):使用 Citus 对 PostgreSQL 数据库进行分片

在线广告商正在根据绩效数据做出越来越多的决策。无论是选择要投资的受众或创意,还是启用广告活动预算的算法优化,决策越来越依赖于随时可用的数据。我们的开发团队构建了强大的工具来帮助我们的客户分析性能数据并做出更好的决策。

我们的解决方案由高度可定制的报告组成,包括由我们自己的极其灵活的查询语言提供支持的下钻表和图表。支持查询语言的数据服务处理数 TB 的数据。除了作为我们面向用户的分析工具的后端之外,它还为我们所有的自动优化功能和我们的一些内部 BI 系统提供支持。在这篇博文中,我将向您介绍我们如何通过对后端系统使用的数据库进行分片来解决扩展问题。


海量数据库等于扩展麻烦



我们的分析数据处理服务,称为 Distillery,使用 PostgreSQL 数据库。该服务将 JSON 格式的查询安全地转换为最终在数据库级别运行的 SQL 查询。大多数数据处理都发生在数据库中,因此 Distillery 后端主要将我们自己的查询语言转换为 SQL 查询。原始的 API 查询很复杂,这使得一些生成的 SQL 查询变得复杂,并使得它们对数据库级别的要求很高。因此,当我们在报告系统的开发过程中遇到扩展问题时,我们并不感到惊讶。


过去,我们垂直扩展了我们的主副本数据库架构,但后来很明显我们已经达到了这种方法的极限。我们的数据库在运行三年中积累了近 5TB 的数据,并且变得无法管理。大尺寸使得更新繁重的应用程序写入速度变慢,维护任务难以执行。最后,最大的问题是我们的数据中心无法提供更大的服务器。


解决方案:使用 Citus 分片 PostgreSQL 数据库



当垂直扩展失败时,我们不得不开始水平扩展我们的报告数据库。这意味着我们需要在多个数据库服务器之间拆分数据和处理。我们还必须缩小包含每个单独数据库实例中统计数据的庞大数据库表。


这种将数据库数据切片成更小单元的方法称为数据库分片。我们的团队决定使用 PostgreSQLCitus 插件来处理分片。这不是唯一的选择 — 我们考虑使用自定义应用程序级分片,但决定使用 Citus 插件,因为:


  • 我们有大量复杂的查询,需要同时使用多个不同的分片。Citus 插件自动处理这些复杂的查询并在分片之间分配处理。
  • 它还广泛支持我们运行复杂报告查询所需的 PostgreSQL 功能。
  • 该扩展使分片管理相对容易,因此我们不必花费太多精力来管理单独数据库实例中的分片表。


Citus 基于 coordinator(协调器)worker(工作器)PostgreSQL 数据库实例。worker 持有数据库表分片,coordinator 计划 SQL 查询,以便它们可以跨 worker 之间的多个分片表运行。这允许将大型表分布在多个服务器上,并分布到更小、更易于管理的数据库表中。写入较小的表更有效,因为数据库索引维护成本降低。此外,写入负载是并行化的,并在数据库实例之间共享。Citus 解决了我们最大的两个痛点:写入效率低下垂直扩展即将结束


Citus 的数据库分片带来了额外的好处,因为新架构加速了我们的报告查询。我们的一些查询命中了多个 worker 实例和分片,Citus 扩展可以对其进行优化以在不同的数据库实例中并行运行它们。由于较小的表索引和更多资源可用于在单独的 worker 中进行查询处理,因此仅针对单个 worker 分片的查询也会加快速度。


将大型数据库和复杂的报告查询迁移到这种类型的分片数据库架构中绝非易事。它涉及仔细的准备和计划,我们将在接下来进行研究。


迁移到新数据库



过去,我们通过旧的 PHP 单体运行报告查询。早在数据库扩展问题出现之前,我们就开始使用 Ruby on Rails 构建更新的报告后端。在决定只在新后端处理 SQL 查询迁移后,我们开始逐步淘汰旧后端。这使我们能够专门针对 Citus 优化新的报告查询。它使从应用程序级别的迁移更容易,因为我们只需迁移此服务即可与 Citus 分片 PostgreSQL 一起使用。


分片数据库对数据库模式有一定的要求。模式必须具有一个作为分片条件的值。分片逻辑使用此值来区分数据位于哪个分片上。在 Citus-PostgreSQL 中,分片是使用表主键控制的。此复合主键包含一个或多个列,其中第一个定义的列用作分片值:


ALTER TABLE ad_stats ADD PRIMARY KEY (account_id, ad_id, date);
SELECT create_distributed_table('ad_stats', 'account_id'); -- Defines sharding for Citus cluster


这里 account ID 列用作分片键,这意味着我们正在根据我们的客户帐户分配数据(单个客户也可以有多个帐户)。这意味着单个帐户的数据位于单个表分片中。我们必须确保所有主键都采用这种格式,并且表中包含帐户 ID 信息。我们还必须更改一些外键和唯一性约束,因为它们还必须包含分片列。幸运的是,所有这些更改都可以安全地应用于正在运行的生产数据库,而没有任何性能或数据完整性问题,尽管我们不得不进行一些更广泛的数据库索引重建。


第二步是让我们的报表后端生成的 SQL 查询与分片数据库兼容。首先,查询必须包含 SQL WHERE 子句中的分片值。这意味着,例如,过滤器必须采用以下形式


SELECT * FROM campaigns WHERE account_id = 'xxx' AND name = 'yyy'


如果我们没有 account_id 条件,Citus 分布式查询计划器将没有信息需要从哪个分片中找到相关行。从所有可能的分片中读取不会像从单个分片中读取那样有效。

此外,Citus 对您可以在分片表之间执行的 JOIN 类型有一定的限制。通常 JOIN 要求分片列出现在 JOIN 条件中。例如,这将不起作用:


SELECT *
FROM
  campaigns
  LEFT JOIN ads ON campaigns.id = ads.campaign_id
WHERE
  campaigns.account_id = 'xxx'


这将导致错误:

ERROR: cannot run outer join query if join is not on the partition column&

这意味着 SQL 外连接需要 Citus 无法从查询中确定的表分片之间的一对一匹配。因此,查询需要在 JOIN 条件中包含分片列,Citus 能够从中检测到 ads 表连接的范围在一个分片内:


SELECT *
FROM
  campaigns
  LEFT JOIN ads ON campaigns.account_id = ads.account_id -- Use sharding column
    AND campaigns.id = ads.campaign_id
WHERE
  campaigns.account_id = 'xxx'


我们进行了各种其他 SQL 查询优化,使 Citus 查询规划器能够有效地运行我们复杂的统计报告查询。例如,我们使用通用表表达式 (CTE) 组织查询,这允许 Citus 查询计划器为涉及同时读取多个分片的繁重查询选择最佳计划。这些针对多个帐户的查询也在 Citusworker 集群中高度并行化,从而提高数据处理效率。此外,我们还为 Citus 扩展做出了贡献,增加了对 PostgreSQL JSON(B) 聚合的支持,我们的报告查询将其用于某些数据预聚合步骤。您可以在 Github 中查看PR。

PR


运行中的新数据库系统



我们的数据库系统完全从单一主副本配置迁移到 coordinator + 4 个 worker 服务器,每个服务器都复制以实现高可用性。这意味着我们包含 5TB 数据的旧数据库被分割成一个集群,其中每个数据库服务器保存大约 1TB 数据。Citus 允许我们相当容易地添加更多的 worker 服务器,以便在公司继续发展时将其进一步分割。我们还可以将拥有大量统计数据的最苛刻的客户隔离到他们自己的数据库服务器上。


image.png


迁移前的数据库架构。


image.png


迁移后的数据库架构。


上图描绘了迁移前后的数据库架构。与之前拥有 2 台大型数据库服务器的状态相比,我们现在总共拥有 10 台数据库服务器。这些较小的数据库实例更易于管理,因为大多数数据存在于单独的数据库工作服务器中。协调器持有较少量的数据,例如一些元数据和对分片不敏感的数据。第二张图还显示了我们用来确保在一个数据库实例出现故障时快速恢复的数据库副本。这种从 primary master 服务器到副本服务器的故障转移由 pgpool 组件处理。副本还共享来自主服务器的一些读取负载。


最后,我们在数据处理方面要求最高的数据透视表报告查询从新数据库系统中获得了 2-10 倍的性能提升。此功能生成的数据库查询非常复杂,因为我们允许用户自由定义数据的分组过滤聚合方式。它还允许查询跨分片自由运行,因为用户可以定义任何帐户组合。Citus 分片数据库的好处真正体现在这些特定的查询中。数据库迁移非常必要,因为我们的旧数据库基础架构几乎被它生成的复杂查询所淹没。


image.png


该图显示了在数据库迁移项目期间,某些类型的查询获得性能提升的 90 个百分点的持续时间。

相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
相关文章
|
9天前
|
关系型数据库 分布式数据库 数据库
PostgreSQL+Citus分布式数据库
PostgreSQL+Citus分布式数据库
39 15
|
17天前
|
关系型数据库 MySQL Linux
Linux系统如何设置自启动服务在MySQL数据库启动后执行?
【10月更文挑战第25天】Linux系统如何设置自启动服务在MySQL数据库启动后执行?
63 3
|
17天前
|
关系型数据库 MySQL Linux
在 CentOS 7 中通过编译源码方式安装 MySQL 数据库的详细步骤,包括准备工作、下载源码、编译安装、配置 MySQL 服务、登录设置等。
本文介绍了在 CentOS 7 中通过编译源码方式安装 MySQL 数据库的详细步骤,包括准备工作、下载源码、编译安装、配置 MySQL 服务、登录设置等。同时,文章还对比了编译源码安装与使用 RPM 包安装的优缺点,帮助读者根据需求选择最合适的方法。通过具体案例,展示了编译源码安装的灵活性和定制性。
59 2
|
29天前
|
存储 NoSQL MongoDB
基于阿里云数据库MongoDB版,微财数科“又快又稳”服务超7000万客户
选择MongoDB主要基于其灵活的数据模型、高性能、高可用性、可扩展性、安全性和强大的分析能力。
|
1月前
|
SQL 关系型数据库 分布式数据库
Citus 简介,将 Postgres 转换为分布式数据库
【10月更文挑战第4天】Citus 简介,将 Postgres 转换为分布式数据库
83 4
|
19天前
|
SQL 关系型数据库 数据库连接
"Nacos 2.1.0版本数据库配置写入难题破解攻略:一步步教你排查连接、权限和配置问题,重启服务轻松解决!"
【10月更文挑战第23天】在使用Nacos 2.1.0版本时,可能会遇到无法将配置信息写入数据库的问题。本文将引导你逐步解决这一问题,包括检查数据库连接、用户权限、Nacos配置文件,并提供示例代码和详细步骤。通过这些方法,你可以有效解决配置写入失败的问题。
44 0
|
8天前
|
SQL 关系型数据库 MySQL
go语言数据库中mysql驱动安装
【11月更文挑战第2天】
22 4
|
6天前
|
SQL 关系型数据库 MySQL
12 PHP配置数据库MySQL
路老师分享了PHP操作MySQL数据库的方法,包括安装并连接MySQL服务器、选择数据库、执行SQL语句(如插入、更新、删除和查询),以及将结果集返回到数组。通过具体示例代码,详细介绍了每一步的操作流程,帮助读者快速入门PHP与MySQL的交互。
19 1
|
1月前
|
存储 关系型数据库 MySQL
Mysql(4)—数据库索引
数据库索引是用于提高数据检索效率的数据结构,类似于书籍中的索引。它允许用户快速找到数据,而无需扫描整个表。MySQL中的索引可以显著提升查询速度,使数据库操作更加高效。索引的发展经历了从无索引、简单索引到B-树、哈希索引、位图索引、全文索引等多个阶段。
61 3
Mysql(4)—数据库索引
|
15天前
|
监控 关系型数据库 MySQL
数据库优化:MySQL索引策略与查询性能调优实战
【10月更文挑战第27天】本文深入探讨了MySQL的索引策略和查询性能调优技巧。通过介绍B-Tree索引、哈希索引和全文索引等不同类型,以及如何创建和维护索引,结合实战案例分析查询执行计划,帮助读者掌握提升查询性能的方法。定期优化索引和调整查询语句是提高数据库性能的关键。
78 1