<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>WAL on 吾爱主机</title><link>https://blog.waihost.com/tags/wal/</link><description>Recent content in WAL on 吾爱主机</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Fri, 31 Jul 2026 00:00:00 +0800</lastBuildDate><atom:link href="https://blog.waihost.com/tags/wal/index.xml" rel="self" type="application/rss+xml"/><item><title>PostgreSQL WAL、检查点与崩溃恢复：原理、参数与工程实践</title><link>https://blog.waihost.com/posts/postgresql-wal-checkpoint-crash-recovery/</link><pubDate>Fri, 31 Jul 2026 00:00:00 +0800</pubDate><guid>https://blog.waihost.com/posts/postgresql-wal-checkpoint-crash-recovery/</guid><description>&lt;img src="https://blog.waihost.com/images/covers/postgresql-wal-checkpoint-crash-recovery.svg" alt="Featured image of post PostgreSQL WAL、检查点与崩溃恢复：原理、参数与工程实践" /&gt;&lt;p&gt;生产环境里，数据库“提交成功”并不等于数据页已经刷到表文件。PostgreSQL 用 &lt;strong&gt;Write-Ahead Logging（WAL，预写日志）&lt;/strong&gt; 把变更先顺序写到日志，再异步回写堆/索引页；一旦进程或主机崩溃，就从最近检查点（checkpoint）开始 &lt;strong&gt;REDO 前滚&lt;/strong&gt;，把还没落盘的变更重放回来。理解 WAL 与检查点，才能在“提交延迟、恢复时间、磁盘占用”之间做正确取舍，而不是盲目把 &lt;code&gt;fsync&lt;/code&gt; 关掉。&lt;/p&gt;
&lt;h2 id="问题背景为什么不能每次提交都刷数据页"&gt;&lt;a href="#%e9%97%ae%e9%a2%98%e8%83%8c%e6%99%af%e4%b8%ba%e4%bb%80%e4%b9%88%e4%b8%8d%e8%83%bd%e6%af%8f%e6%ac%a1%e6%8f%90%e4%ba%a4%e9%83%bd%e5%88%b7%e6%95%b0%e6%8d%ae%e9%a1%b5" class="header-anchor"&gt;&lt;/a&gt;问题背景：为什么不能每次提交都刷数据页？
&lt;/h2&gt;&lt;p&gt;一次事务可能修改很多表页。如果每次 &lt;code&gt;COMMIT&lt;/code&gt; 都把相关数据页 &lt;code&gt;fsync&lt;/code&gt; 到磁盘：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;随机写成本高&lt;/strong&gt;：小事务会在不同数据文件之间来回寻道。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;吞吐难扩展&lt;/strong&gt;：并发提交会互相争抢刷脏页。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;恢复语义更复杂&lt;/strong&gt;：缺少统一的“已持久化边界”。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;WAL 的核心规则（官方表述）：&lt;strong&gt;对数据文件的修改，必须在描述该修改的 WAL 记录刷到永久存储之后才能写入数据页&lt;/strong&gt;。遵循这条规则后，提交时只需保证相关 WAL 已落盘，崩溃时再用日志重做（roll-forward / REDO）尚未写入数据页的变更。&lt;/p&gt;
&lt;p&gt;这带来两个工程收益：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;提交路径偏顺序写&lt;/strong&gt;：同步成本主要在 WAL 文件，而不是整页随机写。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;支持在线备份与 PITR&lt;/strong&gt;：归档 WAL + 基准备份，可回放到任意覆盖时间点。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="核心原理lsn段文件检查点与-redo"&gt;&lt;a href="#%e6%a0%b8%e5%bf%83%e5%8e%9f%e7%90%86lsn%e6%ae%b5%e6%96%87%e4%bb%b6%e6%a3%80%e6%9f%a5%e7%82%b9%e4%b8%8e-redo" class="header-anchor"&gt;&lt;/a&gt;核心原理：LSN、段文件、检查点与 REDO
&lt;/h2&gt;&lt;h3 id="1-lsn日志里的字节尺"&gt;&lt;a href="#1-lsn%e6%97%a5%e5%bf%97%e9%87%8c%e7%9a%84%e5%ad%97%e8%8a%82%e5%b0%ba" class="header-anchor"&gt;&lt;/a&gt;1. LSN：日志里的“字节尺”
&lt;/h3&gt;&lt;p&gt;每条 WAL 记录写入后，插入位置用 &lt;strong&gt;Log Sequence Number（LSN）&lt;/strong&gt; 描述，本质上是 WAL 中单调递增的字节偏移。PostgreSQL 用 &lt;code&gt;pg_lsn&lt;/code&gt; 类型表示它，可用于比较两点之间产生了多少 WAL、衡量复制与恢复进度。&lt;/p&gt;
&lt;p&gt;常用函数：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 当前 WAL 写位置
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pg_current_wal_lsn&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 计算两个 LSN 之间的字节差（备份/复制场景常用）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pg_wal_lsn_diff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pg_current_wal_lsn&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;0/0&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="2-段文件布局pg_wal-里的-16mb-段"&gt;&lt;a href="#2-%e6%ae%b5%e6%96%87%e4%bb%b6%e5%b8%83%e5%b1%80pg_wal-%e9%87%8c%e7%9a%84-16mb-%e6%ae%b5" class="header-anchor"&gt;&lt;/a&gt;2. 段文件布局：&lt;code&gt;pg_wal&lt;/code&gt; 里的 16MB 段
&lt;/h3&gt;&lt;p&gt;WAL 默认存放在数据目录下的 &lt;code&gt;pg_wal/&lt;/code&gt;（历史版本曾叫 &lt;code&gt;pg_xlog&lt;/code&gt;）。段文件通常 &lt;strong&gt;每段 16MB&lt;/strong&gt;（&lt;code&gt;initdb --wal-segsize&lt;/code&gt; 可改），段内再按页划分（常见 8kB）。文件名是递增编号，例如从 &lt;code&gt;000000010000000000000001&lt;/code&gt; 起。&lt;/p&gt;
&lt;p&gt;运维提示：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;WAL 磁盘与主数据盘分离，有助于把顺序写与随机写解耦（可在停库后迁移 &lt;code&gt;pg_wal&lt;/code&gt; 并用符号链接指回原路径）。&lt;/li&gt;
&lt;li&gt;段文件在检查点之后、确认不再需要时会被 &lt;strong&gt;回收重命名&lt;/strong&gt; 或删除；归档模式下必须先归档再回收。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="3-检查点恢复的安全锚点"&gt;&lt;a href="#3-%e6%a3%80%e6%9f%a5%e7%82%b9%e6%81%a2%e5%a4%8d%e7%9a%84%e5%ae%89%e5%85%a8%e9%94%9a%e7%82%b9" class="header-anchor"&gt;&lt;/a&gt;3. 检查点：恢复的“安全锚点”
&lt;/h3&gt;&lt;p&gt;检查点是 WAL 序列中的一个保证点：&lt;strong&gt;该点之前的堆/索引变更，都已反映到数据文件并刷盘&lt;/strong&gt;。检查点过程会：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;把脏数据页写回磁盘；&lt;/li&gt;
&lt;li&gt;在 WAL 中写入特殊的 checkpoint 记录；&lt;/li&gt;
&lt;li&gt;把检查点位置写入 &lt;code&gt;pg_control&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;崩溃恢复时，服务器：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;读 &lt;code&gt;pg_control&lt;/code&gt; 找到最新检查点；&lt;/li&gt;
&lt;li&gt;定位 checkpoint 记录中的 &lt;strong&gt;redo 起点&lt;/strong&gt;；&lt;/li&gt;
&lt;li&gt;从该 LSN &lt;strong&gt;向前扫描 WAL 做 REDO&lt;/strong&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;因此：检查点越频繁，崩溃后需要重放的 WAL 通常越少，&lt;strong&gt;恢复更快&lt;/strong&gt;；但刷脏页更勤，&lt;strong&gt;运行期 I/O 更重&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;默认自动触发条件（二者先到为准）：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;参数&lt;/th&gt;
					&lt;th&gt;含义&lt;/th&gt;
					&lt;th&gt;默认（官方文档）&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;checkpoint_timeout&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;两次自动检查点的最长时间间隔&lt;/td&gt;
					&lt;td&gt;5 分钟&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;max_wal_size&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;自动检查点期间 WAL 增长的软上限&lt;/td&gt;
					&lt;td&gt;1 GB&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;checkpoint_completion_target&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;检查点 I/O 在间隔内的目标完成比例&lt;/td&gt;
					&lt;td&gt;0.9&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;min_wal_size&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;低于该占用时优先回收复用段，而不是删除&lt;/td&gt;
					&lt;td&gt;80 MB&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;code&gt;CHECKPOINT&lt;/code&gt; SQL 可强制立即检查点，但文档明确：&lt;strong&gt;不适合作为常规业务操作&lt;/strong&gt;，一般留给维护窗口或排障。&lt;/p&gt;
