PG 12引入REINDEX CONCURRENTLY,https://www.postgresql.org/docs/12/sql-reindex.html
When this option is used, PostgreSQL will rebuild the index without taking any locks that prevent concurrent inserts, updates, or deletes on the table; whereas a standard index rebuild locks out writes (but not reads) on the table until it’s done. There are several caveats to be aware of when using this option
使用此选项时,PostgreSQL 将重建索引,而不在表上采取任何阻止并发插入、更新或删除的锁; 标准的索引重建将会锁定表上的写操作(而不是读操作),直到它完成。
Rebuilding Indexes Concurrently
重建索引可能会影响数据库的常规操作。通常PostgreSQL会锁定重建的表以防止写操作,并通过单次扫描表来执行整个索引构建。 其他事务仍可以读取表,但如果它们尝试在表中插入、更新或删除行,它们将被阻止,直到索引重建完成。 如果系统是实时生产数据库,这可能会产生严重影响。非常大的表可能需要几个小时才能编制索引,即使对于较小的表,索引重建也会锁定编写器,这些时间段对于生产系统来说是不可接受的。PostgreSQL支持以最少的写入锁定来重建索引。此方法通过指定REINDEX的CONCURRENTLY选项来调用。 使用此选项时,PostgreSQL必须对需要重新生成的每个索引执行两次表扫描,并等待可能使用索引的所有现有事务的终止。 此方法需要比标准索引重建更大的工作量,并且需要相当长的时间才能完成,因为它需要等待可能修改索引的未完成的事务。 但是,由于它允许在重建索引时继续正常操作,此方法可用于在生产环境中重建索引。当然,重建索引所需的额外 CPU、内存和 I/O 负载可能会减慢其他操作的速度。
以下步骤发生在并发重建索引中。 每个步骤在单独的事务中运行。 如果要重建多个索引,则每个步骤在进入到下一步之前都要循环遍历所有索引。
1、新的临时索引定义将添加到目录pg_index中。 此定义将用于替换旧索引。 一个SHARE UPDATE EXCLUSIVE会话级别的锁将放在要重建的索引以及其关联的表上,以防止处理时的任何模式修改。
2、为每个新索引完成生成索引的首个操作。 生成索引后,其标志pg_index.indisready切换到“true”使其准备好插入,使其在执行生成的事务完成后对其他会话可见。 此步骤在每个索引的单独事务中完成。
3、然后执行第二个操作以添加在第一个操作运行时添加的元组。此步骤也在每个索引的单独事务中完成。
4、引用索引的所有约束都已更改以引用新的索引定义,并且索引名称也已经更改。 此时,pg_index.indisvalid会为新索引切换到“true”,以及为旧索引切换到“false”,并且缓存无效会导致引用旧索引的所有会话失效。
5、旧索引有pg_index.indisready切换到“false”以防止任何新的元组插入,在等待可能引用旧索引的查询之后完成。
6、旧索引被丢弃。索引和表的SHARE UPDATE EXCLUSIVE会话锁被释放。
如果在重建索引时出现问题,例如唯一索引中的唯一性冲突, REINDEX命令将失败,但会留下一个 “invalid”新索引,在已经存在的索引之外。 出于查询目的此索引将被忽略,因为它可能不完整;但是它仍将消耗更新开销。如果标记为INVALID的索引后缀为ccnew,那么它对应的是并发操作时创建的临时索引,推荐的恢复方法是使用DROP INDEX删除,然后再次尝试 REINDEX CONCURRENTLY。 如果无效索引改为后缀ccold,则对应于无法删除的原始索引; 推荐的恢复方法是删除所述索引,因为正确的重建已经成功。
常规索引创建允许在同一表上的其他常规索引创建同时发生,但在一个表上一次只能发生一个并发索引创建。在这两种情况下,不允许同时对表上其他类型的模式进行修改。 另一个区别是,常规REINDEX TABLE或REINDEX INDEX命令可以在事务块中执行,但REINDEX CONCURRENTLY不能执行。
总之:REINDEX INDEX CONCURRENTLY indexname就是以不阻塞DML的方式重建索引
查询索引XXX是否valid
SELECT pg_class.relname,pg_index.indisvalid FROM pg_class, pg_index WHERE pg_class.relname='XXX' AND pg_index.indexrelid = pg_class.oid;
手工使某个索引XXX失效
update pg_index set indisvalid = false where indexrelid = 'XXX'::regclass
查询某张表XXX上是否有失效的索引
SELECT c.relname tablename,c2.relname indexname,i.indisprimary,i.indisunique,i.indisclustered,i.indisvalid,pg_catalog.pg_get_indexdef(i.indexrelid, 0, true),pg_catalog.pg_get_constraintdef(con.oid, true),contype,condeferrable,condeferred,i.indisreplident,c2.reltablespace
FROM pg_catalog.pg_class c, pg_catalog.pg_class c2,pg_catalog.pg_index iLEFT JOIN pg_catalog.pg_constraint con ON (conrelid = i.indrelid AND conindid = i.indexrelid AND contype IN ('p','u','x'))
WHERE c.relname = 'XXX' AND c.oid = i.indrelid AND i.indexrelid = c2.oid
order by 2;
重建索引,会堵塞DML
REINDEX INDEX indexname
重建索引加CONCURRENTLY,不会堵塞dML
REINDEX INDEX CONCURRENTLY indexname
查看索引重建进度
SELECT * FROM pg_stat_progress_create_index;
Postgresql没有禁用某个索引alter index indexname disable这样的功能