SQL索引失效的常见情况

索引在查询优化中的作用

索引(Index)是数据库优化中最重要的机制之一,它的作用主要包括:

  1. 加速数据查找

    • 索引类似于书的目录,能够快速定位所需数据,而不需要扫描整个表。

    • 示例:如果 employeesid 列有索引:

      SELECT * FROM employees WHERE id = 123;
      
      • 无索引:全表扫描(O(N))。

      • 有索引:二分查找或 B+ 树搜索(O(log N))。

  2. 优化排序(ORDER BY)

    • 索引列上的排序可以直接使用索引,加速查询:

      SELECT * FROM employees ORDER BY salary;
      
      • 有索引(salary 列):数据库可以直接按索引顺序返回数据,而不需要额外排序。

      • 无索引:数据库需要排序整个结果集(O(N log N))。

  3. 优化分组(GROUP BY)

    • 索引列上的 GROUP BY 计算更快

      SELECT department_id, COUNT(*) FROM employees GROUP BY department_id;
      
      • 有索引(department_id):数据已按 department_id 排序,数据库可以直接进行分组。

      • 无索引:数据库可能需要全表扫描和排序。

  4. 提高关联查询效率(JOIN)

    • 索引能加速表之间的关联

      SELECT e.name, d.name 
      FROM employees e 
      JOIN departments d ON e.department_id = d.id;
      
      • 有索引(e.department_id, d.id):数据库可以使用索引快速匹配两张表。

      • 无索引:数据库可能需要嵌套循环(Nested Loop Join),大数据量时性能会很差。


索引失效的常见情况

尽管索引能优化查询,但某些情况下索引会失效,导致数据库退化为全表扫描,常见原因包括:

1. 使用 OR 连接多个查询条件(单列索引失效)

SELECT * FROM employees WHERE department_id = 3 OR salary > 5000;

索引失效原因

  • 如果 department_idsalary 各自有单独的索引,数据库通常不会使用索引,而是进行全表扫描

优化方案

  • 使用联合索引(department_id, salary

    CREATE INDEX idx_dept_salary ON employees(department_id, salary);
    
  • 拆分查询再 UNION ALL

    SELECT * FROM employees WHERE department_id = 3
    UNION ALL
    SELECT * FROM employees WHERE salary > 5000;
    

    这样可以让 department_idsalary 各自利用索引。


2. 使用 LIKE '%关键字%'(前导模糊查询失效)

SELECT * FROM employees WHERE name LIKE '%John%';

索引失效原因

  • % 号出现在开头,数据库无法使用 B+ 树索引,必须全表扫描

优化方案

  • 避免前导 %

    SELECT * FROM employees WHERE name LIKE 'John%';
    
    • 这样索引可以高效查找匹配的记录。

  • 使用全文索引(Full-Text Index)(适用于 MySQL):

    ALTER TABLE employees ADD FULLTEXT(name);
    SELECT * FROM employees WHERE MATCH(name) AGAINST('John');
    

3. 计算、函数或类型转换导致索引失效

SELECT * FROM employees WHERE YEAR(birth_date) = 1990;

索引失效原因

  • YEAR(birth_date) 对索引列做了计算,导致数据库无法直接使用索引。

  • 需要对每一行数据计算 YEAR(birth_date),然后进行匹配,导致全表扫描。

优化方案

  • 避免对索引列进行计算,改写 SQL:

    SELECT * FROM employees WHERE birth_date BETWEEN '1990-01-01' AND '1990-12-31';
    
    • 这样 birth_date 索引能直接生效。


4. !=<>NOT IN 可能导致索引失效

SELECT * FROM employees WHERE department_id != 3;

索引失效原因

  • != 会导致数据库遍历所有记录来查找不等于 3 的值,索引无法高效筛选数据。

优化方案

  • BETWEENIN 代替

    SELECT * FROM employees WHERE department_id IN (1, 2, 4, 5);
    
    • 这样索引可以生效。


5. IS NULLIS NOT NULL 可能导致索引失效

SELECT * FROM employees WHERE department_id IS NULL;

索引失效原因

  • 索引通常不会存储 NULL 值,导致数据库可能执行全表扫描。

优化方案

  • 使用默认值代替 NULL(如 0):

    ALTER TABLE employees MODIFY department_id INT DEFAULT 0;
    UPDATE employees SET department_id = 0 WHERE department_id IS NULL;
    
  • 建立索引时包含 NULL 值

    CREATE INDEX idx_department_id ON employees(department_id) WHERE department_id IS NOT NULL;
    

6. 隐式类型转换导致索引失效

SELECT * FROM employees WHERE phone_number = 1234567890;

索引失效原因

  • phone_numberVARCHAR,但查询值是 INT,数据库会进行类型转换

    SELECT * FROM employees WHERE CAST(phone_number AS INT) = 1234567890;
    
  • 索引无法生效,导致全表扫描

优化方案

  • 保持数据类型一致

    SELECT * FROM employees WHERE phone_number = '1234567890';
    

总结

索引失效原因优化方案
OR 条件使用 联合索引UNION ALL
LIKE '%关键字%'避免 % 前导,使用 全文索引
计算、函数 (YEAR(birth_date))重写查询,避免索引列计算
!=<>NOT INBETWEENIN 代替
IS NULLIS NOT NULL使用默认值替代 NULL索引 NULL 值
隐式类型转换确保数据类型匹配

最佳实践

  1. 创建合适的索引(主键、联合索引、覆盖索引)。

  2. 避免索引失效的查询模式(计算、NULL、LIKE '%...')。

  3. 定期分析 EXPLAIN 查询执行计划,找出未使用索引的 SQL 并优化。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值