常见索引失效场景及原因分析(含示例)
1. 使用函数或表达式操作索引列
失效原因:函数/表达式会修改索引列的值,破坏B+树索引的有序性,导致数据库无法通过索引快速定位匹配行。
示例:
-- 表结构:CREATE TABLE users (id INT, name VARCHAR(50), INDEX idx_name(name));
-- 索引失效查询
SELECT * FROM users WHERE SUBSTRING(name, 1, 3) = 'abc'; -- 使用函数截取字符串
2. 索引列参与数学计算或逻辑运算
失效原因:计算操作会改变索引列的原始值,索引存储的有序结构无法匹配计算后的值,触发全表扫描。
示例:
-- 表结构:CREATE TABLE products (id INT, price INT, INDEX idx_price(price));
-- 索引失效查询
SELECT * FROM products WHERE price + 100 > 5000; -- 索引列参与加法运算
3. 字符串索引列与数字比较导致类型转换
失效原因:MySQL会对字符串列执行隐式类型转换(如CAST(name AS SIGNED)
),等价于对索引列使用函数,导致索引失效。
示例:
-- 表结构:CREATE TABLE users (id INT, phone VARCHAR(20), INDEX idx_phone(phone));
-- 索引失效查询
SELECT * FROM users WHERE phone = 13800138000; -- 字符串列与数字比较(无引号)
4. 使用OR连接包含非索引列的条件
失效原因:OR条件需同时满足多个条件,非索引列无法通过索引过滤,数据库只能全表扫描以确保不遗漏数据。
示例:
-- 表结构:CREATE TABLE users (id INT, name VARCHAR(50), age INT, INDEX idx_name(name));
-- 索引失效查询
SELECT * FROM users WHERE name = '张三' OR age = 25; -- age列无索引
5. LIKE模糊查询以%开头
失效原因:B+树索引按前缀排序,%
开头的匹配无法确定起始位置,只能遍历全表逐行匹配。
示例:
-- 表结构:CREATE TABLE goods (id INT, title VARCHAR(100), INDEX idx_title(title));
-- 索引失效查询
SELECT * FROM goods WHERE title LIKE '%手机'; -- %开头的后缀匹配
6. 使用不等于操作符(!=、<>)、NOT IN或IS NOT NULL
失效原因:此类操作通常匹配大量数据,优化器认为全表扫描的IO成本低于索引扫描(尤其是数据分布倾斜时)。
示例:
-- 表结构:CREATE TABLE orders (id INT, status INT, INDEX idx_status(status));
-- 索引失效查询
SELECT * FROM orders WHERE status != 1; -- 使用不等于操作符
7. 复合索引不满足最左前缀原则
失效原因:复合索引的B+树按前缀列排序,跳过左列会破坏索引的有序性定位能力,无法使用索引范围扫描。
示例:
-- 表结构:CREATE TABLE employees (id INT, dept_id INT, job VARCHAR(50), INDEX idx_dept_job(dept_id, job));
-- 索引失效查询
SELECT * FROM employees WHERE job = 'developer'; -- 跳过最左列dept_id
8. 索引选择性过低或使用前缀索引不当
失效原因:低选择性(如性别列)导致索引扫描需访问大量数据页;前缀索引过短会降低选择性,优化器可能放弃使用。
示例:
-- 表结构:CREATE TABLE users (id INT, gender CHAR(1), INDEX idx_gender(gender)); -- gender仅'M'/'F'
-- 索引失效查询
SELECT * FROM users WHERE gender = 'M'; -- 选择性过低(50%数据匹配)
9. MySQL优化器基于成本选择全表扫描
失效原因:当索引扫描的IO成本(如回表查询)高于全表扫描时,优化器会选择成本更低的全表扫描(如小表或高选择性索引)。
示例:
-- 表结构:CREATE TABLE users (id INT, name VARCHAR(50), INDEX idx_name(name)); -- 仅10行数据
-- 索引失效查询(优化器选择全表扫描)
SELECT * FROM users WHERE name = '张三'; -- 小表全表扫描成本更低
10. IN子句包含过多值导致索引失效
失效原因:IN值超过eq_range_index_dive_limit
阈值(默认200)时,优化器改用统计信息估算行数,可能因偏差选择非最优计划。
示例:
-- 表结构:CREATE TABLE orders (id INT, user_id INT, INDEX idx_user(user_id));
-- 索引失效查询(IN值过多)
SELECT * FROM orders WHERE user_id IN (1,2,3,...,300); -- IN列表含300个值(超过阈值200)
11. 对索引列使用IS NULL判断(非NULL记录占比高时)
失效原因:非NULL记录占比过高时,索引扫描需访问大量数据页,优化器判定全表扫描更高效。
示例:
-- 表结构:CREATE TABLE users (id INT, email VARCHAR(50), INDEX idx_email(email)); -- 90%记录非NULL
-- 索引失效查询
SELECT * FROM users WHERE email IS NOT NULL; -- 非NULL记录占比高
12. 索引列被频繁更新导致碎片过多
失效原因:频繁UPDATE/DELETE会导致索引页分裂、空洞增多,降低索引扫描效率,优化器可能选择全表扫描。
示例:
-- 表结构:CREATE TABLE logs (id INT, content VARCHAR(255), INDEX idx_content(content)); -- 日均10万次更新
-- 索引失效查询(碎片过多)
SELECT * FROM logs WHERE content LIKE 'error%'; -- 索引碎片导致扫描效率下降