优化PostgreSQL以进行快速测试


203

我从典型的Rails应用程序从SQLite切换到PostgreSQL。

问题在于PG的运行规范变得缓慢。
在SQLite上花了〜34秒,在PG上花了〜76秒,原来慢2倍以上

因此,现在我想应用一些技术来使规范的性能与SQLite保持一致,而无需修改代码(理想情况下仅通过设置连接选项,这可能是不可能的)。

从我的脑海中显而易见的几件事是:

  • RAM磁盘(在OSX上使用RSpec进行良好的设置很不错)
  • 未记录的表(可以将其应用于整个数据库,这样我就不必更改所有脚本了吗?)

正如您可能已经了解的那样,我不在乎可靠性和其他方面(这里DB只是一堆东西而已)。
我需要充分利用PG,并使其尽可能快

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

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

更新2:尝试使用RAM磁盘,但性能提升在误差范围内。因此似乎不值得。

更新3:* 我发现了最大的瓶颈,现在我的规格与SQLite一样快。

问题是进行了截断的数据库清理。显然,SQLite的速度太快了。

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

约700个测试的一些数字。

  • 截断:SQLite-34s,PG-76s。
  • 交易:SQLite-17s,PG-18s。

SQLite的速度提高了2倍。PG的速度提高了4倍。


2
我真的怀疑您能否使它像SQLite一样快。SQLite的与单个用户是出奇的快。SQLite的设计速度非常快,用户数量很少,扩展性很差。Pg的设计可以很好地扩展,但对于仅一个用户的简单批量工作而言,速度却不如之前。
Craig Ringer '02

1
我意识到这一点,但是有一个特殊的情况,我希望针对(测试运行)优化PG,使其尽可能快。我不介意它是稍微慢那里,但2.2X是慢了一点。明白了吗?
Dmytrii Nagirniak 2012年

+1如果您有任何有关RAM磁盘方法的更新,我将非常感兴趣。
tscho'2

@tscho我一定会在这里发布。但是需要一些时间,因为我正在研究其他内容并在“背景”中“研究” PG内容。
Dmytrii Nagirniak 2012年

插入数据您的问题或查询?您的问题不清楚。
a_horse_with_no_name 2012年

Answers:


281

首先,请始终使用最新版本的PostgreSQL。性能改进总是会不断出现,因此如果您要调整旧版本,可能会浪费时间。例如,PostgreSQL 9.2大大提高了速度TRUNCATE,当然也增加了仅索引扫描。即使是较小的发布,也应始终遵循;请参阅版本政策

不要

千万不要把表放在ramdisk上或其他非耐久存

如果丢失表空间,则整个数据库可能会损坏,并且如果不进行大量工作就很难使用。与仅使用UNLOGGED表和拥有大量RAM用于高速缓存相比,这几乎没有什么好处。

如果您确实想要一个基于ramdisk的系统,initdb则通过在ramdisk上initdb使用新的PostgreSQL实例,在ramdisk 上建立一个全新的集群,因此您将拥有一个完全可抛弃的PostgreSQL实例。

PostgreSQL服务器配置

测试时,您可以将服务器配置为非耐用但运行速度更快

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

不用说,fsync=off除非将Pg用作可从其他地方重新生成的数据的临时数据库,否则切勿在生产中启用它。当且仅当您要关闭fsync full_page_writes时,它也可以关闭,因为它不再起作用。请注意fsync=offfull_page_writes集群级别应用,因此它们会影响PostgreSQL实例中的所有数据库。

对于生产用途,您可以使用synchronous_commit=off和设置commit_delay,因为您将获得许多与fsync=off没有巨大数据损坏风险的相同的好处。如果启用异步提交,则确实会丢失最近数据的一小窗口-就是这样。

如果可以选择稍微更改DDL,则还可以使用UNLOGGEDPg 9.1+中的表完全避免WAL日志记录,并在服务器崩溃时以删除表为代价获得真正的速度提升。没有配置选项可以使所有表都注销,必须在期间进行设置CREATE TABLE。如果您的表中数据库中充满了生成的数据或不重要的数据,否则该表很容易进行测试,否则这些表中包含需要确保安全的内容。

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

调整shared_buffers以适合您的工作量。这取决于操作系统,取决于您的计算机上正在发生的其他情况,并且需要反复试验。默认值非常保守。如果shared_buffers在PostgreSQL 9.2及更低版本上增加,则可能需要增加操作系统的最大共享内存限制。9.3及更高版本更改了如何使用共享内存来避免这种情况。

如果您仅使用几个连接来完成大量工作,请增加连接数work_mem以提供更多RAM以便进行排序等。请注意,work_mem设置值过高会导致内存不足的问题,因为它不是每个排序的每个连接,因此一个查询可以具有许多嵌套排序。仅当您看到排序溢出到磁盘或通过设置记录(建议使用)时,才真正需要增加,但是更高的值也可以让Pg选择更明智的计划。work_memEXPLAINlog_temp_files

