合 PG插件之amcheck用于检查B-Tree索引的完整性
pg11中新增了amcheck扩展来检查B-Tree索引的完整性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | postgres@lhrpg11:~$ psql psql (11.7 (Debian 11.7-2.pgdg90+1)) Type "help" for help. postgres=# \dx; List of installed extensions Name | Version | Schema | Description ---------+---------+------------+------------------------------ plpgsql | 1.0 | pg_catalog | PL/pgSQL procedural language (1 row) postgres=# select * from pg_available_extensions where name='amcheck'; name | default_version | installed_version | comment ---------+-----------------+-------------------+-------------------------------------------- amcheck | 1.1 | | functions for verifying relation integrity (1 row) postgres=# create extension amcheck; CREATE EXTENSION postgres=# \dx; List of installed extensions Name | Version | Schema | Description ---------+---------+------------+-------------------------------------------- amcheck | 1.1 | public | functions for verifying relation integrity plpgsql | 1.0 | pg_catalog | PL/pgSQL procedural language (2 rows) postgres=# select pp.proname,pp.prosrc,pp.probin postgres-# from pg_proc pp where probin like '%amcheck%'; proname | prosrc | probin -----------------------+-----------------------+----------------- bt_index_check | bt_index_check | $libdir/amcheck bt_index_parent_check | bt_index_parent_check | $libdir/amcheck bt_index_check | bt_index_check | $libdir/amcheck bt_index_parent_check | bt_index_parent_check | $libdir/amcheck (4 rows) postgres=# postgres=# SELECT bt_index_check('idx1_data1') ; ERROR: invalid page in block 0 of relation base/16385/16479 postgres=# CREATE INDEX idxh1_data1 ON data1 USING hash (c1) ; CREATE INDEX postgres=# SELECT bt_index_check('idxh1_data1') ; ERROR: only B-Tree indexes are supported as targets for verification DETAIL: Relation 'idxh1_data1' is not a B-Tree index postgres=# |
amcheck
amcheck
模块提供的函数让用户能验证关系结构的逻辑一致性。如果结构有效,则不会发生错误。
这些函数验证特定关系的结构表达中的各种不变条件\。索引扫描以及其他重要操作背后的访问方法的正确性都要依仗这些不变条件的成立。例如,在这些函数中,有一些负责验证所有B树页面中的项都按照“逻辑”顺序(比如,对于text
上的B树索引,索引元组应该按照词典顺序排列)摆放。如果特定的不变条件由于某种原因无法成立,则我们可以预料受影响页面上的二分搜索将无法正确地引导索引扫描,最终导致SQL查询得到错误的答案。
验证过程采用索引扫描自身使用的同种过程来执行,这些过程可能是用户定义的操作符类代码。例如,B树索引验证依赖于由一个或者多个B树支持函数1例程构成的比较。操作符类支持函数的详情请见第 37.16.3 节。
amcheck
函数只能由超级用户使用。
1. 函数
bt_index_check(index regclass, heapallindexed boolean) returns void
bt_index_check
测试一个B树索引,检查各种不变条件。用法实例:123456789101112131415161718192021222324252627test=# SELECT bt_index_check(index => c.oid, heapallindexed => i.indisunique),c.relname,c.relpagesFROM pg_index iJOIN pg_opclass op ON i.indclass[0] = op.oidJOIN pg_am am ON op.opcmethod = am.oidJOIN pg_class c ON i.indexrelid = c.oidJOIN pg_namespace n ON c.relnamespace = n.oidWHERE 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.indisvalidORDER 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
时将验证所有堆元组是否作为索引中的索引元组存在。当在生产环境中要求一个使用bt_index_check
的例程进行轻量化损坏测试时,它常常需要在验证彻底性和减小对应用性能及可用性的影响之间做出权衡。bt_index_parent_check(index regclass, heapallindexed boolean, rootdescend boolean) returns void
bt_index_parent_check
测试一个B树索引,检查多种不变条件。 可选地,当heapallindexed
参数为true
时,该函数验证所有应该在索引中找到的堆元组的存在。 当可选参数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的开发人员有作用。 高级用户也许会发现这些信息很有帮助,因为它提供了额外的上下文将验证实际检测的不一致。运行:
1 | SET client_min_messages = DEBUG1; |
在运行验证查询之前的交互式psql会话中,将显示有关验证进度的消息,并具有可管理级别的详细信息。
2. 可选的heapallindexed验证
当验证函数的heapallindexed
参数为true
时,会针对与目标索引关系关联的表执行一个额外的验证过程。这种验证由一个“假的”CREATE INDEX
操作组成,它针对一个临时的、内存中的汇总结构(根据需要在基础的第一阶段验证过程中建立)检查所有假想的新索引元组的存在。这个汇总结构对目标索引中的每一个元组“采集指纹”。heapallindexed
验证背后的高层原则是:等效于现有目标索引的新索引必须仅拥有能在现有结构中找得到的项。
额外的heapallindexed
阶段会增加明显的开销:验证的时间通常将会延长几倍。不过,在执行heapallindexed
验证时,所要求的关系级锁没有变化。
这一汇总结构的尺寸以maintenance_work_mem
为界。为了确保对于每个堆元组应该存在于索引中这一检测有不超过2%的失效概率能检测到不一致,每个元组需要大约2个字节的内存。因为每个元组可用的内存变少,错失一处不一致的概率就会慢慢增加。这种方法显著地限制了验证的开销,但仅仅略微降低了检测到问题的概率,对于将验证当作例行维护任务的安装来说更是如此。对于每一次新的验证尝试,任何单一的缺失或者畸形元组都有新的机会被检测到。