屏幕截图2014-05-30下午1:37.33

智能工程博客

发现RDS吞吐量限制

邮寄人弗勒德

在以下网站上找到我:

2021年2月12日下午1:31:39

我们最近将平台的搜索功能与主应用程序分离。搜索功能需要对某些专门系统(如Elasticsearch)有深入的了解,其他希望提供文本搜索的服务无法访问,并且与应用程序的其他部分有很大不同的吞吐量需求。

我们决定将索引和搜索功能作为单独的服务运行。索引使用Kafka事件流中的数据更改,RESTAPI支持对该数据执行文本搜索。

我们对性能和可伸缩性做了一些调整,包括一个新的Postgres数据库来存储Elasticsearch数据的副本。如果Elasticsearch出现问题,数据库副本可以提供更快的恢复,简化管理任务(如调整索引的碎片数),并允许我们跨实体对数据进行非规范化,即使这些实体由不同的服务拥有。

在生产运行

在移动了大部分客户流量之后,我们开始注意到明显的减速从Kafka事件流索引更改所用的时间。该过程首先从Kafka读取事件,然后将事件数据应用到Postgres数据库中的相应模型,最后将更新后的模型索引到Elasticsearch中。

缓慢时期的指标表明Postgres响应时间跳了10 -100x尽管存在正常事件和API流量。某物导致了Postgres响应时间的周期性峰值,我们需要深入研究。

从技术上重新解释这个问题:我们的数据库处理一个同质的查询工作负载,但一次明显减慢数小时,然后恢复并再次正常处理。

一些糟糕的问题

不一致的性能听起来不像是一个典型的低效查询问题。一个昂贵的查询可能会在偶发的缓慢过程中导致尾部延迟放大效应,或者在执行过程中消耗不成比例的资源,从而减慢其他查询的速度。我们所有的查询都应该有足够的索引,但我们不想因为依赖“应该”发生的事情而错过一个简单的修复。

我们选择了一些较慢的查询和其他具有高磁盘IO的查询。我们使用Postgres'EXPLAIN ANALYZE'在缓慢的时间段内捕获每个查询的痕迹,但没有任何提示导致缓慢。查询和执行计划看起来是相同的,但是执行时间在缓慢时期跳了100倍。

深入研究RDS性能

我们使用AmazonWebServices(AWS)来运行Salsify的几乎所有基础设施,因此我们有问题的Postgres数据库是在他们的关系数据库服务(RDS)上运行的。RDS极大地简化了在生产环境中运行数据库的任务,并减少了执行此操作所需的知识。这一好处的另一面是,当事情出错时,你可能没有那么多的背景

AWS建议的监测以下内容用于确定RDS的性能问题:

  • CPU或内存消耗高
  • 磁盘空间消耗
  • 网络流量
  • 数据库连接
  • IOPS度量

我们的CPU、RAM和磁盘空间消耗峰值约为其总容量的50%,这排除了它们作为限制因素的可能性。网络流量和数据库连接更接近我们实例大小的最大值,但从未攀升到超过~70%。最后,我们的读+写IOPS达到了实例大小限制的95%以内,并一直停留在这个水平上。太棒了!我们发现了一个在性能下降时具有明显行为的度量!现在我们需要确定它是否是a导致或者只是一个症状

我们决定提高数据库的IOPS限制。这是一个简单且相对便宜的改变,应该能告诉我们IOPS是否导致了缓慢。我们已经使用了预置的IOPS来提供一致的性能,因此我们并不惊讶于可能需要增加分配。几天之后,速度继续变慢,我们的数据库消耗的IOPS从未超过新IOPS限制的80%。不管是什么原因导致了速度放缓,可能也阻碍了我们认识到新的IOPS限制。

Postgres吸尘器和TOAST数据

Salsify在我们的整个平台上使用Postgres,因此我们熟悉它的吸尘功能。Postgres使用多版本并发控制(MVCC)来提供事务隔离。MVCC的副产品是任何事务都看不到的“死”元组或数据库行。Postgres的真空过程扫描数据库中的死元组,并标记可重用的空间。

一些配置选项控制真空过程何时(通过autovacuumer)以及如何运行。我们的数据库使用了一种配置,适用于写容量大的应用程序。编写繁重的工作负载会产生大量的死元组,因此更有效的方法是频繁地运行小型真空,这样它们就可以对合理数量的数据进行操作

工作量

自动真空度刻度系数

(%表大小)

autovacuum_vacuum_threshold

(元组数)

博士后违约

.2

50

寿命长的数据,每行写入数多

.01

50

短期数据,

插入然后删除行

0

5000

我们的监控显示,每当响应时间增加时,Postgres都会清空最大的表。真空也可以正常运行,但我们认为真空过程足够复杂,我们可能无法理解导致差异的细微差别。当我们手动终止进程时,我们设法捕获了一个真空进程,并且性能显著提高。

所以我们有一个线索:当减速发生时会出现真空。我们没想到吸尘会影响用户查询在我们的其他数据库中从未发现过类似的问题。但是这个数据库大量的大数据块,这是我们在其他地方没有处理过的。这些大数据块存储在TOAST存储器中。

当行的列值大于2 kB时,Postgres使用TOAST存储。TOAST存储基本上是另一个将该值分块的表,由原始表中的元组引用。我们最大的一张桌子有几亿行,其中许多都使用了TOAST存储。

真空统计数据表明,它正在进行小时用吸尘器吸我们最大的吐司桌. 我们不断更新该表中的数据,但它通常不会改变TOAST'ed值。Postgres更新通常会创建一个包含行数据当前版本的新元组。新值来自查询,未更改的值从给定行的最新元组复制。由于TOAST'ed值存储在另一个表中,因此只要TOAST'ed值没有更改,就只有TOAST引用在行更新中被重新写入。