&lt;h3 id="4-全页写full-page-writes与页撕裂"&gt;&lt;a href="#4-%e5%85%a8%e9%a1%b5%e5%86%99full-page-writes%e4%b8%8e%e9%a1%b5%e6%92%95%e8%a3%82" class="header-anchor"&gt;&lt;/a&gt;4. 全页写（Full Page Writes）与页撕裂
&lt;/h3&gt;&lt;p&gt;默认 &lt;code&gt;full_page_writes = on&lt;/code&gt;：检查点之后，某个数据页 &lt;strong&gt;第一次被修改&lt;/strong&gt; 时，会把 &lt;strong&gt;整页内容&lt;/strong&gt; 记入 WAL（FPI，full page image）。目的是防止崩溃时页只写了一半（torn page），仅靠“增量修改记录”无法正确还原。&lt;/p&gt;
&lt;p&gt;代价是：检查点刚结束后的一段时间内 WAL 量会冲高。高负载系统常通过合理调大 &lt;code&gt;max_wal_size&lt;/code&gt; / &lt;code&gt;checkpoint_timeout&lt;/code&gt;，避免检查点过于密集导致 FPI 风暴。&lt;/p&gt;
&lt;h2 id="提交耐久性synchronous_commit-与-fsync"&gt;&lt;a href="#%e6%8f%90%e4%ba%a4%e8%80%90%e4%b9%85%e6%80%a7synchronous_commit-%e4%b8%8e-fsync" class="header-anchor"&gt;&lt;/a&gt;提交耐久性：&lt;code&gt;synchronous_commit&lt;/code&gt; 与 &lt;code&gt;fsync&lt;/code&gt;
&lt;/h2&gt;&lt;p&gt;这是最容易踩坑的一组旋钮，必须分清 &lt;strong&gt;丢最近事务&lt;/strong&gt; 与 &lt;strong&gt;数据库损坏&lt;/strong&gt; 的差别。&lt;/p&gt;
&lt;h3 id="synchronous_commit可会话级调整"&gt;&lt;a href="#synchronous_commit%e5%8f%af%e4%bc%9a%e8%af%9d%e7%ba%a7%e8%b0%83%e6%95%b4" class="header-anchor"&gt;&lt;/a&gt;&lt;code&gt;synchronous_commit&lt;/code&gt;（可会话级调整）
&lt;/h3&gt;&lt;p&gt;控制：在向客户端返回“成功”前，WAL 需要处理到什么程度。合法值包括：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;remote_apply&lt;/code&gt;、&lt;code&gt;on&lt;/code&gt;（默认）、&lt;code&gt;remote_write&lt;/code&gt;、&lt;code&gt;local&lt;/code&gt;、&lt;code&gt;off&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;关键语义：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;非 &lt;code&gt;off&lt;/code&gt; 的本地行为：等待 &lt;strong&gt;本地 WAL 刷盘&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;off&lt;/code&gt;（异步提交）：逻辑提交完成后即可返回，不等待 WAL 落盘；崩溃可能丢失最近若干已“成功”的事务。&lt;/li&gt;
&lt;li&gt;官方强调：异步提交的风险是 &lt;strong&gt;数据丢失，不是数据损坏&lt;/strong&gt;。恢复仍会重放到最后已刷盘的 WAL，库状态自洽，只是像最近事务被干净回滚一样。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;典型用法：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 会话级：日志/埋点等可接受极短窗口丢失
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;synchronous_commit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;off&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;INTO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;event_log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;VALUES&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;...&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 默认仍是 on；按事务开始时的参数值决定该事务提交模式
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;银行出钞、扣款等“客户端会据此做外部动作”的场景，不要关同步提交。&lt;/p&gt;
&lt;h3 id="fsync集群级危险"&gt;&lt;a href="#fsync%e9%9b%86%e7%be%a4%e7%ba%a7%e5%8d%b1%e9%99%a9" class="header-anchor"&gt;&lt;/a&gt;&lt;code&gt;fsync&lt;/code&gt;（集群级，危险）
&lt;/h3&gt;&lt;p&gt;&lt;code&gt;fsync = on&lt;/code&gt; 时，PostgreSQL 通过 &lt;code&gt;fsync()&lt;/code&gt; 或等价方法（见 &lt;code&gt;wal_sync_method&lt;/code&gt;）尽量把更新真正写到磁盘，保证 OS/硬件崩溃后仍可恢复到一致状态。&lt;/p&gt;
&lt;p&gt;关掉 &lt;code&gt;fsync&lt;/code&gt; 可能换来吞吐，但在掉电/系统崩溃时可能导致 &lt;strong&gt;不可恢复损坏&lt;/strong&gt;。文档只建议在可整体重建的场景（初始装载、用完即丢的批处理库、只读克隆等）临时关闭。&lt;/p&gt;
&lt;p&gt;工程对照：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;设置&lt;/th&gt;
					&lt;th&gt;主要风险&lt;/th&gt;
					&lt;th&gt;适用&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;synchronous_commit=off&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;丢最近事务，库仍一致&lt;/td&gt;
					&lt;td&gt;可丢失的短事务&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;fsync=off&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;可能损坏，需重建&lt;/td&gt;
					&lt;td&gt;几乎仅限可重建环境&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;默认两者开启&lt;/td&gt;
					&lt;td&gt;提交稍慢，耐久性强&lt;/td&gt;
					&lt;td&gt;绝大多数生产&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="可复现观测实验实验室"&gt;&lt;a href="#%e5%8f%af%e5%a4%8d%e7%8e%b0%e8%a7%82%e6%b5%8b%e5%ae%9e%e9%aa%8c%e5%ae%9e%e9%aa%8c%e5%ae%a4" class="header-anchor"&gt;&lt;/a&gt;可复现观测实验（实验室）
