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