← 返回资讯
陈默
AI 行业分析师
已审核

数据库索引优化实战:查询速度提升 100 倍

我们的订单表有 5000 万条记录,某些查询需要 30 秒才能返回。

数据库索引优化实战:查询速度提升 100 倍

数据库索引优化实战:查询速度提升 100 倍

我们的订单表有 5000 万条记录,某些查询需要 30 秒才能返回。

通过索引优化,同样的查询现在只需要 0.3 秒。

问题背景

慢查询日志

SQL
-- 查询用户最近的订单
SELECT * FROM orders 
WHERE user_id = 12345 
ORDER BY created_at DESC 
LIMIT 10;

-- 执行时间:32.5 秒
-- 扫描行数:50,000,000

表结构

SQL
CREATE TABLE orders (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    user_id BIGINT NOT NULL,
    product_id BIGINT NOT NULL,
    amount DECIMAL(10, 2) NOT NULL,
    status VARCHAR(20) NOT NULL,
    created_at DATETIME NOT NULL,
    updated_at DATETIME NOT NULL
);

-- 5000 万条记录
-- 无索引(除了主键)

索引优化过程

步骤 1:分析查询模式

SQL
-- 常见查询 1:按用户查询订单
SELECT * FROM orders WHERE user_id = ? ORDER BY created_at DESC LIMIT 10;

-- 常见查询 2:按状态查询
SELECT * FROM orders WHERE status = 'pending' AND created_at > DATE_SUB(NOW(), INTERVAL 7 DAY);

-- 常见查询 3:按产品和时间查询
SELECT COUNT(*), SUM(amount) FROM orders 
WHERE product_id = ? AND created_at BETWEEN ? AND ?;

-- 常见查询 4:复合条件查询
SELECT * FROM orders 
WHERE user_id = ? AND status = 'completed' 
ORDER BY amount DESC LIMIT 20;

步骤 2:创建合适的索引

SQL
-- 索引 1:用户 + 时间(覆盖查询 1)
CREATE INDEX idx_user_created ON orders(user_id, created_at DESC);

-- 索引 2:状态 + 时间(覆盖查询 2)
CREATE INDEX idx_status_created ON orders(status, created_at);

-- 索引 3:产品 + 时间(覆盖查询 3)
CREATE INDEX idx_product_created ON orders(product_id, created_at);

-- 索引 4:用户 + 状态 + 金额(覆盖查询 4)
CREATE INDEX idx_user_status_amount ON orders(user_id, status, amount DESC);

步骤 3:验证效果

SQL
-- 查询 1 优化后
EXPLAIN SELECT * FROM orders 
WHERE user_id = 12345 
ORDER BY created_at DESC 
LIMIT 10;

-- 结果:
-- type: ref
-- possible_keys: idx_user_created
-- key: idx_user_created
-- rows: 10(之前是 50,000,000)
-- Extra: Using index condition
-- 执行时间:0.003 秒

索引设计原则

原则 1:最左前缀原则

SQL
-- 索引:(a, b, c)
CREATE INDEX idx_abc ON table(a, b, c);

-- ✅ 可以使用索引
SELECT * FROM table WHERE a = 1;
SELECT * FROM table WHERE a = 1 AND b = 2;
SELECT * FROM table WHERE a = 1 AND b = 2 AND c = 3;

-- ❌ 无法使用索引
SELECT * FROM table WHERE b = 2;
SELECT * FROM table WHERE c = 3;
SELECT * FROM table WHERE b = 2 AND c = 3;

原则 2:选择性高的列放前面

SQL
-- 假设:
-- user_id 有 100 万个不同值
-- status 只有 5 个不同值

-- ✅ 好的索引
CREATE INDEX idx_user_status ON orders(user_id, status);

-- ❌ 差的索引
CREATE INDEX idx_status_user ON orders(status, user_id);

原则 3:覆盖索引

SQL
-- 如果查询只需要索引中的列,不需要回表

-- 创建覆盖索引
CREATE INDEX idx_user_amount ON orders(user_id, amount, created_at);

-- 这个查询只需要扫描索引
SELECT user_id, amount, created_at 
FROM orders 
WHERE user_id = 12345 
ORDER BY created_at DESC 
LIMIT 10;

-- Extra: Using index(覆盖索引)

原则 4:避免索引失效

SQL
-- ❌ 索引失效的情况

-- 1. 函数操作
SELECT * FROM orders WHERE YEAR(created_at) = 2026;
-- 改为:
SELECT * FROM orders WHERE created_at >= '2026-01-01' AND created_at < '2027-01-01';

