【问题标题】:Optimise PostgreSQL for fast testing优化 PostgreSQL 以实现快速测试
【发布时间】:2012-03-13 12:39:22
【问题描述】:

我正在为一个典型的 Rails 应用程序从 SQLite 切换到 PostgreSQL。

问题是使用 PG 运行规范变得很慢。
在 SQLite 上大约需要 34 秒,在 PG 上大约需要 76 秒,这慢了 2 倍以上

所以现在我想应用一些技术在不修改代码的情况下使规范的性能与 SQLite 相媲美(最好只通过设置连接选项,这可能是不可能的)。

我想到的几个明显的事情是:

  • RAM 磁盘(在 OSX 上使用 RSpec 进行良好设置会很高兴)
  • 未记录的表(是否可以应用于整个数据库,这样我就不需要更改所有脚本?)

正如您可能已经理解的那样,我不关心可靠性和其他方面(数据库在这里只是一个一次性的东西)。
我需要充分利用 PG 并使其尽可能快

最佳答案将理想地描述这样做的技巧、设置以及这些技巧的缺点。

更新: fsync = off + full_page_writes = off 仅将时间减少到 ~65 秒(~-16 秒)。良好的开端,但远未达到 34 的目标。

更新 2:tried to use RAM disk 但性能提升在误差范围内。所以似乎不值得。

更新 3:* 我发现了最大的瓶颈,现在我的规范运行速度与 SQLite 一样快。

问题在于执行截断的数据库清理。显然 SQLite 太快了。

为了“修复”它,我在每次测试之前打开一个事务,并在最后回滚。

大约 700 次测试的一些数字。

  • 截断:SQLite - 34s,PG - 76s。
  • 事务:SQLite - 17s,PG - 18s。

SQLite 速度提高 2 倍。 PG 速度提升 4 倍。

【问题讨论】:

  • 我真的怀疑你会不会让它像 SQLite 一样快。单个用户的 SQLite 速度非常快。 SQLite 的设计速度非常快,用户数量少且扩展性差; Pg 的设计可以很好地扩展,但对于只有一个用户的简单批量工作来说并没有那么快。
  • 我意识到这一点,但是我希望针对特定情况优化 PG(测试运行),使其尽可能快。我不介意它稍微慢一点,但是 2.2x 有点太慢了。明白我的意思了吗?
  • +1 如果您对此有任何结果,我会对 RAM 磁盘方法的更新非常感兴趣。
  • @tscho 我一定会把它贴在这里。但是需要一些时间,因为我正在研究其他东西并在“背景”中“研究”PG的东西。
  • 插入数据是您的问题还是查询?从你的问题看不清楚。

标签: sql database performance postgresql database-tuning


【解决方案1】:

首先,始终使用最新版本的 PostgreSQL。性能改进总是会到来,所以如果你正在调整旧版本,你可能会浪费你的时间。例如,PostgreSQL 9.2 significantly improves the speed of TRUNCATE 当然添加了仅索引扫描。即使是次要版本也应始终遵循;见version policy

注意事项

Do NOT put a tablespace on a RAMdisk or other non-durable storage.

如果您丢失了一个表空间,整个数据库可能会被损坏并且如果不做大量工作就很难使用。与仅使用 UNLOGGED 表并拥有大量 RAM 用于缓存相比,这几乎没有什么优势。

如果你真的想要一个基于 ramdisk 的系统,initdb 在 ramdisk 上创建一个全新的集群,initdb在 ramdisk 上创建一个新的 PostgreSQL 实例,这样你就有了一个完全一次性的 PostgreSQL 实例。

PostgreSQL 服务器配置

测试时,您可以将服务器配置为non-durable but faster operation

这是 PostgreSQL 中 fsync=off 设置唯一可接受的用途之一。这个设置几乎告诉 PostgreSQL 不要打扰有序写入或任何其他讨厌的数据完整性保护和崩溃安全的东西,如果你断电或操作系统崩溃,它可以完全丢弃你的数据。

不用说,你永远不应该在生产环境中启用fsync=off,除非你使用 Pg 作为临时数据库来存储你可以从其他地方重新生成的数据。当且仅当您关闭 fsync 时也可以关闭 full_page_writes,因为它不再有任何好处。请注意,fsync=offfull_page_writes 适用于 集群 级别,因此它们会影响 PostgreSQL 实例中的所有数据库。

