PostgreSQL 10.1 手册_部分 II. SQL 语言_第 11 章 索引_11.11. 只用索引的扫描

本文涉及的产品
云原生数据库 PolarDB MySQL 版,通用型 2核8GB 50GB
云原生数据库 PolarDB PostgreSQL 版,标准版 2核4GB 50GB
简介: 11.11. 只用索引的扫描 PostgreSQL中的所有索引都是二级索引,表示每一个索引都被存储在表的主数据区域(在PostgreSQL术语中被称为该表的堆)之外。这意味着在一次普通索引扫描中,每次取一行需要从索引和堆中取得数据。

11.11. 只用索引的扫描

PostgreSQL中的所有索引都是二级索引,表示每一个索引都被存储在表的主数据区域(在PostgreSQL术语中被称为该表的)之外。这意味着在一次普通索引扫描中,每次取一行需要从索引和堆中取得数据。此外,虽然满足一个给定的可索引WHERE条件的索引项通常在索引中都靠拢在一起,但是它们所引用的表行可能分布在堆中的任何地方。因此一次索引扫描的堆访问部分可能会涉及到很多对堆的随机访问,这可能会很慢,尤其在传统的磁盘上(如第 11.5 节中所述,位图扫描试图通过有序地执行堆访问来减轻这种开销,但是效果也就那样而已)。

为了解决这种性能问题,PostgreSQL支持只用索引的扫描,这类扫描可以仅用一个索引来回答查询而不产生任何堆访问。其基本思想是直接从每一个索引项中直接返回值,而不是去参考相关的堆项。在使用这种方法时有两个根本的限制:

  1. 索引类型必须支持只用索引的扫描。B-树索引总是支持只用索引的扫描。GiST 和 SP-GiST 索引只对某些操作符类支持只用索引的扫描。其他索引类型不支持这种扫描。底层的要求是索引必须在物理上存储或者可以重构出每一个索引项对应的原始数据值。GIN 索引是一个不支持只用索引的扫描的反例,因为它的每一个索引项通常只包含原始数据值的一部分。

  2. 查询必须只引用存储在该索引中的列。例如,给定的索引建立在表的列xy上,而该表还有一个列z,这些查询可以使用只用索引的扫描:

    SELECT x, y FROM tab WHERE x = 'key';
    SELECT x FROM tab WHERE x = 'key' AND y < 42;

    但是这些查询不能使用只用索引的查询:

    SELECT x, z FROM tab WHERE x = 'key';
    SELECT x FROM tab WHERE x = 'key' AND z < 42;

    (如下面所讨论的,表达式索引和部分索引会让这条规则更加复杂)。

如果符合这两个根本要求,那么该查询所要求的所有数据值都可以从索引得到,因此才可能使用只用索引的扫描。但是对PostgreSQL中的任何表扫描还有一个额外的要求:必须验证每一个检索到的行对该查询的 MVCC 快照是可见的,如第 13 章中讨论的那样。可见性信息并不存储在索引项中,只存储在堆项中。因此,乍一看似乎每一次行检索无论如何都会要求一次堆访问。如果表行最近被修改过,确实是这样。但是,对于很少更改的数据有一种方法可以解决这个问题。PostgreSQL为表堆中的每一个页面跟踪是否其中所有的行的年龄都足够大,以至于对所有当前以及未来的事务都可见。这个信息存储在该表的可见性映射的一个位中。在找到一个候选索引项后,只用索引的扫描会检查对应堆页面的可见性映射位。如果该位被设置,那么这一行就是可见的并且该数据库可以直接被返回。如果该位没有被设置,那么就必须访问堆项以确定这一行是否可见,这种情况下相对于标准索引扫描就没有性能优势。即便是在成功的情况下,这种方法也是把对堆的访问换成了对可见性映射的访问。不过由于可见性映射比它所描述的堆要小四个数量级,所以访问可见性映射所需的物理 I/O 要少很多。在大部分情况下,可见性映射总是会被保留在内存中的缓冲中。

