预写式日志(Write-Ahead Logging (WAL))

8 篇文章 0 订阅
3 篇文章 0 订阅

预写式日志 (WAL) 是一种实现事务日志的标准方法。有关它的详细描述可以在大多数(如果不是全部的话)有关事务处理的书中找到。 简而言之,WAL 的中心思想是对数据文件的修改(它们是表和索引的载体)必须是只能发生在这些修改已经记录了日志之后, 也就是说,在描述这些变化的日志记录冲刷到永久存储器之后。 如果我们遵循这个过程,那么我们就不需要在每次事务提交的时候都把数据页冲刷到磁盘,因为我们知道在出现崩溃的情况下, 我们可以用日志来恢复数据库:任何尚未附加到数据页的记录都将先从日志记录中重做(这叫向前滚动恢复,也叫做 REDO)。

25.1. WAL 的好处

使用 WAL 的第一个主要的好处就是显著地减少了磁盘写的次数。 因为在日志提交的时候只有日志文件需要冲刷到磁盘;而不是事务修改的所有数据文件。 在多用户环境里,许多事务的提交可以用日志文件的一次 fsync() 来完成。而且,日志文件是顺序写的, 因此同步日志的开销要远比同步数据页的开销要小。 这一点对于许多小事务修改数据存储的许多不同的位置更是如此。

另外一个好处就是数据页的完整性。实际情况是,在 WAL 之前,PostgreSQL 从来不能保证在崩溃的情况下数据页的完整性。 在 WAL 之前,在写的过程中的任何崩溃都可能导致:

  1. 索引记录指向一个不存在的表的行

  2. 索引记录在分裂操作中丢失

  3. 完全崩溃了的表和索引页的内容,因为数据页只写了一部分

索引的问题(问题 1 和 2)可能已经通过额外的 fsync 调用修补好了,但是如果没有 WAL,那么没有很明显的处理第三种情况的方法; WAL 在日志里保存整个数据页的内容 -- 如果那些内容在崩溃后的恢复中需要确保数据页的完整性的话。

最后,WAL 还提供了数据库在线备份和恢复(backup and restore (BAR))的可能, 就像 Section 22.3 里描述的那样。 通过归档的 WAL 文件,我们可以支持恢复到手头的 WAL 文件包含的任意时刻: 我们只需要简单地安装以前的数据库的物理备份,然后重放 WAL 到自己希望的时间。 另外,物理备份还不必是数据库状态的一个即时快照 — 如果它是花了一段时间制作的话, 因为 WAL 日志的重放将修复任何内部的不一致。


25.2. WAL 配置

有几个与 WAL 相关的参数会影响数据库性能。 本节讨论它们的使用。参阅 Section 16.4 获取有关服务器配置参数的一般信息。

检查点(Checkpoints) 是事务序列中的点, 我们保证在该点之前的所有日志信息都更新到数据文件中去了。 在检查点时,所有脏数据页都冲刷到磁盘并且向日志文件中写入一条特殊的检查点记录。 结果是,在发生崩溃的时候,恢复器就知道应该从日志中的哪个点(称做 redo 记录)开始做 REDO 操作, 因为在该记录前的对数据文件的任何修改都已经在磁盘上了。 在完成检查点处理之后,任何在 redo 记录之前写的日志段都不再需要, 因此可以循环使用或者删除。(在进行 WAL 归档的时候, 这些日志在循环利用或者删除之前必须先归档。)

服务器的后端写进程将每隔这段时间就自动执行一个检查点。 每隔 checkpoint_segments 个日志段就创建一个检查点, 或者每隔 checkpoint_timeout 秒创建一个。 以先到为准。缺省设置分别是 3 个段和 300 秒。 我们也可以用 SQL 命令 CHECKPOINT 强制一个检查点。

减少 checkpoint_segments 和/或 checkpoint_timeout 会令检查点更频繁一些。 这样就允许更快的崩溃后恢复(因为需要重做的工作更少)。不过, 我们必须在这个目的和更频繁地冲刷脏数据页所带来的额外开销之间取得平衡。 另外,为了保证数据页的一致性,在每个检查点之后的第一次数据页的变化会导致对整个页面内容的日志记录。 因此,检查点时间间隔短了会导致输出到 WAL 日志中的数据的增加,会抵销一部分缩短间隔的目标, 并且怎么着都会产生更多的磁盘 I/O。

检查点开销相当高,首先是因为它需要写出所有当前脏的缓冲区,其实是因为他们导致上面讨论的额外的后继 WAL 流量。 因此把检查点参数设置得足够高,让检查点发生的频率降低是明智的。要对你的检查点参数的一个简单自检,可以设置 checkpoint_warning 参数。如果检查点发生的间隔接近 checkpoint_warning 秒, 那么将向服务器日志输出一条消息,建议你增加 checkpoint_segments 的数值。 偶尔出现的这样的警告并不会导致警告,但是如果它出现得太频繁,那么就应该增加检查点控制参数。

至少会有一个 WAL 段文件,而且通常不会超过 2 * checkpoint_segments + 1 个文件。每个段文件通常 16MB 大(当然你可以在制作服务器的时候修改它)。你可以用这些信息来估计 WAL 需要的空间。 通常,如果一个旧的日志段文件不再需要了,那么它将得到循环使用(重命名为顺序的下一个可用段)。 如果由于短期的日志输出峰值,导致了超过 2 * checkpoint_segments + 1 个段文件, 那么到系统再次回到这个限制之内的时候,多于的段文件会被删除,而不是循环使用。

