索引失效的问题如何排查?
索引失效问题全面排查指南
索引失效是数据库性能优化的关键问题,以下是系统化的排查方法和解决方案:
一、快速诊断索引失效
1. 执行计划分析(核心工具)
-- MySQL
EXPLAIN FORMAT=JSON
SELECT * FROM orders WHERE user_id = 100;-- PostgreSQL
EXPLAIN (ANALYZE, BUFFERS, VERBOSE)
SELECT * FROM orders WHERE user_id = 100;
关键指标解读:
type列(MySQL):ALL:全表扫描(索引失效)ref/range:索引有效Index ScanvsSeq Scan(PG)key:实际使用的索引rows:预估扫描行数filtered:过滤效率
2. 索引使用统计
-- MySQL
SHOW INDEX FROM orders;
SELECT * FROM sys.schema_index_statistics;-- PostgreSQL
SELECT * FROM pg_stat_all_indexes
WHERE relname = 'orders';
二、索引失效的10大常见原因及解决方案
1. 数据类型不匹配
-- 失效案例(user_id是字符串类型)
SELECT * FROM users WHERE user_id = 100; -- 数字vs字符串-- 解决方案:
SELECT * FROM users WHERE user_id = '100'; -- 类型一致
2. 索引列使用函数
-- 失效案例
SELECT * FROM orders WHERE DATE(created_at) = '2023-01-01';-- 解决方案:
SELECT * FROM orders
WHERE created_at >= '2023-01-01 00:00:00'
AND created_at < '2023-01-02 00:00:00';
3. 前导通配符查询
-- 失效案例
SELECT * FROM products WHERE name LIKE '%apple%';-- 解决方案:
-- 添加全文索引
ALTER TABLE products ADD FULLTEXT(name);
SELECT * FROM products WHERE MATCH(name) AGAINST('apple');
4. OR条件不当使用
-- 失效案例(status无索引)
SELECT * FROM orders
WHERE user_id = 100 OR status = 'pending';-- 解决方案:
-- 方案1:为status添加索引
CREATE INDEX idx_status ON orders(status);-- 方案2:使用UNION
SELECT * FROM orders WHERE user_id = 100
UNION ALL
SELECT * FROM orders WHERE status = 'pending';
5. 复合索引顺序错误
-- 索引定义:INDEX (city, age)
-- 失效查询:
SELECT * FROM users WHERE age > 30; -- 未使用city条件-- 解决方案:
-- 调整索引顺序或创建新索引
CREATE INDEX idx_age_city ON users(age, city);
6. 隐式编码转换
-- 失效案例(连接表字符集不同)
SELECT *
FROM users u
JOIN orders o ON u.name = o.customer_name
-- 当字符集不一致时索引失效-- 解决方案:
ALTER TABLE orders CONVERT TO CHARACTER SET utf8mb4;
7. 统计信息过期
-- MySQL更新统计信息
ANALYZE TABLE orders;-- PostgreSQL更新统计信息
VACUUM ANALYZE orders;
8. 索引选择错误
-- 强制使用特定索引(MySQL)
SELECT * FROM orders FORCE INDEX(idx_user) WHERE user_id = 100;
9. NULL值处理不当
-- 失效案例
SELECT * FROM users WHERE phone IS NOT NULL;-- 解决方案:
-- 添加条件使索引可用
SELECT * FROM users WHERE phone > '';
10. 数据量过少
-- 当表中数据量 < 10% 时,优化器可能选择全表扫描
-- 解决方案:使用提示强制索引
SELECT /*+ INDEX(orders) */ * FROM orders;
三、高级诊断技巧
1. 优化器跟踪(MySQL)
SET optimizer_trace="enabled=on";
SELECT * FROM orders WHERE user_id = 100;
SELECT * FROM information_schema.optimizer_trace;
2. 索引有效性测试(PostgreSQL)
-- 禁用顺序扫描测试索引效果
SET enable_seqscan = off;
EXPLAIN SELECT * FROM orders WHERE user_id = 100;
SET enable_seqscan = on;
3. 索引碎片检查
-- MySQL InnoDB
SELECT
table_name,
index_name,
ROUND(stat_value * @@innodb_page_size / 1024 / 1024, 2) AS size_mb,
stat_description
FROM mysql.innodb_index_stats
WHERE stat_name = 'size';-- 重建索引
ALTER TABLE orders REBUILD PARTITION ALL;
四、索引设计最佳实践
- 复合索引设计原则:
- 索引选择策略:
- 高基数(唯一值多)的列优先
- WHERE条件中最常用的列
- 避免过度索引(写性能下降)
- 覆盖索引优化:
-- 添加包含列
CREATE INDEX idx_cover ON orders (user_id) INCLUDE (amount, status);-- 查询验证
EXPLAIN SELECT user_id, amount FROM orders WHERE user_id = 100;
五、系统级排查工具
| 工具类型 | MySQL 工具 | PostgreSQL 工具 |
|---|---|---|
| 性能分析 | EXPLAIN ANALYZE | EXPLAIN (ANALYZE, BUFFERS) |
| 索引监控 | SHOW INDEX_STATISTICS | pg_stat_all_indexes |
| SQL审计 | general_log | pg_stat_statements |
| 可视化 | MySQL Workbench | pgAdmin Query Tool |
| 压力测试 | sysbench | pgbench |
六、预防索引失效的架构设计
- 查询规范化:
- 自动索引管理:
# 伪代码:自动索引推荐系统
def recommend_index(query_pattern):
analyze_query_where_clause()
calculate_column_selectivity()
if new_index_benefit > maintenance_cost:
create_index_async()
monitor_index_usage()
if index_unused_for_30days:
drop_index()
- 持续监控方案:
# Prometheus配置
- alert: Index_not_used
expr: mysql_index_usage_ratio < 0.1
for: 10m
labels:
severity: warning
annotations:
summary: "索引 {{ $labels.index }} 使用率过低"
七、特殊场景处理
- 分区表索引失效:
-- 需要在每个分区上单独创建索引
ALTER TABLE sales PARTITION BY RANGE (YEAR(order_date)) (
PARTITION p0 VALUES LESS THAN (2020),
PARTITION p1 VALUES LESS THAN (2021)
);ALTER TABLE sales REBUILD PARTITION p0, p1;
- JSON字段索引:
-- MySQL
CREATE INDEX idx_profile ON users((CAST(profile->>'$.age' AS SIGNED)));-- PostgreSQL
CREATE INDEX idx_profile ON users((profile->>'age'));
- GIS空间索引:
-- PostgreSQL
CREATE INDEX idx_gis ON locations USING GIST (geom);
SELECT * FROM locations
WHERE ST_DWithin(geom, ST_Point(0,0)::geography, 1000);
通过以上系统化的排查方法,90%的索引失效问题都能快速定位解决。关键要点:先看执行计划,再查数据类型,后验索引结构,配合持续监控预防问题复发。