对于生产用途,您可以使用synchronous_commit=off 并设置commit_delay,因为您将获得与fsync=off 相同的许多好处,而没有巨大的数据损坏风险。如果启用异步提交,您确实会丢失最近的数据 - 仅此而已。

如果您可以选择稍微修改 DDL,您还可以使用 Pg 9.1+ 中的 UNLOGGED 表来完全避免 WAL 日志记录,并以在服务器崩溃时表被擦除为代价来获得真正的速度提升。没有使所有表不记录的配置选项,它必须在CREATE TABLE 期间设置。除了有利于测试之外,如果您的数据库中有充满生成或不重要数据的表,而这些数据包含您需要安全的内容,这会很方便。

检查您的日志,看看您是否收到有关检查点过多的警告。如果你是,你应该增加你的checkpoint_segments。您可能还需要调整 checkpoint_completion_target 以平滑写入。

调整 shared_buffers 以适应您的工作量。这取决于操作系统,取决于您的机器的其他情况,并且需要一些试验和错误。默认值非常保守。如果在 PostgreSQL 9.2 及更低版本上增加shared_buffers,您可能需要增加操作系统的最大共享内存限制; 9.3 及更高版本更改了他们使用共享内存的方式来避免这种情况。

如果您只使用几个连接来完成大量工作,请增加 work_mem 以给它们更多的 RAM 来进行排序等操作。请注意,work_mem 设置太高会导致用尽-内存问题,因为它是按排序而不是按连接,因此一个查询可以有许多嵌套排序。如果您在EXPLAIN 中看到排序溢出到磁盘或使用log_temp_files setting 记录(推荐),那么您只有真的需要增加work_mem,但更高的值也可能让Pg 选择更智能的计划.

正如另一位发帖人所说,如果可能,将 xlog 和主表/索引放在单独的 HDD 上是明智的。单独的分区是毫无意义的,你真的想要单独的驱动器。如果您使用fsync=off 运行,这种分离的好处要少得多,而如果您使用UNLOGGED 表,则几乎没有。

最后,调整您的查询。确保您的random_page_costseq_page_cost 反映了您系统的性能,确保您的effective_cache_size 是正确的,等等。使用EXPLAIN (BUFFERS, ANALYZE) 检查单个查询计划,并打开auto_explain 模块以报告所有慢查询。您通常可以通过创建适当的索引或调整成本参数来显着提高查询性能。

AFAIK 无法将整个数据库或集群设置为 UNLOGGED。能够这样做会很有趣。考虑在 PostgreSQL 邮件列表中询问。

主机操作系统调优

您也可以在操作系统级别进行一些调整。您可能想要做的主要事情是说服操作系统不要积极地将写入刷新到磁盘,因为您真的不在乎它们何时/是否写入磁盘。

在 Linux 中,您可以使用virtual memory subsystemdirty_* 设置来控制它,例如dirty_writeback_centisecs

将写回设置调整为过于宽松的唯一问题是,某些其他程序的刷新可能会导致所有 PostgreSQL 的累积缓冲区也被刷新,从而在所有内容都阻塞写入时导致大停顿。您可以通过在不同的文件系统上运行 PostgreSQL 来缓解这种情况,但某些刷新可能是设备级别或整个主机级别而不是文件系统级别,因此您不能依赖它。

此调整确实需要调整设置以查看最适合您的工作负载的设置。

在较新的内核上,您可能希望确保将 vm.zone_reclaim_mode 设置为零,因为与 PostgreSQL 管理 shared_buffers 的方式发生交互,这可能会导致 NUMA 系统(目前大多数系统)出现严重的性能问题。

查询和工作负载调整

这些确实需要更改代码;他们可能不适合你。有些是您可以申请的。

如果您不将工作批量处理成更大的事务,请开始。很多小交易都是昂贵的,所以你应该尽可能地批量处理东西。如果您使用的是异步提交,这不太重要,但仍然强烈推荐。

尽可能使用临时表。它们不会产生 WAL 流量,因此它们的插入和更新速度要快得多。有时值得将一堆数据放入临时表中,根据需要对其进行操作,然后执行INSERT INTO ... SELECT ... 将其复制到最终表中。请注意,临时表是每个会话的;如果您的会话结束或您失去连接,那么临时表就会消失,并且没有其他连接可以看到会话临时表的内容。

