ddia/pg/pg-admin-wal.md
2018-02-08 14:07:06 +08:00

122 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
author: "Vonng"
description: "PostgreSQL WAL与检查点"
categories: ["DBA"]
tags: ["PostgreSQL","WAL", "Internal"]
type: "post"
---
# PostgreSQL WAL & Checkpoint
数据库需要保证两个基本的特性:**可靠性**与**可用性**。通俗来讲:
可靠性就是:出了故障,既不会丢数据,也不会弄脏数据。
可用性就是:保证足够的读写性能,出了故障后,能够快速恢复服务。
朴素的数据库实现有两个选项:在内存中修改数据页,或者将事物变更直接写入磁盘。但这产生了一个两难困境:
* 内存支持随机读写,因此在性能上表现强悍,然而作为易失性存储,一旦故障就会丢数据。
* 硬盘恰恰相反,随机读写表现糟糕,但在故障时数据要可靠的多。
内存可用性强可靠性差,硬盘可用性差但可靠性强,如何解决这一对矛盾,让内存与硬盘取长补短,就是生产级数据库需要考虑的问题了。
## 0x1 核心思想
硬盘的随机写入性能很糟糕但顺序写入的性能却非常可观。即使是SSD也符合这一规律因为一次写入的擦除单位是Block通常是几M而操作系统的写入单元是Page通常约4k。如果每次事物提交都要直接将脏数据页落盘性能表现肯定不会可观。但如果采用另一种方式将**数据的变更**而不是**变更后的最新数据本身**落盘,就可以将随机写入变为顺序写入,从而极大地提高磁盘写入效率。
于是预写式日志WALWrite Ahead Log 出现了所谓日志在最朴素的意义上来讲就是一个Append-Only的数据文件记录了操作的内容。只要保留了WAL数据库就是可靠的可以恢复的。从一个给定的状态例如空数据库开始回放所有的操作日志到当前的时间点就可以恢复出当前数据库应有的状态。与此同时如果日志已经落盘确保了可靠性数据页就不需要在每次提交时落盘了。数据页的读写可以完全在内存进行从而提供强悍的性能支持。
**但可用性不仅仅包括足够的性能,当发生故障时能够快速恢复也是可用性要求的一部分**。考虑最极端的情况从数据库创建之初所有数据页就在内存里一直飘着只有操作日志落了盘。现在数据库运行了一整年突然崩溃了这时候要想恢复就需要重放一整年的操作日志也许需要几个小时也许需要好几天。对于生产环境这是无法接受的检查点Checkpoint解决了这个问题。
检查点Checkpoint类似于游戏中存档的概念远古时期的很多游戏没有存档一旦Game Over就要重头再来。后来的游戏有了记忆和存档当挑战Boss失败时只要读取最近的存档就可以避免从头开始。
数据库中的检查点代表这样一种操作,在某一个检查点时,所有脏数据页会写回到磁盘中,使得磁盘和内存中的数据保持一致。这样当故障恢复时,只需要从该检查点开始回放操作日志即可。
例如每个整点执行一次检查点存档一次那么当故障时只需要从本小时开始的检查点开始回放WAL就可以完成恢复。同时检查点还有一个好处是当数据页落盘之后在这个检查点之前的WAL日志就可以不用了。对于高负载数据库例如每小时产生TB级别WAL的数据库使用检查点能够极大地减少恢复的时间和磁盘的用量。
通过检查点和预写式日志,数据库可以同时保证高度的可靠性和可用性。
## 0x2 WAL概述
预写式日志WAL是保证数据完整性的一种标准方法。对其详尽的描述几乎可以在所有如果不是全部有关事务处理的书中找到。简单来说WAL的中心概念是数据文件存储着表和索引的修改必须在这些动作被日志记录之后才被写入即在描述这些改变的日志记录被刷到持久存储以后。如果我们遵循这种过程我们不需要在每个事务提交时刷写数据页面到磁盘因为我们知道在发生崩溃时可以使用日志来恢复数据库任何还没有被应用到数据页面的改变可以根据其日志记录重做这是前滚恢复也被称为REDO
使用WAL可以显著降低磁盘的写次数因为只有日志文件需要被刷出到磁盘以保证事务被提交而被事务改变的每一个数据文件则不必被刷出。**日志文件被按照顺序写入,因此同步日志的代价要远低于刷写数据页面的代价**。在处理很多影响数据存储不同部分的小事务的服务器上这一点尤其明显。此外,当服务器在处理很多小的并行事务时,日志文件的一个`fsync`可以提交很多事务。
##异步提交
*异步提交*是一个允许事务能更快完成的选项,代价是在数据库崩溃时最近的事务会丢失。在很多应用中这是一个可接受的交换。
如前一节所述,事务提交通常是*同步的*服务器等到事务的WAL记录被刷写到持久存储之后才向客户端返回成功指示。因此客户端可以确保那些报告已被提交的事务确会被保存即便随后马上发生了一次服务器崩溃。但是对于短事务来说这种延迟是其总执行时间的主要部分。选择异步提交模式意味着服务器将在事务被逻辑上提交后立刻返回成功而此时由它生成的WAL记录还没有被真正地写到磁盘上。这将为小型事务的生产力产生显著地提升。
异步提交会带来数据丢失的风险。在向客户端报告事务完成到事务真正被提交即能保证服务器崩溃时它也不会被丢失之间有一个短的时间窗口。因此如果客户端将会做一些要求其事务被记住的外部动作就不应该用异步提交。例如一个银行肯定不会使用异步提交事务来记录一台ATM的现金分发。但是在很多情境中不需要这种强的保证例如事件日志。
使用异步提交带来的风险是数据丢失而不是数据损坏。如果数据库可能崩溃它会通过重放WAL到被刷写的最后一个记录来进行恢复。数据库将因此被恢复到一个自身一致状态但是任何还没有被刷写到磁盘的事务将不会反映在该状态中。因此其影响就是丢失了最后的少量事务。由于事务按照提交顺序被重放所以不会出现任何不一致性 — 例如一个事务B按照前面一个事务A的效果来进行修改则不会出现A的效果丢失而B的效果被保留的情况。
用户可以选择每一个事务的提交模式,这样可以有同步提交和异步提交的事务并行运行。这允许我们灵活地在性能和事务持久性之间进行权衡。提交模式由用户可设置的参数[synchronous_commit](http://www.postgres.cn/docs/9.6/runtime-config-wal.html#GUC-SYNCHRONOUS-COMMIT)控制,它可以使用任何一种修改配置参数的方法进行设置。一个事务真正使用的提交模式取决于当事务提交开始时`synchronous_commit`的值。
特定的实用命令,如`DROP TABLE`,被强制按照同步提交而不考虑`synchronous_commit`的设定。这是为了确保服务器文件系统和数据库逻辑状态之间的一致性。支持两阶段提交的命令页总是同步提交的,如`PREPARE TRANSACTION`。
如果数据库在异步提交和事务WAL记录写入之间的风险窗口期间崩溃在该事务期间所作的修改*将*丢失。风险窗口的持续时间是有限制的,因为一个后台进程("WAL写进程")每[wal_writer_delay](http://www.postgres.cn/docs/9.6/runtime-config-wal.html#GUC-WAL-WRITER-DELAY)毫秒会把未写入的WAL记录刷写到磁盘。风险窗口实际的最大持续时间是`wal_writer_delay`的3倍因为WAL写进程被设计成倾向于在忙时一次写入所有页面。
一个立刻关闭等同于一次服务器崩溃,因此也将会导致未刷写的异步提交丢失。
异步提交提供的行为与配置[fsync](http://www.postgres.cn/docs/9.6/runtime-config-wal.html#GUC-FSYNC) = off不同。`fsync`是一个服务器范围的设置它将会影响所有事务的行为。它禁用了PostgreSQL中所有尝试同步写入到数据库不同部分的逻辑并且因此一次系统崩溃一个硬件或操作系统崩溃不是PostgreSQL本身的失败可能造成数据库状态的任意损坏。在很多情境中带来大部分性能提升的异步提交可以通过关闭`fsync`来获得,而且不会带来数据损坏的风险。
[commit_delay](http://www.postgres.cn/docs/9.6/runtime-config-wal.html#GUC-COMMIT-DELAY)也看起来很像异步提交,但它实际上是一种同步提交方法(事实上,`commit_delay`在异步提交时被忽略)。`commit_delay`会使事务在刷写WAL到磁盘之前有一个延迟它期望由一个这样的事务所执行的刷写能够也服务于其他同时提交的事务。该设置可以被看成是一种时间窗口在其期间事务可以参与到一次单一的刷写中这种方式用于在多个事务之间摊销刷写的开销。
## 0x3 查看WAL状态
## 0x5 流复制
## 0x6 Checkpoint
## 相关命令
- `CHECKPOINT` : 强制一个事务日志检查点
一个检查点是事务日志序列中的一个点,在该点上所有数据文件 都已经被更新为反映日志中的信息。所有数据文件将被刷写到磁盘。 检查点期间发生的细节可见[第 30.4 节](http://www.postgres.cn/docs/9.6/wal-configuration.html)。
`CHECKPOINT`命令在发出时强制一个 立即的检查点,而不用等待由系统规划的常规检查点(由 [第 19.5.2 节](http://www.postgres.cn/docs/9.6/runtime-config-wal.html#RUNTIME-CONFIG-WAL-CHECKPOINTS)中的设置控制)。 `CHECKPOINT`不是用来在普通操作中 使用的命令。
如果在恢复期间执行,`CHECKPOINT` 命令将强制一个重启点(见[第 30.4 节](http://www.postgres.cn/docs/9.6/wal-configuration.html) 而不是写一个新检查点。
只有超级用户能够调用`CHECKPOINT`。
## WAL相关视图与函数
```
```
## Checkpoint相关参数