MySQL索引不生效的10大场景

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS PostgreSQL,高可用系列 2核4GB
云数据库 RDS MySQL,高可用系列 2核4GB
简介: MySQL索引不生效的10大场景

前言

索引在数据库中具有重要的作用,它可以加快查询速度、提高数据库性能,以及减少资源消耗。索引是建立在表或视图上的数据结构,可以快速定位和访问数据,特别是在大型数据表中。

索引失效导致全表扫描:当查询的条件无法使用索引来加速检索,数据库可能会选择执行全表扫描操作,这会导致查询性能低下。

1、隐式的类型转换导致索引失效

假设我们有一个包含user_idusername两列的表格user_info:

user_id (int)   |   username (varchar)
-------------------------------------
1               |   John
2               |   Kate
3               |   Mike

现在我们创建了一个索引在user_id列上

CREATE INDEX idx_user_id ON user_info(user_id);

如果我们执行一个查询,以user_id作为查询条件,但是以字符串形式传递该条件,这会导致隐式类型转换:

SELECT * FROM user_info WHERE user_id = '1';

尽管我们为user_id列创建了索引,但由于查询条件中的隐式类型转换,MySQL 将'1'视为一个字符串,而不是整数。这会导致索引无法生效,数据库将会执行全表扫描来搜索匹配的行。为了确保索引的有效使用,我们应该使用匹配列的正确数据类型进行查询:

SELECT * FROM user_info WHERE user_id = 1;

在此例中,将查询条件中的字符串转换为整数,使得索引能够生效并提高查询性能。因此,当涉及到隐式类型转换时,特别是在涉及索引的查询中,需要小心使用正确的数据类型,以确保索引的有效使用

2、查询条件包括or,可能导致索引失效

假设我们有一个包含categoryprice两列的表格product:

category (varchar)   |   price (int)
-------------------------------------
Electronics          |   100
Clothing             |   50
Furniture            |   200

现在我们创建了一个索引在category列上:

CREATE INDEX idx_category ON product(category);

如果我们执行一个查询,以categoryprice作为查询条件,并使用 OR 运算符将它们组合起来:

SELECT * FROM product WHERE category = 'Electronics' OR price > 150;

尽管我们为category列创建了索引,但是由于查询条件中存在 OR 运算符,MySQL 将无法有效使用索引。这是因为 OR 运算符要求数据库同时检索满足两个条件的数据,而无法利用单一列的索引进行快速查找。

在这种情况下,数据库将执行全表扫描来搜索匹配的行,否则将针对每个条件进行单独的索引扫描,并将结果合并起来。

为了确保索引的有效使用,我们可以考虑使用 UNION 或多个独立的查询来替代 OR 运算符,以分别针对每个条件使用索引:

SELECT * FROM product WHERE category = 'Electronics'
UNION
SELECT * FROM product WHERE price > 150;

我们将查询拆分为两个独立的查询,并分别使用适当的索引,以提高查询性能。因此,当查询条件中存在 OR 运算符时,我们需要小心处理,尝试使用其他查询构造方式,以确保索引的有效使用。

3、like通配符可能导致索引失效

假设我们有一个包含 nameage 两列的表格 users

name (varchar)   |   age (int)
-------------------------------
John             |   25
Kate             |   30
Mike             |   35

现在我们创建了一个索引在 name 列上:

CREATE INDEX idx_name ON users(name);

如果我们执行一个查询,使用 LIKE 通配符搜索包含某个模式的 name

SELECT * FROM users WHERE name LIKE '%at%';

尽管我们为 name 列创建了索引,但是由于使用了 % 通配符在 LIKE 子句中,这会导致索引无法生效。因为 % 通配符表示匹配任意数量的字符,导致数据库无法使用索引进行快速查找,而需要对每一行进行模式匹配的比较。

在这种情况下,数据库可能会选择执行全表扫描来搜索匹配的行,以满足模式的要求。

为了确保索引的有效使用,我们可以考虑使用前缀搜索或者全文索引来优化查询:

  1. 使用前缀搜索:如果我们只关心 name 列以 “at” 开头的数据,可以改为使用前缀搜索,这样索引可以生效:
SELECT * FROM users WHERE name LIKE 'at%';
  1. 全文索引:如果我们需要更灵活的模式匹配功能,可以考虑使用全文索引,如 MySQL 中的全文搜索(FULLTEXT)索引或其他搜索引擎。

在使用 LIKE 通配符时,特别是在通配符前使用 % 时,需要注意可能导致索引失效,需要考虑使用其他方法优化查询。

4、查询条件不满足联合索引的最左匹配原则

假设我们有一个包含 categorypricecolor 三列的表格 products

