tencent cloud

云数据库 PostgreSQL

动态与公告
产品动态
产品简介
产品概述
产品特性
产品优势
应用场景
信息安全说明
地域和可用区
产品功能列表
大版本生命周期说明
MSSQL 兼容版
产品计费
计费概述
实例类型与规格
购买方式
退费说明
欠费说明
备份空间收费说明
快速入门
创建 PostgreSQL 实例
连接 PostgreSQL 实例
管理 PostgreSQL 实例
数据导入
通过 DTS 迁移数据
内核能力介绍
内核版本概述
内核版本更新动态
查看内核版本
自研内核功能介绍
数据库审计
审计服务说明
开通审计服务
查看审计日志
修改审计服务
审计性能说明
用户指南
实例管理
升级实例
CPU 弹性扩容
只读实例
账号管理
数据库管理
参数管理
日志管理及分析
备份与恢复
数据迁移
插件管理
网络管理
访问管理
数据安全
租户及资源隔离
安全组
监控与告警
标签
AI 实践
使用 tencentdb_ai 插件调用大模型
使用 tencentdb_ai 插件构建 AI 应用
结合 Supabase 快速构建基于云数据库 PostgreSQL 的后端服务
实践教程
跨库访问
如何在 PostgreSQL 中自动创建分区
基于 pg_roaringbitmap 实现超大规模标签查找
一条 SQL 实现查询附近的人
如何配置云数据库 PostgreSQL 作为 GitLab 外部数据源
通过 cos_fdw 插件支持分级存储能力
通过 pgpool 实现读写分离
通过 auto_explain 插件实现慢 SQL 分析
使用 pglogical 进行逻辑复制
使用 Debezium 采集 PostgreSQL 数据
在 CVM 本地搭建 PostgreSQL 异地灾备环境
只读实例与只读组实践教程
如何使用云函数定时操作数据库
表膨胀处理
性能白皮书
测试方法
测试结果
API 文档
History
Introduction
API Category
Making API Requests
Instance APIs
Read-only Replica APIs
Backup and Recovery APIs
Parameter Management APIs
Security Group APIs
Performance Optimization APIs
Account APIs
Specification APIs
Network APIs
Data Types
Error Codes
常见问题
相关协议
Service Level Agreement
Terms of Service
词汇表
联系我们

索引失效

PDF
聚焦模式
字号
最后更新时间: 2026-03-23 16:16:21
云数据库 PostgreSQL 支持索引失效内核功能,本文为您介绍关于索引失效内核功能的说明及使用示例。

功能背景

数据库管理员在面临版本发布或非常规运维的场景时,需要一种快速的索引控制手段,在不删除索引的前提下使得指定索引失效,以便在升级验证、灰度测试或紧急排查时灵活评估索引对查询性能的影响,同时避免因索引维护带来的写入开销或触发已知 bug,保障系统在调试过程中的稳定性与数据完整性,提升运维效率。

功能介绍

云数据库 PostgreSQL 支持索引失效内核功能,该功能允许用户通过标准 SQL 命令临时禁用或启用索引,且无需修改系统表权限。该功能支持事务回滚、分区表独立控制,且禁用期间索引数据持续维护,启用后无需重建。

核心语法

-- 禁用索引(立即生效,索引对优化器不可见)
ALTER INDEX idx_name UNUSABLE;

-- 启用索引(无需重建,索引重新对优化器可见)
ALTER INDEX idx_name USABLE;

适用场景

需要临时禁用或启用索引,以提升运维效率的运维场景。

注意事项

分区表处理:索引 USABLE/UNUSABLE 不支持递归操作,分区表需要逐个处理子分区。
版本升级:大版本升级时,设置了 UNUSABLE 的索引会被删掉。
REINDEX 行为:普通表设置成 UNUSABLE 后,REINDEX 会直接变成 USABLE,但分区表的父表会维持 UNUSABLE。
与 pg_hint_plan 配合:可以结合 pg_hint_plan 指定索引使用策略。

支持版本

数据库内核版本:v11.22_r1.34、v12.22_r1.36、v13.22_r1.31、v14.19_r1.40、v15.14_r1.25、v16.10_r1.20、v17.6_r1.14、v18.1_r1.4 及以上。

使用示例

示例1:基本用法 - 禁用和启用索引

步骤1:创建测试表

