MySQL索引不生效的10大场景

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 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 列的联合索引。

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

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
21天前
|
存储 SQL 关系型数据库
MySQL高级篇——索引失效的11种情况
索引优化思路、要尽量满足全值匹配、最佳左前缀法则、主键插入顺序尽量自增、计算、函数导致索引失效、类型转换(手动或自动)导致索引失效、范围条件右边的列索引失效、不等于符号导致索引失效、is not null、not like无法使用索引、左模糊查询导致索引失效、“OR”前后存在非索引列,导致索引失效、不同字符集导致索引失败,建议utf8mb4
MySQL高级篇——索引失效的11种情况
|
21天前
|
存储 SQL 关系型数据库
【MySQL调优】如何进行MySQL调优?从参数、数据建模、索引、SQL语句等方向,三万字详细解读MySQL的性能优化方案(2024版)
MySQL调优主要分为三个步骤:监控报警、排查慢SQL、MySQL调优。 排查慢SQL:开启慢查询日志 、找出最慢的几条SQL、分析查询计划 。 MySQL调优: 基础优化:缓存优化、硬件优化、参数优化、定期清理垃圾、使用合适的存储引擎、读写分离、分库分表; 表设计优化:数据类型优化、冷热数据分表等。 索引优化:考虑索引失效的11个场景、遵循索引设计原则、连接查询优化、排序优化、深分页查询优化、覆盖索引、索引下推、用普通索引等。 SQL优化。
168 15
【MySQL调优】如何进行MySQL调优?从参数、数据建模、索引、SQL语句等方向,三万字详细解读MySQL的性能优化方案(2024版)
|
21天前
|
存储 SQL 关系型数据库
一篇文章搞懂MySQL的分库分表,从拆分场景、目标评估、拆分方案、不停机迁移、一致性补偿等方面详细阐述MySQL数据库的分库分表方案
MySQL如何进行分库分表、数据迁移?从相关概念、使用场景、拆分方式、分表字段选择、数据一致性校验等角度阐述MySQL数据库的分库分表方案。
一篇文章搞懂MySQL的分库分表,从拆分场景、目标评估、拆分方案、不停机迁移、一致性补偿等方面详细阐述MySQL数据库的分库分表方案
|
21天前
|
存储 关系型数据库 MySQL
MySQL高级篇——覆盖索引、前缀索引、索引下推、SQL优化、主键设计
覆盖索引、前缀索引、索引下推、SQL优化、EXISTS 和 IN 的区分、建议COUNT(*)或COUNT(1)、建议SELECT(字段)而不是SELECT(*)、LIMIT 1 对优化的影响、多使用COMMIT、主键设计、自增主键的缺点、淘宝订单号的主键设计、MySQL 8.0改造UUID为有序
MySQL高级篇——覆盖索引、前缀索引、索引下推、SQL优化、主键设计
|
5天前
|
存储 关系型数据库 MySQL
MySQL索引失效及避免策略:优化查询性能的关键
MySQL索引失效及避免策略:优化查询性能的关键
22 3
|
10天前
|
关系型数据库 MySQL 数据库
MySQL删除全局唯一索引unique
这篇文章介绍了如何在MySQL数据库中删除全局唯一的索引(unique index),包括查看索引、删除索引的方法和确认删除后的状态。
32 9
|
5天前
|
存储 SQL 关系型数据库
MySQL 的索引是怎么组织的?
MySQL 的索引是怎么组织的?
11 1
|
5天前
|
存储 关系型数据库 MySQL
MySQL索引的概念与好处
本文介绍了MySQL存储引擎及其索引类型,重点对比了MyISAM与InnoDB引擎的不同之处。文中详细解释了InnoDB引擎的自适应Hash索引及聚簇索引的特点,并阐述了索引的重要性及使用原因,包括提升数据检索速度、实现数据唯一性等。最后,文章还讨论了主键索引的选择与页分裂问题,并提供了使用自增字段作为主键的建议。
MySQL索引的概念与好处
|
13天前
|
关系型数据库 MySQL 数据库
MYSQL索引的分类与创建语法详解
理解并合理应用这些索引类型,能够有效提高MySQL数据库的性能和查询效率。每种索引类型都有其特定的优势,适当地使用它们可以为数据库操作带来显著的性能提升。
37 3
|
5天前
|
监控 关系型数据库 MySQL
如何优化MySQL数据库的索引以提升性能?
如何优化MySQL数据库的索引以提升性能?
14 0
下一篇
无影云桌面