(1)索引生效的查询
-- 联合索引 (a, b, c)
-- 有效:按索引顺序使用 a 和 b
WHERE a = 1 AND b = 2;
-- 无效:跳过 a,索引无法使用
WHERE b = 2;
(2)范围查询影响索引
-- `b` 是范围查询,`c` 无法使用索引
WHERE a = 100 AND b > 200 AND c = 300;
-- 同样的问题
WHERE a = 100 AND b BETWEEN 200 AND 300;
索引使用情况如下:
字段 | 是否使用索引 | 说明 |
---|---|---|
a | ✅ 是 | 等值查询,索引可用 |
b | ✅ 是 | 范围查询,索引可用 |
c | ❌ 否 | 受范围查询影响,索引失效 |
解决方案:尽量将范围查询放在 WHERE
末尾,避免影响后续索引。
(3)IN
查询
WHERE a IN (100, 101) AND b IN (200, 201) AND c = 300;
索引使用情况如下:
字段 | 是否使用索引 | 说明 |
---|---|---|
a | ✅ 是 | 等值查询 |
b | ✅ 是 | IN 属于等值查询 |
c | ✅ 是 | 仍然是等值查询 |
(4)LIKE
非前缀匹配导致索引失效
WHERE a = 100 AND b LIKE '%abc%' AND c = 300;
索引使用情况如下:
字段 | 是否使用索引 | 说明 |
---|---|---|
a | ✅ 是 | 等值查询 |
b | ❌ 否 | LIKE '%abc%' 无法使用索引 |
c | ❌ 否 | 受 b 影响,索引被截断 |
解决方案:使用 LIKE 'abc%'
进行前缀匹配,可保持索引可用。
小结
=
或 IN
)**,避免范围查询影响索引。> < BETWEEN LIKE '%abc%'
) 应尽量放在 WHERE
末尾,减少索引失效风险。LIKE
查询应使用前缀匹配 (LIKE 'abc%'
)**,避免索引失效。若查询字段全部包含在索引中(即覆盖索引),数据库可直接从索引树获取数据,无需回表查询主键索引。
回表代价:若未覆盖字段,需通过主键 ID 回主索引读取完整数据,增加磁盘 I/O。
-- 覆盖索引示例
CREATE INDEX idx_cover ON users(city, age);
SELECT city, age FROM users WHERE city = 'Shanghai'; -- ✅ 无需回表
SELECT name FROM users WHERE city = 'Shanghai'; -- ❌ 需回表获取name
若索引字段类型与查询条件类型不一致(如字符串字段匹配数字),数据库需隐式转换类型,导致无法使用索引。
-- ❌ 索引失效(supplier_id为VARCHAR)
SELECT * FROM orders WHERE supplier_id = 7067;
-- ✅ 正确写法
SELECT * FROM orders WHERE supplier_id = '7067';
对索引列使用函数(如 YEAR(date_column)
)会破坏 B+树的有序性,导致索引失效。
-- ❌ 索引失效
SELECT * FROM orders WHERE YEAR(create_time) = 2023;
-- ✅ 优化写法
SELECT * FROM orders WHERE create_time BETWEEN '2023-01-01' AND '2023-12-31';
LIMIT offset, N
需要扫描前 offset + N
行数据,偏移量越大性能越差。-- ❌ 低效分页(扫描100万+20行)
SELECT * FROM orders LIMIT 1000000, 20;
-- ✅ 高效分页(基于主键游标)
SELECT * FROM orders WHERE id > 1000000 ORDER BY id LIMIT 20;
-- ❌ 时间复杂度 O(N²)
SELECT name, (SELECT COUNT(*) FROM orders WHERE user_id = u.id)
FROM users u;
-- ✅ 优化为 JOIN(时间复杂度 O(N))
SELECT u.name, COUNT(o.id)
FROM users u LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id;
-- ❌ 先JOIN后过滤(处理全量数据)
SELECT u.name, o.amount
FROM users u LEFT JOIN orders o ON u.id = o.user_id
WHERE u.city = 'Shanghai';
-- ✅ 先过滤后JOIN(减少JOIN数据量)
SELECT u.name, o.amount
FROM (SELECT * FROM users WHERE city = 'Shanghai') u
LEFT JOIN orders o ON u.id = o.user_id;
分步查询 + 应用层组装
本文系作者在时代Java发表,未经许可,不得转载。
如有侵权,请联系nowjava@qq.com删除。