-- 输入
CREATE TABLE test_table (id int, val text);
-- 输出
CREATE TABLE;

步骤2:插入测试数据

-- 输入
INSERT INTO test_table SELECT i, 'val' || i FROM generate_series(1, 1000) i;
-- 输出
INSERT 0 1000;

步骤3:创建索引

-- 输入
CREATE INDEX idx_test_id ON test_table (id);
-- 输出
CREATE INDEX;

步骤4:更新统计信息

-- 输入
ANALYZE test_table;
-- 输出
ANALYZE;

步骤5:查看索引初始状态(indisvalid = true 表示索引有效)

-- 输入
SELECT indexrelid::regclass AS index_name, indisvalid
FROM pg_index
WHERE indexrelid = 'idx_test_id'::regclass;
-- 输出
index_name | indisvalid
-------------+------------
idx_test_id | t
(1 row);

步骤6:查看当前查询计划(使用 Index Scan)

-- 输入
EXPLAIN (COSTS OFF) SELECT * FROM test_table WHERE id = 100;
-- 输出
QUERY PLAN
---------------------------------------
Index Scan using idx_test_id on test_table
Index Cond: (id = 100)
(2 rows);

步骤7:禁用索引

-- 输入
ALTER INDEX idx_test_id UNUSABLE;
-- 输出
ALTER INDEX;

步骤8:确认索引已禁用(indisvalid = false)

-- 输入
SELECT indexrelid::regclass AS index_name, indisvalid
FROM pg_index
WHERE indexrelid = 'idx_test_id'::regclass;
-- 输出
index_name | indisvalid
-------------+------------
idx_test_id | f
(1 row);

步骤9:查看查询计划(改为 Seq Scan,不再使用索引)

-- 输入
EXPLAIN (COSTS OFF) SELECT * FROM test_table WHERE id = 100;
-- 输出
QUERY PLAN
-------------------------------
Seq Scan on test_table
Filter: (id = 100)
(2 rows);

步骤10:重新启用索引

-- 输入
ALTER INDEX idx_test_id USABLE;
-- 输出
ALTER INDEX;

步骤11:确认索引已启用(indisvalid = true)

-- 输入
SELECT indexrelid::regclass AS index_name, indisvalid
FROM pg_index
WHERE indexrelid = 'idx_test_id'::regclass;
-- 输出
index_name | indisvalid
-------------+------------
idx_test_id | t
(1 row);

步骤12:查看查询计划(恢复使用 Index Scan)

-- 输入
EXPLAIN (COSTS OFF) SELECT * FROM test_table WHERE id = 100;
-- 输出
QUERY PLAN
---------------------------------------
Index Scan using idx_test_id on test_table
Index Cond: (id = 100)
(2 rows);

示例2:分区表索引操作

步骤1:创建分区表

-- 输入
CREATE TABLE test_part (id int, val text) PARTITION BY RANGE (id);
-- 输出
CREATE TABLE;

步骤2:创建分区

-- 输入
CREATE TABLE test_part_p1 PARTITION OF test_part FOR VALUES FROM (1) TO (500);
CREATE TABLE test_part_p2 PARTITION OF test_part FOR VALUES FROM (500) TO (1001);
-- 输出
CREATE TABLE
CREATE TABLE;

步骤3:插入测试数据

-- 输入
INSERT INTO test_part SELECT i, 'val' || i FROM generate_series(1, 1000) i;
-- 输出
INSERT 0 1000;

步骤4:创建分区索引(会自动在各分区创建子索引)

-- 输入
CREATE INDEX idx_test_part_id ON test_part (id);
-- 输出
CREATE INDEX;

步骤5:更新统计信息

-- 输入
ANALYZE test_part;
-- 输出
ANALYZE;

步骤6:查看所有索引状态(父索引和子索引都有效)

-- 输入
SELECT indexrelid::regclass AS index_name, indisvalid
FROM pg_index
WHERE indexrelid::regclass::text LIKE '%test_part%';
-- 输出
index_name | indisvalid
-------------------------+------------
idx_test_part_id | t
test_part_p1_id_idx | t
test_part_p2_id_idx | t
(3 rows);

步骤7:禁用父索引(注意:子索引不受影响)

-- 输入
ALTER INDEX idx_test_part_id UNUSABLE;
-- 输出
ALTER INDEX;

步骤8:查看索引状态(父索引禁用,子索引仍有效)

