amcheck
模块提供了一些函数,允许您验证关系结构的逻辑一致性。
B 树检查函数验证特定关系表示结构中的各种不变式。索引扫描和其他重要操作背后的访问方法函数的正确性依赖于这些不变式始终成立。例如,某些函数会验证,除其他外,所有 B 树页面中的项都按“逻辑”顺序排列(例如,对于 text
上的 B 树索引,索引元组应按排序的词法顺序排列)。如果该特定不变式以某种方式未能成立,我们可以预期受影响页面上的二进制搜索会错误地引导索引扫描,从而导致 SQL 查询的错误答案。如果结构看起来有效,则不会引发错误。在运行这些检查函数时,search_path 会临时更改为 pg_catalog, pg_temp
。
验证使用与索引扫描本身使用的相同过程执行,这可能是用户定义的操作符类代码。例如,B 树索引验证依赖于使用一个或多个 B 树支持函数 1 例程进行的比较。有关操作符类支持函数的详细信息,请参阅第 36.16.3 节。
与通过引发错误来报告损坏的 B 树检查函数不同,堆检查函数 verify_heapam
检查一个表,并尝试返回一组行,每个检测到的损坏对应一行。尽管如此,如果 verify_heapam
依赖的设施本身已损坏,则该函数可能无法继续,而是可能引发错误。
执行 amcheck
函数的权限可以授予非超级用户,但在授予此类权限之前,应仔细考虑数据安全和隐私问题。尽管这些函数生成的损坏报告并不侧重于已损坏数据的内容,而更多地侧重于该数据的结构和发现的损坏性质,但获得执行这些函数权限的攻击者,尤其是在攻击者还能导致损坏的情况下,可能会从这些消息中推断出一些关于数据本身的信息。
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
不验证跨越子/父关系的不变式,但当 heapallindexed
为 true
时,将验证索引中是否存在所有作为索引元组的堆元组。当 checkunique
为 true
时,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
时,该函数会验证索引中是否存在应该找到的所有堆元组。当 checkunique
为 true
时,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
)。这些锁定会阻止来自 INSERT
、UPDATE
和 DELETE
命令的并发数据修改。这些锁定还会阻止 VACUUM
并发处理基础关系,以及所有其他实用程序命令。请注意,该函数仅在运行时持有锁定,而不是在整个事务中持有锁定。
bt_index_parent_check
的额外验证更有可能检测到各种病理情况。这些情况可能涉及所检查的索引使用的错误实现的 B 树操作符类,或者假设地说,底层 B 树索引访问方法代码中未发现的错误。请注意,与 bt_index_check
不同,当启用热备模式(即在只读物理副本上)时,不能使用 bt_index_parent_check
。
bt_index_check
和 bt_index_parent_check
都会以 DEBUG1
和 DEBUG2
严重级别输出有关验证过程的日志消息。这些消息提供了有关验证过程的详细信息,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-visible 或 all-frozen 的块,如指定。有效选项为 all-visible
、all-frozen
和 none
。
默认为 none
。
startblock
如果指定,则损坏检查从指定的块开始,跳过所有之前的块。指定超出目标表中块范围的 startblock
是一个错误。
默认情况下,检查从第一个块开始。
endblock
如果指定,则损坏检查在指定的块处结束,跳过所有剩余的块。指定超出目标表中块范围的 endblock
是一个错误。
默认情况下,会检查所有块。
对于检测到的每个损坏,verify_heapam
返回一行,其中包含以下列
blkno
包含损坏页面的块的编号。
offnum
损坏元组的 OffsetNumber。
attnum
元组中损坏列的属性编号,如果损坏是特定于列而不是整个元组。
msg
描述检测到的问题的消息。
heapallindexed
验证 #当 B 树验证函数的 heapallindexed
参数为 true
时,将针对目标索引关系关联的表执行额外的验证阶段。这包括一个 “虚拟” 的 CREATE INDEX
操作,它会针对一个临时的、内存中的汇总结构检查所有假设的新索引元组是否存在(这个结构在基本的第一阶段验证期间按需构建)。该汇总结构 “指纹” 记录目标索引中找到的每个元组。 heapallindexed
验证背后的高级原则是,与现有目标索引等效的新索引,其条目只能在现有结构中找到。
额外的 heapallindexed
阶段会增加显著的开销:验证通常会花费数倍的时间。但是,执行 heapallindexed
验证时,获取的关系级锁不会发生变化。
汇总结构的大小受 maintenance_work_mem
的限制。为了确保对于每个应该在索引中表示的堆元组,检测到不一致的概率不超过 2%,每个元组大约需要 2 字节的内存。随着每个元组可用的内存减少,错过不一致的概率会缓慢增加。这种方法在显著限制验证开销的同时,仅略微降低了检测问题的概率,特别是对于将验证视为例行维护任务的安装来说。任何单个缺失或格式错误的元组,在每次新的验证尝试中都有一个新的机会被检测到。
amcheck
#amcheck
可以有效地检测 数据校验和 将无法捕获的各种类型的故障模式。这些包括:
由不正确的运算符类实现引起的结构不一致。
这包括操作系统排序规则更改引起的各种问题。像 text
这样的可排序类型数据的比较必须是不可变的(正如 B 树索引扫描使用的所有比较都必须是不可变的一样),这意味着操作系统的排序规则绝不能更改。尽管这种情况很少见,但操作系统排序规则的更新会导致这些问题。更常见的情况是,主服务器和备用服务器之间的排序规则顺序不一致,这可能是因为所使用的 主要 操作系统版本不一致。这种不一致通常只会在备用服务器上出现,因此通常只能在备用服务器上检测到。
如果出现类似问题,可能不会影响每个使用受影响排序规则排序的索引,仅仅因为 索引的 值可能恰好具有相同的绝对顺序,而与行为不一致无关。有关 PostgreSQL 如何使用操作系统区域设置和排序规则的更多详细信息,请参阅 第 23.1 节 和 第 23.2 节。
索引和索引的堆关系之间的结构不一致(当执行 heapallindexed
验证时)。
在正常操作期间,不会对索引及其堆关系进行交叉检查。堆损坏的症状可能很微妙。
由底层 PostgreSQL 访问方法代码、排序代码或事务管理代码中假设的未发现的错误引起的损坏。
索引结构完整性的自动验证在新或提议的 PostgreSQL 功能的通用测试中发挥作用,这些功能可能会引入逻辑不一致。表结构以及相关的可见性和事务状态信息的验证也发挥着类似的作用。一个明显的测试策略是在运行标准回归测试时连续调用 amcheck
函数。有关运行测试的详细信息,请参阅 第 31.1 节。
文件系统或存储子系统故障,其中碰巧没有启用校验和。
请注意,如果访问块时只有共享缓冲区命中,则 amcheck
会在验证时检查共享内存缓冲区中表示的页面。因此,amcheck
不一定会检查验证时从文件系统读取的数据。请注意,当启用校验和时,如果将损坏的块读取到缓冲区中,amcheck
可能会由于校验和失败而引发错误。
由错误的 RAM 或更广泛的内存子系统引起的损坏。
PostgreSQL 不会防止可纠正的内存错误,并且假设您将使用采用行业标准纠错码 (ECC) 或更好保护的 RAM 进行操作。但是,ECC 内存通常仅对单比特错误免疫,并且不应假定它能提供针对导致内存损坏的故障的 绝对 保护。
当执行 heapallindexed
验证时,通常会大大增加检测单比特错误的可能性,因为会测试严格的二进制相等性,并且会测试堆中的索引属性。
由于错误的存储硬件,或者不相关的软件覆盖或修改关系文件,可能会发生结构损坏。这种损坏也可以使用 数据页校验和 检测到。
相对于其内部校验和,格式正确、内部一致且正确的关系页面可能仍然包含逻辑损坏。因此,这种损坏无法使用 校验和 检测到。示例包括主表中缺少 toast 表中对应条目的 toasted 值,以及主表中事务 ID 比数据库或集群中最旧的有效事务 ID 更早的元组。
在生产系统中已经观察到多种逻辑损坏的原因,包括 PostgreSQL 服务器软件中的错误、错误和考虑不周的备份和恢复工具以及用户错误。
损坏的关系在实时生产环境中最为令人担忧,而高风险活动在这些环境中是最不受欢迎的。因此,verify_heapam
的设计目的是在没有不必要风险的情况下诊断损坏。它不能防止所有后端崩溃的原因,因为即使在严重损坏的系统上执行调用查询也可能是不安全的。访问目录表是会执行的,如果目录本身已损坏,可能会有问题。
一般来说,amcheck
只能证明存在损坏;它不能证明不存在损坏。
amcheck
引发的任何关于损坏的错误都不应是误报。amcheck
在定义上不应该发生的情况下会引发错误,因此通常需要仔细分析 amcheck
错误。
没有通用的方法来修复 amcheck
检测到的问题。应该寻求违反不变量的根本原因的解释。pageinspect 可以在诊断 amcheck
检测到的损坏方面发挥有用的作用。 REINDEX
可能无法有效修复损坏。
如果您在文档中看到任何不正确的内容,或者与您使用特定功能的经验不符,或者需要进一步澄清的内容,请使用 此表格 来报告文档问题。