在Postgres世界中,索引对于有效导航数据库存储库(称为堆,堆)至关重要。 Postgres不支持集群,并且MVCC体系结构使您可以累积同一元组的多个版本。 因此,能够创建和维护有效的索引以支持应用程序非常重要。
这里是一些优化和改进索引使用的技巧。
注意:下面显示的查询适用于未修改的pagila示例数据库 。使用覆盖索引
让我们回顾一下为非活动用户检索电子邮件地址的请求。
customer
表具有
active
列,并且请求很简单:
pagila=
该查询将调用扫描
customer
表的完整顺序。 让我们为
active
列创建索引:
pagila=
它有所帮助,随后的扫描变成了“
index scan
”。 这意味着Postgres将扫描
idx_cust1
索引,然后继续搜索表的堆以读取查询所需的其他列(在本例中为
email
列)的值。
PostgreSQL 11引入了覆盖索引。 它们允许您在索引本身中包括一个或多个其他列-它们的值存储在索引数据存储中。
如果我们使用此功能并在索引内添加了电子邮件值,则Postgres无需在表堆中查找
email
值。 让我们看看这是否有效:
pagila=
“
Index Only Scan
”告诉我们查询现在仅需要一个索引,这有助于避免所有磁盘I / O读取表堆。
如今,覆盖索引仅适用于B树。 但是,在这种情况下,陪同人员的努力会更高。
使用部分索引
部分索引仅索引表中行的子集。 这样可以节省索引的大小和更快的扫描速度。
假设我们需要从加利福尼亚客户那里获得电子邮件地址列表。 该请求将如下所示:
SELECT c.email FROM customer c JOIN address a ON c.address_id = a.address_id WHERE a.district = 'California'; which has a query plan that involves scanning both the tables that are joined: pagila=
普通索引会给我们带来什么:
pagila=
address
扫描由
idx_address1
索引
idx_address1
代替,然后
idx_address1
了
address
堆。
由于这是一个频繁查询并且需要优化,因此我们可以使用部分索引,该索引仅索引那些地址为
'California'
:
pagila=
现在,该请求仅读取
idx_address2
,而不接触
address
表。
使用多值索引
一些需要索引的列可能不包含标量数据类型。 诸如
jsonb
,
arrays
和
tsvector
类的
jsonb
类型包含多个值。 如果需要索引此类列,通常必须搜索这些列中的所有单个值。
让我们尝试查找所有包含未成功剪裁的电影的名称。
film
表中有一个名为
special_features
的文本列。 如果电影具有此“特殊属性”,则该列包含
Behind The Scenes
文本数组形式的元素。 要搜索所有此类电影,我们需要选择“幕后花絮”的所有行作为
special_features
数组的
任何值:
SELECT title FROM film WHERE special_features @> '{"Behind The Scenes"}';
遏制运算符
@>
检查右侧是否是左侧的子集。
申请方案:
pagila=
它要求以67的成本进行全堆扫描。
让我们看看常规的B树索引是否对我们有帮助:
pagila=
该指数甚至没有被考虑。 B树索引不知道索引值中是否存在单个元素。
我们需要一个GIN索引。
pagila=
GIN-index支持将单个值与索引的复合值进行比较,因此,查询计划的成本降低了一半以上。
摆脱重复索引
索引随时间累积,有时新索引可能包含与先前索引相同的定义。 要获得人类可读的索引SQL定义,可以使用目录视图
pg_indexes
。 您还可以轻松找到相同的定义:
SELECT array_agg(indexname) AS indexes, replace(indexdef, indexname, '') AS defn FROM pg_indexes GROUP BY defn HAVING count(*) > 1; And here's the result when run on the stock pagila database: pagila=
超集索引
您可能会积累许多索引,其中一个索引建立了索引其他索引的列的子集。 这可能是合乎需要的,也可能不是-超集只能导致通过索引进行扫描,这很好,但是会占用太多空间,或者不再使用要为其优化的查询。
如果需要自动化此类索引的定义,则可以从
pg_catalog
表中的
pg_index开始。
未使用的索引
随着使用数据库的应用程序的发展,它们使用的查询也在发展。 先前添加的索引可能不再被任何查询使用。 每次扫描索引时,都会由统计信息管理器对其进行标记,并且在系统目录
pg_stat_user_indexes
您可以看到
idx_scan
值,它是一个累积计数器。 在一段时间(例如一个月)内跟踪此值可以很好地了解哪些索引未使用且可以删除。
这是获取
'public'
模式中所有索引的当前扫描计数的请求:
SELECT relname, indexrelname, idx_scan FROM pg_catalog.pg_stat_user_indexes WHERE schemaname = 'public'; with output like this: pagila=
用更少的锁重新创建索引
例如,当索引大小过大时,通常必须重新创建索引,并且重新创建可以加快扫描速度。 另外,索引可能已损坏。 更改索引参数可能还需要重新创建它。
启用并行索引创建
在PostgreSQL 11中,创建B树索引是有竞争力的。 为了加快创建过程,可以使用几个并行工作器。 但是,请确保正确设置以下配置参数:
SET max_parallel_workers = 32; SET max_parallel_maintenance_workers = 16;
默认值太小。 理想情况下,这些数量应与处理器内核数量一起增加。 阅读
文档以获取更多详细信息。
背景索引创建
您可以使用
CREATE INDEX
命令的
CONCURRENTLY
参数在后台
CREATE INDEX
:
pagila=
此索引创建过程不同于通常的索引创建过程,因为它不需要表锁定,因此不会阻止写操作。 另一方面,它花费更多的时间并消耗更多的资源。
Postgres提供了许多灵活的选项来创建索引和解决任何特殊情况的方法,并提供了在应用程序爆炸性增长的情况下管理数据库的方法。 我们希望这些技巧将帮助您快速进行查询,并准备好扩展数据库。