如果您使用的是 PostgreSQL 9.1 或更新版本,您可以使用 UNLOGGED 表来存储您可以承受丢失的数据,例如会话状态。这些在不同的会话中是可见的,并在连接之间保留。如果服务器不干净地关闭,它们会被截断,因此它们不能用于您无法重新创建的任何东西,但它们非常适合缓存、物化视图、状态表等。

一般来说,不要DELETE FROM blah;。请改用TRUNCATE TABLE blah;;当您将所有行转储到表中时,它会快得多。如果可以的话,在一个TRUNCATE 调用中截断许多表。但是,如果您一遍又一遍地做很多TRUNCATES 的小桌子,那就需要注意了;见:Postgresql Truncation speed

如果您没有外键索引,DELETEs 涉及由这些外键引用的主键将会非常缓慢。如果您希望从引用的表中创建DELETE,请确保创建此类索引。 TRUNCATE 不需要索引。

不要创建你不需要的索引。每个索引都有维护成本。尝试使用最小的索引集并让位图索引扫描将它们组合起来,而不是维护太多庞大、昂贵的多列索引。如果需要索引,请尝试先填充表,然后在最后创建索引。

硬件

如果您可以管理它,拥有足够的 RAM 来容纳整个数据库是一个巨大的胜利。

如果您没有足够的 RAM,存储速度越快越好。即使是便宜的 SSD 也会对旋转生锈产生巨大影响。不过,不要相信廉价的 SSD 用于生产,它们通常不具备碰撞安全性,并且可能会吃掉您的数据。

学习

Greg Smith 的书 PostgreSQL 9.0 High Performance 尽管提到了一个较旧的版本,但仍然具有相关性。它应该是一个有用的参考。

加入 PostgreSQL 通用邮件列表并关注它。

阅读:

【讨论】:

  • 我还可以推荐 @GregSmith 的 PostgreSQL 9.0 High Performance,这真的是一本很棒的书。本书涵盖了从磁盘布局到查询调优的性能调优的方方面面,让您对 PG 的内部结构有一个非常好的理解。
  • 我没有为本书发布 PostgreSQL 9.1 的更新,这是自出版以来的唯一版本,因为 9.1 中没有足够的与性能相关的更改来保证它。
  • 很棒的文章。正如一个微小的更新,“如果增加 shared_buffers,您可能需要增加操作系统的最大共享内存限制”在 PostgreSQL 9.3 下不再适用(对于大多数用户):postgresql.org/docs/9.3/static/release-9-3.html#AEN114343
  • @brauliobo 我的测试经常以高 TPS 进行许多 tx ......因为我试图模拟生产,包括并发繁重的工作负载。如果您的意思是“单连接、线性测试”,那么我同意您的看法。
  • stackoverflow.com/questions/11419536/… 对于行数很少的表,DELETE 可能比 TRUNCATE 快,测试中可能就是这种情况。
【解决方案2】:

使用不同的磁盘布局:

  • $PGDATA 的不同磁盘
  • $PGDATA/pg_xlog 的不同磁盘
  • tem 文件的不同磁盘(每个数据库 $PGDATA/base//pgsql_tmp)(参见关于 work_mem 的说明)

postgresql.conf 调整:

  • shared_memory:可用 RAM 的 30%,但不超过 6 到 8GB。对于写入密集型工作负载,使用更少的共享内存(2GB - 4GB)似乎更好
  • work_mem:主要用于带有排序/聚合的选择查询。这是每个连接设置,查询可以多次分配该值。如果数据不适合,则使用磁盘(pgsql_tmp)。检查“解释分析”以查看您需要多少内存
  • fsync 和 synchronous_commit:默认值是安全的,但如果您可以容忍数据丢失,那么您可以关闭然后关闭
  • random_page_cost:如果您有 SSD 或快速 RAID 阵列,您可以将其降低到 SSD 的 2.0 (RAID) 甚至更低 (1.1)
  • checkpoint_segments:您可以提高 32 或 64 并将 checkpoint_completion_target 更改为 0.9。较低的值允许更快的崩溃后恢复

【讨论】:

  • 请注意,如果您已经在使用fsync=off 运行,则将 pg_xlog 放在单独的磁盘上不会再有太大改进。
  • SSD 1.1 的值似乎很不合格。我承认这是一些专业人士盲目推荐的。即使是 SSD 的顺序读取也比随机读取要快得多。
  • @A-B-B 是的,但是您还可以使用操作系统缓冲区缓存效果。无论如何,所有这些参数都有些摇摆不定......
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多