category (varchar)   |   price (int)   |   color (varchar)
-------------------------------------------------------
Electronics          |   100           |   Red
Clothing             |   50            |   Blue
Furniture            |   200           |   Green

现在我们创建了一个联合索引在 categoryprice 两列上:

CREATE INDEX idx_category_price ON products(category, price);

如果我们执行一个查询,以 price 作为查询条件:

SELECT * FROM products WHERE price = 100;

尽管我们为 categoryprice 列创建了联合索引,但是查询条件只涉及到 price 列,不满足最左匹配原则。按照最左匹配原则,索引的左边列必须出现在查询条件中才能使用索引,而右边列可以省略。

按照mysql的索引底层是用B+树来实现的,我们可以知道只有,从上而下的查询才能使用索引这也是最左原则的底层

由于查询条件不包含 category 列,查询优化器无法利用联合索引,可能选择执行全表扫描来搜索匹配的行。

为了确保联合索引的有效使用,我们应该按照索引的顺序,保证查询条件满足最左匹配原则:

SELECT * FROM products WHERE category = 'Electronics' AND price = 100;

当使用联合索引时,需要确保查询条件满足最左匹配原则,为索引的多个列提供适当的查询条件,以避免索引失效。

5、在索引列上使用mysql的内置函数

假设有一个表 users,其中有一个索引列 name,我们想要执行一个查询,获取所有姓为 “Smith” 的用户记录。我们可能会尝试使用 MySQL 的内置函数 SUBSTRING() 来提取姓氏,然后在索引列上使用该函数进行查询,如下所示:

SELECT * FROM users WHERE SUBSTRING(name, 1, 5) = 'Smith';

然而,这种查询在索引列上使用了 SUBSTRING() 函数,这会导致索引失效。MySQL 查询优化器无法直接利用索引来定位满足条件的记录,而会执行全表扫描或使用其他索引进行查询。

为了能够有效地利用索引,应尽量避免在索引列上使用内置函数。在上述情况下,最好直接使用类似以下的查询,不使用函数:

SELECT * FROM users WHERE name LIKE 'Smith%';

6、对索引进行列运算(如,+、-、*、/)

假设有一个表 products,其中有两个列 pricediscount,我们希望查询所有价格大于原价减去折扣后的一半的产品。我们可能会尝试在查询中对索引列进行算术运算,如下所示:

SELECT * FROM products WHERE price > (price - discount) / 2;

然而,这样的查询会导致索引失效。MySQL 查询优化器无法直接利用索引来执行列级别的算术运算,并且可能会执行全表扫描或使用其他索引进行查询。

为了有效利用索引,应尽量避免在查询条件中对索引列进行算术运算。在上述情况下,可以先计算需要的值,然后在查询中使用计算结果进行比较,如下所示:

SELECT * FROM products WHERE price > ((price - discount) / 2);

通过这种方式,在执行查询时,可以有效地利用索引,并加快查询的性能。

需要注意的是,如果在查询中使用了常量或者已经存在的列进行算术运算,MySQL 查询优化器可能会对查询进行一些优化,以提高性能。但是,在索引列上执行算术运算往往会导致索引失效,因此仍建议避免在索引列上进行列级别的计算操作。

7、索引字段上使用(! = 或者 < >)

假设有一个表 users,其中有一个索引字段 age,我们想要查询所有年龄不等于 25 的用户记录。我们可能会尝试使用 != 操作符来进行查询,如下所示:

SELECT * FROM users WHERE age != 25;

然而,这样的查询可能会导致索引失效。因为 MySQL 的 B-Tree 索引存储的是有序的键值,它只能高效地利用索引来定位特定键值或键值范围的数据。但是,使用 != 操作符相当于一个不等条件,无法构建有效的键值范围,因此 MySQL 通常会选择不使用索引,而执行全表扫描来获取结果。

为了避免索引失效,可以使用等于操作符 =NOT 运算符来取代 !=,如下所示:

SELECT * FROM users WHERE age = 25;

或者使用等价的条件:

SELECT * FROM users WHERE NOT (age = 25);

这样可以有效利用索引,提高查询性能。

需要注意的是,对于一些特殊情况,MySQL 查询优化器可能会对查询进行一些优化,以在某些情况下使用索引和避免索引失效。但是,一般情况下,使用 != 或者 <> 操作符会导致索引失效,因此最好避免在索引字段上使用这些操作符。

8、使用not in 或not exists

假设有两个表:orders 表和 order_items 表,它们之间通过订单ID进行关联。我们想要查询所有没有对应订单的订单项。

  1. 使用 NOT IN
