我们在 Sublight 上用 PostgreSQL 写入:1 台主机,1 天,1TB

最近我告诉你如何使用标准食谱 提高 SQL 读取查询的性能 来自 PostgreSQL 数据库。今天我们就来说说如何 录音可以更有效地进行 在数据库中,无需在配置中使用任何“扭曲” - 只需正确组织数据流即可。

我们在 Sublight 上用 PostgreSQL 写入:1 台主机,1 天,1TB

#1.切片

一篇关于如何以及为何值得组织的文章 “理论上”应用分区 已经有了,这里我们将讨论在我们的范围内应用一些方法的实践 数百台 PostgreSQL 服务器的监控服务.

“那些年的事情……”

最初,和任何 MVP 一样,我们的项目在相当轻的负载下开始 - 只对最关键的十台服务器进行监控,所有表都相对紧凑......但随着时间的推移,监控的主机数量越来越多,我们再次尝试用其中之一做某事 表大小为 1.5TB,我们意识到虽然可以继续这样生活,但是很不方便。

那个时代几乎就像史诗般的时代,不同版本的 PostgreSQL 9.x 是相关的,因此所有分区都必须“手动”完成 - 通过 表继承和触发器 动态路由 EXECUTE.

我们在 Sublight 上用 PostgreSQL 写入:1 台主机,1 天,1TB
事实证明,最终的解决方案足够通用,可以转换为所有表:

  • 声明了一个空的“header”父表,它描述了所有 必要的索引和触发器.
  • 从客户端的角度来看的记录是在“根”表中进行的,并且内部使用 路由触发 BEFORE INSERT 该记录被“物理”插入到所需的部分。如果还没有这样的事情,我们就会捕获一个异常并且......
  • … 通过使用 CREATE TABLE ... (LIKE ... INCLUDING ...) 根据父表的模板创建 对所需日期有限制的部分这样当检索数据时,仅在其中执行读取。

PG10:第一次尝试

但从历史上看,通过继承进行分区并不适合处理活动写入流或大量子分区。例如,您可以回忆一下用于选择所需部分的算法 二次复杂度,它适用于 100 多个部分,您自己了解如何...

在PG10中,通过实施支持大大优化了这种情况 本机分区。因此,我们在迁移存储后立即尝试立即应用,但是......

仔细阅读手册后发现,该版本中的本机分区表是:

  • 不支持索引描述
  • 不支持触发器
  • 不能成为任何人的“后代”
  • 不支持 INSERT ... ON CONFLICT
  • 无法自动生成节

额头被耙子狠狠地敲了一下,我们意识到如果不修改应用程序就不可能做到这一点,因此将进一步的研究推迟了六个月。

PG10:第二次机会

于是,我们开始一一解决出现的问题:

  1. 因为触发器和 ON CONFLICT 我们发现我们仍然到处需​​要它们,所以我们做了一个中间阶段来解决它们 代理表.
  2. 摆脱“路由” 在触发器中 - 也就是说,从 EXECUTE.
  3. 他们分别拿出来 包含所有索引的模板表因此它们甚至不存在于代理表中。

我们在 Sublight 上用 PostgreSQL 写入:1 台主机,1 天,1TB
最后,在这一切之后,我们对主表进行了原生分区。新部分的创建仍然取决于应用程序的良心。

“锯”字典

与任何分析系统一样,我们也有 “事实”与“删减” (词典)。在我们的案例中,他们以此身份行事,例如, 模板体 类似的慢查询或查询本身的文本。

“事实”已经按天划分很长时间了,所以我们平静地删除了过时的部分,它们并没有打扰我们(日志!)。但是字典有问题...

并不是说有很多,但大约 100TB 的“事实”生成了 2.5TB 的字典。您无法方便地从这样的表中删除任何内容,也无法在足够的时间内压缩它,并且写入它的速度逐渐变慢。

