支持的版本:当前 (17) / 16 / 15 / 14 / 13
开发版本:devel
不支持的版本:12 / 11 / 10

F.1. amcheck — 用于验证表和索引一致性的工具 #

amcheck 模块提供了允许您验证关系结构的逻辑一致性的函数。

B 树检查函数验证特定关系的表示结构中的各种 不变量。索引扫描和其他重要操作背后的访问方法函数的正确性依赖于这些不变量始终成立。例如,某些函数会验证,除其他事项外,所有 B 树页中的项都按 逻辑 顺序排列(例如,对于 text 上的 B 树索引,索引元组应按整理的词法顺序排列)。如果该特定不变量以某种方式未能成立,我们可以预期受影响页上的二进制搜索会错误地引导索引扫描,从而导致 SQL 查询的错误答案。如果结构看起来有效,则不会引发错误。在运行这些检查函数时,search_path 会临时更改为 pg_catalog, pg_temp

验证是使用与索引扫描本身使用的相同过程执行的,这可能是用户定义的操作符类代码。例如,B 树索引验证依赖于使用一个或多个 B 树支持函数 1 例程进行的比较。有关操作符类支持函数的详细信息,请参阅 第 36.16.3 节

与通过引发错误来报告损坏的 B 树检查函数不同,堆检查函数 verify_heapam 检查一个表并尝试返回一组行,每个检测到的损坏一行。尽管如此,如果 verify_heapam 依赖的设施本身已损坏,则该函数可能无法继续,而是可能引发错误。

执行 amcheck 函数的权限可以授予非超级用户,但在授予此类权限之前,应仔细考虑数据安全和隐私问题。虽然这些函数生成的损坏报告并未专注于已损坏数据的内容,而是更多地关注该数据的结构和发现的损坏性质,但获得执行这些函数权限的攻击者(特别是如果攻击者还能导致损坏),可能能够从这些消息中推断出数据本身的一些信息。

F.1.1. 函数 #

bt_index_check(index regclass, heapallindexed boolean, checkunique boolean) returns void

bt_index_check 测试其目标(B 树索引)是否遵守各种不变量。用法示例

test=# SELECT bt_index_check(index => c.oid, heapallindexed => i.indisunique),
               c.relname,
               c.relpages
FROM pg_index i
JOIN pg_opclass op ON i.indclass[0] = op.oid
JOIN pg_am am ON op.opcmethod = am.oid
JOIN pg_class c ON i.indexrelid = c.oid
JOIN pg_namespace n ON c.relnamespace = n.oid
WHERE am.amname = 'btree' AND n.nspname = 'pg_catalog'
-- Don't check temp tables, which may be from another session:
AND c.relpersistence != 't'
-- Function may throw an error when this is omitted:
AND c.relkind = 'i' AND i.indisready AND i.indisvalid
ORDER BY c.relpages DESC LIMIT 10;
 bt_index_check |             relname             | relpages
----------------+---------------------------------+----------
                | pg_depend_reference_index       |       43
                | pg_depend_depender_index        |       40
                | pg_proc_proname_args_nsp_index  |       31
                | pg_description_o_c_o_index      |       21
                | pg_attribute_relid_attnam_index |       14
                | pg_proc_oid_index               |       10
                | pg_attribute_relid_attnum_index |        9
                | pg_amproc_fam_proc_index        |        5
                | pg_amop_opr_fam_index           |        5
                | pg_amop_fam_strat_index         |        5
(10 rows)

此示例显示了一个会话,该会话对数据库 test 中 10 个最大的目录索引执行验证。请求对唯一索引子集验证是否存在堆元组作为索引元组。由于没有引发错误,因此所有测试的索引都显示出逻辑一致性。当然,可以很容易地更改此查询,为数据库中支持验证的每个索引调用 bt_index_check

bt_index_check 在目标索引及其所属的堆关系上获取 AccessShareLock。此锁定模式与简单 SELECT 语句在关系上获取的锁定模式相同。bt_index_check 不验证跨越父/子关系的不变量,但是当 heapallindexedtrue 时,它会验证索引中是否存在所有堆元组。当 checkuniquetrue 时,bt_index_check 将检查唯一索引中的重复项中是否只有一个可见。当需要在实时生产环境中进行例行的轻量级损坏测试时,使用 bt_index_check 通常可以在验证的彻底性与限制对应用程序性能和可用性的影响之间提供最佳权衡。

bt_index_parent_check(index regclass, heapallindexed boolean, rootdescend boolean, checkunique boolean) returns void

