正确使用数据库索引的实用建议
索引是提高数据库查询性能的关键工具,但不当使用反而会降低系统性能。以下是正确使用索引的详细建议:
一、索引设计原则
1. 选择合适的列建立索引
- 不为 NULL 的字段:索引字段的数据应该尽量不为 NULL,因为对于数据为 NULL 的字段,数据库较难优化。如果字段频繁被查询,但又避免不了为 NULL,建议使用 0、1、true、false 这样语义较为清晰的短值或短字符作为替代。
- 被频繁查询的字段:我们创建索引的字段应该是查询操作非常频繁的字段。
- 被作为条件查询的字段:被作为 WHERE 条件查询的字段,应该被考虑建立索引。
- 频繁需要排序的字段:索引已经排序,这样查询可以利用索引的排序,加快排序查询时间。
- 被经常频繁用于连接的字段:经常用于连接的字段可能是一些外键列,对于外键列并不一定要建立外键,只是说该列涉及到表与表的关系。对于频繁被连接查询的字段,可以考虑建立索引,提高多表连接查询的效率。
2. 联合索引设计技巧
- 最左前缀原则:将最常用的列放在联合索引左侧
- 列顺序策略:
- 等值查询列优先于范围查询列
- 高选择性列放在前面
- 经常用于排序的列考虑放在后面
- 5-7列原则:单个联合索引最好不要超过5-7列
被频繁更新的字段应该慎重建立索引
虽然索引能带来查询上的效率,但是维护索引的成本也是不小的。 如果一个字段不被经常查询,反而被经常修改,那么就更不应该在这种字段上建立索引了。
二、SQL编写最佳实践
1. 充分利用现有索引
-- 确保查询条件匹配索引的最左前缀
-- 好的写法(使用索引):
SELECT * FROM users WHERE last_name='Smith' AND first_name='John'-- 差的写法(可能无法使用(last_name, first_name)索引):
SELECT * FROM users WHERE first_name='John'
2. 避免索引失效的常见陷阱
-
隐式类型转换:确保比较时类型一致
-- 假设user_id是字符串类型 SELECT * FROM users WHERE user_id = 12345 -- 错误(数字转字符串) SELECT * FROM users WHERE user_id = '12345' -- 正确
-
使用函数或运算:
-- 索引失效写法: SELECT * FROM orders WHERE YEAR(order_date) = 2023 SELECT * FROM products WHERE price*1.1 > 100-- 优化写法: SELECT * FROM orders WHERE order_date BETWEEN '2023-01-01' AND '2023-12-31' SELECT * FROM products WHERE price > 100/1.1
3. 覆盖索引优化
-- 只查询索引包含的列,避免回表
-- 假设有索引(idx_name_age)
SELECT name, age FROM users WHERE name LIKE 'A%' -- 好的(覆盖索引)
SELECT * FROM users WHERE name LIKE 'A%' -- 差的(需要回表)
三、索引维护与管理
1. 定期维护索引
-
重建碎片化索引:特别是频繁更新的表
-- MySQL ALTER TABLE orders REBUILD INDEX idx_order_date;-- SQL Server ALTER INDEX idx_order_date ON orders REBUILD;
-
监控索引使用:删除未使用的冗余索引
-- MySQL查看未使用索引 SELECT * FROM sys.schema_unused_indexes;
2. 索引数量控制
- 平衡读写性能:每个INSERT/UPDATE/DELETE需要更新所有相关索引
- 建议单张表索引不超过 5 个:索引可以提高效率,同样可以降低效率。
- 大表建议:大型表通常需要更多索引,但要监控写入性能
四、特殊场景处理
1. 模糊查询优化
-- 左前缀LIKE可以使用索引
SELECT * FROM products WHERE name LIKE 'Apple%'-- 通配符开头的LIKE无法使用索引
SELECT * FROM products WHERE name LIKE '%Phone' -- 索引失效-- 解决方案:考虑全文索引或倒排索引
2. NULL值处理
-- IS NULL条件可以使用索引
SELECT * FROM customers WHERE phone IS NULL-- 建议:重要查询字段避免NULL,使用默认值代替
3. OR条件优化
-- 差的写法(可能导致索引失效):
SELECT * FROM orders WHERE status = 'shipped' OR customer_id = 1001-- 优化方案1:改用UNION ALL
SELECT * FROM orders WHERE status = 'shipped'
UNION ALL
SELECT * FROM orders WHERE customer_id = 1001-- 优化方案2:确保OR两侧都有索引
五、监控与调优
-
使用EXPLAIN分析:检查执行计划是否使用预期索引
EXPLAIN SELECT * FROM orders WHERE customer_id = 1001;
-
监控慢查询:定期分析并优化慢查询
-- MySQL慢查询日志 SET GLOBAL slow_query_log = 'ON';
-
索引统计信息:确保统计信息最新
-- MySQL更新统计信息 ANALYZE TABLE orders;