作者:Falco
译者:魏波,中国PG分会培训认证执行总监、资深数据库工程师,十多年的数据库运维管理及培训经验,掌握PostgreSQL架构部署、性能优化等,致力于推动PostgreSQL在中国的发展。
2021年4月2日
原文链接:
https://blog.discourse.org/2021/04/standing-on-the-shoulders-of-a-giant-elephant/
当Discourse项目开始于2013年时,团队不得不在少数工具中进行选择,然后将这些工具变成我们的“堆栈”,这是构建Discourse的基础软件。某些选择在早期被证明不是最佳选择,但是我们能够快速进行迁移,例如,从CoffeeScript到Javascript的迁移。
事实证明,我们的大多数选择都是不错的选择,最好选择是将PostgreSQL作为数据库。为了说明我们对它的满意程度,让我们谈谈最喜欢的PostgreSQL最新版本的功能:B-Tree Deduplication。
当然,Discourse首先是100%开源软件,而我们首先是一家托管公司。自2014年我们开始提供商业托管服务以来,我们的托管服务已发展为拥有超过4亿的页面浏览量,每月存储超过400万个新帖子。
所有这些数据都存储在PostgreSQL实例中,所以你可以想象,当PostgreSQL 13的发布说明中提到“索引和查找系统的显著改进有利于大型数据库,包括索引的空间节省和性能提高”时,我们非常感兴趣。它甚至让我们考虑打破跳过PostgreSQL奇数版本而仅每两年升级一次的传统。为了做出明智的决定,我们必须进行基准测试。
为了评估新的B-Tree重复数据删除特性是否会以任何方式使Discourse受益,我们决定检查它是否会对我们主机中大多数Discourse实例中最大的表posts_timings表产生影响。这个表存储了每个用户在每个帖子中的读取时间,定义为:
discourse=# \d post_timings Table "public.post_timings" Column | Type | Collation | Nullable | Default -------------+---------+-----------+----------+--------- topic_id | integer | | not null | post_number | integer | | not null | user_id | integer | | not null | msecs | integer | | not null | Indexes: "index_post_timings_on_user_id" btree (user_id) "post_timings_summary" btree (topic_id, post_number) "post_timings_unique" UNIQUE, btree (topic_id, post_number, user_id)
我们还在研究是否可以删除post_timings_summary索引,因为它是其中最左列的子集post_timings_unique,这意味着它有可能被重用。
在我们托管的特定实例中,该表最近刚超过10亿行,因此我们使用此行数进行测试。另外,由于在实时系统中该表会不断接收更新,因为MVCC机制,我们最终可能会产生相当多的“膨胀”,从而使我们的分析变得不正确。因此,为了在干净的环境中进行比较,我们使用了PG 12 和 13 的全新安装。加载每个版本后,数字如下:
表大小
PostgreSQL 12: 114 GB
PostgreSQL 13: 85 GB
表大小减少25%,棒极了!
深入研究细节,我们有:
PostgreSQL 12
Table: 42 GB
Index: 72 GB
PostgreSQL 13
Table: 42 GB
Index: 43 GB
如发行说明中所述,优化仅适用于索引,我们可以在此处重新生成它。表的大小仍然相同,但是索引大小几乎是一半。
如果我们进一步增强它:
PostgreSQL 12 relation | size --------------------------------------+------------ public.post_timings | 42 GB public.post_timings_unique | 30 GB public.index_post_timings_on_user_id | 21 GB public.post_timings_summary | 21 GB PostgreSQL 13 relation | size --------------------------------------+------------ public.post_timings | 42 GB public.post_timings_unique | 30 GB public.post_timings_summary | 6939 MB public.index_post_timings_on_user_id | 6766 MB
再次,正如预期的那样,UNIQUE根据定义,重复项为0的索引的大小没有变化,但是具有重复值的索引被优化为原始大小的三分之一。
不仅索引大小变化,而且性能也变化。根据有关该主题的PostgreSQL文档:
这大大减少了每个值(或每个列值的不同组合)平均出现几次的索引的存储大小;
查询的延迟可以大大减少;
总体查询吞吐量会显著增加;
例行索引清理的开销也可以大大减少。
他们还增加了一个警告,即对于没有重复的繁重写工作负载,将产生小的固定性能损失。这不是我们的情况,但是如果是这样,可以通过在应用程序中的完全异步代码路径中编写代码来减轻这种情况:这是客户端中的后台请求,而Rails应用程序中的非阻塞路由利用了Rack Hijack.
因此,预言是正确的:PostgreSQL 13为Discourse带来了重大改进!
这很重要,因为在这里,我们在一个数据库中的一个表中看到了这种效果,在该数据库中,数据库架构包含数十个表。而且,我们托管了数千个Discourse实例,每个实例都有多个PostgreSQL实例以实现高可用性,因此收益是原来的许多倍。
正如我们在Discourse Gives Back 2017中所说的那样,Discourse一直是一个100%开放源代码项目,它建立在许多其他开源项目数十年努力工作的基础上才得以生存。随着我们的成长,我们很高兴能够直接为我们最依赖的项目提供资金。因此,去年我们又向PostgreSQL基金会捐款,我们的目标是每年都这样做。
如果想更多了解B-Tree Deduplication相关知识,推荐阅读:
https://www.highgo.ca/2020/07/06/features-in-pg13-deduplication-in-b-tree-indexes/