我正在为典型的 Rails 应用程序从 SQLite 切换到 PostgreSQL。
问题是 PG 运行规范变得很慢。 在 SQLite 上大约需要 34 秒,在 PG 上大约需要 76 秒,慢了 2 倍以上。
所以现在我想应用一些技术来使规范的性能与 SQLite 相提并论,而无需修改代码(理想情况下只需设置连接选项,这可能是不可能的)。
从我的脑海中浮出水面的几个明显的事情是:
正如您可能已经理解的那样,我不关心可靠性和其他(数据库在这里只是一个一次性的东西)。 我需要充分利用 PG 并尽可能快地完成它。
最佳答案理想地描述了这样做的技巧,设置和这些技巧的缺点。
更新: fsync = off +full_page_writes = off仅将时间减少到 ~65 秒(~-16 秒)。良好的开端,但远未达到 34 的目标。
fsync = off
full_page_writes = off
更新 2:我尝试使用 RAM 磁盘,但性能提升在误差范围内。所以似乎不值得。
更新 3:* 我发现了最大的瓶颈,现在我的规范运行速度与 SQLite 一样快。
问题是执行截断的数据库清理。显然 SQLite 太快了。
为了“修复”它,我在每次测试之前打开一个事务并在最后回滚。
大约 700 次测试的一些数字。
SQLite 速度提高 2 倍。PG 速度提高 4 倍。
首先,始终使用最新版本的 PostgreSQL。性能改进总是会到来,所以如果你正在调整旧版本,你可能会浪费你的时间。例如,PostgreSQL 9.2 显着提高了速度TRUNCATE,当然也增加了仅索引扫描。即使是次要版本也应始终遵循;请参阅版本政策。
TRUNCATE
不要将表空间放在RAMdisk或其他非持久存储上。
如果您丢失了一个表空间,整个数据库可能会被损坏并且很难在没有大量工作的情况下使用。UNLOGGED与仅使用表并拥有大量 RAM 用于缓存相比,这几乎没有什么优势。
UNLOGGED
如果你真的想要一个基于 ramdisk 的系统,通过在 ramdisk 上创建一个新的 PostgreSQL 实例,在 ramdisk 上建立initdb一个全新的集群initdb,这样你就有了一个完全一次性的 PostgreSQL 实例。
initdb
测试时,您可以将服务器配置为非持久但更快的操作。
fsync=off这是PostgreSQL中唯一可接受的设置用途之一。这个设置几乎告诉 PostgreSQL 不要打扰有序写入或任何其他讨厌的数据完整性保护和崩溃安全的东西,如果你断电或操作系统崩溃,它可以完全丢弃你的数据。
fsync=off
不用说,fsync=off除非您将 Pg 用作可以从其他地方重新生成的数据的临时数据库,否则您永远不应该在生产环境中启用。当且仅当您关闭 fsync 时也可以full_page_writes关闭,因为它不再有任何好处。请注意这一点fsync=off并full_page_writes在集群级别应用,因此它们会影响PostgreSQL 实例中的所有数据库。
full_page_writes
对于生产用途,您可以使用synchronous_commit=off并设置 a ,因为您将获得许多与没有巨大数据损坏风险commit_delay相同的好处。fsync=off如果启用异步提交,您确实会丢失最近的数据 - 仅此而已。
synchronous_commit=off
commit_delay
如果您可以选择稍微更改 DDL,您还可以使用UNLOGGEDPg 9.1+ 中的表来完全避免 WAL 日志记录并获得真正的速度提升,但代价是表在服务器崩溃时会被删除。没有配置选项可以取消所有表的记录,必须在CREATE TABLE. 除了有利于测试之外,如果您的数据库中有充满生成或不重要数据的表,而这些数据包含您需要安全的内容,这将非常方便。
CREATE TABLE
检查您的日志,看看您是否收到有关检查点过多的警告。如果你是,你应该增加你的checkpoint_segments。您可能还想调整 checkpoint_completion_target 以平滑写入。
调整shared_buffers以适应您的工作量。这取决于操作系统,取决于您的机器的其他情况,并且需要一些试验和错误。默认值非常保守。shared_buffers如果您在 PostgreSQL 9.2 及以下版本上增加,您可能需要增加操作系统的最大共享内存限制;9.3 及更高版本更改了他们使用共享内存的方式来避免这种情况。
shared_buffers
如果您使用的只是几个完成大量工作的连接,请增加work_mem以给它们更多的 RAM 来进行排序等。请注意,设置太高work_mem会导致内存不足问题,因为它不是按排序的每个连接,因此一个查询可以有许多嵌套排序。如果您可以看到排序溢出到磁盘或使用设置(推荐)登录,您只需要增加,但更高的值也可能让 Pg 选择更智能的计划。work_mem``EXPLAINlog_temp_files
work_mem
work_mem``EXPLAIN
log_temp_files
正如另一位海报所说,如果可能的话,将 xlog 和主表/索引放在单独的 HDD 上是明智的。单独的分区是毫无意义的,你真的想要单独的驱动器。如果您正在运行,则这种分离的好处要少得多,而如果您正在使用表格fsync=off,则几乎没有。UNLOGGED
最后,调整您的查询。确保您的random_page_cost和seq_page_cost反映您系统的性能,确保您effective_cache_size是正确的等。EXPLAIN (BUFFERS, ANALYZE)用于检查单个查询计划,并打开auto_explain模块以报告所有慢查询。您通常可以通过创建适当的索引或调整成本参数来显着提高查询性能。
random_page_cost
seq_page_cost
effective_cache_size
EXPLAIN (BUFFERS, ANALYZE)
auto_explain
AFAIK 无法将整个数据库或集群设置为UNLOGGED. 能够这样做会很有趣。考虑在 PostgreSQL 邮件列表上询问。
您也可以在操作系统级别进行一些调整。您可能想要做的主要事情是说服操作系统不要积极地将写入刷新到磁盘,因为您真的不在乎它们何时/是否将其写入磁盘。
在 Linux 中,您可以使用虚拟内存子系统的dirty_*设置来控制它,例如dirty_writeback_centisecs.
dirty_*
dirty_writeback_centisecs
将写回设置调整为过于宽松的唯一问题是,某些其他程序的刷新可能会导致所有 PostgreSQL 的累积缓冲区也被刷新,从而在所有内容都阻塞写入时导致大停顿。您可以通过在不同的文件系统上运行 PostgreSQL 来缓解这种情况,但是某些刷新可能是设备级别或整个主机级别而不是文件系统级别,因此您不能依赖它。
这种调整确实需要使用设置来查看最适合您的工作负载的设置。
在较新的内核上,您可能希望确保将vm.zone_reclaim_mode其设置为零,因为与 PostgreSQL 管理方式的交互会导致 NUMA 系统(目前大多数系统)出现严重的性能问题shared_buffers。
vm.zone_reclaim_mode
这些是确实需要更改代码的事情;他们可能不适合你。有些是你可以申请的。
如果您不将工作批量处理成更大的事务,请开始。很多小交易都很昂贵,所以你应该尽可能地批量处理东西。如果您使用的是异步提交,这不太重要,但仍然强烈推荐。
尽可能使用临时表。它们不会产生 WAL 流量,因此它们的插入和更新速度要快得多。有时值得将一堆数据放入临时表中,根据需要对其进行操作,然后将INSERT INTO ... SELECT ...其复制到最终表中。请注意,临时表是每个会话的;如果您的会话结束或者您失去连接,那么临时表就会消失,并且没有其他连接可以看到会话临时表的内容。
INSERT INTO ... SELECT ...
如果您使用的是 PostgreSQL 9.1 或更新版本,您可以使用UNLOGGED表来存储您可以承受丢失的数据,例如会话状态。这些在不同的会话中是可见的,并在连接之间保留。如果服务器不干净地关闭,它们会被截断,因此它们不能用于您无法重新创建的任何东西,但它们非常适合缓存、物化视图、状态表等。
一般来说,不要DELETE FROM blah;。改为使用TRUNCATE TABLE blah;;当您将所有行转储到表中时,它会快得多。TRUNCATE如果可以的话,在一次调用中截断许多表。但是,如果您TRUNCATES一遍又一遍地做很多小桌子,那就需要注意了。
DELETE FROM blah;
TRUNCATE TABLE blah;
TRUNCATES
如果您没有外键索引,那么DELETE涉及这些外键引用的主键的 s 将非常慢。如果您希望DELETE从引用的表中创建此类索引,请确保创建此类索引。不需要索引TRUNCATE。
DELETE
不要创建你不需要的索引。每个索引都有维护成本。尝试使用最小的索引集并让位图索引扫描将它们组合起来,而不是维护太多庞大、昂贵的多列索引。在需要索引的地方,首先尝试填充表,然后在最后创建索引。
如果你能管理它,拥有足够的 RAM 来保存整个数据库是一个巨大的胜利。
如果您没有足够的 RAM,则存储速度越快越好。即使是便宜的 SSD 也会对旋转生锈产生巨大影响。不过,不要相信廉价的 SSD 用于生产,它们通常不是安全的,并且可能会吃掉你的数据。