就像一本字典......其中,每个条目应该只出现一次......这是正确的,但是!......没有人阻止我们拥有 每天都有一本单独的词典!是的,这带来了一定的冗余,但它允许:

  • 写/读速度更快 由于截面尺寸较小
  • 消耗更少的内存 通过使用更紧凑的索引
  • 存储较少的数据 由于能够快速删除过时的

由于采取了一系列复杂的措施 CPU 负载降低约 30%,磁盘负载降低约 50%:

我们在 Sublight 上用 PostgreSQL 写入:1 台主机,1 天,1TB
与此同时,我们继续将完全相同的内容写入数据库,只是负载较少。

#2.数据库演化与重构

所以我们决定了我们所拥有的 每天都有自己的部分 与数据。实际上, CHECK (dt = '2018-10-12'::date) — 并且有一个分区键和一条记录落入特定部分的条件。

由于我们服务中的所有报告都是在特定日期的背景下构建的,因此自“非分区时间”以来它们的索引都是所有类型 (服务器, 日期,计划模板), (服务器, 日期,计划节点), (日期,错误类别,服务器),...

但现在他们住在每个区域 你的副本 每个这样的索引......以及在每个部分中 日期是一个常数...事实证明,现在我们在每个这样的索引中 只需输入一个常数 作为字段之一,这会增加其数量和搜索时间,但不会带来任何结果。他们把耙子留给了自己,哎呀......

我们在 Sublight 上用 PostgreSQL 写入:1 台主机,1 天,1TB
优化的方向很明显——简单 从所有索引中删除日期字段 在分区表上。考虑到我们的数量,收益约为 1TB/周!

现在让我们注意,这个太字节仍然必须以某种方式记录。也就是说,我们也 磁盘现在应该加载更少!这张图清楚地显示了我们花了一周时间进行清洁所获得的效果:

我们在 Sublight 上用 PostgreSQL 写入:1 台主机,1 天,1TB

#3。 “分散”高峰负荷

负载系统的一大麻烦是 冗余同步 一些不需要它的操作。有时“因为他们没有注意到”,有时“这样更容易”,但迟早你必须摆脱它。

让我们放大上一张图片,看看我们有一个磁盘 双振幅负载下的“泵” 相邻样本之间的差异,显然“统计上”不应发生如此数量的操作:

我们在 Sublight 上用 PostgreSQL 写入:1 台主机,1 天,1TB

这是很容易实现的。我们已经开始监控 近1000台服务器,每个都由一个单独的逻辑线程处理,每个线程都会以一定的频率重置要发送到数据库的累积信息,如下所示:

setInterval(sendToDB, interval)

这里的问题恰恰在于: 所有线程几乎同时启动,因此他们的发送时间几乎总是“恰到好处”地一致。哎呀#2...

幸运的是,这很容易解决, 添加“随机”助跑 按时间:

setInterval(sendToDB, interval * (1 + 0.1 * (Math.random() - 0.5)))

#4。我们缓存我们需要的东西

第三个传统的高负载问题是 无缓存 他在哪 可能 是的。

例如,我们可以根据计划节点进行分析(所有这些 Seq Scan on users),但立即认为它们在很大程度上是相同的 - 他们忘记了。

不,当然,没有任何内容再次写入数据库,这会切断触发器 INSERT ... ON CONFLICT DO NOTHING。但这些数据仍然到达数据库,而且没有必要 阅读以检查冲突 得做。哎呀#3...

启用缓存之前/之后发送到数据库的记录数差异很明显:

我们在 Sublight 上用 PostgreSQL 写入:1 台主机,1 天,1TB

这是随之而来的存储负载下降:

我们在 Sublight 上用 PostgreSQL 写入:1 台主机,1 天,1TB

在总

“每天太字节”听起来很可怕。如果你做的一切都是正确的,那么这只是 2^40 字节/86400 秒 = ~12.5MB/s甚至桌面 IDE 螺丝也能固定。 🙂

但说实话,即使白天的负载“倾斜”十倍,您也可以轻松满足现代 SSD 的功能。

我们在 Sublight 上用 PostgreSQL 写入:1 台主机,1 天,1TB

来源: habr.com

添加评论