索引失效场景详解

1. 全值匹配但类型不匹配

-- 假设name为VARCHAR类型
SELECT * FROM users WHERE name = 123; -- 数值会被隐式转换为字符串 '123'
  • 原因:MySQL执行隐式类型转换(CAST(123 AS CHAR)),导致无法直接使用索引。
  • 解决方案:确保查询条件与字段类型一致。

2. 组合索引不满足最左前缀原则

-- 索引:idx_age_email (age, email)
SELECT * FROM users WHERE email = '[email protected]'; -- 无法使用索引
  • 原因:组合索引必须从最左侧字段开始匹配。
  • 解决方案:调整查询条件顺序,或创建单独的email索引。

3. 对索引字段使用函数

SELECT * FROM users WHERE YEAR(created_at) = 2023;
  • 执行计划
    +----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+-------------+
    | id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows   | filtered | Extra       |
    +----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+-------------+
    | 1  | SIMPLE      | users | NULL       | ALL  | idx_created   | NULL | NULL    | NULL | 100000 | 10.00    | Using where |
    +----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+-------------+
    
  • 解决方案:改写为范围查询:WHERE created_at >= '2023-01-01' AND created_at < '2024-01-01'

4. 隐式类型转换

-- 假设age为INT类型
SELECT * FROM users WHERE age = '25';
  • 原因:字符串到整数的转换(CAST('25' AS SIGNED))导致索引失效。
  • 执行计划type=ALL(全表扫描)

5. 范围条件右侧字段失效

-- 索引:idx_age_email (age, email)
SELECT * FROM users WHERE age > 20 AND email = '[email protected]';
  • 原因:MySQL在遇到范围条件(><等)后,停止使用组合索引的后续字段。
  • 执行计划key_len=5(仅使用age字段的索引)

6. LIKE以通配符开头

SELECT * FROM users WHERE name LIKE '%test';
  • 原因:无法利用B-Tree索引的有序性。
  • 解决方案:改为LIKE 'test%',或使用全文索引。

7. OR条件导致索引失效

SELECT * FROM users WHERE age = 20 OR email = '[email protected]';
  • 执行计划
    +----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+-------------+
    | id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows   | filtered | Extra       |
    +----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+-------------+
    | 1  | SIMPLE      | users | NULL       | ALL  | idx_age_email | NULL | NULL    | NULL | 100000 | 50.00    | Using where |
    +----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+-------------+
    
  • 解决方案:拆分为两个查询并使用UNION
    SELECT * FROM users WHERE age = 20
    UNION
    SELECT * FROM users WHERE email = '[email protected]';
    

8. IS NULL/IS NOT NULL(部分情况)

SELECT * FROM users WHERE created_at IS NULL;
  • 索引是否生效:取决于索引设计:
    • 若索引包含NULL值,则可能使用索引。
    • 若索引设置为NOT NULL,则可能全表扫描。

9. 索引字段参与计算

SELECT * FROM users WHERE age + 10 < 50; -- 等价于 age < 40
  • 原因:索引无法直接比较计算后的结果。
  • 解决方案:改写为WHERE age < 40

10. 统计信息不准确

ANALYZE TABLE users; -- 更新统计信息
  • 原因:MySQL优化器依赖统计信息选择执行计划,过时的统计信息可能导致错误选择。

11.索引优化建议

  1. 使用EXPLAIN分析查询:关注type列(理想值:constref)和key_len列。
  2. 避免在索引字段上使用函数:优先将计算逻辑移到查询条件外。
  3. 合理设计组合索引:根据查询频率和选择性排序字段。
  4. 定期更新统计信息:通过ANALYZE TABLE命令保持统计信息准确性。
  5. 谨慎使用OR条件:考虑使用UNION替代。
-- 创建测试表和数据
CREATE TABLE users (
    id INT PRIMARY KEY,
    name VARCHAR(50),
    age INT,
    email VARCHAR(100),
    created_at DATETIME
);

-- 创建索引
CREATE INDEX idx_name ON users (name);
CREATE INDEX idx_age_email ON users (age, email);
CREATE INDEX idx_created ON users (created_at);

-- 1. 全值匹配但类型不匹配
EXPLAIN SELECT * FROM users WHERE name = 123; -- 预期:类型不匹配导致索引失效

-- 2. 组合索引不满足最左前缀原则
EXPLAIN SELECT * FROM users WHERE email = '[email protected]'; -- 预期:无法使用idx_age_email

-- 3. 对索引字段使用函数
EXPLAIN SELECT * FROM users WHERE YEAR(created_at) = 2023; -- 预期:函数导致索引失效

-- 4. 隐式类型转换
EXPLAIN SELECT * FROM users WHERE age = '25'; -- 预期:字符串转整数导致索引失效

-- 5. 范围条件右侧字段失效
EXPLAIN SELECT * FROM users WHERE age > 20 AND email = '[email protected]'; -- 预期:email无法使用索引

-- 6. LIKE以通配符开头
EXPLAIN SELECT * FROM users WHERE name LIKE '%test'; -- 预期:索引失效

-- 7. OR条件导致索引失效
EXPLAIN SELECT * FROM users WHERE age = 20 OR email = '[email protected]'; -- 预期:全表扫描

-- 8. IS NULL/IS NOT NULL(部分情况)
EXPLAIN SELECT * FROM users WHERE created_at IS NULL; -- 预期:取决于索引设计

-- 9. 索引字段参与计算
EXPLAIN SELECT * FROM users WHERE age + 10 < 50; -- 预期:索引失效

-- 10. 统计信息不准确
ANALYZE TABLE users; -- 更新统计信息前
EXPLAIN SELECT * FROM users WHERE age = 30;
ANALYZE TABLE users; -- 更新统计信息后
EXPLAIN SELECT * FROM users WHERE age = 30;

你可能感兴趣的:(数据库,数据库,mysql)