SELECT * FROM order_items WHERE order_id NOT IN (SELECT order_id FROM orders);
  1. 这个查询会查询 order_items 表中的订单项,但是需要排除掉在 orders 表中存在的订单ID。在这个查询中,子查询 (SELECT order_id FROM orders) 会返回所有的订单ID。如果 orders 表中的订单ID较多,子查询的结果集会很大,导致 MySQL 在执行主查询时需要扫描大量数据,无法高效地利用索引。这可能导致索引失效,性能下降。
  2. 使用 NOT EXISTS
SELECT * FROM order_items oi WHERE NOT EXISTS (SELECT 1 FROM orders o WHERE o.order_id = oi.order_id);
  1. 这个查询会查询 order_items 表中的订单项,并使用子查询 (SELECT 1 FROM orders o WHERE o.order_id = oi.order_id) 来判断是否存在对应的订单。尽管 NOT EXISTS 子查询只需要找到第一个匹配的结果,就可以确定不存在匹配的结果,但是如果子查询中的条件复杂或执行计划不佳,也可能导致索引失效或影响查询性能。

为了避免这种情况,可以考虑使用其他方式,比如使用 LEFT JOIN 结合 IS NULL

SELECT oi.* FROM order_items oi LEFT JOIN orders o ON o.order_id = oi.order_id WHERE o.order_id IS NULL;

这种方式可以利用 LEFT JOIN 并检查 orders 表中的订单ID是否为 NULL,来确定哪些订单项没有对应的订单。这样就能更有效地利用索引,提高查询性能。

需要根据实际情况评估并选择最适合的查询方式来提高性能,并进行必要的索引调整和查询优化

9、order by

举个例子,假设有一个 customers 表,其中有一个索引在 last_name 列上。我们想要按照姓氏字母顺序对客户进行排序。

如果我们直接在 last_name 列上进行排序,索引可以被有效利用:

SELECT * FROM customers ORDER BY last_name;

然而,如果我们对非索引列进行排序,可能导致索引失效:

SELECT * FROM customers ORDER BY first_name;

在这个例子中,我们尝试对 first_name 列进行排序,而该列并没有索引。这可能导致无法有效利用索引,因为 MySQL 会优先使用索引的排序而不使用随机读取(全表扫描)。

排序顺序与索引的顺序不匹配也可能导致索引失效:

SELECT * FROM customers ORDER BY last_name DESC;

索引是按照升序(ASC)排列的,而我们尝试以降序(DESC)进行排序。虽然数据库可以通过排序结果的逆向扫描来实现降序排序,但这可能会导致索引失效,因为索引无法直接按照所需的顺序匹配。

为了避免索引失效,需要确保选择的排序列与索引列匹配,并尽量避免在排序中使用函数、表达式或非索引列。如果需要使用函数或表达式进行排序,可以考虑创建适当的函数索引来优化查询性能。

需要根据具体情况评估并选择最佳的查询方式,以确保索引能够有效使用,从而提高查询性能。

10、优化器选错了索引

假设有一个 orders 表,其中包含了订单的不同属性,包括订单号 order_number(主键)、客户ID customer_id、订单日期 order_date 等属性。我们要查询特定客户在某个时间范围内的订单数量。

假设我们执行以下查询:

SELECT COUNT(*) FROM orders
WHERE customer_id = 12345 AND order_date BETWEEN '2022-01-01' AND '2022-12-31';

如果在 orders 表上存在 customer_id 列和 order_date 列的索引,优化器应该选择同时使用这两个索引进行查询。但是,优化器有可能错误地选择了仅使用其中一个索引。

例如,优化器可能选择了只使用 customer_id 索引,然后在结果集中进一步过滤日期范围。这将导致优化器扫描大量不符合日期过滤条件的记录,导致性能下降。

为了解决这个问题,我们可以引导优化器选择正确的索引,通过使用查询提示(query hint)或重构查询语句来指定优化器应选择的索引:

SELECT COUNT(*) FROM orders USE INDEX (customer_id, order_date)
WHERE customer_id = 12345 AND order_date BETWEEN '2022-01-01' AND '2022-12-31';

在这个改进后的查询中,我们使用了 USE INDEX 查询提示来告诉优化器显式使用了包含 customer_idorder_date 列的联合索引。

通过引导优化器选择正确的索引,我们可以提高查询性能,避免优化器选择错误的索引导致性能下降的问题。需要注意的是,引导优化器的行为需要基于实际情况进行评估,并确保所选的索引在大多数查询场景下都是最优的选择。