总之,虽然当两个根本要求满足时可以使用只用索引的扫描,但是只有该表的堆页面中有很大一部分的“所有都可见”映射位被设置时这种索引才有优势。不过,有很大一部分行不被更改的表是很常见的,这也让这一类扫描在实际中非常有用。

为了有效地使用只用索引的扫描,可以选择创建时只让前导列来匹配WHERE子句,而让后面的列保存载荷数据用以返回查询所需的数据。例如,如果经常要运行这样的查询

SELECT y FROM tab WHERE x = 'key';

加速这类查询的传统方法是只在x上创建一个索引。但是,在(x, y)上创建一个索引可以将这种查询实现为只用索引的扫描。正如之前讨论的,这样一个索引比只在x上的索引更大并且因此开销更大,因此只有知道表几乎是静态时这种方法才具有吸引力。注意,很重要的一点是,该索引应该定义在(x, y)而不是(y, x)上。这是因为对于大部分索引类型(特别是 B-树)来说,不限制前导索引列的搜索效率不高。

原则上,只用索引的扫描可以被用于表达式索引。例如,给定一个f(x)上的索引(x是一个表列),可以把

SELECT f(x) FROM tab WHERE f(x) < 1;

作为只用索引的扫描执行,如果f()是一个计算代价昂贵的函数,这会非常有吸引力。不过,PostgreSQL的规划器当前面对这类情况时并不是很聪明。只有在索引中有查询所需要的所有时,规划器才会考虑用只用索引的扫描来执行一个查询。在这个例子中,除了在f(x)环境中之外,查询的其他部分不需要x,但是规划器并不能意识到这一点,因此它会得出不能使用只用索引的扫描的结论。如果只用索引的扫描足够有价值,有一种解决方法是把该索引定义在(f(x), x)上,其中第二个列实际上并不会被使用,它只是用来说服规划器可以使用只用索引的扫描而已。如果目标是避免重复计算f(x),一个额外的警示是规划器不一定会把不在可索引WHERE子句中对f(x)的使用匹配到索引列。通常在上述那种简单查询中一切正常,但是涉及到连接的查询中就不行了。这些不足将在未来的PostgreSQL版本中修正。

部分索引也和只用索引的扫描之间有着有趣的关系。考虑例 11.3中所展示的部分索引:

CREATE UNIQUE INDEX tests_success_constraint ON tests (subject, target)
    WHERE success;

原则上,我们可以在这个索引上使用只用索引的扫描来满足查询

SELECT target FROM tests WHERE subject = 'some-subject' AND success;

但是有一个问题:WHERE子句引用的是不能作为索引结果列的success。尽管如此,还是可以使用只用索引的扫描,因为在运行时计划不需要重新检查WHERE子句的那个部分:在该索引中找到的所有项必定具有success = true,因此在计划中检查这个部分的需要并不明显。PostgreSQL 9.6 和以后的版本将会识别这种情况,并且允许生成只用索引的扫描,但是旧版本无法这样做。