有两个常用的 WAL 函数: LogInsert 和 LogFlush。 LogInsert 用于向共享内存中的 WAL 缓冲区里加一条新的记录。如果没有空间存放新记录, 那么LogInsert 就不得不写出(向内核缓存里写)一些填满了的WAL缓冲。 我们可不想这样,因为 LogInsert 用于每次数据库低层修改(比如,记录插入), 都要花在受影响的数据页上持有一个排它锁的时间,因为该操作需要越快越好;更糟糕的是, 写 WAL 缓冲可能还会强制创建新的日志段, 它花的时间甚至更多。通常,WAL 缓冲区应该由一个 LogFlush 请求来写和冲刷, 在大部分时候它都是发生在事务提交的时候以确保事务记录被冲刷到永久存储器上去了。在那些日志输入量比较大的系统上, LogFlush 请求可能不够频繁,这样就不能避免 LogInsert 进行写操作。在这样的系统上,我们应该通过修改配置参数 wal_buffers 的值来增加 WAL 缓冲区的数量。缺省的 WAL 缓冲区数量是 8。增加这个数值将造成对应的共享内存使用量的增加。 (要注意的是,目前我们没有什么证据表明把 wal_buffers 的设置增大超过缺省是值得的。)

commit_delay 定义了后端在使用 LogInsert 向日志中写了一条已提交的记录之后, 在执行一次 LogFlush 之前休眠的毫秒数。 这样的延迟可以允许其它的后端把它们提交的记录追加到日志中,这样就可以用一次日志同步把所有日志冲刷到日志中。 如果没有打开fsync或者当前少于 commit_siblings 个其它后端处于活跃事务状态的时候则不会发生休眠; 这样就避免了在其它事务一时半会不会提交的情况下睡眠。 请注意在大多数平台上,休眠要求的分辩率是十毫秒, 所以任何介于 1 和 10000 微秒之间的非零 commit_delay 的作用都是一样的。 适用这些参数的比较好的数值还不太清楚;我们鼓励你多做试验。

wal_sync_method 参数决定PostgreSQL 如何请求内核强制将 WAL 更新输出到磁盘。只要满足可靠性,那么所有选项应该都是一样的,但是哪个最快则可能和平台密切相关。 请注意如果你关闭了 fsync,那么这个参数就无所谓了。

打开 wal_debug 配置参数(前提是 PostgreSQL编译的时候打开了这个支持) 将导致每次 LogInsert 和 LogFlush WAL 调用都被记录到服务器日志。这个选项以后可能会被更通用的机制取代。



5.3. 内部

在版本 7.1 以后,WAL 是自动打开的。 除了要求一些磁盘空间存放 WAL日志以及一些必要的调节以外(参阅Section 25.2), 对管理员没有什么其他要求,

WAL 日志存放在数据目录的 pg_xlog 目录里,它是作为一个文件段的集合存储的,通常每个段 16 MB 大。 每个段分割成多个页,通常 8K 大。日志记录头在 access/xlog.h 里描述;日志内容取决于它记录的事件的类型。 段文件的名字是递增自然数,从 000000010000000000000000 开始。目前这些数字不能循环使用, 不过要把所有可用的数字都用光也需要非常长的时间。

WAL 的缓冲区和控制结构在共享内存里, 并且由后端操纵;它们是用轻量的锁保护的。对共享内存的需求由缓冲区数量决定。 缺省的 WAL 缓冲区大小是 8 个 8 KB 的缓冲区,也就是 64KB。

日志位于和主数据库文件不同的另外一个磁盘上会比较好。 你可以通过把pg_xlog目录移动到另外一个位置( postmaster 当然得关闭), 然后在 $PGDATA 里原来的位置创建一个指向新位置的符号链接来实现。

WAL 的目的是确保在数据库记录被修改之前, 先写了日志,但是这个目的有可能被那些向内核谎报成功写的磁盘驱动器破坏, 这时候,它们实际上只是缓冲了数据而并未把数据存储到磁盘上。 这种情况下的电源失效仍然可能导致不可恢复的数据崩溃; 管理员应该确保保存 PostgreSQL 的日志文件的磁盘不会做这种虚假汇报。

在完成一个检查点并且日志文件冲刷了之后,检查点的位置保存在了文件 pg_control 里。因此在需要做恢复的时候, 后端首先读取 pg_control 和检查点记录; 然后它通过从检查点记录里标识的日志位置开始向前扫描执行 REDO 操作。 因为数据页的所有内容都保存在检查点之后的第一个页面修改的日志里, 所以自检查点以来的所有变化都将被恢复到一个一致的状态。

但是为了处理 pg_control 可能的损坏, 我们实际上应该实现对现存的日志段的反向读取顺序 -- 从最新到最老 -- 这样才能找到最后的检查点。这些还没有实现。 pg_control 很小(比一个磁盘页小),因此它出现只写了一部分的问题的概率几乎为零, 到目前为止,我们还没有看到只是说不能读取 pg_control 自身的错误。 因此,尽管这在理论上是一个薄弱环节,但是 pg_control 看起来似乎并不是实际会发生的问题。



https://www.xuchao.org/docs/postgresql/wal.html


  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值