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

问题在于PG的运行速度变慢了。 在SQLite上花了34秒,在PG上花了76秒,慢了2倍多。

因此,现在我想应用一些技术,在不修改代码的情况下使规范的性能与SQLite相当(理想情况下,只需设置连接选项,这可能是不可能的)。

我首先想到的是:

RAM磁盘(在OSX上与RSpec的良好设置将很好地看到) Unlogged table(它可以应用于整个数据库,这样我就不用改变所有的脚本了吗?)

正如你可能已经理解的,我不关心可靠性和其他(DB在这里只是一个扔掉的东西)。 我需要充分发挥控卫的作用,尽可能快地发挥。

最好的答案是描述这样做的技巧,设置和这些技巧的缺点。

更新: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倍。


当前回答

首先,始终使用最新版本的PostgreSQL。性能改进总是会出现,所以如果您调优旧版本,可能是在浪费时间。例如,PostgreSQL 9.2显著提高了TRUNCATE的速度,当然还增加了索引扫描。即使是小版本也应该遵循;请参见版本策略。

不该做的事

不要将表空间放在RAMdisk或其他非持久存储上。

如果您丢失了一个表空间,整个数据库可能会被损坏,并且在没有大量工作的情况下很难使用。与使用UNLOGGED表和使用大量RAM进行缓存相比,这样做几乎没有什么优势。

如果你真的想要一个基于ramdisk的系统,通过在ramdisk上初始化一个新的PostgreSQL实例,在ramdisk上初始化一个全新的集群,这样你就有了一个完全可丢弃的PostgreSQL实例。

PostgreSQL服务器配置

在测试时,您可以将服务器配置为非持久但更快的操作。

这是PostgreSQL中fsync=off设置的唯一可接受的用法之一。这个设置基本上告诉PostgreSQL不要为有序写入或任何其他讨厌的数据完整性保护和崩溃安全问题而烦恼,允许它在你断电或操作系统崩溃时完全丢弃你的数据。

不用说,您永远不应该在生产环境中启用fsync=off,除非您使用Pg作为临时数据库,用于保存可以从其他地方重新生成的数据。当且仅当你关闭fsync时也可以关闭full_page_writes,因为它不再有任何好处。注意fsync=off和full_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设置(推荐)进行记录时,你才需要增加work_mem,但更高的值也可以让Pg选择更明智的计划。

正如另一个帖子所说,如果可能的话,将xlog和主表/索引放在不同的hdd上是明智的。单独的分区是没有意义的,你真的需要单独的驱动器。如果使用fsync=off运行,这种分离的好处就小得多,如果使用UNLOGGED表则几乎没有好处。

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

AFAIK没有办法将整个数据库或集群设置为UNLOGGED。如果能做到这一点,那就太有趣了。考虑在PostgreSQL邮件列表上询问。

主机操作系统调优

您还可以在操作系统级别进行一些调优。您可能要做的主要事情是说服操作系统不要积极地将写入写入磁盘,因为您实际上并不关心它们何时/是否写入磁盘。

在Linux中,你可以通过虚拟内存子系统的dirty_*设置来控制这一点,比如dirty_writeback_centisecs。

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

这种调优实际上需要摆弄各种设置,以确定哪些设置最适合您的工作负载。

在更新的内核上,您可能希望确保vm。zone_reclaim_mode设置为零,因为它会导致NUMA系统(现在的大多数系统)严重的性能问题,因为它与PostgreSQL管理shared_buffers的方式相互作用。

查询和工作负载调优

这些事情确实需要修改代码;它们可能不适合你。有些东西你也许可以应用。

如果你不打算把工作分批处理成更大的事务,那就开始吧。许多小的事务是昂贵的,所以你应该在可能和实际的情况下批量处理。如果你正在使用异步提交,这就不那么重要了,但仍然强烈推荐。

尽可能使用临时表。它们不会产生WAL流量,所以它们在插入和更新时要快得多。有时,将一堆数据放入临时表中,根据需要对其进行操作,然后执行INSERT into…选择……复制到期末表中。注意,临时表是每个会话;如果会话结束或失去连接,那么临时表就会消失,其他连接就不能看到会话的临时表的内容。

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

一般来说,不要删除FROM之类的。使用TRUNCATE TABLE blah;相反;当您转储表中的所有行时,这要快得多。如果可以,在一个Truncate调用中截断多个表。但是,如果你一遍又一遍地对小表进行truncate,有一个警告;参见:Postgresql截断速度

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

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

硬件

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

如果没有足够的RAM,那么存储越快越好。即使是一个便宜的SSD也比旋转生锈有巨大的不同。不要相信廉价的ssd硬盘,它们通常不安全,可能会吃掉你的数据。

学习

Greg Smith的书《PostgreSQL 9.0高性能》尽管参考了一个稍旧的版本,但仍然具有重要意义。这应该是一个有用的参考。

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

阅读:

优化您的PostgreSQL服务器- PostgreSQL wiki 数据库连接数- PostgreSQL wiki

其他回答