bt_index_parent_check 测试其目标(B 树索引)是否遵守各种不变量。可选地,当 heapallindexed 参数为 true 时,该函数会验证索引中是否存在所有应存在的堆元组。当 checkuniquetrue 时,bt_index_parent_check 将检查唯一索引中的重复项中是否只有一个可见。当可选的 rootdescend 参数为 true 时,验证会通过从根页为每个元组执行新的搜索来重新查找叶级别的元组。bt_index_parent_check 可以执行的检查是 bt_index_check 可以执行的检查的超集。可以将 bt_index_parent_check 视为 bt_index_check 的更彻底的变体:与 bt_index_check 不同,bt_index_parent_check 还检查跨越父/子关系的不变量,包括检查索引结构中是否存在任何缺失的下行链路。bt_index_parent_check 遵循一般约定,即如果发现逻辑不一致或其他问题,则引发错误。

bt_index_parent_check 需要目标索引上的 ShareLock(在堆关系上也获取 ShareLock)。这些锁可防止并发数据修改(来自 INSERTUPDATEDELETE 命令)。这些锁还可防止 VACUUM 并发处理基础关系,以及所有其他实用程序命令。请注意,该函数仅在运行时才持有锁,而不是在整个事务中持有锁。

bt_index_parent_check 的其他验证更可能检测到各种病态情况。这些情况可能涉及检查索引使用的错误实现的 B 树操作符类,或者假设地,底层 B 树索引访问方法代码中未发现的错误。请注意,与 bt_index_check 不同,当启用热备用模式时(即在只读物理副本上)无法使用 bt_index_parent_check

提示

bt_index_checkbt_index_parent_check 都会在 DEBUG1DEBUG2 严重级别输出有关验证过程的日志消息。这些消息提供了有关验证过程的详细信息,PostgreSQL 开发人员可能对此感兴趣。高级用户也可能会发现此信息有用,因为它在验证实际检测到不一致时提供了其他上下文。在交互式 psql 会话中运行

SET client_min_messages = DEBUG1;

,然后在运行验证查询之前,将显示有关验证进度的消息,并具有可管理级别的详细信息。

verify_heapam(relation regclass, on_error_stop boolean, check_toast boolean, skip text, startblock bigint, endblock bigint, blkno OUT bigint, offnum OUT integer, attnum OUT integer, msg OUT text) returns setof record

检查表、序列或物化视图是否存在结构性损坏(其中关系中的页面包含格式无效的数据)以及逻辑损坏(其中页面在结构上有效但与数据库集群的其余部分不一致)。

可识别以下可选参数

on_error_stop

如果为 true,则损坏检查会在找到任何损坏的第一个块的末尾停止。

默认为 false。

check_toast

如果为 true,则会针对目标关系的 TOAST 表检查 toast 值。

已知此选项速度较慢。此外,如果 toast 表或其索引已损坏,则将其与 toast 值进行检查可能会使服务器崩溃,尽管在许多情况下,这只会产生错误。

默认为 false。

skip

如果不是 none,则损坏检查会跳过标记为全部可见或全部冻结的块(如指定)。有效选项为 all-visibleall-frozennone

默认为 none

startblock

如果指定,则损坏检查将从指定的块开始,跳过所有先前的块。指定超出目标表中块范围的 startblock 是错误的。

默认情况下,检查从第一个块开始。

endblock

如果指定,则损坏检查将在指定的块处结束,跳过所有剩余的块。指定超出目标表中块范围的 endblock 是错误的。

默认情况下,会检查所有块。

对于检测到的每个损坏,verify_heapam 返回一行,其中包含以下列

blkno

包含损坏页面的块的编号。

offnum

损坏元组的 OffsetNumber。

attnum

元组中损坏列的属性编号(如果损坏特定于列,而不是整个元组)。

msg

描述检测到的问题的消息。

F.1.2. 可选的 heapallindexed 验证 #

当 B 树验证函数的 heapallindexed 参数为 true 时,将针对与目标索引关系相关联的表执行额外的验证阶段。这包括一个 虚拟 CREATE INDEX 操作,该操作会针对一个临时的、内存中的汇总结构(在基本的第一阶段验证期间需要时构建)检查所有假设的新索引元组是否存在。汇总结构 指纹 目标索引中找到的每个元组。heapallindexed 验证背后的高层原则是,与现有目标索引等效的新索引必须只包含在现有结构中可以找到的条目。

额外的 heapallindexed 阶段会增加显著的开销:验证通常会花费更长的时间。但是,在执行 heapallindexed 验证时,获取的关系级锁不会发生变化。

汇总结构的大小受 maintenance_work_mem 的限制。为了确保每个应该在索引中表示的堆元组检测到不一致的概率不超过 2%,每个元组大约需要 2 个字节的内存。随着每个元组可用内存的减少,错过不一致的概率会缓慢增加。这种方法显著限制了验证的开销,同时仅稍微降低了检测问题的概率,尤其是对于将验证视为例行维护任务的安装。每次新的验证尝试都有新的机会检测到任何单个缺失或格式错误的元组。