&lt;/h2&gt;&lt;p&gt;下面命令假设你有一个可写的测试实例。目标是把“看不见的 WAL 机制”变成可观察指标。&lt;/p&gt;
&lt;h3 id="1看当前-wal-与检查点统计"&gt;&lt;a href="#1%e7%9c%8b%e5%bd%93%e5%89%8d-wal-%e4%b8%8e%e6%a3%80%e6%9f%a5%e7%82%b9%e7%bb%9f%e8%ae%a1" class="header-anchor"&gt;&lt;/a&gt;1）看当前 WAL 与检查点统计
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SHOW&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;wal_level&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SHOW&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fsync&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SHOW&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;synchronous_commit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SHOW&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;checkpoint_timeout&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SHOW&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;max_wal_size&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SHOW&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;min_wal_size&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SHOW&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;full_page_writes&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SHOW&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;checkpoint_completion_target&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pg_current_wal_lsn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;current_lsn&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 新版本检查点统计在 pg_stat_checkpointer
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;num_timed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;num_requested&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;num_done&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;write_time&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sync_time&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;buffers_written&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pg_stat_checkpointer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- WAL 生成量与 FPI
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;wal_records&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;wal_fpi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;wal_bytes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;wal_buffers_full&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pg_stat_wal&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;说明：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;num_timed&lt;/code&gt; / &lt;code&gt;num_requested&lt;/code&gt; 会计入完成与跳过；&lt;code&gt;num_done&lt;/code&gt; 只计真正完成的检查点。&lt;/li&gt;
&lt;li&gt;空闲实例可能因“自上次检查点以来没有 WAL”而跳过定时检查点。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="2制造-wal-并观察-lsn-前进"&gt;&lt;a href="#2%e5%88%b6%e9%80%a0-wal-%e5%b9%b6%e8%a7%82%e5%af%9f-lsn-%e5%89%8d%e8%bf%9b" class="header-anchor"&gt;&lt;/a&gt;2）制造 WAL 并观察 LSN 前进
&lt;/h3&gt;&lt;p&gt;在 &lt;code&gt;psql&lt;/code&gt; 中：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NOT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;EXISTS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;wal_lab&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bigserial&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;PRIMARY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 记录写入前 LSN（psql 变量）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pg_current_wal_lsn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;before_lsn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="n"&gt;gset&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;INTO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;wal_lab&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;repeat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;x&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;generate_series&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pg_current_wal_lsn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;after_lsn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pg_wal_lsn_diff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pg_current_wal_lsn&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;before_lsn&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;wal_bytes&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;pg_wal_lsn_diff&lt;/code&gt; 返回两个 LSN 之间的字节差，便于量化一次批量写入产生了多少 WAL。若不用 &lt;code&gt;\gset&lt;/code&gt;，可先手工抄下 &lt;code&gt;before_lsn&lt;/code&gt; 再代入比较。&lt;/p&gt;
&lt;h3 id="3对比同步异步提交延迟概念实验"&gt;&lt;a href="#3%e5%af%b9%e6%af%94%e5%90%8c%e6%ad%a5%e5%bc%82%e6%ad%a5%e6%8f%90%e4%ba%a4%e5%bb%b6%e8%bf%9f%e6%a6%82%e5%bf%b5%e5%ae%9e%e9%aa%8c" class="header-anchor"&gt;&lt;/a&gt;3）对比同步/异步提交延迟（概念实验）
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="n"&gt;timing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;on&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;LOCAL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;synchronous_commit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;on&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;INTO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;wal_lab&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;VALUES&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;sync&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;COMMIT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;LOCAL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;synchronous_commit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;off&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;INTO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;wal_lab&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;VALUES&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;async&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;COMMIT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在高并发小事务压测下，异步提交通常降低提交等待；但要清楚这是 &lt;strong&gt;用丢失窗口换延迟&lt;/strong&gt;，不是免费午餐。&lt;/p&gt;
&lt;h3 id="4强制检查点仅实验"&gt;&lt;a href="#4%e5%bc%ba%e5%88%b6%e6%a3%80%e6%9f%a5%e7%82%b9%e4%bb%85%e5%ae%9e%e9%aa%8c" class="header-anchor"&gt;&lt;/a&gt;4）强制检查点（仅实验）
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CHECKPOINT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;num_done&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;write_time&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sync_time&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;buffers_written&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pg_stat_checkpointer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;观察 &lt;code&gt;write_time&lt;/code&gt; / &lt;code&gt;sync_time&lt;/code&gt; 与 &lt;code&gt;buffers_written&lt;/code&gt; 的变化，理解检查点 I/O 的代价。&lt;/p&gt;
&lt;h2 id="工程调参与排错清单"&gt;&lt;a href="#%e5%b7%a5%e7%a8%8b%e8%b0%83%e5%8f%82%e4%b8%8e%e6%8e%92%e9%94%99%e6%b8%85%e5%8d%95" class="header-anchor"&gt;&lt;/a&gt;工程调参与排错清单
&lt;/h2&gt;&lt;h3 id="恢复时间-vs-运行期-io"&gt;&lt;a href="#%e6%81%a2%e5%a4%8d%e6%97%b6%e9%97%b4-vs-%e8%bf%90%e8%a1%8c%e6%9c%9f-io" class="header-anchor"&gt;&lt;/a&gt;恢复时间 vs 运行期 I/O
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;缩短崩溃恢复&lt;/strong&gt;：减小 &lt;code&gt;checkpoint_timeout&lt;/code&gt; / &lt;code&gt;max_wal_size&lt;/code&gt; → 检查点更勤 → REDO 更少。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;减轻检查点尖峰&lt;/strong&gt;：增大二者，并把 &lt;code&gt;checkpoint_completion_target&lt;/code&gt; 维持在接近 &lt;code&gt;0.9&lt;/code&gt;，让刷脏页摊到整个间隔。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;不要用检查点频率去“保证归档频率”&lt;/strong&gt;：若关心归档 RPO，应调 &lt;code&gt;archive_timeout&lt;/code&gt;，而不是把检查点拧得很紧。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="wal-目录膨胀"&gt;&lt;a href="#wal-%e7%9b%ae%e5%bd%95%e8%86%a8%e8%83%80" class="header-anchor"&gt;&lt;/a&gt;WAL 目录膨胀
&lt;/h3&gt;&lt;p&gt;常见原因：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;归档命令失败，段无法回收；&lt;/li&gt;
&lt;li&gt;复制槽（replication slot）落后，强制保留旧段；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;wal_keep_size&lt;/code&gt; / 相关保留设置过大；&lt;/li&gt;
&lt;li&gt;批量导入、索引重建产生大量 WAL。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;排查思路：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 归档相关（若启用）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SHOW&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;archive_mode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SHOW&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;archive_command&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SHOW&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;archive_timeout&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 当前是否在恢复（备库）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pg_is_in_recovery&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;同时检查 &lt;code&gt;pg_wal&lt;/code&gt; 磁盘占用、复制槽延迟与归档日志报错。&lt;/p&gt;
&lt;h3 id="wal_level-选型"&gt;&lt;a href="#wal_level-%e9%80%89%e5%9e%8b" class="header-anchor"&gt;&lt;/a&gt;&lt;code&gt;wal_level&lt;/code&gt; 选型
&lt;/h3&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;级别&lt;/th&gt;
					&lt;th&gt;大致用途&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;minimal&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;仅崩溃恢复所需信息，体积最小；&lt;strong&gt;不足以&lt;/strong&gt; PITR/流复制&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;replica&lt;/code&gt;（默认）&lt;/td&gt;
					&lt;td&gt;支持归档与物理复制、备库只读查询&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;logical&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;在 replica 基础上增加逻辑解码所需信息&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;生产若要做流复制或连续归档，不要用 &lt;code&gt;minimal&lt;/code&gt;；文档还指出：&lt;code&gt;max_wal_senders&lt;/code&gt; 非 0 时甚至不能以 &lt;code&gt;minimal&lt;/code&gt; 启动。&lt;/p&gt;