-- 输入
SELECT indexrelid::regclass AS index_name, indisvalid
FROM pg_index
WHERE indexrelid::regclass::text LIKE '%test_part%';
-- 输出
index_name | indisvalid
-------------------------+------------
idx_test_part_id | f
test_part_p1_id_idx | t
test_part_p2_id_idx | t
(3 rows);

步骤9:单独禁用 p1 分区的索引

-- 输入
ALTER INDEX test_part_p1_id_idx UNUSABLE;
-- 输出
ALTER INDEX;

步骤10:查看索引状态(p1 索引禁用,p2 索引仍有效)

-- 输入
SELECT indexrelid::regclass AS index_name, indisvalid
FROM pg_index
WHERE indexrelid::regclass::text LIKE '%test_part%';
-- 输出
index_name | indisvalid
-------------------------+------------
idx_test_part_id | f
test_part_p1_id_idx | f
test_part_p2_id_idx | t
(3 rows);

步骤11:查询 p1 分区数据的执行计划(使用 Seq Scan)

-- 输入
EXPLAIN (COSTS OFF) SELECT * FROM test_part WHERE id = 100;
-- 输出
QUERY PLAN
-------------------------------------------------------
Seq Scan on test_part_p1 test_part
Filter: (id = 100)
(2 rows);

步骤12:查询 p2 分区数据的执行计划(使用 Index Scan)

-- 输入
EXPLAIN (COSTS OFF) SELECT * FROM test_part WHERE id = 900;
-- 输出
QUERY PLAN
-------------------------------------------------------------------------------
Index Scan using test_part_p2_id_idx on test_part_p2 test_part
Index Cond: (id = 900)
(2 rows);

示例3:事务回滚支持

步骤1:确保索引处于启用状态

-- 输入
ALTER INDEX idx_test_id USABLE;
-- 输出
ALTER INDEX;

步骤2:查看当前状态

-- 输入
SELECT indexrelid::regclass AS index_name, indisvalid
FROM pg_index
WHERE indexrelid = 'idx_test_id'::regclass;
-- 输出
index_name | indisvalid
-------------+------------
idx_test_id | t
(1 row);

步骤3:开始事务

-- 输入
BEGIN;
-- 输出
BEGIN;

步骤4:在事务中禁用索引

-- 输入
ALTER INDEX idx_test_id UNUSABLE;
-- 输出
ALTER INDEX;

步骤5:查看事务中的状态(索引已禁用)

-- 输入
SELECT indexrelid::regclass AS index_name, indisvalid
FROM pg_index
WHERE indexrelid = 'idx_test_id'::regclass;
-- 输出
index_name | indisvalid
-------------+------------
idx_test_id | f
(1 row);

步骤6:回滚事务

-- 输入
ROLLBACK;
-- 输出
ROLLBACK;

步骤7:查看回滚后的状态(索引恢复为启用)

-- 输入
SELECT indexrelid::regclass AS index_name, indisvalid
FROM pg_index
WHERE indexrelid = 'idx_test_id'::regclass;
-- 输出
index_name | indisvalid
-------------+------------
idx_test_id | t
(1 row);

示例4:DML 操作期间索引维护

步骤1:禁用索引

-- 输入
ALTER INDEX idx_test_id UNUSABLE;
-- 输出
ALTER INDEX;

步骤2:在索引禁用期间插入新数据

-- 输入
INSERT INTO test_table VALUES (9999, 'new_value');
-- 输出
INSERT 0 1;

步骤3:重新启用索引

-- 输入
ALTER INDEX idx_test_id USABLE;
-- 输出
ALTER INDEX;

步骤4:验证新数据可以通过索引查询到

-- 输入
EXPLAIN (COSTS OFF) SELECT * FROM test_table WHERE id = 9999;
-- 输出
QUERY PLAN
---------------------------------------
Index Scan using idx_test_id on test_table
Index Cond: (id = 9999)
(2 rows);

步骤5:执行查询确认数据存在

-- 输入
SELECT * FROM test_table WHERE id = 9999;
-- 输出
id | val
------+-----------
9999 | new_value
(1 row);
说明:
索引禁用期间插入的数据会继续维护索引结构,索引启用后无需 REINDEX 即可正常使用。

帮助和支持

本页内容是否解决了您的问题?

填写满意度调查问卷,共创更好文档体验。

文档反馈