explain analyze更可靠,因其提供真实执行路径、实际行数、索引使用情况等动态信号,远超静态规则或sql模式识别的“智能推荐”,能精准识别全表扫描等瓶颈。

为什么直接用 EXPLAIN ANALYZE 比“智能推荐索引”更可靠
多数人想写个“自动加索引”的模块,其实是被慢查询日志或监控告警推着走的——但盲目推荐索引往往适得其反。PostgreSQL 和 MySQL 的 EXPLAIN ANALYZE 输出里已经包含真实执行路径、实际行数、是否用了索引、是否触发 seq scan 等关键信号,比任何静态规则更准。Go 本身不解析 SQL 语义,硬做“SQL 模式识别 + 索引建议”容易漏掉 JOIN 顺序、隐式类型转换、函数索引适用性等细节。
实操建议:
- 从慢查询日志(如 MySQL 的
slow_query_log或 PostgreSQL 的log_min_duration_statement)中提取SELECT语句,逐条调用数据库原生命令执行EXPLAIN ANALYZE - 用 Go 的
database/sql执行时,注意加/*+ NO_INDEX() */(MySQL)或SET enable_indexscan = off(PG)做对照实验,验证当前索引是否真被命中 - 避免在生产库直接跑
EXPLAIN ANALYZE高频大表扫描——先用EXPLAIN(不带 ANALYZE)看计划,确认无风险再测
如何用 Go 解析 EXPLAIN 输出并定位缺失索引
不同数据库返回格式差异大:MySQL 是表格形式,PostgreSQL 是缩进文本,TiDB 接近 PG。别自己写正则硬 parse,优先用官方驱动或成熟库暴露的结构化字段。
实操建议:
- PostgreSQL:用
pgx驱动配合pgconn获取原始Explain结果,再用github.com/jackc/pgx/v5/pgconn的Conn.PgConn().ExecParams发送EXPLAIN (FORMAT JSON),直接得map[string]interface{} - MySQL:用
go-sql-driver/mysql执行EXPLAIN FORMAT=JSON,然后json.Unmarshal到map[string]interface{};重点看query_block->table->possible_keys和key字段是否为空或为NULL - 通用判断逻辑:只要出现
"type": "ALL"(MySQL)或"Node Type": "Seq Scan"(PG),且对应表数据量 > 10k 行,就标记为“高优先级索引候选”
CREATE INDEX CONCURRENTLY 在 Go 中的安全封装要点
直接拼接 SQL 创建索引风险极高:事务阻塞、锁表、重复建索引报错、索引名冲突。Go 层必须做幂等控制和错误分类处理。
实操建议:
- 索引名统一用哈希生成:
fmt.Sprintf("idx_%s_%x", table, md5.Sum([]byte(columns))),避免人工命名冲突 - 建索引前先查
pg_indexes(PG)或information_schema.statistics(MySQL),确认不存在同名索引;对 MySQL 还要检查INDEX_COMMENT字段是否含"auto-generated"标记 - 执行时捕获明确错误码:
42710(PG 重复索引)、ER_DUP_KEYNAME(MySQL),这些直接跳过;但57014(canceling statement due to statement timeout)需重试 - 务必用
CREATE INDEX CONCURRENTLY(PG)或ALGORITHM=INPLACE(MySQL 5.6+),禁止在主库执行阻塞式建索引
如何让索引优化模块不变成“定时炸弹”
自动建索引最危险的不是没建对,而是建了太多冷门组合索引,拖慢写入、涨磁盘、干扰查询优化器选择。真正的“自动化”必须带衰减和回收机制。
实操建议:
- 每个自动生成的索引加注释:
COMMENT ON INDEX idx_user_email_created_at IS 'auto:20240520:used_3_times',记录首次创建时间和近期使用频次(从pg_stat_all_indexes或performance_schema.table_io_waits_summary_by_index_usage同步) - 每周跑一次清理:删除连续 7 天
idx_scan = 0且非主键/唯一约束的索引 - 禁止对
TEXT、JSONB字段自动建普通 B-tree 索引——这类字段必须显式声明USING gin或USING pgroonga,模块里硬编码拦截
真正难的不是生成索引,是判断一条查询值不值得加索引、加什么类型、加在哪个字段组合上。日志采样率、执行计划稳定性、写入负载变化,这些没法全靠代码 cover,得留人工复核入口。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