&lt;h3 id="硬件与虚假写成功"&gt;&lt;a href="#%e7%a1%ac%e4%bb%b6%e4%b8%8e%e8%99%9a%e5%81%87%e5%86%99%e6%88%90%e5%8a%9f" class="header-anchor"&gt;&lt;/a&gt;硬件与虚假“写成功”
&lt;/h3&gt;&lt;p&gt;WAL 要求日志先于数据页持久化。若磁盘/控制器 &lt;strong&gt;谎报写完成&lt;/strong&gt;（只进缓存未真正落盘），掉电仍可能造成不可恢复损坏。务必确认 WAL 所在存储关闭了危险写缓存，或有电池备份写缓存（BBU）。&lt;/p&gt;
&lt;h2 id="常见坑对照表"&gt;&lt;a href="#%e5%b8%b8%e8%a7%81%e5%9d%91%e5%af%b9%e7%85%a7%e8%a1%a8" class="header-anchor"&gt;&lt;/a&gt;常见坑对照表
&lt;/h2&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;现象&lt;/th&gt;
					&lt;th&gt;可能原因&lt;/th&gt;
					&lt;th&gt;处理方向&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;提交延迟高、小事务 TPS 低&lt;/td&gt;
					&lt;td&gt;每次提交都在等 WAL fsync&lt;/td&gt;
					&lt;td&gt;评估 &lt;code&gt;synchronous_commit=off&lt;/code&gt;（可丢）或提升 WAL 盘延迟；关注 group commit&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;检查点期间延迟尖刺&lt;/td&gt;
					&lt;td&gt;脏页集中刷写&lt;/td&gt;
					&lt;td&gt;调大 &lt;code&gt;max_wal_size&lt;/code&gt;/&lt;code&gt;checkpoint_timeout&lt;/code&gt;，保持 &lt;code&gt;checkpoint_completion_target≈0.9&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;崩溃恢复很久&lt;/td&gt;
					&lt;td&gt;检查点过稀，REDO 区间过长&lt;/td&gt;
					&lt;td&gt;适度降低 &lt;code&gt;max_wal_size&lt;/code&gt; 或 &lt;code&gt;checkpoint_timeout&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;pg_wal&lt;/code&gt; 撑满磁盘&lt;/td&gt;
					&lt;td&gt;归档失败 / 复制槽落后&lt;/td&gt;
					&lt;td&gt;修归档、清理无用槽、扩容并监控&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;关 &lt;code&gt;fsync&lt;/code&gt; “更快”&lt;/td&gt;
					&lt;td&gt;用损坏风险换吞吐&lt;/td&gt;
					&lt;td&gt;生产禁止；可重建场景才临时关闭&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;异步提交后“丢单”&lt;/td&gt;
					&lt;td&gt;崩溃窗口内事务未刷盘&lt;/td&gt;
					&lt;td&gt;业务上接受丢失，或关键路径保持 &lt;code&gt;on&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;检查点后 WAL 暴涨&lt;/td&gt;
					&lt;td&gt;FPI（&lt;code&gt;full_page_writes&lt;/code&gt;）&lt;/td&gt;
					&lt;td&gt;避免检查点过密；观察 &lt;code&gt;pg_stat_wal.wal_fpi&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="总结"&gt;&lt;a href="#%e6%80%bb%e7%bb%93" class="header-anchor"&gt;&lt;/a&gt;总结
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;WAL 先于数据页&lt;/strong&gt;：提交持久化的关键路径是刷 WAL，而不是立刻刷所有脏表页。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;检查点定义 REDO 起点&lt;/strong&gt;：&lt;code&gt;checkpoint_timeout&lt;/code&gt; 与 &lt;code&gt;max_wal_size&lt;/code&gt; 共同决定自动检查点节奏，直接影响崩溃恢复时间与运行期 I/O。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;synchronous_commit=off&lt;/code&gt; ≠ &lt;code&gt;fsync=off&lt;/code&gt;&lt;/strong&gt;：前者可能丢最近事务但保持一致；后者可能损坏。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;用 LSN 与统计视图观测&lt;/strong&gt;：&lt;code&gt;pg_current_wal_lsn&lt;/code&gt;、&lt;code&gt;pg_stat_checkpointer&lt;/code&gt;、&lt;code&gt;pg_stat_wal&lt;/code&gt; 把机制落到指标。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;调参要绑定目标&lt;/strong&gt;：RTO（恢复时间）、提交延迟、磁盘占用、复制/归档 RPO，不要只抄一组“神秘最优参数”。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;把这些机制吃透后，再去谈高可用、PITR、逻辑复制，才会知道每条配置背后买的是什么、卖的是什么。&lt;/p&gt;
&lt;h2 id="参考资料"&gt;&lt;a href="#%e5%8f%82%e8%80%83%e8%b5%84%e6%96%99" class="header-anchor"&gt;&lt;/a&gt;参考资料
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;a class="link" href="https://www.postgresql.org/docs/current/wal-intro.html" target="_blank" rel="noopener"
 &gt;PostgreSQL 文档：Write-Ahead Logging (WAL)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.postgresql.org/docs/current/wal-configuration.html" target="_blank" rel="noopener"
 &gt;PostgreSQL 文档：WAL Configuration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.postgresql.org/docs/current/wal-async-commit.html" target="_blank" rel="noopener"
 &gt;PostgreSQL 文档：Asynchronous Commit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.postgresql.org/docs/current/wal-internals.html" target="_blank" rel="noopener"
 &gt;PostgreSQL 文档：WAL Internals&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.postgresql.org/docs/current/runtime-config-wal.html" target="_blank" rel="noopener"
 &gt;PostgreSQL 文档：Write Ahead Log 运行参数（19.5）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.postgresql.org/docs/current/sql-checkpoint.html" target="_blank" rel="noopener"
 &gt;PostgreSQL 文档：CHECKPOINT&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.postgresql.org/docs/current/monitoring-stats.html" target="_blank" rel="noopener"
 &gt;PostgreSQL 文档：Monitoring Statistics（含 &lt;code&gt;pg_stat_checkpointer&lt;/code&gt; / &lt;code&gt;pg_stat_wal&lt;/code&gt;）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.postgresql.org/docs/current/functions-admin.html" target="_blank" rel="noopener"
 &gt;PostgreSQL 文档：系统管理函数（&lt;code&gt;pg_current_wal_lsn&lt;/code&gt; 等）&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;</description></item></channel></rss>