F.2. amcheck #
amcheck 模块提供了一组函数,可用于验证索引结构的逻辑一致性。如果结构看起来有效,就不会引发错误。
这些函数会验证特定索引表示结构中的多种不变式。索引扫描以及其他重要操作背后的访问方法函数是否正确,依赖于这些不变式始终成立。例如,某些函数除其他事项外,还会验证所有 B-树页面中的项都按“逻辑”顺序排列(例如,对于 text 上的 B-树索引,索引元组应当按排序规则定义的词法顺序排列)。如果这种特定不变式由于某种原因不再成立,那么受影响页面上的二分查找就可能错误地引导索引扫描,从而使 SQL 查询返回错误答案。
验证工作使用的过程与索引扫描本身所使用的过程相同,而这些过程可能是用户定义的操作符类代码。例如,B-树索引验证依赖于一个或多个 B-树支持函数 1 例程执行的比较。关于操作符类支持函数的细节,见第 37.14.3 节。
只有超级用户才能使用 amcheck 函数。
F.2.1. 函数
bt_index_check(index regclass) returns voidbt_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 voidbt_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)。这些锁会阻止INSERT、UPDATE以及DELETE命令并发修改数据。这些锁还会阻止底层关系被并发VACUUM处理,以及执行所有其他实用命令。注意,该函数只在运行期间持有这些锁,而不是在整个事务期间持有。bt_index_parent_check所做的额外验证,更有可能检测出各种异常情形。这些情形可能涉及被检查索引所使用的 B-树操作符类实现错误,或者假设存在的、底层 B-树索引访问方法代码中尚未发现的缺陷。请注意,与bt_index_check不同,在启用热备模式时(即在只读物理副本上),不能使用bt_index_parent_check。
F.2.2. 有效使用 amcheck
amcheck 能够有效检测出多种数据页校验和始终无法捕捉的故障模式,包括:
由操作符类实现错误导致的结构不一致。
这也包括因操作系统排序规则的比较规则发生变化而引起的问题。像
text这类支持排序规则的类型的 datum 之间的比较必须是不可变的(正如用于 B-树索引扫描的所有比较都必须不可变一样),这就意味着操作系统排序规则绝不能发生变化。虽然这种情况比较少见,但操作系统排序规则的更新确实可能导致此类问题。更常见的是主库与备库之间的排序顺序不一致,这可能是因为两边使用的操作系统主版本不同。这类不一致通常只会出现在备库上,因此通常也只能在备库上检测到。如果出现此类问题,它未必会影响每一个按受影响排序规则排序的索引,因为被索引的值也可能恰好在行为不一致的情况下仍具有相同的绝对顺序。关于 PostgreSQL 如何使用操作系统区域设置和排序规则的更多细节,见第 23.1 节和第 23.2 节。
假设存在的、底层 PostgreSQL 访问方法代码、排序代码中尚未发现的缺陷所导致的损坏。
对索引结构完整性的自动验证,在对新的或拟议中的 PostgreSQL 特性进行一般性测试时具有作用,因为这些特性完全可能引入逻辑不一致。一种显而易见的测试策略,是在运行标准回归测试时持续调用
amcheck函数。关于如何运行测试,见第 32.1 节。在未启用校验和时发生的文件系统或存储子系统故障。
请注意,如果访问某个块时只是命中了共享缓冲区,那么
amcheck检查的是验证时该页面在某个共享内存缓冲区中的表示。因此,amcheck并不一定会检查验证时从文件系统读入的数据。另请注意,当启用了校验和时,如果某个损坏块被读入缓冲区,amcheck可能会因为校验和失败而引发错误。由有缺陷的 RAM 或更广义的内存子系统导致的损坏。
PostgreSQL 并不防御可纠正的内存错误,并且假定所用 RAM 采用业界标准的纠错码(ECC)或更强的保护机制。然而,ECC 内存通常只对单比特错误有效,不应被视为能对导致内存损坏的故障提供绝对保护。
一般来说,amcheck 只能证明损坏存在,无法证明损坏不存在。
F.2.3. 修复损坏
amcheck 报告的与损坏有关的错误都不应是误报。实际中,amcheck 更可能发现软件缺陷,而不是硬件问题。amcheck 会在那些按定义绝不应该发生的情况下抛出错误,因此通常需要对 amcheck 错误进行仔细分析。
对于 amcheck 检测到的问题,并不存在通用的修复方法。应当查明导致不变式遭到破坏的根本原因。在诊断 amcheck 检测到的损坏时,pageinspect 可能会发挥有用作用。REINDEX 未必能够有效修复损坏。