前言
在执行大量写操作的系统上,调优检查点对于获得良好的性能至关重要。然而,检查点是我们经常发现混淆和配置问题的地方之一,无论是在社区邮件列表中,还是在为客户提供支持和咨询期间。这篇文章旨在解释检查点是什么——目的和数据库如何实现它——以及如何调优它们。
注:这是在2016年最初原作者写的一篇博文的更新版本,更新后反映了PostgreSQL配置的各种变化。否则,总体调优方法基本保持不变。
检查点的意义是什么?
PostgreSQL是依赖于预写日志(write-ahead log, WAL)的数据库之一——在对数据文件进行任何更改之前,它被记录在一个单独的日志(一个更改流)中。提供了持久性,因为在发生崩溃的情况下,数据库可以使用WAL执行恢复—从WAL读取更改并将其重新应用到数据文件中。
乍一看,这似乎效率不高,因为它使写入量增加了一倍——我们在更改数据文件的同时也将更改写入日志。但它实际上可能会提高性能,原因有几个。COMMIT只需要等待WAL持久(写入并刷新到磁盘),而数据文件只在内存(共享缓冲区)中修改,然后可能在稍后写入磁盘/刷新。这很好,因为WAL是以顺序的方式写入的,而对数据文件的写入通常是相当随机的,因此刷新WAL要便宜得多。此外,在共享缓冲区中,数据页可以被修改多次,然后在单个物理I/O写入中持久化——这是另一个显著的好处。
假设系统崩溃了,数据库需要执行恢复。最简单的方法是从头开始——从一个新的实例开始,从一开始就应用所有的WAL。最后,我们应该得到一个完整的(和正确的)数据库。当然,明显的缺点是需要保存和重放自创建实例以来的所有WAL。我们经常处理的数据库不是很大(比如几百GB),但非常活跃,每天产生几TB的WAL。想象一下,在运行数据库一年的时间里,需要多少磁盘空间来保存所有的WAL,以及恢复需要多长时间。显然,这似乎不是一个非常实用的方法。
但是,如果数据库能够保证对于给定的WAL位置(日志中的偏移量,称为“日志序列号”—LSN),到该位置的所有数据文件更改都被刷新到磁盘上,那会怎么样呢? 然后,它可以在恢复期间确定此位置,并仅重播WAL的其余部分(从此位置开始)。这将大大减少恢复时间,并且还可以在“已知的刚刚好的”位置之前丢弃WAL,因为恢复不需要它。
这正是检查点的魅力所在——确保恢复不再需要某些LSN之前的WAL,从而减少磁盘空间需求和恢复时间。数据库只是查看当前的WAL位置,并将所有未完成的更改(可能需要旧的WAL)刷新到磁盘,并记录LSN以备需要恢复时使用。
这些检查点由数据库定期生成,根据时间或生成的WAL(从上一个检查点开始)。
注:如果你是一名玩家,你可能对检查点的概念很熟悉——你的角色在游戏中通过一个特定的点,如果你没能打败下一关或掉进一个熔岩湖,你就从最后一个点开始,而不是从头开始。让我们看看如何在PostgreSQL中实现这一点;-)
稍后我们将讨论影响检查点发生频率的配置部分,但让我们简要讨论两种极端配置。
我们已经描述了一种极端情况——检查点根本不发生,或者很少发生。这样可以最大限度地提高一些好处(将数据文件更改合并为单个异步写操作ÿ