-- 2. 隐式类型转换
SELECT * FROM orders WHERE user_id = '12345';  -- user_id 是 BIGINT
-- 改为:
SELECT * FROM orders WHERE user_id = 12345;

-- 3. LIKE 以通配符开头
SELECT * FROM orders WHERE product_name LIKE '%phone%';
-- 无法使用索引,考虑全文索引

-- 4. OR 条件
SELECT * FROM orders WHERE user_id = 123 OR status = 'pending';
-- 改为 UNION:
SELECT * FROM orders WHERE user_id = 123
UNION
SELECT * FROM orders WHERE status = 'pending';

高级索引技巧

技巧 1:部分索引(PostgreSQL)

SQL
-- 只索引活跃用户的订单
CREATE INDEX idx_active_orders ON orders(user_id, created_at)
WHERE status != 'archived';

-- 索引更小,查询更快

技巧 2:表达式索引(PostgreSQL)

SQL
-- 索引日期的年月
CREATE INDEX idx_orders_year_month ON orders(EXTRACT(YEAR FROM created_at), EXTRACT(MONTH FROM created_at));

-- 查询时可以使用
SELECT * FROM orders 
WHERE EXTRACT(YEAR FROM created_at) = 2026 
AND EXTRACT(MONTH FROM created_at) = 7;

技巧 3:全文索引

SQL
-- MySQL 全文索引
ALTER TABLE products ADD FULLTEXT INDEX ft_name_desc(name, description);

-- 查询
SELECT * FROM products 
WHERE MATCH(name, description) AGAINST('iPhone 15' IN BOOLEAN MODE);

-- PostgreSQL 全文索引
CREATE INDEX idx_products_search ON products 
USING gin(to_tsvector('english', name || ' ' || description));

-- 查询
SELECT * FROM products 
WHERE to_tsvector('english', name || ' ' || description) @@ to_tsquery('iphone & 15');

技巧 4:JSON 字段索引

SQL
-- MySQL JSON 索引
ALTER TABLE orders ADD INDEX idx_metadata 
((CAST(metadata->>'$.category' AS CHAR(50))));

-- 查询
SELECT * FROM orders 
WHERE metadata->>'$.category' = 'electronics';

-- PostgreSQL JSONB 索引
CREATE INDEX idx_orders_metadata ON orders USING gin(metadata);

-- 查询
SELECT * FROM orders 
WHERE metadata @> '{"category": "electronics"}';

索引维护

监控索引使用情况

SQL
-- MySQL:查看索引使用情况
SELECT * FROM sys.schema_unused_indexes 
WHERE object_schema = 'mydb';

-- PostgreSQL:查看索引使用情况
SELECT schemaname, tablename, indexname, idx_scan 
FROM pg_stat_user_indexes 
ORDER BY idx_scan ASC;

删除无用索引

SQL
-- 索引占用空间但不被使用
DROP INDEX idx_unused ON orders;

定期重建索引

SQL
-- MySQL:优化表
OPTIMIZE TABLE orders;

-- PostgreSQL:重建索引
REINDEX INDEX idx_user_created;

性能对比

| 查询类型 | 优化前 | 优化后 | 提升 |

|---------|--------|--------|------|

| 按用户查询 | 32.5s | 0.003s | 10833x |

| 按状态查询 | 15.2s | 0.05s | 304x |

| 按产品查询 | 8.7s | 0.02s | 435x |

| 复合查询 | 45.3s | 0.01s | 4530x |

总结

索引优化核心原则:

1. 分析查询模式:了解常见查询

2. 最左前缀:索引列顺序很重要

3. 选择性优先:高选择性列放前面

4. 覆盖索引:减少回表

5. 避免失效:注意函数、类型转换

6. 定期维护:删除无用索引

做好这些,查询速度可以提升 100 倍以上。


优化时间:2026年7月

数据规模:5000 万条记录

查询提升:100-10000 倍

#数据库 #索引优化 #性能优化 #MySQL

254
4248 阅读
2 评论
分享
链接已复制
编辑说明

本文由 MakeSense 编辑团队撰写并审核。文中引用的数据和观点均经过交叉验证,如有疏漏欢迎在评论区指正。最后更新:2026年07月10日 18:07

陈默

AI 行业分析师

前某大厂 AI 实验室研究员,关注大模型技术演进和商业化落地。写过 200+ 篇行业分析,擅长从产品视角拆解技术趋势。

读者评论 2

A
AI研究员 5天前
观点有道理,不过我觉得还需要考虑算力成本的问题。
回复 点赞 (11)
M
创业者Mark 1周前
正在做相关方向,这篇文章给了我不少启发。
回复 点赞 (7)