CREATE INDEX — 定义新索引
CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ]name
] ON [ ONLY ]table_name
[ USINGmethod
] ( {column_name
| (expression
) } [ COLLATEcollation
] [opclass
[ (opclass_parameter
=value
[, ... ] ) ] ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ] [, ...] ) [ INCLUDE (column_name
[, ...] ) ] [ NULLS [ NOT ] DISTINCT ] [ WITH (storage_parameter
[=value
] [, ... ] ) ] [ TABLESPACEtablespace_name
] [ WHEREpredicate
]
CREATE INDEX
在指定关系(可以是表或物化视图)的指定列上构建索引。索引主要用于提高数据库性能(尽管使用不当会导致性能下降)。
索引的键字段指定为列名,或者可选地用括号括起来的表达式。如果索引方法支持多列索引,则可以指定多个字段。
索引字段可以是根据表行的一列或多列的值计算得出的表达式。此功能可用于根据对基本数据的某种转换获得对数据的快速访问。例如,根据 upper(col)
计算的索引将允许子句 WHERE upper(col) = 'JIM'
使用索引。
PostgreSQL 提供了 B-tree、hash、GiST、SP-GiST、GIN 和 BRIN 索引方法。用户还可以定义自己的索引方法,但这相当复杂。
当存在 WHERE
子句时,将创建 部分索引。部分索引是仅包含表一部分的条目(通常是比表其余部分更适合索引的部分)的索引。例如,如果您有一个表包含已开具发票和未开具发票的订单,其中未开具发票的订单占表总量的很小一部分,但这是一个经常使用的部分,那么您可以通过在该部分上创建一个索引来提高性能。另一个可能的应用是将 WHERE
与 UNIQUE
一起使用来对表的子集强制唯一性。有关更多讨论,请参阅 第 11.8 节。
在 WHERE
子句中使用的表达式只能引用基础表的列,但它可以使用所有列,而不仅仅是正在索引的列。目前,子查询和聚合表达式在 WHERE
中也被禁止。相同的限制适用于作为表达式的索引字段。
在索引定义中使用的所有函数和运算符必须是 “不可变的”,也就是说,它们的结果必须仅取决于它们的参数,而不能取决于任何外部影响(例如其他表的内容或当前时间)。此限制可确保索引的行为是定义明确的。要在索引表达式或 WHERE
子句中使用用户定义的函数,请记住在创建函数时将其标记为不可变的。
UNIQUE
在创建索引时(如果数据已存在)以及每次添加数据时,都会使系统检查表中是否存在重复值。尝试插入或更新会导致重复条目的数据将生成错误。
当唯一索引应用于分区表时,将应用其他限制;请参阅 CREATE TABLE。
CONCURRENTLY
使用此选项时,PostgreSQL 将构建索引,而不会获取阻止表上并发插入、更新或删除的任何锁;而标准索引构建会锁定表上的写入(但不会锁定读取),直到完成。在使用此选项时,有一些注意事项需要注意 - 请参阅以下 并发构建索引。
对于临时表,CREATE INDEX
始终是非并发的,因为没有其他会话可以访问它们,并且非并发索引创建更便宜。
IF NOT EXISTS
如果已经存在具有相同名称的关系,则不会抛出错误。在这种情况下,将发出通知。请注意,不能保证现有索引与将要创建的索引有任何相似之处。当指定 IF NOT EXISTS
时,需要索引名称。
INCLUDE
可选的 INCLUDE
子句指定将作为 非键 列包含在索引中的列列表。非键列不能用于索引扫描搜索限定,并且在索引强制的任何唯一性或排除约束的用途方面被忽略。但是,索引仅扫描可以返回非键列的内容,而无需访问索引的表,因为它们可以直接从索引条目中获得。因此,添加非键列允许将索引仅扫描用于原本无法使用它们的查询。
明智的做法是对添加到索引中的非键列保持谨慎,尤其是宽列。如果索引元组超过了索引类型允许的最大大小,则数据插入将失败。在任何情况下,非键列都会从索引的表中复制数据,并使索引的大小膨胀,从而可能减慢搜索速度。此外,对于具有非键列的索引,永远不会使用 B-tree 重复数据删除。
在 INCLUDE
子句中列出的列不需要相应的运算符类;该子句可以包含数据类型没有为给定访问方法定义运算符类的列。
表达式不被支持为包含的列,因为它们不能用于索引仅扫描。
目前,B-tree、GiST 和 SP-GiST 索引访问方法支持此功能。在这些索引中,在 INCLUDE
子句中列出的列的值包含在对应于堆元组的叶元组中,但不包含在用于树导航的更高级别索引条目中。
name
要创建的索引的名称。这里不能包含模式名称;索引始终在与其父表相同的模式中创建。索引的名称必须与该模式中任何其他关系(表、序列、索引、视图、物化视图或外部表)的名称不同。如果省略名称,PostgreSQL 将根据父表的名称和索引的列名选择一个合适的名称。
ONLY
表示如果表已分区,则不递归创建分区上的索引。默认情况下为递归。
table_name
要索引的表的名称(可能是模式限定的)。
method
要使用的索引方法的名称。选择包括 btree
、hash
、gist
、spgist
、gin
、brin
或用户安装的访问方法,例如 bloom。默认方法是 btree
。
column_name
表的列名。
expression
基于表的一列或多列的表达式。表达式通常必须用周围的括号编写,如语法所示。但是,如果表达式具有函数调用的形式,则可以省略括号。
collation
要用于索引的排序规则的名称。默认情况下,索引使用为要索引的列声明的排序规则或要索引的表达式的结果排序规则。具有非默认排序规则的索引对于涉及使用非默认排序规则的表达式的查询很有用。
opclass
运算符类的名称。有关详细信息,请参阅以下内容。
opclass_parameter
运算符类参数的名称。有关详细信息,请参阅以下内容。
ASC
指定升序排序(这是默认值)。
DESC
指定降序排序。
NULLS FIRST
指定空值在非空值之前排序。这是指定 DESC
时的默认值。
NULLS LAST
指定空值在非空值之后排序。这是未指定 DESC
时的默认值。
NULLS DISTINCT
NULLS NOT DISTINCT
指定对于唯一索引,空值是否应被视为不同的(不相等)。默认值是它们是不同的,因此唯一索引可以在一列中包含多个空值。
storage_parameter
索引方法特定的存储参数的名称。有关详细信息,请参阅以下 索引存储参数。
tablespace_name
要创建索引的表空间。如果未指定,则会查询 default_tablespace,或者对于临时表上的索引,则查询 temp_tablespaces。
predicate
部分索引的约束表达式。
可选的 WITH
子句指定索引的 存储参数。每个索引方法都有自己的一组允许的存储参数。B-tree、hash、GiST 和 SP-GiST 索引方法都接受此参数
fillfactor
(integer
) #索引的填充因子是一个百分比,它决定了索引方法将尝试填充索引页面的程度。对于 B 树,叶子页面在初始索引构建期间以及在右侧扩展索引(添加新的最大键值)时,将填充到此百分比。如果页面随后变得完全满,它们将被拆分,导致磁盘上索引结构的碎片化。B 树使用 90 的默认填充因子,但可以选择 10 到 100 之间的任何整数值。
在预计会有很多插入和/或更新的表上的 B 树索引,可以在 CREATE INDEX
时(在对表进行批量加载后)从较低的填充因子设置中获益。50 到 90 范围内的值可以有效地 “平滑” B 树索引早期生命周期中页面拆分的 速率(降低填充因子甚至可以降低页面拆分的绝对数量,尽管这种效果高度依赖于工作负载)。第 64.1.4.2 节 中描述的自下而上索引删除技术依赖于页面上有一些 “额外” 空间来存储 “额外” 元组版本,因此会受到填充因子的影响(尽管这种影响通常并不显着)。
在其他特定情况下,在 CREATE INDEX
时将填充因子增加到 100 作为最大化空间利用率的一种方式可能是有用的。只有在您完全确定表是静态的(即它永远不会受到插入或更新的影响)时,才应该考虑这样做。否则,100 的填充因子设置会存在 损害 性能的风险:即使只有几个更新或插入也会导致页面突然大量拆分。
其他索引方法以不同的方式使用填充因子,但大体上类似;默认填充因子因方法而异。
B 树索引另外接受以下参数
deduplicate_items
(boolean
) #控制 第 64.1.4.3 节 中描述的 B 树重复数据删除技术的用法。设置为 ON
或 OFF
来启用或禁用此优化。(ON
和 OFF
的替代拼写方式如 第 19.1 节 中所述。)默认值为 ON
。
通过 ALTER INDEX
关闭 deduplicate_items
会阻止未来的插入触发重复数据删除,但本身不会使现有的发布列表元组使用标准元组表示。
GiST 索引另外接受以下参数
buffering
(enum
) #确定是否使用 第 64.2.4.1 节 中描述的缓冲构建技术来构建索引。使用 OFF
禁用缓冲,使用 ON
启用缓冲,使用 AUTO
初始禁用缓冲,但一旦索引大小达到 effective_cache_size,就会动态启用缓冲。默认值为 AUTO
。请注意,如果可以进行排序构建,则将使用排序构建而不是缓冲构建,除非指定了 buffering=ON
。
GIN 索引接受不同的参数
fastupdate
(boolean
) #此设置控制 第 64.4.4.1 节 中描述的快速更新技术的用法。它是一个布尔参数:ON
启用快速更新,OFF
禁用快速更新。默认值为 ON
。
通过 ALTER INDEX
关闭 fastupdate
会阻止未来的插入进入待处理索引条目列表,但本身不会刷新之前的条目。您可能希望之后 VACUUM
表或调用 gin_clean_pending_list
函数以确保待处理列表为空。
gin_pending_list_limit
(integer
) #自定义 gin_pending_list_limit 参数。此值以千字节为单位指定。
BRIN索引接受不同的参数
pages_per_range
(integer
) #定义每个索引条目(即一个块范围)由多少个表块组成BRIN索引(有关更多详细信息,请参见 第 64.5.1 节)。默认值为 128
。
autosummarize
(boolean
) #定义在检测到下一个页面范围上的插入时,是否为上一个页面范围排队一个汇总运行。有关更多详细信息,请参见 第 64.5.1.1 节。默认值为 off
。
创建索引可能会干扰数据库的正常运行。通常,PostgreSQL 会锁定要索引的表以防止写入,并使用对表的单次扫描来执行整个索引构建。其他事务仍然可以读取表,但如果它们尝试在表中插入、更新或删除行,它们将阻塞,直到索引构建完成。如果系统是一个实时生产数据库,这可能会产生严重的影响。非常大的表可能需要几个小时才能被索引,即使对于较小的表,索引构建也可能锁定写入器,锁定时间对于生产系统来说是不可接受的。
PostgreSQL 支持在不锁定写入的情况下构建索引。这种方法是通过在 CREATE INDEX
中指定 CONCURRENTLY
选项来调用的。使用此选项时,PostgreSQL 必须执行对表的两次扫描,此外还必须等待所有可能修改或使用索引的现有事务终止。因此,此方法比标准索引构建需要更多总工作量,并且需要更长的时间才能完成。但是,由于它允许在构建索引时继续进行正常操作,因此此方法对于在生产环境中添加新索引很有用。当然,索引创建带来的额外 CPU 和 I/O 负载可能会减慢其他操作的速度。
在并发索引构建中,索引实际上是在一个事务中作为 “无效” 索引输入系统目录,然后在另外两个事务中执行两次表扫描。在每次表扫描之前,索引构建必须等待已修改表的现有事务终止。第二次扫描之后,索引构建必须等待任何具有快照(参见 第 13 章)早于第二次扫描的事务终止,包括其他表上并发索引构建的任何阶段使用的事务(如果涉及的索引是部分索引或具有不是简单列引用的列)。最后,索引可以标记为 “有效” 并准备好使用,并且 CREATE INDEX
命令终止。但是,即使在那样之后,索引也可能不会立即用于查询:在最坏的情况下,只要存在早于索引构建开始的事务,它就不能使用。
如果在扫描表时出现问题,例如死锁或唯一索引中的唯一性冲突,则 CREATE INDEX
命令将失败,但会留下一个 “无效” 索引。此索引将被忽略以用于查询目的,因为它可能不完整;但是它仍然会消耗更新开销。psql \d
命令将报告此类索引为 INVALID
postgres=# \d tab Table "public.tab" Column | Type | Collation | Nullable | Default --------+---------+-----------+----------+--------- col | integer | | | Indexes: "idx" btree (col) INVALID
在这种情况下,建议的恢复方法是删除索引并尝试再次执行 CREATE INDEX CONCURRENTLY
。(另一种可能性是使用 REINDEX INDEX CONCURRENTLY
重新构建索引)。
在并发构建唯一索引时,另一个需要注意的是,在第二次表扫描开始时,唯一性约束已经对其他事务进行强制。这意味着约束违反可能会在索引变得可用之前或之后在其他查询中报告,甚至在索引构建最终失败的情况下也会报告。此外,如果第二次扫描中发生错误,则 “无效” 索引将在之后继续强制其唯一性约束。
支持表达式索引和部分索引的并发构建。在这些表达式的计算中发生的错误可能会导致类似于上述唯一性约束冲突的类似行为。
常规索引构建允许对同一表进行其他常规索引构建同时发生,但一次只能在一个表上执行一个并发索引构建。在这两种情况下,在构建索引时都不允许对表进行模式修改。另一个区别是,常规的 CREATE INDEX
命令可以在事务块内执行,但 CREATE INDEX CONCURRENTLY
不能。
目前不支持对分区表的索引进行并发构建。但是,您可以分别对每个分区并发构建索引,然后最后以非并发方式创建分区索引,以减少对分区表的写入被锁定的时间。在这种情况下,构建分区索引只是一个元数据操作。
有关何时可以使用索引、何时不能使用索引以及在哪些特定情况下索引可能很有用的信息,请参见 第 11 章。
目前,只有 B 树、GiST、GIN 和 BRIN 索引方法支持多键列索引。是否可以有多个键列与是否可以将 INCLUDE
列添加到索引无关。索引最多可以有 32 列,包括 INCLUDE
列。(此限制可以在构建 PostgreSQL 时更改。)目前只有 B 树支持唯一索引。
每个索引列都可以指定一个具有可选参数的操作符类。操作符类标识索引用于该列的操作符。例如,一个四字节整数的 B 树索引将使用 int4_ops
类;该操作符类包括用于四字节整数的比较函数。在实践中,列数据类型的默认操作符类通常就足够了。操作符类存在的意义是,对于某些数据类型,可能存在多个有意义的排序方式。例如,我们可能希望根据绝对值或实部对复数数据类型进行排序。我们可以通过为数据类型定义两个操作符类,然后在创建索引时选择适当的类来实现这一点。有关操作符类的更多信息,请参见第 11.10 节和第 36.16 节。
当在分区表上调用 CREATE INDEX
时,默认行为是递归到所有分区,以确保它们都具有匹配的索引。首先检查每个分区以确定是否存在等效的索引,如果存在,则该索引将作为分区索引附加到正在创建的索引,该索引将成为其父索引。如果不存在匹配的索引,则将创建一个新索引并自动附加;每个分区中新索引的名称将像命令中未指定索引名称一样确定。如果指定了 ONLY
选项,则不会进行递归,并且索引将被标记为无效。(ALTER INDEX ... ATTACH PARTITION
将标记索引为有效,一旦所有分区都获得了匹配的索引。)但是,请注意,将来使用 CREATE TABLE ... PARTITION OF
创建的任何分区都将自动具有匹配的索引,无论是否指定 ONLY
。
对于支持有序扫描的索引方法(目前只有 B 树),可以指定可选子句 ASC
、DESC
、NULLS FIRST
和/或 NULLS LAST
来修改索引的排序顺序。由于有序索引可以向前或向后扫描,因此通常创建单列 DESC
索引没有用——这种排序顺序已经可以通过常规索引获得。这些选项的价值在于,可以创建多列索引,以匹配混合排序查询请求的排序顺序,例如 SELECT ... ORDER BY x ASC, y DESC
。如果需要支持 “空值排序最低” 行为,而不是默认的 “空值排序最高” 行为,则 NULLS
选项非常有用,在依赖索引以避免排序步骤的查询中,这一点很重要。
系统定期收集所有表列的统计信息。新创建的非表达式索引可以立即使用这些统计信息来确定索引是否有用。对于新的表达式索引,需要运行 ANALYZE
或等待 自动清理守护进程 分析表以生成这些索引的统计信息。
在 CREATE INDEX
运行期间,search_path 将临时更改为 pg_catalog, pg_temp
。
对于大多数索引方法,创建索引的速度取决于 maintenance_work_mem 的设置。较大的值将减少创建索引所需的时间,只要你不将其设置得大于实际可用的内存量,否则会导致机器进行交换。
PostgreSQL 可以构建索引,同时利用多个 CPU 以更快地处理表行。此功能称为并行索引构建。对于支持并行构建索引的索引方法(目前只有 B 树),maintenance_work_mem
指定每个索引构建操作作为一个整体可以使用的最大内存量,而不管启动了多少个工作进程。通常,成本模型会自动确定应该请求多少个工作进程(如果有)。
并行索引构建可能会受益于增加 maintenance_work_mem
,而在等效的串行索引构建中,几乎不会或根本不会看到任何好处。请注意,maintenance_work_mem
可能会影响请求的工作进程数量,因为并行工作进程必须至少有 32MB
的总 maintenance_work_mem
预算份额。领导者进程还必须剩余 32MB
的份额。增加 max_parallel_maintenance_workers 可能会允许使用更多工作进程,这将减少创建索引所需的时间,只要索引构建不已经是 I/O 绑定。当然,还应该有足够的 CPU 容量,否则会处于空闲状态。
通过 ALTER TABLE
为 parallel_workers
设置一个值将直接控制 CREATE INDEX
对表的请求的并行工作进程数量。这完全绕过了成本模型,并阻止了 maintenance_work_mem
影响请求的并行工作进程数量。通过 ALTER TABLE
将 parallel_workers
设置为 0 将在所有情况下禁用表上的并行索引构建。
在将 parallel_workers
设置为调整索引构建的一部分后,你可能希望将其重置。这避免了对查询计划的无意更改,因为 parallel_workers
会影响 所有 并行表扫描。
虽然 CREATE INDEX
使用 CONCURRENTLY
选项支持并行构建,但没有特殊限制,但实际上只有第一个表扫描是并行执行的。
使用 DROP INDEX
删除索引。
与任何长时间运行的事务一样,对表的 CREATE INDEX
可能会影响并发 VACUUM
对任何其他表可以删除哪些元组。
早期版本的 PostgreSQL 还具有 R 树索引方法。该方法已被删除,因为它没有比 GiST 方法明显的优势。如果指定了 USING rtree
,CREATE INDEX
将将其解释为 USING gist
,以简化将旧数据库转换为 GiST 的操作。
每个运行 CREATE INDEX
的后端将在 pg_stat_progress_create_index
视图中报告其进度。有关详细信息,请参见第 27.4.4 节。
在表 films
中的列 title
上创建一个唯一的 B 树索引
CREATE UNIQUE INDEX title_idx ON films (title);
在表 films
中的列 title
上创建一个唯一的 B 树索引,其中包含列 director
和 rating
CREATE UNIQUE INDEX title_idx ON films (title) INCLUDE (director, rating);
创建一个禁用重复数据删除的 B 树索引
CREATE INDEX title_idx ON films (title) WITH (deduplicate_items = off);
在表达式 lower(title)
上创建一个索引,允许进行有效的区分大小写的搜索
CREATE INDEX ON films ((lower(title)));
(在此示例中,我们选择省略索引名称,因此系统将选择一个名称,通常是 films_lower_idx
。)
创建一个具有非默认排序规则的索引
CREATE INDEX title_idx_german ON films (title COLLATE "de_DE");
创建一个具有非默认空值排序顺序的索引
CREATE INDEX title_idx_nulls_low ON films (title NULLS FIRST);
创建一个具有非默认填充因子的索引
CREATE UNIQUE INDEX title_idx ON films (title) WITH (fillfactor = 70);
创建一个GIN禁用快速更新的索引
CREATE INDEX gin_idx ON documents_table USING GIN (locations) WITH (fastupdate = off);
在表 films
中的列 code
上创建一个索引,并将索引驻留在表空间 indexspace
中
CREATE INDEX code_idx ON films (code) TABLESPACE indexspace;
在点属性上创建一个 GiST 索引,以便我们可以有效地对转换函数的结果使用框操作符
CREATE INDEX pointloc ON points USING gist (box(location,location)); SELECT * FROM points WHERE box(location,location) && '(0,0),(1,1)'::box;
创建一个索引,不会锁定对表的写入
CREATE INDEX CONCURRENTLY sales_quantity_index ON sales_table (quantity);
CREATE INDEX
是 PostgreSQL 语言扩展。SQL 标准中没有关于索引的规定。
如果你在文档中发现任何不正确的内容、与你对特定功能的体验不符或需要进一步说明,请使用此表格报告文档问题。