使用不同的磁盘布局:

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

postgresql.conf调整:

shared_memory: 30% of available RAM but not more than 6 to 8GB. It seems to be better to have less shared memory (2GB - 4GB) for write intensive workloads work_mem: mostly for select queries with sorts/aggregations. This is per connection setting and query can allocate that value multiple times. If data can't fit then disk is used (pgsql_tmp). Check "explain analyze" to see how much memory do you need fsync and synchronous_commit: Default values are safe but If you can tolerate data lost then you can turn then off random_page_cost: if you have SSD or fast RAID array you can lower this to 2.0 (RAID) or even lower (1.1) for SSD checkpoint_segments: you can go higher 32 or 64 and change checkpoint_completion_target to 0.9. Lower value allows faster after-crash recovery

首先,始终使用最新版本的PostgreSQL。性能改进总是会出现,所以如果您调优旧版本,可能是在浪费时间。例如,PostgreSQL 9.2显著提高了TRUNCATE的速度,当然还增加了索引扫描。即使是小版本也应该遵循;请参见版本策略。

不该做的事

不要将表空间放在RAMdisk或其他非持久存储上。

如果您丢失了一个表空间,整个数据库可能会被损坏,并且在没有大量工作的情况下很难使用。与使用UNLOGGED表和使用大量RAM进行缓存相比,这样做几乎没有什么优势。

如果你真的想要一个基于ramdisk的系统,通过在ramdisk上初始化一个新的PostgreSQL实例,在ramdisk上初始化一个全新的集群,这样你就有了一个完全可丢弃的PostgreSQL实例。

PostgreSQL服务器配置

在测试时,您可以将服务器配置为非持久但更快的操作。

这是PostgreSQL中fsync=off设置的唯一可接受的用法之一。这个设置基本上告诉PostgreSQL不要为有序写入或任何其他讨厌的数据完整性保护和崩溃安全问题而烦恼,允许它在你断电或操作系统崩溃时完全丢弃你的数据。

不用说,您永远不应该在生产环境中启用fsync=off,除非您使用Pg作为临时数据库,用于保存可以从其他地方重新生成的数据。当且仅当你关闭fsync时也可以关闭full_page_writes,因为它不再有任何好处。注意fsync=off和full_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设置(推荐)进行记录时,你才需要增加work_mem,但更高的值也可以让Pg选择更明智的计划。

正如另一个帖子所说,如果可能的话,将xlog和主表/索引放在不同的hdd上是明智的。单独的分区是没有意义的,你真的需要单独的驱动器。如果使用fsync=off运行,这种分离的好处就小得多,如果使用UNLOGGED表则几乎没有好处。

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

AFAIK没有办法将整个数据库或集群设置为UNLOGGED。如果能做到这一点,那就太有趣了。考虑在PostgreSQL邮件列表上询问。

主机操作系统调优

您还可以在操作系统级别进行一些调优。您可能要做的主要事情是说服操作系统不要积极地将写入写入磁盘,因为您实际上并不关心它们何时/是否写入磁盘。

在Linux中,你可以通过虚拟内存子系统的dirty_*设置来控制这一点,比如dirty_writeback_centisecs。

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

这种调优实际上需要摆弄各种设置,以确定哪些设置最适合您的工作负载。

在更新的内核上,您可能希望确保vm。zone_reclaim_mode设置为零,因为它会导致NUMA系统(现在的大多数系统)严重的性能问题,因为它与PostgreSQL管理shared_buffers的方式相互作用。

查询和工作负载调优

这些事情确实需要修改代码;它们可能不适合你。有些东西你也许可以应用。

如果你不打算把工作分批处理成更大的事务,那就开始吧。许多小的事务是昂贵的,所以你应该在可能和实际的情况下批量处理。如果你正在使用异步提交,这就不那么重要了,但仍然强烈推荐。

尽可能使用临时表。它们不会产生WAL流量,所以它们在插入和更新时要快得多。有时,将一堆数据放入临时表中,根据需要对其进行操作,然后执行INSERT into…选择……复制到期末表中。注意,临时表是每个会话;如果会话结束或失去连接,那么临时表就会消失,其他连接就不能看到会话的临时表的内容。

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

一般来说,不要删除FROM之类的。使用TRUNCATE TABLE blah;相反;当您转储表中的所有行时,这要快得多。如果可以,在一个Truncate调用中截断多个表。但是,如果你一遍又一遍地对小表进行truncate,有一个警告;参见:Postgresql截断速度

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

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

硬件

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

如果没有足够的RAM,那么存储越快越好。即使是一个便宜的SSD也比旋转生锈有巨大的不同。不要相信廉价的ssd硬盘,它们通常不安全,可能会吃掉你的数据。

学习

Greg Smith的书《PostgreSQL 9.0高性能》尽管参考了一个稍旧的版本,但仍然具有重要意义。这应该是一个有用的参考。

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

阅读:

优化您的PostgreSQL服务器- PostgreSQL wiki 数据库连接数- PostgreSQL wiki