【问题标题】:Possible bottlenecks when inserting and updating BYTEA rows?插入和更新 BYTEA 行时可能出现瓶颈?
【发布时间】:2013-01-25 20:41:24
【问题描述】:

该项目需要将二进制数据存储到 PostgreSQL(项目要求)数据库中。为此,我们制作了一个包含以下列的表格:

id   : integer, primary key, generated by client
data : bytea, for storing client binary data

客户端是一个 C++ 程序,在 Linux 上运行。 必须插入行(用一大块二进制数据初始化),然后更新(将额外的二进制数据连接到数据字段)。 简单的测试表明,这会产生更好的性能。

根据您的输入,我们将让客户端使用并发线程来插入/更新数据(具有不同的数据库连接),或者只有一个数据库连接的单线程。

我们在 PostgreSQL 方面没有太多经验,所以您能否帮助我们提供一些关于可能存在瓶颈的指针,以及使用多线程插入数据是否比使用单线程更好。

谢谢你:)

编辑 1:

更多详细信息:

  • 只有一个客户端访问数据库,只使用一个 Linux 进程
  • 数据库和客户端在同一个高性能服务器上,但这一定没关系,无论机器如何,客户端都必须快速,无需额外的客户端配置
  • 我们将每 10 秒获取新的数据流,流将每 0.5 秒提供新的 16000 字节(CBR,但我们可以使用缓冲,并且最多只能每 4 秒插入一次)
  • 流将持续 10 秒到 5 分钟之间的任何时间

【问题讨论】:

  • 完成一行后,bytea 字段的平均大小和最大大小是多少?
  • @DanielVérité: max: 300s * 32000 b/s = 9.6mb, min: 10s * 32000 b/s = 320kb, 平均: 大约 3mb

标签: database postgresql concurrency insert locking


【解决方案1】:

如果您使用的是bytea,那么插入一行然后附加到该行就应该获得更好的性能,这几乎没有意义。

PostgreSQL 的 MVCC 设计意味着 UPDATE 在逻辑上等同于 DELETEINSERT。当您插入行然后更新它时,发生的情况是您插入的原始元组被标记为已删除,并且 new tuple 被写入,其中包含旧数据和添加数据的连接。

我质疑您的测试方法 - 您能否更详细地解释一下您如何确定 insert-then-append 更快?没有意义。

除此之外,我认为这个问题写得太宽泛了,无法真正说出很多用处。你没有提供任何细节或数字;没有估计二进制数据大小、行数估计、客户端数估计等。

bytea 插入性能与 PostgreSQL 中的任何其他插入性能调整没有什么不同。所有相同的建议都适用:批量处理事务,使用多个并发会话(但不要太多;经验法则是 number_of_cpus + number_of_hard_drives)插入数据,避免事务使用彼此的数据,所以你不需要UPDATE如果您没有带有安全回写缓存的磁盘子系统(如电池供电的 RAID 控制器等),请使用异步提交和/或 commit_delay。

鉴于您在主 cmets 线程中提供的更新统计信息,您想要使用的数据量听起来完全符合适当的硬件和应用程序设计。如果您必须提交每个进入的块,即使在普通硬盘驱动器上也可以实现峰值负载,因为它每秒需要大约 60 个事务。您可以使用commit_delay 来实现组提交并显着降低 fsync() 开销,或者甚至使用synchronous_commit = off,如果您可以承受在崩溃的情况下丢失事务的时间窗口。

使用回写式缓存存储设备,如电池支持的缓存 RAID 控制器或具有可靠断电安全缓存的 SSD,这种负载应该很容易处理。

我没有针对不同的场景进行基准测试,所以我只能笼统地说。如果我自己设计这个,我会担心 PostgreSQL 的检查点停顿,并且想确保我可以缓冲一些数据。听起来你可以,所以你应该没问题。

这是我测试、基准测试和负载测试的第一种方法,因为在我看来它可能是最实用的:

每个数据流一个连接,synchronous_commit = off + 一个commit_delay

INSERT 每条 16kb 记录进入临时表(如果可能,UNLOGGEDTEMPORARY,如果你能承受丢失不完整的记录)并让 Pg 同步和分组提交。当每个流结束时,读取字节数组,将它们连接起来,然后将记录写入最终表。

为了获得这种方法的绝对最佳速度,请为 bytea 实现一个 bytea_agg 聚合函数作为扩展模块(并将其提交给 PostgreSQL 以包含在未来版本中)。实际上,您很可能可以通过读取数据来避免在应用程序中进行 bytea 连接,或者使用效率相当低且非线性的缩放:

CREATE AGGREGATE bytea_agg(bytea) (SFUNC=byteacat,STYPE=bytea);

INSERT INTO final_table SELECT stream_id, bytea_agg(data_block) FROM temp_stream_table;

您需要确保调整检查点行为,如果您使用普通或 UNLOGGED 表而不是 TEMPORARY 表来累积这些 16kb 记录,则需要确保它是相当积极VACUUMed。

另见:

【讨论】:

  • 但是bytea 列的更新不符合 HOT 更新的条件,因为它没有被索引?所以它不会导致 "delete/insert" 。也不确定TOAST。完全“烘烤”的 bytea 值是否会在更新时导致元组的删除/插入?
  • 为问题添加了附加信息,将于周一上传代码示例。
  • @a_horse_with_no_name HOT 更新实际上仍然是轻量级的插入+删除。有一个新的元组和一个用 xmax 标记的旧元组。与 HOT 的区别在于 如果页面上有空闲空间可以写入新的元组,并且更新没有触及索引列,则可以将新元组写入堆并避免更新索引。新的元组仍然被写入,它不是覆盖,并且只有在有足够的空间来添加新旧元组时才会这样做。 TOAST 通过离线存储数据使事情进一步复杂化。
  • @omittones 你说的是消耗大量数据。如果每 10 秒有一个新流进入,并且可以持续 10 秒到 5 分钟,则最多有 30 个并发流(300 秒持续时间/10 秒启动间隔),每个流每秒传输 32 兆字节的数据。这相当于 960 每秒兆字节的数据。那是巨大的。有没有可能我误解了你的解释?假设平均流持续时间为 2.5 分钟,则平均数据速率为半峰值速率,即每小时 1.7 PB。
  • @Craig:问题是每 0.5 秒 16000 字节,而不是 16 兆字节
猜你喜欢
  • 1970-01-01
  • 2015-12-30
  • 2021-02-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多