相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
相关文章
|
2月前
|
存储 SQL 关系型数据库
mysql底层原理:索引、慢查询、 sql优化、事务、隔离级别、MVCC、redolog、undolog(图解+秒懂+史上最全)
mysql底层原理:索引、慢查询、 sql优化、事务、隔离级别、MVCC、redolog、undolog(图解+秒懂+史上最全)
mysql底层原理:索引、慢查询、 sql优化、事务、隔离级别、MVCC、redolog、undolog(图解+秒懂+史上最全)
|
2月前
|
存储 关系型数据库 MySQL
MySQL数据库索引的数据结构?
MySQL中默认使用B+tree索引,它是一种多路平衡搜索树,具有树高较低、检索速度快的特点。所有数据存储在叶子节点,非叶子节点仅作索引,且叶子节点形成双向链表,便于区间查询。
98 4
|
4月前
|
存储 关系型数据库 MySQL
阿里面试:MySQL 一个表最多 加几个索引? 6个?64个?还是多少?
阿里面试:MySQL 一个表最多 加几个索引? 6个?64个?还是多少?
阿里面试:MySQL 一个表最多 加几个索引? 6个?64个?还是多少?
|
6月前
|
关系型数据库 MySQL 数据库
Mysql的索引
MYSQL索引主要有 : 单列索引 , 组合索引和空间索引 , 用的比较多的就是单列索引和组合索引 , 空间索引我这边没有用到过 单列索引 : 在MYSQL数据库表的某一列上面创建的索引叫单列索引 , 单列索引又分为 ● 普通索引:MySQL中基本索引类型,没有什么限制,允许在定义索引的列中插入重复值和空值,纯粹为了查询数据更快一点。 ● 唯一索引:索引列中的值必须是唯一的,但是允许为空值 ● 主键索引:是一种特殊的唯一索引,不允许有空值 ● 全文索引: 只有在MyISAM引擎、InnoDB(5.6以后)上才能使⽤用,而且只能在CHAR,VARCHAR,TEXT类型字段上使⽤用全⽂文索引。
|
2月前
|
存储 SQL 关系型数据库
MySQL 核心知识与索引优化全解析
本文系统梳理了 MySQL 的核心知识与索引优化策略。在基础概念部分,阐述了 char 与 varchar 在存储方式和性能上的差异,以及事务的 ACID 特性、并发事务问题及对应的隔离级别(MySQL 默认 REPEATABLE READ)。 索引基础部分,详解了 InnoDB 默认的 B+tree 索引结构(多路平衡树、叶子节点存数据、双向链表支持区间查询),区分了聚簇索引(数据与索引共存,唯一)和二级索引(数据与索引分离,多个),解释了回表查询的概念及优化方法,并分析了 B+tree 作为索引结构的优势(树高低、效率稳、支持区间查询)。 索引优化部分,列出了索引创建的六大原则
|
3月前
|
关系型数据库 MySQL 分布式数据库
Super MySQL|揭秘PolarDB全异步执行架构,高并发场景性能利器
阿里云瑶池旗下的云原生数据库PolarDB MySQL版设计了基于协程的全异步执行架构,实现鉴权、事务提交、锁等待等核心逻辑的异步化执行,这是业界首个真正意义上实现全异步执行架构的MySQL数据库产品,显著提升了PolarDB MySQL的高并发处理能力,其中通用写入性能提升超过70%,长尾延迟降低60%以上。
|
3月前
|
存储 关系型数据库 MySQL
MySQL覆盖索引解释
总之,覆盖索引就像是图书馆中那些使得搜索变得极为迅速和简单的工具,一旦正确使用,就会让你的数据库查询飞快而轻便。让数据检索就像是读者在图书目录中以最快速度找到所需信息一样简便。这样的效率和速度,让覆盖索引成为数据库优化师傅们手中的尚方宝剑,既能够提升性能,又能够保持系统的整洁高效。
122 9
|
4月前
|
机器学习/深度学习 关系型数据库 MySQL
对比MySQL全文索引与常规索引的互异性
现在,你或许明白了这两种索引的差异,但任何技术决策都不应仅仅基于理论之上。你可以创建你的数据库实验环境,尝试不同类型的索引,看看它们如何影响性能,感受它们真实的力量。只有这样,你才能熟悉它们,掌握什么时候使用全文索引,什么时候使用常规索引,以适应复杂多变的业务需求。
108 12
|
8月前
|
存储 关系型数据库 MySQL
MySQL索引学习笔记
本文深入探讨了MySQL数据库中慢查询分析的关键概念和技术手段。
608 81
|
5月前
|
SQL 存储 关系型数据库
MySQL选错索引了怎么办?
本文探讨了MySQL中因索引选择不当导致查询性能下降的问题。通过创建包含10万行数据的表并插入数据,分析了一条简单SQL语句在不同场景下的执行情况。实验表明,当数据频繁更新时,MySQL可能因统计信息不准确而选错索引,导致全表扫描。文章深入解析了优化器判断扫描行数的机制,指出基数统计误差是主要原因,并提供了通过`analyze table`重新统计索引信息的解决方法。
138 3

推荐镜像

更多