本文转自PostgreSQL中文社区,原文链接: 11.11. 只用索引的扫描
相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍如何基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
相关文章
|
6月前
|
存储 监控 关系型数据库
B-tree不是万能药:PostgreSQL索引失效的7种高频场景与破解方案
在PostgreSQL优化实践中,B-tree索引虽承担了80%以上的查询加速任务,但因多种原因可能导致索引失效,引发性能骤降。本文深入剖析7种高频失效场景,包括隐式类型转换、函数包裹列、前导通配符等,并通过实战案例揭示问题本质,提供生产验证的解决方案。同时,总结索引使用决策矩阵与关键原则,助你让索引真正发挥作用。
383 0
|
3月前
|
关系型数据库 MySQL 数据库
阿里云数据库RDS费用价格:MySQL、SQL Server、PostgreSQL和MariaDB引擎收费标准
阿里云RDS数据库支持MySQL、SQL Server、PostgreSQL、MariaDB,多种引擎优惠上线!MySQL倚天版88元/年,SQL Server 2核4G仅299元/年,PostgreSQL 227元/年起。高可用、可弹性伸缩,安全稳定。详情见官网活动页。
|
3月前
|
关系型数据库 分布式数据库 数据库
阿里云数据库收费价格:MySQL、PostgreSQL、SQL Server和MariaDB引擎费用整理
阿里云数据库提供多种类型,包括关系型与NoSQL,主流如PolarDB、RDS MySQL/PostgreSQL、Redis等。价格低至21元/月起,支持按需付费与优惠套餐,适用于各类应用场景。
|
6月前
|
SQL 关系型数据库 MySQL
Go语言数据库编程:使用 `database/sql` 与 MySQL/PostgreSQL
Go语言通过`database/sql`标准库提供统一数据库操作接口,支持MySQL、PostgreSQL等多种数据库。本文介绍了驱动安装、连接数据库、基本增删改查操作、预处理语句、事务处理及错误管理等内容,涵盖实际开发中常用的技巧与注意事项,适合快速掌握Go语言数据库编程基础。
476 62
|
3月前
|
关系型数据库 MySQL 数据库
阿里云数据库RDS支持MySQL、SQL Server、PostgreSQL和MariaDB引擎
阿里云数据库RDS支持MySQL、SQL Server、PostgreSQL和MariaDB引擎,提供高性价比、稳定安全的云数据库服务,适用于多种行业与业务场景。
|
6月前
|
SQL 关系型数据库 PostgreSQL
CTE vs 子查询:深入拆解PostgreSQL复杂SQL的隐藏性能差异
本文深入探讨了PostgreSQL中CTE(公共表表达式)与子查询的选择对SQL性能的影响。通过分析两者底层机制,揭示CTE的物化特性及子查询的优化融合优势,并结合多场景案例对比执行效率。最终给出决策指南,帮助开发者根据数据量、引用次数和复杂度选择最优方案,同时提供高级优化技巧和版本演进建议,助力SQL性能调优。
578 1
|
关系型数据库 Go 网络安全
go语言中PostgreSQL驱动安装
【11月更文挑战第2天】
530 5
|
10月前
|
SQL 关系型数据库 OLAP
云原生数据仓库AnalyticDB PostgreSQL同一个SQL可以实现向量索引、全文索引GIN、普通索引BTREE混合查询,简化业务实现逻辑、提升查询性能
本文档介绍了如何在AnalyticDB for PostgreSQL中创建表、向量索引及混合检索的实现步骤。主要内容包括:创建`articles`表并设置向量存储格式,创建ANN向量索引,为表增加`username`和`time`列,建立BTREE索引和GIN全文检索索引,并展示了查询结果。参考文档提供了详细的SQL语句和配置说明。
288 2
|
11月前
|
JSON 关系型数据库 PostgreSQL
PostgreSQL 9种索引的原理和应用场景
PostgreSQL 支持九种主要索引类型,包括 B-Tree、Hash、GiST、SP-GiST、GIN、BRIN、Bitmap、Partial 和 Unique 索引。每种索引适用于不同场景,如 B-Tree 适合范围查询和排序,Hash 仅用于等值查询,GiST 支持全文搜索和几何数据查询,GIN 适用于多值列和 JSON 数据,BRIN 适合非常大的表,Bitmap 适用于低基数列,Partial 只对部分数据创建索引,Unique 确保列值唯一。
|
SQL 关系型数据库 C语言
PostgreSQL SQL扩展 ---- C语言函数(三)
可以用C(或者与C兼容,比如C++)语言编写用户自定义函数(User-defined functions)。这些函数被编译到动态可加载目标文件(也称为共享库)中并被守护进程加载到服务中。“C语言函数”与“内部函数”的区别就在于动态加载这个特性,二者的实际编码约定本质上是相同的(因此,标准的内部函数库为用户自定义C语言函数提供了丰富的示例代码)

推荐镜像

更多