我们甚至避免在创建时加载TOAST值知道它们不会改变,但这种自定义处理也阻止了我们的ORM(Rails的ActiveRecord)检测数据何时被删除事实上加载时已更改。Postgres的TOAST文档没有指出当重复写入相同的值时是否会创建新的TOAST行,因此我们编写了一个快速而脏的脚本来验证[Github gist]博士后在TOAST表中为现有值更新生成新行,因此发送未更改的TOAST值会产生大量不必要的工作。

通常,行更新会创建一个死元组,但由于TOAST值在TOAST表中分块,因此包含TOAST值的单行更新会在主表中生成一个死元组,然后在TOAST表中生成(value size/TOAST\u MAX\u CHUNK\u size)死元组。例如,包含10kB值且TOAST\u MAX\u CHUNK\u大小为2kB的更新会生成6个死元组。

我们的服务每小时处理数百万个事件,但这些事件并没有改变TOAST值。我们强迫博士后不必要地读写数以百万计的TOAST行,这会影响博士后的表现。我们在使用Postgres时发现了一个明显的问题能够解释缓慢,但是我们仍然不明白为什么吸尘会减慢用户查询的速度

我们调整了ORM的脏跟踪,忽略了TOAST 'ed值,并兴奋地将其推向生产。经济放缓继续发生,尽管频率有所降低,因此我们感到困惑。死掉的TOAST元组导致了放缓,但它们不是主要原因。是时候做更多的挖掘了。

EBS吞吐量

解包一些Postgres内部流程(如吸尘)并没有发现任何明显的问题,因此我们继续筛选RDS指标和文档。阅读更多关于IOPS的信息表明,我们应该看到消耗量接近我们极限的100%。但即使在我们的IOPS从20k增加到30k之后,数据库IOPS也从未超过19k。它在缓慢时期徘徊在15k左右,周期性峰值接近19k。我们没有接近3万

与IOPS奇怪之处同时出现的是RDS的磁盘队列深度度量。在线文章建议,磁盘队列深度通常只有在实例超过其IOPS限制时才会上升到0以上,但我们看到,在缓慢运行期间,队列深度为50-100,而IOPS限制为50-70%。

我们仔细研究了这个19k IOPS的无形上限,并在网上寻找解释。我们在一份报告中找到了答案AWS文档关于EBS优化实例,我们的RDS实例类用于存储。我们运行的是一个r5.2xlarge RDS实例,其IOPS限制为18750。这一限制解释了我们的数据库暂时面临的19k IOPS上限,但它并没有解释为什么我们的数据库在缓慢运行期间(15k IOPS)始终在该限制以下运行。

该文档还指出,我们的实例可能会“突发”到593.75 MB/s的吞吐量至少每24小时30分钟(可能更多,如下图所示),但最大保证吞吐量为287.5 MB/s,要低得多。带宽、吞吐量和IOPS限制列在EBS限制文档中,但我们只熟悉IOPS。虽然不确定其他人到底是什么意思,但我们转向了Cloudwatch。

r5.2x大型资源最大值

度规

破裂

基线

带宽(Mbps)

4,750

2,300

吞吐量(Mbps)

593.75

287.5

眼压

18750年

12,000

奇怪的是,Cloudwatch并没有直接报告这些指标。相反,它们被分解为读写组件。单独查看读和写吞吐量并没有发现任何模式。然而,堆叠指标显示我们遇到了一个明确的上限:

我们的读写吞吐量固定在213MB。缩小到最后24小时显示出更多的模式,显示出一个上限:

吞吐量在一夜之间飙升并徘徊在443MB左右,然后在当天剩余的时间里一直保持在213MB的上限。我们的最大基线213MB与上表不完全一致,因为我们的实例是在2019年12月3日之前启动的。AWS指示启动和停止旧实例以实现当前最大值。

我们联系了AWS,确认我们对指标的解释是正确的。我们的RDS实例正在达到其EBS吞吐量限制.我们计划在那天晚上升级到r5.4xlarge实例大小,因为这将使我们的最大基线吞吐量翻倍。

升级后的第二天,我们没有经历任何减速!我们已经运行这个实例大小近6个月了,现在开始看到偶尔的吞吐量峰值达到了极限。我们已经开始探索改变,以减少移动的数据量,但现在我们知道我们可以扩大数据库,为自己购买一些空间。

数据库性能

度规

之前

之后

改变

平均读取延迟

5ms

千分之一秒

快5倍

平均写入延迟

100毫秒

女士15

快6.67倍

平均吞吐量

213 MB

325 MB

66%以上

结论

消耗最大的IOPS、吞吐量或带宽可能会妨碍您最大限度地消耗其他资源。达到最大吞吐量使我们无法达到IOPS极限。因为我们只监视内存、CPU和IOPS,所以我们并不怀疑我们的速度减慢是由资源分配引起的。

IOPS、吞吐量和带宽应该是RDS监控的基本指标,类似于CPU和内存。它们是RDS(通过使用EBS)强加的硬限制,并且在意外时刻会抬起头来。

一般来说,你可以学习很多通过工具挖掘性能问题。虽然这个问题最终并没有涉及到一些内部的博士后设计决策,但我们对吸尘、MVCC和TOAST存储有了更深入的了解。有效地使用这些概念对于成功地扩展研究生规模至关重要。

话题:博士后,无线电数据系统,AWS

评论的论文

最近的职位

    Baidu