网站首页 > 技术文章 正文
引言
MySQL索引对于提升查询性能至关重要,能够显著加速数据检索速度,降低磁盘I/O消耗。然而,在某些特定条件下,原本应发挥作用的索引可能无法正常工作,导致查询效率大幅下降,甚至退化为全表扫描。理解这些可能导致索引失效的场景及其背后的原理,有助于我们编写更高效的SQL查询,避免潜在性能陷阱。本篇文章将详细介绍MySQL索引失效的常见场景,并提供相应的解决方案及示例。
一、索引失效场景及原因
1. 全值匹配之外的条件查询
场景:仅对索引列的一部分进行查询(如使用LIKE关键字进行模糊查询,或者范围查询时条件不完整)。
原因:B树索引只能根据左前缀原则进行高效查找。对于非左前缀的部分查询,索引无法利用。
示例:
SELECT * FROM users WHERE name LIKE 'John%';
上述查询只对name字段的起始部分进行了匹配,索引可以有效利用。但若改为如下查询:
SELECT * FROM users WHERE name LIKE '%ohn%';
此时,由于查询条件不在索引的左前缀,索引失效。
2. 类型转换导致索引失效
场景:查询条件与索引列类型不完全匹配,导致MySQL在比较时进行隐式类型转换。
原因:类型转换会使索引无法直接用于查找,必须先对所有索引记录进行类型转换后才能进行比较。
示例:
CREATE TABLE test (
id INT PRIMARY KEY,
value VARCHAR(50)
);
ALTER TABLE test ADD INDEX idx_value (value);
SELECT * FROM test WHERE value = 1; -- 错误的查询,value列是字符串类型
在此例中,尝试将整数1与value列(字符串类型)进行比较,引发类型转换,索引失效。
3. 使用函数或表达式操作索引列
场景:查询中对索引列应用了函数、运算符或其他表达式。
原因:索引存储的是原始列值,对索引列应用函数或表达式后,数据库无法直接使用索引来查找符合条件的记录。
示例:
SELECT * FROM users WHERE UPPER(name) = 'JOHN';
此查询对name列应用了UPPER函数,导致索引失效。
4. 联合索引未遵循最左前缀原则
场景:使用联合索引时,查询条件没有按照索引列的定义顺序进行匹配。
原因:联合索引实质上是按索引列组合构建的多级索引结构,查询时需遵循从左到右的最左前缀原则。
示例:
CREATE TABLE orders (
id INT PRIMARY KEY,
user_id INT,
order_date DATE,
status ENUM('pending', 'completed')
);
ALTER TABLE orders ADD INDEX idx_user_date_status (user_id, order_date, status);
SELECT * FROM orders WHERE order_date = '2022-01-01' AND user_id = 100; -- 索引失效
此处查询条件的顺序与索引定义相反,未遵循最左前缀原则,导致索引失效。
5. 索引列参与计算或与其他列相关联
场景:索引列在查询中与其他列进行比较、计算,或者出现在IN子句的非第一个位置。
原因:这种情况下,数据库无法独立使用索引来过滤记录。
示例:
SELECT * FROM products WHERE price + tax > 100; -- 索引列参与计算
SELECT * FROM orders WHERE product_id IN (SELECT id FROM products WHERE category = 'Electronics') AND status = 'completed'; -- 索引列在IN子句中非首位置
在这两个示例中,索引列均未直接用于等值比较,而是参与了计算或与其他条件关联,导致索引失效。
二、解决方案与优化策略
针对上述索引失效场景,我们可以采取以下措施来优化查询并确保索引的有效利用:
1. 调整查询条件
对于模糊查询,尽可能使用左前缀匹配,避免在索引列中间或末尾使用%通配符。
SELECT * FROM users WHERE name LIKE 'John%';
避免在查询中进行不必要的类型转换。确保查询条件与索引列类型一致。
SELECT * FROM test WHERE value = '1'; -- 将查询值改为字符串类型
不要在索引列上使用函数或表达式。若必须使用,考虑在插入数据时就计算好结果并存入另一列,对该列建立索引。
-- 在表中增加一个新的列存储name的全大写版本,并对其建立索引
ALTER TABLE users ADD COLUMN name_uppercase VARCHAR(50);
UPDATE users SET name_uppercase = UPPER(name);
ALTER TABLE users ADD INDEX idx_name_uppercase (name_uppercase);
SELECT * FROM users WHERE name_uppercase = 'JOHN';
2. 合理设计和使用联合索引
确保查询条件按照联合索引的列顺序编写。
SELECT * FROM orders WHERE user_id = 100 AND order_date = '2022-01-01'; -- 按照索引列顺序编写查询
如果查询条件经常变动或不固定,可以考虑创建覆盖索引(包含查询所需全部列的索引),减少回表次数。
ALTER TABLE orders ADD INDEX idx_user_date_status_covered (user_id, order_date, status, id); -- 添加覆盖索引
SELECT status FROM orders WHERE user_id = 100 AND order_date = '2022-01-01'; -- 使用覆盖索引,无需回表
3. 优化涉及计算或关联查询的语句
尽量避免在查询中对索引列进行计算。如果必须进行计算,考虑在应用程序层面提前计算结果,或将计算结果存储为单独的列并建立索引。
-- 提前计算price_with_tax并在插入数据时保存
ALTER TABLE products ADD COLUMN price_with_tax DECIMAL(10, 2);
UPDATE products SET price_with_tax = price + tax;
ALTER TABLE products ADD INDEX idx_price_with_tax (price_with_tax);
SELECT * FROM products WHERE price_with_tax > 100;
对于复杂的IN子句查询,尝试重构查询或使用连接查询替代,确保索引列在IN子句中处于首位。
SELECT o.*
FROM orders o
JOIN products p ON o.product_id = p.id AND p.category = 'Electronics'
WHERE o.status = 'completed';
总结来说,识别并避免MySQL索引失效的关键在于理解索引的工作原理、熟知可能导致索引失效的各种场景,并据此调整查询语句或数据模型。通过合理的索引设计、查询优化以及数据预处理,我们可以最大限度地发挥索引的优势,提升查询性能,保证数据库系统的高效运行。
猜你喜欢
- 2024-10-28 MySQL查询为什么没走索引?这篇文章带你全面解析
- 2024-10-28 什么情况会导致 MySQL 索引失效?(mysql什么情况下会导致索引失效)
- 2024-10-28 用了索引一定就有用吗?如何排查?(使用索引)
- 2024-10-28 MySQL 索引优化分析:为啥你的SQL慢?为啥你建的索引常失效?
- 2024-10-28 MySQL基础(索引分析和使用)(mysql各种索引的使用场景)
- 2024-10-28 MySQL基础(其它SQL优化)(mysql数据库优化及sql调优)
- 2024-10-28 除了网络问题之外,即使对于数据量较小的表
- 2024-10-28 研究了 4.7 个小时终于了解到了索引使用了却没变快的原因
- 2024-10-28 MySQL索引失效带来的性能瓶颈:如何解决这个棘手问题?
- 2024-10-28 常见mysql索引失效条件(常见mysql索引失效条件是什么)
- 02-21走进git时代, 你该怎么玩?_gits
- 02-21GitHub是什么?它可不仅仅是云中的Git版本控制器
- 02-21Git常用操作总结_git基本用法
- 02-21为什么互联网巨头使用Git而放弃SVN?(含核心命令与原理)
- 02-21Git 高级用法,喜欢就拿去用_git基本用法
- 02-21Git常用命令和Git团队使用规范指南
- 02-21总结几个常用的Git命令的使用方法
- 02-21Git工作原理和常用指令_git原理详解
- 最近发表
- 标签列表
-
- cmd/c (57)
- c++中::是什么意思 (57)
- sqlset (59)
- ps可以打开pdf格式吗 (58)
- phprequire_once (61)
- localstorage.removeitem (74)
- routermode (59)
- vector线程安全吗 (70)
- & (66)
- java (73)
- org.redisson (64)
- log.warn (60)
- cannotinstantiatethetype (62)
- js数组插入 (83)
- resttemplateokhttp (59)
- gormwherein (64)
- linux删除一个文件夹 (65)
- mac安装java (72)
- reader.onload (61)
- outofmemoryerror是什么意思 (64)
- flask文件上传 (63)
- eacces (67)
- 查看mysql是否启动 (70)
- java是值传递还是引用传递 (58)
- 无效的列索引 (74)