管理工具、访问方法与完整性检查
管理工具、访问方法与完整性检查
本文件包含模块:adminpack(管理函数)、amcheck(一致性校验)、auth_delay(认证延迟)、auto_explain(自动执行计划)、bloom(Bloom 过滤索引)、btree_gin/btree_gist(在 GiST/GIN 中支持 B-tree 等值类型)。
adminpack
adminpack 提供了一些支持函数,例如服务器日志文件的远程管理。所有这些函数默认只允许由超级用户使用,但是可以通过使用 GRANT 命令允许其他用户使用。
表 D.1中展示的函数提供了文件写入的功能。对于这些函数,只有位于数据库群集目录中的文件才能被访问,允许使用相对路径和绝对路径。
表 D.1. adminpack Functions
函数/描述:
pg_catalog.pg_file_write ( filename text, data text, append boolean ) →bigint:写入或追加到一个文本文件.pg_catalog.pg_file_sync ( filename text ) → void:刷新一个文件或目录到硬盘。pg_catalog.pg_file_rename (oldname text, newname text [, archivename text ] )→ Boolean:重命名一个文件。pg_catalog.pg_file_unlink ( filename text ) → boolean:移除一个文件。pg_catalog.pg_logdir_ls () → setof record:列出log_directory 目录中的日志文件。
pg_file_write 把指定的 data 写入到filename 命名的文件中。如果 append 为假,文件不能已经存在。如果 append 为真,该文件可能已经存在,存在就会被追加。这个函数返回写入的字节数。
pg_file_sync fsyncs由filename命名的指定的文件或目录。在失败时会出现错误 (例如:指定的文件不存在)。注意 data_sync_retry 对此函数没有影响,因此在刷新数据库文件失败的时候不会引发PANIC-级别的错误。
pg_file_rename重命名一个文件。如果 archivename被省略或者为 NULL,它简单地把 oldname重命名为newname(不能已经存在)。如果提供了archivename,该函数首先把newname重命名为archivename(不 能已经存在),然后把oldname重命名为 newname。当第二次重命名失败时,这个函数会在报告错误之前尝试把archivename重命名成 newname。成功时函数返回真,如果源文件不存在或者不可写,则返回假,其他情况下会抛出错误。
pg_file_unlink移除指定的文件。成功时函数返回真,如果指定的文件不存在或者unlink()调用失败,则返回假,其他情况下会抛出错误。
pg_logdir_ls返回log_directory 目录中所有日志文件的开始时间戳以及路径名。要使用这个函数,log_filename参数必须具有其默认设置( postgresql-%Y-%m-%d_%H%M%S.log)。
amcheck
amcheck 模块提供的函数让用户能验证关系结构的逻辑一致性。如果结构有效,则不会发生错误。
这些函数验证特定关系的结构,表达各种的不变条件。索引扫描以及重要操作背后的访问方法正确性,都要依仗这些不变条件的成立。例如,在这些函数中,有一些负责验证所有B树页面中的项都按照"逻辑"顺序(比如,对于text上的B树索引,索引元组应该按照词典顺序排列)摆放。如果特定的不变条件由于某种原因无法成立,则可以预料受影响页面上的二分搜索将无法正确地引导索引扫描,最终导致SQL查询得到错误的答案。
验证过程采用索引扫描自身,使用的同种过程来执行,这些过程可能是用户定义的操作符类代码。例如,B树索引验证依赖于由一个或者多个B树支持函数1例程构成的比较。
amcheck函数只能由超级用户使用。
函数
bt_index_check(index regclass, heapallindexed boolean) returns void
bt_index_check测试一个B树索引,检查各种不变条件。用法实例:
test=# create extension amcheck;
est=# SELECT bt_index_check(index => c.oid, heapallindexed => i.indisunique),
test-# c.relname,
test-# c.relpages
test-# FROM pg_index i
test-# JOIN pg_opclass op ON i.indclass[0] = op.oid
test-# JOIN pg_am am ON op.opcmethod = am.oid
test-# JOIN pg_class c ON i.indexrelid = c.oid
test-# JOIN pg_namespace n ON c.relnamespace = n.oid
test-# WHERE am.amname = 'btree' AND n.nspname = 'pg_catalog'
-- Don't check temp tables, which may be from another session:
test-# AND c.relpersistence != 't'
-- Function may throw an error when this is omitted:
test-# AND c.relkind = 'i' AND i.indisready AND i.indisvalid
test-# ORDER BY c.relpages DESC LIMIT 10;
bt_index_check | relname | relpages
----------------+---------------------------------+----------
| pg_depend_depender_index | 53
| pg_depend_reference_index | 53
| pg_proc_proname_args_nsp_index | 36
| pg_description_o_c_o_index | 22
| pg_attribute_relid_attnam_index | 18
| pg_proc_oid_index | 13
| pg_attribute_relid_attnum_index | 12
| pg_collation_name_enc_nsp_index | 10
| pg_collation_oid_index | 7
| pg_operator_oprname_l_r_n_index | 7
(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 严重性级别。这些消息提供关于验证过程的详细信息,或许对Halo的开发人员有作用。高级用户也许会发现这些信息很有帮助,因为它提供了额外的上下文将验证实际检测的不一致。运行: SET client_min_messages = DEBUG1; 在运行验证查询之前的交互式psql会话中,将显示有关验证进度的消息,并具有可管理级别的详细信息。
可选的heapallindexed验证
当验证函数的heapallindexed参数为true时,会针对与目标索引关系关联的表,执行一个额外的验证过程。这种验证由一个"假的"CREATE INDEX操作组成,它针对一个临时的、内存中的汇总结构(根据需要在基础的第一阶段验证过程中建立)检查所有假想的新索引元组的存在。这个汇总结构对目标索引中的每一个元组"采集指纹"。heapallindexed验证背后的高层原则是:等效于现有目标索引的新索引,必须仅拥有能在现有结构中找得到的项。
额外的heapallindexed阶段会增加明显的开销:验证的时间通常将会延长几倍。不过,在执行heapallindexed验证时,所要求的关系级锁没有变化。
这一汇总结构的尺寸以maintenance_work_mem为界。为了确保对于每个堆元组存在于索引中,这一检测不超过2%的失效概率,能检测到不一致,每个元组需要大约2个字节的内存。
因为每个元组可用的内存变少,错失一处不一致的概率就会慢慢增加。这种方法显著地限制了验证的开销,但仅仅略微降低了检测到问题的概率,将验证当作例行维护任务的安装来说更是如此。对于每一次新的验证尝试,任何单一的缺失或者畸形元组都有机会被检测到。
有效地使用amcheck
amcheck对于检测多种数据页面校验和无法捕捉到的失效模式非常有效。包括:
• 由不正确的操作符类实现,导致的结构性不一致。
这包括操作系统排序规则的比较,规则变化导致的问题。text之类的可排序类型数据的比较必须是不变的(正如用于B-树索引扫描的所有比较必须不变一样),这意味着操作系统排序规则必须保持不变。很少的情况下,操作系统排序规则的更新会导致这些问题。
更常见的,主服务器和后备服务器之间排序顺序的不一致会相互牵连,这可能是因为使用的主操作系统版本不一致。这类不一致通常仅出现在后备服务器上,因此也仅能在后备服务器上检测到。
如果这类问题出现,它可能不会影响使用,受影响排序规则的每一个索引,其原因是被索引值可能正好具有与行为不一致无关的相同的绝对顺序。
• 索引和被索引的关系之间的结构不一致(在执行heapallindexed验证时)。
在普通操作时,没有将索引针对其关系进行交叉检查。堆损坏的症状可能是很微妙的。
• 由于底层访问方法代码、排序代码或者事务管理代码中(假想的)未发现的缺陷导致的损坏。
在测试可能引入逻辑不一致的新特性或者被提议的特性时,索引的结构完整性自动验证扮演了重要角色。表结构、相关的可见性和事务状态信息的验证扮演了类似的角色。一种显而易见的测试策略是在运行标准回归测试时持续地调用amcheck函数。
• 正巧没有开启校验和的文件系统或者存储子系统故障。
注意,如果在访问块时仅有一次共享缓存命中,验证时amcheck会在检查表示在某个共享内存缓冲区中的页面。因此,amcheck没有必要在验证时检查从文件系统读出的数据。注意:当校验和被启用时,如果一个损坏的块被读取到缓冲区中,amcheck可能会由于校验和失效而产生错误。
• 有缺陷的RAM或者内存子系统导致的损坏。
Halo无法提供针对可更正内存错误的保护,并且它假定用户使用的是具有工业标准纠错码(ECC)或更好保护技术的RAM。不过,ECC内存通常只能免疫单个位错误,并且不应该假定它能提供对导致内存损坏失效的绝对保护。
在执行heapallindexed验证时,通常有大幅增加的机会可以检测单个位错误,因为会测试严格的二元等值并且会在堆中测试被索引属性。
通常,amcheck仅能证明损坏的存在,但它无法证明损坏不存在。
修复损坏
amcheck没有产生与损坏相关的错误绝不应该被当做假阳性。amcheck会在(定义上)应该绝不会发生的情况中抛出错误,因此常常需要对amcheck错误进行仔细地分析。
对于amcheck检测到的问题没有一般性的修复方法。应该寻找产生不变条件违背的根本原因。在诊断amcheck检测到的损坏时,pageinspect可能会扮演一个非常有用的角色。REINDEX在修复损坏过程中可能无法起到效果。
auth_delay
auth_delay使鉴定失败之前短暂地停顿一会儿,这对数据库口令的蛮力攻击更加困难。注意:它无助于组织拒绝服务攻击,甚至可能会加剧它们,因为在报告鉴定失败之前,等待的进程仍然要消耗连接。
要使之工作,该模块必须通过postgresql.conf中的shared_preload_libraries载入。
配置参数
auth_delay.milliseconds (int)
在报告鉴定失败之前等待的毫秒数。默认值为0。
这些参数必须在postgresql.conf中设置。典型的使用是:
# postgresql.conf
shared_preload_libraries = 'auth_delay'
auth_delay.milliseconds = '500'
auto_explain
auto_explain模块提供了一种方式来自动记录慢速语句的执行计划,而不需要手工运行EXPLAIN。这在大型应用中追踪未被优化的查询时有用。
该模块没有提供 SQL 可访问的函数。要使用它,简单地将它载入服务器。你可以把它载入到一个单独的会话:
LOAD 'auto_explain';
(你必须作为超级用户来这样做)。更典型的用法是通过在postgresql.conf的session_preload_libraries或shared_preload_libraries参数中包括auto_explain将它预先载入到某些或者所有会话中。然后你就可以追踪那些出乎意料地慢的查询,而不管它们何时发生。当然为此会付出一些额外的负荷作为代价。
配置参数
有几个配置参数用来控制auto_explain的行为。注意默认行为是什么也不做,因此如果你想要任何结果就必须至少设置auto_explain.log_min_duration。
auto_explain.log_min_duration (integer)
auto_explain.log_min_duration是最小语句执行时间(以毫秒计),这将导致语句的计划被记录。这个参数设置为0时将记录所有计划,设置为-1(默认值)时禁用记录计划。
例如,如果你将它设置为250ms,则所有运行时间等于或超过 250ms 的语句将被记录。
只有超级用户能够改变这个设置。
auto_explain.log_analyze (boolean)
当一个执行计划被记录时,auto_explain.log_analyze导致EXPLAIN ANALYZE输出(而不仅仅是EXPLAIN输出)被打印。默认情况下这个参数是关闭的。只有超级用户能够改变这个设置。
注意:当这个参数为打开时,对所有被执行的语句将引起对每个计划节点的计时,不管它们是否运行得足够长以至于被记录。这可能对性能有极度负面的影响。
auto_explain.log_buffers (boolean)
当一个执行计划被记录时,auto_explain.log_buffers控制是否打印缓冲区使用统计信息;它等效于EXPLAIN的BUFFERS选项。除非 auto_explain.log_analyze参数被设置,否则这个参数没有效果。这个参数默认情况下是关闭的。只有超级用户能够改变这个设置。
auto_explain.log_wal (boolean)
auto_explain.log_wal 控制当执行计划被记录时是否打印WAL使用情况统计信息; 它相当于EXPLAIN 的 WAL 选项。除非启用了auto_explain.log_analyze,否则这个参数不生效。这个参数默认是关闭状态。只有超级用户能够改变这个设置。
auto_explain.log_timing (boolean)
当一个执行计划被记录时,auto_explain.log_timing控制是否打印每个结点上的计时信息;它等效于EXPLAIN的TIMING选项。重复读取系统锁的开销在某些系统上可能会显著地拖慢查询,因此当只需要实际行计数而非确切时间时,关闭这个参数将会很有帮助。只有当auto_explain.log_analyze也被启用时这个参数才有效。这个参数默认情况下是打开的。只有超级用户能够改变这个设置。
auto_explain.log_triggers (boolean)
当一个执行计划被记录时,auto_explain.log_triggers会导致触发器执行统计信息被包括在内。只有当auto_explain.log_analyze也被启用时这个参数才有效。这个参数默认情况下是关闭的。只有超级用户能够改变这个设置。
auto_explain.log_verbose (boolean)
当一个执行计划被记录时,auto_explain.log_verbose控制是否打印很长的详细信息;它等效于EXPLAIN的VERBOSE选项。这个参数默认情况下是关闭的。只有超级用户能够改变这个设置。
auto_explain.log_settings (boolean)
auto_explain.log_settings控制执行计划被日志记录时是否打印关于已修改的配置选项的信息。输出中仅包含影响查询计划的选项,其值与内置缺省值不同。该参数默认关闭。仅超级用户可修改该设置。
auto_explain.log_format (enum)
auto_explain.log_format选择要使用的EXPLAIN输出格式。允许的值是text、xml、json和yaml。默认是文本形式。只有超级用户能够改变这个设置。
auto_explain.log_level (enum)
auto_explain.log_level选择日志的级别,在此级别上,auto_explain会记录查询计划。有效值为DEBUG5, DEBUG4,DEBUG3, DEBUG2, DEBUG1, INFO, NOTICE, WARNING和LOG。
缺省为LOG。仅超级用户可修改该设置。
auto_explain.log_nested_statements (boolean)
auto_explain.log_nested_statements导致嵌套语句(在一个函数内执行的语句)会被考虑在记录范围之内。当它被关闭时,只有顶层查询计划被记录。这个参数默认情况下是关闭的。只有超级用户能够改变这个设置。
auto_explain.sample_rate (real)
auto_explain.sample_rate会让 auto_explain 只解释每个会话中的一部分语句。默认值为 1,表示解释所有的查询。在嵌套语句的情况下,要么所有语句都被解释,要么一个也不被解释。只有超级用户能够更改这个设置。
在普通用法中,这些参数都在postgresql.conf中设置,不过超级用户可以在他们自己的会话中随时修改这些参数。典型的用法可能是:
# postgresql.conf
session_preload_libraries = 'auto_explain'
auto_explain.log_min_duration = '3s'
例子
test=# SET auto_explain.log_min_duration = 0;
SET
test=# SET auto_explain.log_analyze = true;
SET
test=# SELECT count(*)
test-# FROM pg_class, pg_index
test-# WHERE oid = indrelid AND indisunique;
count
-------
161
(1 row)
bloom
bloom提供了一种基于布隆过滤器的索引访问方法。
布隆过滤器是一种空间高效的数据结构,它被用来测试一个元素是否为一个集合的成员。
在索引访问方法的情况下,它可以通过尺寸在索引创建时决定的签名来快速地排除不匹配的元组。
签名是被索引属性的一种有损表达,并且因此容易报告伪肯定,也就是说对于一个不在集合中的元素有可能报告该元素在集合中。因此索引搜索结果必须使用来自堆项的实际属性值进行再次检查。较大的签名可以降低伪肯定的几率并且减少无用的堆访问的次数,但是这显然会让索引更大且扫描起来更慢。
当表具有很多属性并且查询可能会测试其中任意组合时,这种类型的索引最有用。传统的btree 索引比布鲁姆索引更快,但是需要很多 btree 索引来支持所有可能的查询,而对于布鲁姆索引来说只需要一个即可。不过要注意 bloom 索引只支持等值查询,而 btree 索引还能执行不等和范围搜索。
参数
bloom索引在其WITH子句中接受下列参数:
length每个签名(索引项)的长度位数,它会被圆整成为最近的16的倍数。默认是80位,最长是4096位。
col1 --- col32从每一个索引列产生的位数。每个参数的名字表示它所控制的索引列的编号。默认是2位,最大是4095位。没有实际使用的索引列的参数会被忽略。
例子
这是一个创建bloom的例子:
test=# CREATE INDEX bloomidx ON tbloom USING bloom (i1,i2,i3)
test-# WITH (length=80, col1=2, col2=2, col3=4);
CREATE INDE
该索引是用长度为 80 位的签名所创建,其中属性 i1 和 i2 被映射为 2 位,属性 i3 被映射为 4 位。我们可以省略length、col1和col2说明,因为它们都有默认值。
这里是布隆索引定义和使用的更完整的例子,其中还与等效的 btree 做了对比。布隆索引比 btree 索引更小,并且效率更高。
test-# (random() * 1000000)::int as i1,
test-# (random() * 1000000)::int as i2,
test-# (random() * 1000000)::int as i3,
test-# (random() * 1000000)::int as i4,
test-# (random() * 1000000)::int as i5,
test-# (random() * 1000000)::int as i6
test-# FROM
test-# generate_series(1,10000000);
SELECT 10000000
在这个大表上的顺序扫描需要很长时间:
test=# EXPLAIN ANALYZE SELECT * FROM tbloom WHERE i2 = 898732 AND i5 = 123451;
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------
Gather (cost=1000.00..127220.72 rows=250 width=24) (actual time=414.282..415.411 rows=0 loops=1)
Workers Planned: 2
Workers Launched: 2
-> Parallel Seq Scan on tbloom (cost=0.00..126195.72 rows=104 width=24) (actual time=410.361..410.361 rows=0 loop
s=3)
Filter: ((i2 = 898732) AND (i5 = 123451))
Rows Removed by Filter: 3333333
Planning Time: 0.176 ms
Execution Time: 415.484 ms
(8 rows)
即使定义了btree索引,结果将仍然是顺序扫描:
test=# CREATE INDEX btreeidx ON tbloom (i1, i2, i3, i4, i5, i6);
CREATE INDEX
test=# SELECT pg_size_pretty(pg_relation_size('btreeidx'));
pg_size_pretty
----------------
386 MB
(1 row)
test=# EXPLAIN ANALYZE SELECT * FROM tbloom WHERE i2 = 898732 AND i5 = 123451;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------
Gather (cost=1000.00..127195.10 rows=1 width=24) (actual time=220.290..221.186 rows=0 loops=1)
Workers Planned: 2
Workers Launched: 2
-> Parallel Seq Scan on tbloom (cost=0.00..126195.00 rows=1 width=24) (actual time=216.318..216.319 rows=0 loops=
3)
Filter: ((i2 = 898732) AND (i5 = 123451))
Rows Removed by Filter: 3333333
Planning Time: 0.165 ms
Execution Time: 221.241 ms
(8 rows)
在处理这类搜索时,bloom 比 btree 更好:
test=# CREATE INDEX bloomidx ON tbloom USING bloom (i1, i2, i3, i4, i5, i6);
CREATE INDEX
test=# SELECT pg_size_pretty(pg_relation_size('bloomidx'));
pg_size_pretty
----------------
153 MB
(1 row)
现在,btree 搜索的主要问题是:当搜索条件不约束前几个索引列时,btree 的效率不好。
对于 btree 更好的策略是在每一列上创建一个独立的索引。那么规划器将选择这样的计划:
test=# CREATE INDEX btreeidx1 ON tbloom (i1);
CREATE INDEX
test=# CREATE INDEX btreeidx2 ON tbloom (i2);
CREATE INDEX
test=# CREATE INDEX btreeidx3 ON tbloom (i3);
CREATE INDEX
test=# CREATE INDEX btreeidx4 ON tbloom (i4);
CREATE INDEX
test=# CREATE INDEX btreeidx5 ON tbloom (i5);
CREATE INDEX
test=# CREATE INDEX btreeidx6 ON tbloom (i6);
CREATE INDEX
test=# EXPLAIN ANALYZE SELECT * FROM tbloom WHERE i2 = 898732 AND i5 = 123451;
QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------
Bitmap Heap Scan on tbloom (cost=9.28..13.29 rows=1 width=24) (actual time=0.659..0.664 rows=0 loops=1)
Recheck Cond: ((i5 = 123451) AND (i2 = 898732))
-> BitmapAnd (cost=9.28..9.28 rows=1 width=0) (actual time=0.657..0.660 rows=0 loops=1)
-> Bitmap Index Scan on btreeidx5 (cost=0.00..4.51 rows=10 width=0) (actual time=0.627..0.627 rows=5 loops=
1)
Index Cond: (i5 = 123451)
-> Bitmap Index Scan on btreeidx2 (cost=0.00..4.52 rows=11 width=0) (actual time=0.025..0.025 rows=10 loops
=1)
Index Cond: (i2 = 898732)
Planning Time: 0.416 ms
Execution Time: 3.074 ms
(9 rows)
尽管这个查询运行起来比在其中任一单个索引上都快,但是我们在索引尺寸上付出了代价。
每一个单列 btree 索引占用 2 MB,因此总的空间会超过 12 MB,这是bloom索引所使用的空间的 8 倍。
限制
• 在模块中只包括了用于int4以及text的操作符类。
• 搜索只支持=操作符。但是未来可以为合并和交集操作的数组增加支持。
• bloom访问方法不支持UNIQUE索引。
• bloom访问方法不支持对NULL值的搜索。
btree_gin
btree_gin提供了一个为数据类型 int2、int4、int8、float4、 float8、timestamp with、time zone、 timestamp without time zone、time with time zone、 time without
time zone、date、interval、 oid、money、"char"、 varchar、text、bytea、bit、varbit、macaddr、macaddr8、inet、 cidr、uuid、name、bool、 bpchar和所有enum类型实现B树等价行为的GIN操作符类例子。
通常,这些操作符类不会比等效的标准B树索引方法更好,并且它们缺少标准B树代码的一个主要特性:强制一致性的能力。但是,它们有助于GIN测试并且有助于作为开发其他GIN操作符类的基础。另外,对于测试一个GIN可索引的列和一个B树可索引的列的查询,创建一个使用这些操作符类之一的多列GIN索引要比创建必须通过位图AND组合在一起的两个独立索引要更有效。
这个模块被认为是 "trusted", 也就是说, 它可以被当前数据库中拥有CREATE特权的非超级用户安装。
用法示例
test=# create extension btree_gin;
CREATE EXTENSION
test=# CREATE TABLE test (a int4);
CREATE TABLE
test=# CREATE INDEX testidx ON test USING GIN (a);
CREATE INDEX
test=# SELECT * FROM test WHERE a < 10;
a
---
(0 rows)
btree_gist
[btree_gist]{.mark}为数据类型 int2、int4、int8、float4、 float8、numeric、timestamp with time zone、 timestamp without time zone、time with time zone、 time without、time zone、date、interval、 oid、money、char、 varchar、text、bytea、bit、varbit、macaddr、macaddr8、 inet、cidr、uuid和所有enum 类型提供了实现 B 树等效行为的 GiST 索引操作符类。
通常,这些操作符类不会比等效的标准 B 树索引方法更好,并且它们缺少标准 B 树代码的一个主要特性:强制一致性的能力。但是,如下文所述,它们提供了在一个 B 树索引中没有的一些其他特性。另外,当需要一个多列 GiST 索引,并且其某些列的数据类型只在 GiST 中是可索引的而其他列是简单数据类型时,这些操作符类就有用了。最后,这些操作符可以用于 GiST 测试以及作为开发其他 GiST 操作符类的基础。
除了典型的 B 树搜索操作符之外,btree_gist也为<>("不等于")提供了索引支持。这可能与下文描述的排他约束组合在一起产生作用。
另外,对于那些具有自然距离度量的数据类型,btree_gist定义了一个距离操作符<->,并且为使用这个操作符的最近邻搜索提供了 GiST 索引支持。距离操作符还提供给了:int2、int4、 int8、float4、 float8、timestamp with time zone、 timestamp without time zone、 time without time zone、date、interval、 oid和money。
这个模块被认为是 "trusted", 也就是说, 它可以被当前数据库中拥有CREATE特权的非超级用户安装。
用法示例
使用btree_gist代替btree的简单例子:
CREATE TABLE test (a int4);
-- create index
CREATE INDEX testidx ON test USING GIST (a);
-- query
SELECT * FROM test WHERE a < 10;
-- nearest-neighbor search: find the ten entries closest to "42"
SELECT *, a <-> 42 AS dist FROM test ORDER BY a <-> 42 LIMIT 10;
使用一个排他约束来强制规则:一个动物园里的一个笼子只能装一种动物:
=> CREATE TABLE zoo (
cage INTEGER,
animal TEXT,
EXCLUDE USING GIST (cage WITH =, animal WITH <>)
);
=> INSERT INTO zoo VALUES(123, 'zebra');
INSERT 0 1
=> INSERT INTO zoo VALUES(123, 'zebra');
INSERT 0 1
=> INSERT INTO zoo VALUES(123, 'lion');
ERROR: conflicting key value violates exclusion constraint
"zoo_cage_animal_excl"
DETAIL: Key (cage, animal)=(123, lion) conflicts with existing key (cage,
animal)=(123, zebra).
=> INSERT INTO zoo VALUES(124, 'lion');
INSERT 0 1