云数据库 PostgreSQL 提供智能索引推荐扩展,本文为您介绍关于智能索引推荐(tencentdb_index_advisor)扩展的说明及实践。
概述
tencentdb_index_advisor 是云数据库 PostgreSQL 提供的智能索引推荐扩展,能够对单条 SQL 或批量工作负载自动分析并推荐最优索引方案,无需实际创建物理索引即可评估效果。
核心价值:
智能分析:自动识别 WHERE、JOIN、ORDER BY、GROUP BY 等条件中的候选索引列。
零成本评估:通过假设索引(Hypothetical Index)评估效果,不产生任何磁盘开销。
多场景覆盖:支持 SELECT、UPDATE、DELETE 等多种 SQL 语句的索引推荐。
批量优化:支持工作负载级别的索引推荐,一次分析多条 SQL 找到全局最优方案。
安全可靠:只读分析不修改数据,推荐结果由 DBA 审核后手动创建。
典型场景:
新业务上线前,对核心查询进行索引规划。
慢查询排查,快速找到缺失的索引。
数据库性能巡检,批量分析 TOP SQL 的索引优化空间。
索引调优,对现有查询评估是否需要覆盖索引。
环境要求
项目 | 要求 |
云数据库 PostgreSQL 版本 | 15.x 及以上 |
扩展版本 | tencentdb_index_advisor v1.0 |
依赖扩展 | hypopg v1.4.1(自动安装) |
执行权限 | pg_tencentdb_superuser 角色成员 |
执行环境 | 仅支持在主库(Primary)上执行 |
安装与验证
安装扩展
-- 安装依赖扩展 hypopg(如未安装)CREATE EXTENSION IF NOT EXISTS hypopg;-- 安装索引推荐扩展CREATE EXTENSION IF NOT EXISTS tencentdb_index_advisor;
验证安装
SELECT extname, extversion FROM pg_extensionWHERE extname IN ('hypopg', 'tencentdb_index_advisor')ORDER BY extname;
预期输出:
extname | extversion |
hypopg | 1.4.1 |
tencentdb_index_advisor | 1.0 |
函数说明
函数签名
tencentdb_index_advise(query_string text)RETURNS TABLE (schema_name text,table_name text,am_name text,index_size bigint,index_def text)
参数:
参数 | 类型 | 说明 |
query_string | text | 要分析的 SQL 语句,支持单条或多条(以分号分隔) |
输出字段
字段 | 类型 | 说明 |
schema_name | text | 推荐索引所在的 Schema 名称 |
table_name | text | 推荐索引所在的表名 |
am_name | text | 索引访问方法(通常为 btree) |
index_size | bigint | 预估索引大小(字节) |
index_def | text | 完整的 CREATE INDEX 语句,可直接执行 |
GUC 配置参数
参数名 | 类型 | 默认值 | 说明 |
tencentdb_index_advisor.enable_covering_index | bool | on | 是否启用覆盖索引推荐。开启后,推荐的索引会包含查询所需的额外列,支持仅索引扫描(Index-Only Scan) |
tencentdb_index_advisor.show_benefits | bool | on | 是否展示推荐索引的预估代价收益 |
tencentdb_index_advisor.try_variation_timeout | int | 10(秒) | 变异优化阶段的超时时间。设为0可跳过该阶段,加快推荐速度 |
tencentdb_index_advisor.enable_workload_compression | bool | off | 是否启用工作负载压缩。开启后会对相似模式的 SQL 去重,减少处理时间 |
tencentdb_index_advisor.max_index_recommend | int | 8 | 单次推荐的最大索引数量 |
参数设置示例:
-- 关闭覆盖索引推荐(减少索引大小)SET tencentdb_index_advisor.enable_covering_index = off;-- 限制最多推荐 3 个索引SET tencentdb_index_advisor.max_index_recommend = 3;-- 跳过变异优化阶段(加快推荐速度)SET tencentdb_index_advisor.try_variation_timeout = 0;-- 批量分析时启用工作负载压缩SET tencentdb_index_advisor.enable_workload_compression = on;
常用查询场景
场景一:单条慢查询的索引推荐
当某条 SQL 执行缓慢时,直接获取推荐索引:
-- 对慢查询进行索引推荐SELECT schema_name, table_name, am_name,pg_size_pretty(index_size) AS est_size,index_defFROM tencentdb_index_advise('SELECT * FROM orders WHERE customer_id = 123 AND status = ''shipped'' AND order_date > ''2025-01-01''');
输出示例:
schema_name | table_name | am_name | est_size | index_def |
public | orders | btree | 2360 kB | CREATE INDEX ON public.orders USING btree (customer_id,status) |
说明:
推荐在 orders 表上创建(customer_id, status)联合索引。等值条件列(customer_id, status)优先排列,范围条件列(order_date)作为辅助。
场景二:JOIN 查询的跨表索引推荐
SELECT schema_name, table_name, am_name,pg_size_pretty(index_size) AS est_size,index_defFROM tencentdb_index_advise('SELECT o.id, c.name, o.amountFROM orders o JOIN customers c ON o.customer_id = c.idWHERE c.city = ''Beijing'' AND o.status = ''delivered''');
输出示例:
schema_name | table_name | am_name | est_size | index_def |
public | orders | btree | 4528 KB | CREATE INDEX ON public.orders USING btree (status,customer_id,id,amount) |
public | customers | btree | 312 KB | CREATE INDEX ON public.customers USING btree (city) |
说明:
推荐为两个表分别创建索引。orders 表索引以 JOIN 和 WHERE 条件列为主键,customers 表索引覆盖过滤条件 city。
场景三:批量分析 TOP SQL(工作负载推荐)
将多条慢查询一起分析,找到全局最优的索引组合:
-- 同时分析多条业务 SQLSELECT schema_name, table_name,pg_size_pretty(index_size) AS est_size,index_defFROM tencentdb_index_advise('SELECT * FROM orders WHERE customer_id = 100;SELECT * FROM orders WHERE status = ''pending'' AND region = ''east'';SELECT * FROM products WHERE category = ''electronics'' AND price < 200;SELECT o.id, c.name FROM orders o JOIN customers c ON o.customer_id = c.id WHERE c.city = ''Beijing'';');
输出示例:
schema_name | table_name | est_size | index_def |
public | orders | 8350 KB | CREATE INDEX ON public.orders USING btree (customer_id,...) |
public | orders | 8350 KB | CREATE INDEX ON public.orders USING btree (region,status,...) |
public | products | 1416 KB | CREATE INDEX ON public.products USING btree (category,price,...) |
public | customers | 312 KB | CREATE INDEX ON public.customers USING btree (city,...) |
说明:
工作负载推荐会综合考虑所有 SQL 的需求,找到收益最大的索引组合。这比单条分析更加合理。
场景四:覆盖索引对比
-- 开启覆盖索引推荐SET tencentdb_index_advisor.enable_covering_index = on;SELECT index_def FROM tencentdb_index_advise('SELECT customer_id, amount FROM orders WHERE status = ''shipped'' AND region = ''east''');-- 结果: CREATE INDEX ON public.orders USING btree (region, status, customer_id, amount)-- 关闭覆盖索引推荐SET tencentdb_index_advisor.enable_covering_index = off;SELECT index_def FROM tencentdb_index_advise('SELECT customer_id, amount FROM orders WHERE status = ''shipped'' AND region = ''east''');-- 结果: CREATE INDEX ON public.orders USING btree (region, status)
说明:
覆盖索引包含了查询所需的 customer_id 和 amount 列,可以实现仅索引扫描(Index-Only Scan),避免回表。但索引体积更大,需根据实际查询频率权衡。
场景五:UPDATE/DELETE 语句的索引优化
-- UPDATE 语句索引推荐SELECT index_def FROM tencentdb_index_advise('UPDATE orders SET amount = 0 WHERE customer_id = 123 AND status = ''cancelled''');-- 结果: CREATE INDEX ON public.orders USING btree (customer_id, status)-- DELETE 语句索引推荐SELECT index_def FROM tencentdb_index_advise('DELETE FROM orders WHERE customer_id = 999 AND order_date < ''2025-01-01''');-- 结果: CREATE INDEX ON public.orders USING btree (customer_id, order_date)
说明:
UPDATE 和 DELETE 语句中的 WHERE 条件同样可以从索引中受益,减少需要扫描的行数。
索引推荐算法原理
推荐流程
输入 SQL 语句│▼步骤1: 查询解析└── 解析 WHERE/JOIN/GROUP BY/ORDER BY 等子句│步骤2: 候选索引列生成└── 按优先级识别候选列:EQUALITY(等值) > RANGE(范围) > JOIN(连接)> GROUP_ORDER(排序/分组) > COVERING(覆盖)│步骤3: 假设索引创建└── 通过 hypopg 创建假设索引(不占磁盘空间)│步骤4: 代价评估└── 利用 PostgreSQL 优化器评估有无索引的执行代价差异│步骤5: 贪心选择 + 变异优化└── 贪心算法选择收益最大的索引组合变异阶段尝试不同组合寻找更优方案│步骤6: 输出推荐└── 返回 CREATE INDEX 语句和预估索引大小
候选列优先级
优先级 | 类型 | 场景 | 说明 |
1 (最高) | EQUALITY | WHERE col = value | 等值匹配,选择性最好 |
2 | RANGE | WHERE col > value | 范围查询 |
3 | JOIN | ON a.col = b.col | 连接条件 |
4 | GROUP_ORDER | GROUP BY col / ORDER BY col | 排序和分组 |
5 (最低) | COVERING | SELECT col | 覆盖查询列(避免回表) |
运维实战指南
慢查询索引优化工作流
步骤1: 从 pg_stat_statements 提取 TOP 慢查询└── SELECT query FROM pg_stat_statements ORDER BY mean_exec_time DESC LIMIT 10;│步骤2: 批量输入到索引推荐└── SELECT * FROM tencentdb_index_advise('慢查询1; 慢查询2; ...');│步骤3: 评估推荐索引├── 查看预估索引大小,评估磁盘空间影响├── 用 EXPLAIN 验证查询计划变化└── 确认无重复/冗余索引│步骤4: 低峰期创建索引└── CREATE INDEX CONCURRENTLY ... (在线创建,不锁表)│步骤5: 验证效果└── 对比创建前后的查询耗时和执行计划
推荐索引效果验证方法
在正式创建索引前,可以用 EXPLAIN 先验证效果:
-- 第一步:查看当前执行计划和代价EXPLAIN SELECT * FROM orders WHERE customer_id = 123 AND status = 'shipped';-- 结果: Seq Scan, cost=1218.0-- 第二步:获取推荐索引SELECT index_def FROM tencentdb_index_advise('SELECT * FROM orders WHERE customer_id = 123 AND status = ''shipped''');-- 结果: CREATE INDEX ON public.orders USING btree (customer_id, status)-- 第三步:创建索引(生产环境建议用 CONCURRENTLY)CREATE INDEX CONCURRENTLY idx_orders_cust_statusON orders USING btree (customer_id, status);-- 第四步:验证优化效果EXPLAIN SELECT * FROM orders WHERE customer_id = 123 AND status = 'shipped';-- 结果: Index Scan, cost=8.31 (性能提升 99.3%)
参数调优建议
场景 | 推荐参数设置 | 说明 |
快速推荐 | try_variation_timeout = 0 | 跳过变异优化,适合紧急排查 |
深度优化 | try_variation_timeout = 30 | 更充分的组合搜索,适合重要查询 |
控制索引数量 | max_index_recommend = 3 | 限制推荐数量,避免过多索引 |
减少索引体积 | enable_covering_index = off | 不推荐覆盖列,索引更小 |
批量分析 | enable_workload_compression = on | 去重相似 SQL,加快分析速度 |
名词解释
名词 | 解释 |
假设索引(Hypothetical Index) | 通过 hypopg 扩展创建的虚拟索引,仅存在于会话内存中,不占用磁盘空间,但能被 PostgreSQL 优化器识别和使用于执行计划评估 |
覆盖索引(Covering Index) | 包含查询所需全部列的索引,支持仅索引扫描(Index-Only Scan),避免回表读取数据页 |
变异优化(Variation Phase) | 在贪心算法得到初步结果后,通过尝试不同的索引列组合寻找更优方案的优化阶段 |
工作负载压缩(Workload Compression) | 对输入的多条 SQL 进行模式匹配去重,保留查询多样性的同时减少分析时间 |
RMGR | Resource Manager,资源管理器类型 |
Index-Only Scan | 仅索引扫描,查询只需读取索引即可获得全部所需数据,不需要回表访问堆页面 |
CONCURRENTLY | PostgreSQL 的在线创建索引模式,不会阻塞表上的 DML 操作 |
注意事项
仅主库可用:在备库上无法执行索引推荐。
依赖 hypopg:必须先安装 hypopg 扩展。
权限要求:需要 pg_tencentdb_superuser 角色成员权限。
推荐≠执行:推荐结果需要 DBA 审核后手动创建,index_def 字段可直接作为 DDL 执行。
CONCURRENTLY 创建:生产环境创建索引时建议使用 CREATE INDEX CONCURRENTLY 避免锁表
索引不是越多越好:过多索引会增加写入开销和存储成本,建议根据查询频率和收益综合评估。
定期维护:业务变化后建议重新运行索引推荐,清理不再需要的索引。
多条 SQL 分析:工作负载推荐比逐条分析更准确,能找到全局最优的索引组合。
LIKE 查询:对于前缀匹配(LIKE 'prefix%'),扩展会推荐带 text_pattern_ops 的索引;后缀匹配(LIKE '%suffix')无法使用 B-tree 索引。