Index-Only Scans and Covering Indexes
小结:
1、覆盖索引 回表
2、
All indexes in PostgreSQL are secondary indexes, meaning that each index is stored separately from the table's main data area (which is called the table's heap in PostgreSQL terminology). This means that in an ordinary index scan, each row retrieval requires fetching data from both the index and the heap.
3、
To solve this performance problem, PostgreSQL supports index-only scans, which can answer queries from an index alone without any heap access. The basic idea is to return values directly out of each index entry instead of consulting the associated heap entry. There are two fundamental restrictions on when this method can be used:
The index type must support index-only scans. B-tree indexes always do. GiST and SP-GiST indexes support index-only scans for some operator classes but not others. Other index types have no support. The underlying requirement is that the index must physically store, or else be able to reconstruct, the original data value for each index entry. As a counterexample, GIN indexes cannot support index-only scans because each index entry typically holds only part of the original data value.
The query must reference only columns stored in the index. For example, given an index on columns
x
andy
of a table that also has a columnz
, these queries could use index-only scans:SELECT x, y FROM tab WHERE x = 'key';
SELECT x FROM tab WHERE x = 'key' AND y < 42;but these queries could not:
SELECT x, z FROM tab WHERE x = 'key';
SELECT x FROM tab WHERE x = 'key' AND z < 42;(Expression indexes and partial indexes complicate this rule, as discussed below.)
PostgreSQL: Documentation: 12: 11.9. Index-Only Scans and Covering Indexes https://www.postgresql.org/docs/12/indexes-index-only-scans.html
Index-Only Scans and Covering Indexes
11.9. Index-Only Scans and Covering Indexes
All indexes in PostgreSQL are secondary indexes, meaning that each index is stored separately from the table's main data area (which is called the table's heap in PostgreSQL terminology). This means that in an ordinary index scan, each row retrieval requires fetching data from both the index and the heap. Furthermore, while the index entries that match a given indexable WHERE
condition are usually close together in the index, the table rows they reference might be anywhere in the heap. The heap-access portion of an index scan thus involves a lot of random access into the heap, which can be slow, particularly on traditional rotating media. (As described in Section 11.5, bitmap scans try to alleviate this cost by doing the heap accesses in sorted order, but that only goes so far.)
To solve this performance problem, PostgreSQL supports index-only scans, which can answer queries from an index alone without any heap access. The basic idea is to return values directly out of each index entry instead of consulting the associated heap entry. There are two fundamental restrictions on when this method can be used:
The index type must support index-only scans. B-tree indexes always do. GiST and SP-GiST indexes support index-only scans for some operator classes but not others. Other index types have no support. The underlying requirement is that the index must physically store, or else be able to reconstruct, the original data value for each index entry. As a counterexample, GIN indexes cannot support index-only scans because each index entry typically holds only part of the original data value.
The query must reference only columns stored in the index. For example, given an index on columns
x
andy
of a table that also has a columnz
, these queries could use index-only scans:SELECT x, y FROM tab WHERE x = 'key';
SELECT x FROM tab WHERE x = 'key' AND y < 42;but these queries could not:
SELECT x, z FROM tab WHERE x = 'key';
SELECT x FROM tab WHERE x = 'key' AND z < 42;(Expression indexes and partial indexes complicate this rule, as discussed below.)
If these two fundamental requirements are met, then all the data values required by the query are available from the index, so an index-only scan is physically possible. But there is an additional requirement for any table scan in PostgreSQL: it must verify that each retrieved row be “visible” to the query's MVCC snapshot, as discussed in Chapter 13. Visibility information is not stored in index entries, only in heap entries; so at first glance it would seem that every row retrieval would require a heap access anyway. And this is indeed the case, if the table row has been modified recently. However, for seldom-changing data there is a way around this problem. PostgreSQL tracks, for each page in a table's heap, whether all rows stored in that page are old enough to be visible to all current and future transactions. This information is stored in a bit in the table's visibility map. An index-only scan, after finding a candidate index entry, checks the visibility map bit for the corresponding heap page. If it's set, the row is known visible and so the data can be returned with no further work. If it's not set, the heap entry must be visited to find out whether it's visible, so no performance advantage is gained over a standard index scan. Even in the successful case, this approach trades visibility map accesses for heap accesses; but since the visibility map is four orders of magnitude smaller than the heap it describes, far less physical I/O is needed to access it. In most situations the visibility map remains cached in memory all the time.
In short, while an index-only scan is possible given the two fundamental requirements, it will be a win only if a significant fraction of the table's heap pages have their all-visible map bits set. But tables in which a large fraction of the rows are unchanging are common enough to make this type of scan very useful in practice.
To make effective use of the index-only scan feature, you might choose to create a covering index, which is an index specifically designed to include the columns needed by a particular type of query that you run frequently. Since queries typically need to retrieve more columns than just the ones they search on, PostgreSQL allows you to create an index in which some columns are just “payload” and are not part of the search key. This is done by adding an INCLUDE
clause listing the extra columns. For example, if you commonly run queries like
SELECT y FROM tab WHERE x = 'key';
the traditional approach to speeding up such queries would be to create an index on x
only. However, an index defined as
CREATE INDEX tab_x_y ON tab(x) INCLUDE (y);
could handle these queries as index-only scans, because y
can be obtained from the index without visiting the heap.
Because column y
is not part of the index's search key, it does not have to be of a data type that the index can handle; it's merely stored in the index and is not interpreted by the index machinery. Also, if the index is a unique index, that is
CREATE UNIQUE INDEX tab_x_y ON tab(x) INCLUDE (y);
the uniqueness condition applies to just column x
, not to the combination of x
and y
. (An INCLUDE
clause can also be written in UNIQUE
and PRIMARY KEY
constraints, providing alternative syntax for setting up an index like this.)
It's wise to be conservative about adding non-key payload columns to an index, especially wide columns. If an index tuple exceeds the maximum size allowed for the index type, data insertion will fail. In any case, non-key columns duplicate data from the index's table and bloat the size of the index, thus potentially slowing searches. And remember that there is little point in including payload columns in an index unless the table changes slowly enough that an index-only scan is likely to not need to access the heap. If the heap tuple must be visited anyway, it costs nothing more to get the column's value from there. Other restrictions are that expressions are not currently supported as included columns, and that only B-tree indexes currently support included columns.
Before PostgreSQL had the INCLUDE
feature, people sometimes made covering indexes by writing the payload columns as ordinary index columns, that is writing
CREATE INDEX tab_x_y ON tab(x, y);
even though they had no intention of ever using y
as part of a WHERE
clause. This works fine as long as the extra columns are trailing columns; making them be leading columns is unwise for the reasons explained in Section 11.3. However, this method doesn't support the case where you want the index to enforce uniqueness on the key column(s). Also, explicitly marking non-searchable columns as INCLUDE
columns makes the index slightly smaller, because such columns need not be stored in upper B-tree levels.
In principle, index-only scans can be used with expression indexes. For example, given an index on f(x)
where x
is a table column, it should be possible to execute
SELECT f(x) FROM tab WHERE f(x) < 1;
as an index-only scan; and this is very attractive if f()
is an expensive-to-compute function. However, PostgreSQL's planner is currently not very smart about such cases. It considers a query to be potentially executable by index-only scan only when all columns needed by the query are available from the index. In this example, x
is not needed except in the context f(x)
, but the planner does not notice that and concludes that an index-only scan is not possible. If an index-only scan seems sufficiently worthwhile, this can be worked around by adding x
as an included column, for example
CREATE INDEX tab_f_x ON tab (f(x)) INCLUDE (x);
An additional caveat, if the goal is to avoid recalculating f(x)
, is that the planner won't necessarily match uses of f(x)
that aren't in indexable WHERE
clauses to the index column. It will usually get this right in simple queries such as shown above, but not in queries that involve joins. These deficiencies may be remedied in future versions of PostgreSQL.
Partial indexes also have interesting interactions with index-only scans. Consider the partial index shown in Example 11.3:
CREATE UNIQUE INDEX tests_success_constraint ON tests (subject, target)
WHERE success;
In principle, we could do an index-only scan on this index to satisfy a query like
SELECT target FROM tests WHERE subject = 'some-subject' AND success;
But there's a problem: the WHERE
clause refers to success
which is not available as a result column of the index. Nonetheless, an index-only scan is possible because the plan does not need to recheck that part of the WHERE
clause at run time: all entries found in the index necessarily have success = true
so this need not be explicitly checked in the plan. PostgreSQL versions 9.6 and later will recognize such cases and allow index-only scans to be generated, but older versions will not.
Index-Only Scans and Covering Indexes的更多相关文章
- Covering Indexes in MySQL, PostgreSQL, and MongoDB
Covering Indexes in MySQL, PostgreSQL, and MongoDB - Orange Matter https://orangematter.solarwinds.c ...
- 【黑魔法】Covering Indexes、STRAIGHT_JOIN
今天给大家介绍两个黑魔法,这都是压箱底的法宝.大家在使用时,一定要弄清他们的适用场景及用法,用好了,就是一把开天斧,用不好那就是画蛇添足.自从看过耗子哥(左耳朵耗子)的博客,都会给对相应专题有兴趣的小 ...
- 【性能提升神器】Covering Indexes
可能有小伙伴会问,Covering Indexes到底是什么神器呢?它又是如何来提升性能的呢?接下来我会用最通俗易懂的语言来进行介绍,毕竟不是每个程序猿都要像DBA那样深刻理解数据库,知道如何用以及如 ...
- 高性能MySQL笔记-第5章Indexing for High Performance-005聚集索引
一.聚集索引介绍 1.什么是聚集索引? InnoDB’s clustered indexes actually store a B-Tree index and the rows together i ...
- oracle parallel_index hint在非分区表的生效
之前没特别注意,在有些场景下希望使用并行索引扫描的时候,发现parallel_index hint并没有生效,于是抽空看了下文档:The PARALLEL_INDEX hint instructs t ...
- Oracle 常见hint
Hints 应该慎用,收集相关表的统计信息,根据执行计划,来改变查询方式 只能在SELECT, UPDATE, INSERT, MERGE, or DELETE 关键字后面,只有insert可以用2个 ...
- PostgreSQL 11 新特性之覆盖索引(Covering Index)(转载)
通常来说,索引可以用于提高查询的速度.通过索引,可以快速访问表中的指定数据,避免了表上的扫描.有时候,索引不仅仅能够用于定位表中的数据.某些查询可能只需要访问索引的数据,就能够获取所需要的结果,而不需 ...
- MySQL: Building the best INDEX for a given SELECT
Table of Contents The ProblemAlgorithmDigressionFirst, some examplesAlgorithm, Step 1 (WHERE "c ...
- Differences between INDEX, PRIMARY, UNIQUE, FULLTEXT in MySQL?
487down vote Differences KEY or INDEX refers to a normal non-unique index. Non-distinct values for ...
随机推荐
- Redis 设计与实现:字符串 SDS
本文的分析没有特殊说明都是基于 Redis 6.0 版本源码 redis 6.0 源码:https://github.com/redis/redis/tree/6.0 在 Redis 中,字符串都用自 ...
- node2vec实现源码详解
一.按照程序执行的顺序,第一步是walker.py中的preprocess_transition_probs()函数 这个函数的作用是生成两个采样预备数据,alias_nodes,alias_edge ...
- sprignAOP那些术语
那些AOP术语 初看这么多术语,一下子都不好接受,慢慢来,很快就会搞懂.通知.增强处理(Advice) 就是你想要的功能,也就是上说的安全.事物.日志等.你给先定义好,然后再想用的地方用一下.包含As ...
- WEBSERVICE之CXF框架开发webservice
之前学习了使用jdk开发webservice服务,现在开始学习使用框架(cxf)开发webservice. 1.准备工作 A.使用cxf开发webservice服务,需要用到apache-cxf-3. ...
- C# 串口连接的读取与发送
一.串口连接的打开与关闭 串口,即COM口,在.NET中使用 SerialPort 类进行操作.串口开启与关闭,是涉及慢速硬件的IO操作,频繁打开或关闭会影响整体处理速度,甚至导致打开或关闭串口失败. ...
- application.properties 中文乱码问题解决
1. 设置 File Encodings的Transparent native-to-ascii conversion为true,具体步骤如下:依次点击 File -> Settings -&g ...
- 【.NET与树莓派】上手前的一些准备工作
.NET Iot 不是什么新鲜事物,百科很强大,故老周在此也不必多介绍.现在的时代和老周当年学 QBasic 的时代不同,那时候拉根电话线上网,下载速度只有可怜的 3.5 kb/s.而且还要去店里买上 ...
- tf.argmax(vector,axis)函数的使用
1.返回值 vector为向量,返回行或列的最大值的索引号: vector为矩阵,返回值是向量,返回每行或每列的最大值的索引号. 2.参数 vector为向量或者矩阵 axis = 0 或1 0:返回 ...
- LeetCode844 比较含退格的字符串
题目描述: 给定 S 和 T 两个字符串,当它们分别被输入到空白的文本编辑器后,判断二者是否相等,并返回结果. # 代表退格字符. 示例 1: 输入:S = "ab#c", T = ...
- 浅谈JVM垃圾回收
JVM内存区域 要想搞懂啊垃圾回收机制,首先就要知道垃圾回收主要回收的是哪些数据,这些数据主要在哪一块区域. Java8和Java8之前的相同点有很多. 都有虚拟机栈,本地方法栈,程序计数器,这三个是 ...