正如另一位发布者所说,将xlog和主表/索引放置在单独的硬盘上是明智的。单独的分区是毫无意义的,您确实需要单独的驱动器。fsync=off如果您使用UNLOGGED表,则这种分离的好处要少得多,如果使用表,则几乎没有好处。

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

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

主机操作系统调优

您也可以在操作系统级别进行一些调整。您可能要做的主要事情是说服操作系统不要大胆地刷新对磁盘的写入,因为您实际上并不在乎何时/是否将它们写入磁盘。

在Linux中,您可以使用虚拟内存子系统dirty_*设置来控制此设置dirty_writeback_centisecs

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

这种调整确实需要调整设置,以了解最适合您的工作负载的情况。

在较新的内核上,您可能希望确保将vm.zone_reclaim_mode其设置为零,因为由于与PostgreSQL管理方式的交互,它可能导致NUMA系统(当今大多数系统)出现严重的性能问题shared_buffers

查询和工作负载调整

这些确实需要更改代码。他们可能不适合您。有些事情您可能可以应用。

如果您不将工作分批处理到较大的事务中,请开始。许多小额交易非常昂贵,因此您应该在可行时进行批处理。如果您使用的是异步提交,那么它的重要性就不那么重要了,但还是极力推荐。

尽可能使用临时表。它们不会产生WAL流量,因此插入和更新的速度要快得多。有时值得将大量数据放入临时表中,然后按需要进行处理,然后执行将INSERT INTO ... SELECT ...其复制到最终表中的操作。注意临时表是每个会话的;如果会话结束或失去连接,则临时表将消失,并且其他任何连接都无法看到会话的临时表的内容。

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

通常,不要DELETE FROM blah;。使用TRUNCATE TABLE blah;代替;当您转储表中的所有行时,这要快得多。TRUNCATE如果可以,请一次调用截断许多表。但是,如果您要TRUNCATES一遍又一遍地做很多小桌子,那是个警告。请参阅:Postgresql截断速度

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

不要创建不需要的索引。每个索引都有维护成本。尝试使用最少的索引集,并让位图索引扫描将它们组合在一起,而不要维护太多,昂贵的多列索引。需要索引的地方,请尝试首先填充表,然后最后创建索引。

硬件

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

如果您没有足够的RAM,则更快的存储空间将变得更好。即使是便宜的SSD也比旋转锈蚀有很大的不同。但是,不要相信廉价的SSD来进行生产,因为它们通常不安全,可能会吞噬您的数据。

学习

格雷格·史密斯(Greg Smith)所著的《PostgreSQL 9.0高性能》一书虽然仍然有些旧,但仍然有意义。它应该是一个有用的参考。

加入PostgreSQL常规邮件列表并遵循它。

读:


10
我也可以推荐@GregSmith的PostgreSQL 9.0 High Performance,这确实是一本好书。本书涵盖了从磁盘布局到查询调整的性能调整的各个方面,并且使您对PG内部有很好的理解。
tscho'2

10
我没有发布PostgreSQL 9.1本书的更新,因为这是9.1发行以来唯一的发布,因为9.1中没有足够的与性能相关的更改来保证它。
格雷格·史密斯

3
很棒的文章。就像一个小小的更新一样,“对于PostgreSQL 9.3”不再适用(对于大多数用户而言,如果增加shared_buffers,可能需要增加操作系统的最大共享内存限制):postgresql.org/docs/9.3/static/release-9- 3.html#AEN114343
Gunnlaugur Briem

1
@brauliobo我的测试经常在高TPS时执行许多TX操作,因为我试图模拟生产,包括大量并发工作负载。如果您的意思是“单连接,线性测试”,那么我会同意您的看法。
Craig Ringer 2015年

1
stackoverflow.com/questions/11419536/…对于行很少的表,DELETE可能比TRUNCATE更快,这在测试中可能就是这种情况。
乔纳森·克罗斯默'16

9

使用不同的磁盘布局:

  • $ PGDATA的其他磁盘
  • $ PGDATA / pg_xlog的不同磁盘
  • tem文件的不同磁盘(每个数据库$ PGDATA / base // pgsql_tmp)(请参阅有关work_mem的注释)

postgresql.conf调整:

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

4
请注意,如果您已经在运行fsync=off,则将pg_xlog放在单独的磁盘上不会再有太大改善。
intgr 2012年

SSD的1.1值似乎非常不合格。我承认这是一些专业人士盲目推荐的。就连顺序读取而言,甚至SSD都比随机读取要快得多。
Acumenus 2015年

@ABB是的,但是您还可以使用OS缓冲区缓存效果。无论如何,所有这些参数都有些动摇……
Craig Ringer
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.