选择 打开 改范围 完整检索页
受支持版本: 当前版本 (18) / 17 / 16
开发版本: 19 / devel
不受支持的版本: 13 / 12 / 11 / 10
当前 PostgreSQL 版本不在支持生命周期内。
您可以参阅当前版本的对应页面,或其他在上面列出的活跃大版本。

F.2. amcheck #

amcheck模块提供了一组函数,可用于验证索引结构的逻辑一致性。如果结构看起来有效,就不会引发错误。

这些函数会验证特定索引表示结构中的多种不变式。 索引扫描以及其他重要操作背后的访问方法函数是否正确,依赖于这些不变式始终成立。 例如,某些函数除其他事项外,还会验证所有 B-树页面中的项都按逻辑顺序排列 (例如,对于text上的 B-树索引,索引元组应当按排序规则定义的词法顺序排列)。 如果这种特定不变式由于某种原因不再成立,那么受影响页面上的二分查找就可能错误地引导索引扫描, 从而使 SQL 查询返回错误答案。

验证工作使用的过程与索引扫描本身所使用的过程相同,而这些过程可能是用户定义的操作符类代码。 例如,B-树索引验证依赖于一个或多个 B-树支持函数 1 例程执行的比较。 关于操作符类支持函数的细节,见Section 37.14.3

只有超级用户才能使用amcheck函数。

F.2.1. 函数

bt_index_check(index regclass) returns void

bt_index_check测试其目标 B-树索引是否满足多种不变式。示例用法如下:

test=# SELECT bt_index_check(c.oid), 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 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)

本例展示了一个会话,用于验证最大的 10 个系统目录索引,这些索引位于数据库test中。由于没有引发错误,因此所有受测索引看起来都具有逻辑一致性。当然,也很容易修改这个查询,使其调用bt_index_check来验证数据库中每一个支持验证的索引。

bt_index_check会在目标索引及其所属的堆关系上获取 AccessShareLock。这种锁模式与简单 SELECT语句在关系上获取的锁模式相同。 bt_index_check不会验证跨越父子关系的不变式, 也不会验证目标索引是否与其堆关系一致。 当在线生产环境中需要一种例行的、轻量级的损坏检查时, bt_index_check通常能在验证彻底程度与对应用性能、可用性的影响之间提供最佳权衡。

bt_index_parent_check(index regclass) returns void

bt_index_parent_check测试其目标 B-树索引是否满足多种不变式。 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)。这些锁会阻止 INSERTUPDATE以及 DELETE命令并发修改数据。 这些锁还会阻止底层关系被并发VACUUM处理,以及执行所有其他实用命令。 注意,该函数只在运行期间持有这些锁,而不是在整个事务期间持有。

bt_index_parent_check所做的额外验证,更有可能检测出各种异常情形。 这些情形可能涉及被检查索引所使用的 B-树操作符类实现错误, 或者假设存在的、底层 B-树索引访问方法代码中尚未发现的缺陷。 请注意,与bt_index_check不同, 在启用热备模式时(即在只读物理副本上),不能使用 bt_index_parent_check

F.2.2. 有效使用amcheck

amcheck能够有效检测出多种数据页校验和始终无法捕捉的故障模式,包括:

  • 由操作符类实现错误导致的结构不一致。

    这也包括因操作系统排序规则的比较规则发生变化而引起的问题。 像text这类可排序类型的数据值之间的比较必须是不可变的 (正如用于 B-树索引扫描的所有比较都必须不可变一样), 这就意味着操作系统排序规则绝不能发生变化。 虽然这种情况比较少见,但操作系统排序规则的更新确实可能导致此类问题。 更常见的是主库与备库之间的排序顺序不一致, 这可能是因为两边使用的操作系统版本不同。 这类不一致通常只会出现在备库上,因此通常也只能在备库上检测到。

    如果出现此类问题,它未必会影响每一个按受影响排序规则排序的索引, 因为被索引的值也可能恰好在行为不一致的情况下仍具有相同的绝对顺序。 关于PostgreSQL如何使用操作系统区域设置和排序规则的更多细节, 见Section 23.1Section 23.2

  • 假设存在的、底层PostgreSQL访问方法代码、 排序代码中尚未发现的缺陷所导致的损坏。

    对索引结构完整性的自动验证,在对新的或拟议中的 PostgreSQL特性进行一般性测试时具有作用, 因为这些特性完全可能引入逻辑不一致。 一种显而易见的测试策略,是在运行标准回归测试时持续调用 amcheck函数。 关于如何运行测试,见Section 32.1

  • 在禁用数据校验和时,由文件系统或存储子系统故障造成的损坏。

    请注意,如果访问某个块时只是命中了共享缓冲区, 那么amcheck检查的是验证时该页面在某个共享内存缓冲区中的表示。 因此,amcheck并不一定会检查验证时从文件系统读入的数据。 另请注意,当启用了校验和时,如果某个损坏块被读入缓冲区, amcheck可能会因为校验和失败而引发错误。

  • 由有缺陷的 RAM 或更广义的内存子系统导致的损坏。

    PostgreSQL并不防御可纠正的内存错误, 并且假定所用 RAM 采用业界标准的纠错码(ECC)或更强的保护机制。 然而,ECC 内存通常只对单比特错误有效, 不应被视为能对导致内存损坏的故障提供绝对保护。

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

F.2.3. 修复损坏

amcheck报告的与损坏有关的错误绝不应被当作误报。实际中,amcheck更可能发现软件缺陷,而不是硬件问题。amcheck会在那些按定义绝不应该发生的情况下抛出错误,因此通常需要对amcheck错误进行仔细分析。

对于amcheck检测到的问题,并不存在通用的修复方法。 应当查明导致不变式遭到破坏的根本原因。 在诊断amcheck检测到的损坏时, pageinspect可能会发挥有用作用。 REINDEX未必能够有效修复损坏。