F.2. amcheck

amcheck模块提供的功能可让您验证关系结构的逻辑一致性。如果该结构看起来有效,则不会引发任何错误。

这些函数在特定关系表示的结构中验证了各种“不变式”。索引扫描和其他重要操作背后的访问方法功能的正确性始终取决于这些不变式。例如,某些函数可以验证所有 B-Tree 页面是否具有“逻辑”Sequences 的项目(例如,对于text上的 B-Tree 索引,索引 Tuples 应按整理的词法 Sequences)。如果该特定不变式无法保持某种状态,则可以预期在受影响的页面上进行二进制搜索会错误地引导索引扫描,从而导致对 SQL 查询的错误答案。

验证使用与索引扫描本身相同的过程执行,该过程可以是用户定义的操作员类代码。例如,B-Tree 索引验证依赖于使用一个或多个 B-Tree 支持功能 1 例程进行的比较。有关操作员类别支持功能的详细信息,请参见Section 38.15.3

amcheck函数只能由超级用户使用。

F.2.1. Functions

  • bt_index_check(index regclass, heapallindexed 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 个最大目录索引的验证。对于唯一索引的子集,要求验证堆 Tuples 作为索引 Tuples 的存在。由于未引发错误,因此所有测试的索引在逻辑上似乎都是一致的。自然,可以轻松地将此查询更改为对支持验证的数据库中的每个索引调用bt_index_check

bt_index_check在目标索引及其所属的堆关系上获取AccessShareLock。此锁定模式与通过简单SELECT语句对关系获取的锁定模式相同。 bt_index_check不验证跨越子/父关系的不变式,但当* heapallindexed *为true时,将验证所有堆 Tuples 作为索引内的索引 Tuples 的存在。当在实时生产环境中需要对损坏进行例行的轻量级测试时,使用bt_index_check通常可以在验证的彻底性与限制对应用程序性能和可用性的影响之间取得最佳平衡。

  • bt_index_parent_check(index regclass, heapallindexed boolean) returns void

    • bt_index_parent_check测试其目标(即 B 树索引)是否尊重各种不变量。可选地,当* heapallindexed *参数为true时,该函数会验证应在索引中找到的所有堆 Tuples 的存在,并且索引结构中没有丢失的下行链路。 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以及所有其他 Util 命令同时处理基础关系。请注意,该函数仅在运行时才持有锁,而不是整个事务都持有。

bt_index_parent_check的附加验证更有可能检测各种病理情况。这些情况可能涉及被检查的索引使用的实现不正确的 B-Tree 运算符类,或者可能涉及基础 B-Tree 索引访问方法代码中未发现的错误。请注意,与bt_index_check不同,启用热备模式(即在只读物理副本上)时不能使用bt_index_parent_check

F.2.2. 可选的 heapallindexed 验证

当验证函数的* heapallindexed *参数为true时,将对与目标索引关系关联的表执行附加验证阶段。这由“虚拟” CREATE INDEX操作组成,该操作针对临时的内存中汇总结构(在验证的基本第一阶段需要时构建)检查所有假设的新索引 Tuples 的存在。汇总结构“指纹化”在目标索引中找到的每个 Tuples。 * heapallindexed *验证背后的高级原则是,与现有目标索引等效的新索引必须仅具有在现有结构中可以找到的条目。

额外的* heapallindexed 阶段会增加大量开销:验证通常会花费几倍的时间。但是,执行 heapallindexed *验证时获取的关系级别锁没有变化。

汇总结构的大小受maintenance_work_mem约束。为了确保对于应该在索引中表示的每个堆 Tuples 检测到不一致的失败概率不超过 2%,每个 Tuples 大约需要 2 个字节的内存。随着每个 Tuples 可用内存的减少,丢失不一致的可能性会逐渐增加。这种方法极大地限制了验证的开销,同时仅稍微降低了发现问题的可能性,特别是对于将验证视为日常维护任务的安装而言。任何单个不存在或格式错误的 Tuples 都有一个新的机会,可以通过每次新的验证尝试来检测。

F.2.3. 有效使用 amcheck

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

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

这包括由 os 归类的比较规则更改引起的问题。诸如text之类的可整理类型数据的比较必须是不可变的(就像用于 B 树索引扫描的所有比较都必须是不可变的),这意味着 os 排序规则永远不能更改。尽管很少,但对 os 整理规则的更新可能会导致这些问题。更常见的是,牵涉到主服务器和备用服务器之间的排序 Sequences 不一致,这可能是因为使用的主要os 版本不一致。这种不一致通常只会在备用服务器上出现,因此通常只能在备用服务器上检测到。

如果出现这样的问题,它可能不会影响使用受影响的排序规则排序的每个单独索引,这仅仅是因为* indexed *值可能碰巧具有相同的绝对排序,而与行为不一致无关。有关 PostgreSQL 如何使用 os 语言环境和排序规则的更多详细信息,请参见Section 23.1Section 23.2

  • 索引与被索引的堆关系之间的结构不一致(执行* heapallindexed *验证时)。

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

  • 由潜在的 PostgreSQL 访问方法代码,排序代码或事务 Management 代码中的未发现的错误引起的损坏。

索引结构完整性的自动验证在新的或建议的 PostgreSQL 功能的常规测试中扮演着重要的角色,这些新功能可能会导致引入逻辑上的不一致。验证表结构以及相关的可见性和 Transaction 状态信息的作用相似。一种明显的测试策略是在运行标准回归测试时连续调用amcheck函数。有关运行测试的详细信息,请参见Section 33.1

  • 根本不会启用校验和发生的文件系统或存储子系统故障。

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

  • 由错误的 RAM 或更广泛的内存子系统引起的损坏。

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

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

通常,amcheck仅能证明存在腐败;它不能证明它的缺失。

F.2.4. 修复腐败

amcheck引发的腐败有关的任何错误都不应为假阳性。根据定义,永远不会发生的情况下amcheck会引发错误,因此经常需要对amcheck错误进行仔细分析。

没有修复amcheck检测到的问题的通用方法。应当寻求对永久违反的根本原因的解释。 pageinspect在诊断amcheck检测到的损坏中可能扮演有用的角色。 REINDEX可能无法有效修复损坏。