F.1.3. 有效使用 amcheck #

amcheck 可以有效地检测 数据校验和 将无法捕获的各种故障模式。这些包括:

  • 由不正确的操作符类实现导致的结构性不一致。

    这包括由操作系统排序规则更改导致的比较规则问题。诸如 text 之类的可排序类型的数据的比较必须是不可变的(就像 B 树索引扫描使用的所有比较必须是不可变的一样),这意味着操作系统排序规则永远不能更改。虽然这种情况很少见,但操作系统排序规则的更新可能会导致这些问题。更常见的情况是,主服务器和备用服务器之间的排序规则顺序不一致,可能是因为使用的 主要 操作系统版本不一致。这种不一致通常只会在备用服务器上出现,因此通常只能在备用服务器上检测到。

    如果出现这样的问题,它可能不会影响每个使用受影响的排序规则排序的索引,仅仅是因为 索引 值可能恰好具有相同的绝对顺序,而与行为不一致无关。有关 PostgreSQL 如何使用操作系统区域设置和排序规则的更多详细信息,请参阅 第 23.1 节第 23.2 节

  • 索引和索引的堆关系之间的结构性不一致(当执行 heapallindexed 验证时)。

    在正常操作期间,不会对索引及其堆关系进行交叉检查。堆损坏的症状可能很微妙。

  • 由底层 PostgreSQL 访问方法代码、排序代码或事务管理代码中假设的未发现的错误导致的损坏。

    索引结构完整性的自动验证在新或提议的 PostgreSQL 功能的通用测试中发挥作用,这些功能可能允许引入逻辑不一致。表结构以及相关的可见性和事务状态信息的验证也发挥着类似的作用。一种明显的测试策略是在运行标准回归测试时连续调用 amcheck 函数。有关运行测试的详细信息,请参阅 第 31.1 节

  • 校验和恰好未启用的文件系统或存储子系统故障。

    请注意,如果访问块时只有共享缓冲区命中,则 amcheck 会检查验证时共享内存缓冲区中表示的页面。因此,amcheck 不一定检查验证时从文件系统读取的数据。请注意,当启用校验和时,如果将损坏的块读取到缓冲区中,amcheck 可能会因校验和失败而引发错误。

  • 由故障 RAM 或更广泛的内存子系统导致的损坏。

    PostgreSQL 不会防止可纠正的内存错误,并且假定您将使用使用行业标准纠错码 (ECC) 或更好保护的 RAM 进行操作。但是,ECC 内存通常仅对单位错误免疫,不应假定其可提供针对导致内存损坏的故障的 绝对 保护。

    当执行 heapallindexed 验证时,检测单位错误的机会通常会大大增加,因为会测试严格的二进制相等性,并且会测试堆中的索引属性。

结构性损坏可能是由于错误的存储硬件,或者关系文件被不相关的软件覆盖或修改造成的。这种损坏也可以使用 数据页校验和 来检测。

相对于其内部校验和正确格式化、内部一致且正确的关系页可能仍然包含逻辑损坏。因此,这种损坏无法使用 校验和 检测到。示例包括主表中的 toasted 值,这些值在 toast 表中缺少相应的条目,以及主表中的元组的事务 ID 比数据库或集群中最旧的有效事务 ID 更旧。

在生产系统中观察到了多种导致逻辑损坏的原因,包括 PostgreSQL 服务器软件中的错误、错误和考虑不周的备份和恢复工具以及用户错误。

损坏的关系在实时生产环境中最为令人担忧,而恰恰在这些环境中,最不欢迎高风险活动。因此,verify_heapam 的设计目的是在没有不必要风险的情况下诊断损坏。它无法防范所有导致后端崩溃的原因,因为即使在严重损坏的系统上执行调用查询也可能是不安全的。目录表的访问已执行,如果目录本身已损坏,则可能会有问题。

一般来说,amcheck 只能证明存在损坏;它不能证明它不存在。

F.1.4. 修复损坏 #

amcheck 提出的关于损坏的任何错误都不应是误报。amcheck 在根据定义不应该发生的情况下引发错误,因此通常需要仔细分析 amcheck 错误。

没有通用的方法来修复 amcheck 检测到的问题。应该寻找违反不变量的根本原因的解释。pageinspect 可能在诊断 amcheck 检测到的损坏方面发挥有益的作用。REINDEX 可能无法有效地修复损坏。

提交更正

如果您在文档中看到任何不正确、与您特定功能体验不符或需要进一步说明的内容,请使用此表格报告文档问题。