iis服务器助手广告广告
返回顶部
首页 > 资讯 > 数据库 >MySQL优化之避免索引失效的方法
  • 839
分享到

MySQL优化之避免索引失效的方法

MySQL优化之避免索引失效的方法 2018-05-25 13:05:53 839人浏览 猪猪侠
摘要

在上一篇文章中,通过分析执行计划的字段说明,大体说了一下索引优化过程中的一些注意点,那么如何才能避免索引失效呢?本篇文章将来讨论这个问题。 避免索引失效的常见方法 1.对于复合索引的使用,应按照索引建立的顺序使用,尽量不要跨列(最

MySQL优化之避免索引失效的方法

在上一篇文章中,通过分析执行计划的字段说明,大体说了一下索引优化过程中的一些注意点,那么如何才能避免索引失效呢?本篇文章将来讨论这个问题。

避免索引失效的常见方法

1.对于复合索引的使用,应按照索引建立的顺序使用,尽量不要跨列(最佳左前缀原则)

为了说明问题,我们仍然使用上一篇文章中的test01表,其表结构如下所示:

mysql> desc test01;
+--------+-------------+------+-----+---------+-------+
| Field  | Type        | Null | Key | Default | Extra |
+--------+-------------+------+-----+---------+-------+
| id     | int(4)      | YES  | MUL | NULL    |       |
| name   | varchar(20) | YES  |     | NULL    |       |
| passwd | char(20)    | YES  |     | NULL    |       |
| inf    | char(50)    | YES  |     | NULL    |       |
+--------+-------------+------+-----+---------+-------+
4 rows in set (0.01 sec)
mysql> show index from test01;
+--------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+
---------+---------------+
| Table  | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type |
 Comment | Index_comment |
+--------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+
---------+---------------+
| test01 |          1 | t_idx1   |            1 | id          | A         |           0 |     NULL | NULL   | YES  | BTREE      |
         |               |
| test01 |          1 | t_idx1   |            2 | name        | A         |           0 |     NULL | NULL   | YES  | BTREE      |
         |               |
| test01 |          1 | t_idx1   |            3 | passwd      | A         |           0 |     NULL | NULL   | YES  | BTREE      |
         |               |
+--------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+
---------+---------------+
3 rows in set (0.01 sec)

如果常规的sql写法,三个索引全覆盖,没有任何问题:

mysql> explain select * from test01 where id = 1 and name = "zz" and passwd = "123";
+----+-------------+--------+------------+------+---------------+--------+---------+-------------------+------+----------+-------
+
| id | select_type | table  | partitions | type | possible_keys | key    | key_len | ref               | rows | filtered | Extra
|
+----+-------------+--------+------------+------+---------------+--------+---------+-------------------+------+----------+-------
+
|  1 | SIMPLE      | test01 | NULL       | ref  | t_idx1        | t_idx1 | 129     | const,const,const |    1 |   100.00 | NULL
|
+----+-------------+--------+------------+------+---------------+--------+---------+-------------------+------+----------+-------
+
1 row in set, 1 warning (0.00 sec)

但是如果跨列使用,如下所示:

mysql> explain select * from test01 where id = 1 and passwd = "123";
+----+-------------+--------+------------+------+---------------+--------+---------+-------+------+----------+-------------------
----+
| id | select_type | table  | partitions | type | possible_keys | key    | key_len | ref   | rows | filtered | Extra
    |
+----+-------------+--------+------------+------+---------------+--------+---------+-------+------+----------+-------------------
----+
|  1 | SIMPLE      | test01 | NULL       | ref  | t_idx1        | t_idx1 | 5       | const |    1 |   100.00 | Using index condit
ion |
+----+-------------+--------+------------+------+---------------+--------+---------+-------+------+----------+-------------------
----+
1 row in set, 1 warning (0.00 sec)

通过观察,发现key_len已经从129变成5了,说明只有id使用到了索引,而passwd并没有用到索引。
接下来我们看一种更糟糕的情况:

mysql> explain select * from test01 where id = 1 order by passwd;
+----+-------------+--------+------------+------+---------------+--------+---------+-------+------+----------+-------------------
--------------------+
| id | select_type | table  | partitions | type | possible_keys | key    | key_len | ref   | rows | filtered | Extra
                    |
+----+-------------+--------+------------+------+---------------+--------+---------+-------+------+----------+-------------------
--------------------+
|  1 | SIMPLE      | test01 | NULL       | ref  | t_idx1        | t_idx1 | 5       | const |    1 |   100.00 | Using index condit
ion; Using filesort |
+----+-------------+--------+------------+------+---------------+--------+---------+-------+------+----------+-------------------
--------------------+
1 row in set, 1 warning (0.00 sec)

上述语句中,Extra字段中出现了Using filesort,之前说过,这是非常差的一种写法,如果我们做一下改动:

mysql> explain select * from test01 where id = 1 order by name,passwd;
+----+-------------+--------+------------+------+---------------+--------+---------+-------+------+----------+-------------------
----+
| id | select_type | table  | partitions | type | possible_keys | key    | key_len | ref   | rows | filtered | Extra
    |
+----+-------------+--------+------------+------+---------------+--------+---------+-------+------+----------+-------------------
----+
|  1 | SIMPLE      | test01 | NULL       | ref  | t_idx1        | t_idx1 | 5       | const |    1 |   100.00 | Using index condit
ion |
+----+-------------+--------+------------+------+---------------+--------+---------+-------+------+----------+-------------------
----+
1 row in set, 1 warning (0.00 sec)

与上面的SQL相比较,只是在order by 里,加上了name,Using filesort就去掉了,所以这里的不能跨列,指的是where 和order by之间不能跨列,否则会出现很糟糕的情况。

2.不要在索引上进行任何函数操作

包括但不限于sum、trim、甚至对字段进行加减乘除计算。

mysql> explain select id from test01 where id = 1 and trim(name) ="zz" and passwd = "123";
+----+-------------+--------+------------+------+---------------+--------+---------+-------+------+----------+-------------------
-------+
| id | select_type | table  | partitions | type | possible_keys | key    | key_len | ref   | rows | filtered | Extra
       |
+----+-------------+--------+------------+------+---------------+--------+---------+-------+------+----------+-------------------
-------+
|  1 | SIMPLE      | test01 | NULL       | ref  | t_idx1        | t_idx1 | 5       | const |    1 |   100.00 | Using where; Using
 index |
+----+-------------+--------+------------+------+---------------+--------+---------+-------+------+----------+-------------------
-------+
1 row in set, 1 warning (0.00 sec)

因为在name字段上,进行了trim函数操作,所以name失效,连带着后面的passwd也失效了,因为key_len = 5。
所以此处透露出两个信息点:

  • 在索引字段上进行函数操作,会导致索引失效;
  • 复合索引如果前面的字段失效,其后面的所有字段索引都会失效。

3.复合索引不要使用is null或is not null,否则其自身和其后面的索引全部失效。

仍然是看一个例子:

mysql> explain select id from test01 where id = 1 and name is not null and passwd = "123";
+----+-------------+--------+------------+------+---------------+--------+---------+-------+------+----------+-------------------
-------+
| id | select_type | table  | partitions | type | possible_keys | key    | key_len | ref   | rows | filtered | Extra
       |
+----+-------------+--------+------------+------+---------------+--------+---------+-------+------+----------+-------------------
-------+
|  1 | SIMPLE      | test01 | NULL       | ref  | t_idx1        | t_idx1 | 5       | const |    1 |   100.00 | Using where; Using
 index |
+----+-------------+--------+------------+------+---------------+--------+---------+-------+------+----------+-------------------
-------+
1 row in set, 1 warning (0.00 sec)

因为name使用了is not null,所以导致name和passwd都失效了,从key_len = 5可以看出。

4.like尽量不要在前面加%

这一点之前在说明range级别的时候有提到过,此处再次说明一下。

mysql> explain select * from test01 where id = 1 and name like "%a%" and passwd = "123";
+----+-------------+--------+------------+------+---------------+--------+---------+-------+------+----------+-------------------
----+
| id | select_type | table  | partitions | type | possible_keys | key    | key_len | ref   | rows | filtered | Extra
    |
+----+-------------+--------+------------+------+---------------+--------+---------+-------+------+----------+-------------------
----+
|  1 | SIMPLE      | test01 | NULL       | ref  | t_idx1        | t_idx1 | 5       | const |    1 |   100.00 | Using index condit
ion |
+----+-------------+--------+------------+------+---------------+--------+---------+-------+------+----------+-------------------
----+
1 row in set, 1 warning (0.00 sec)

在上例中,name字段使用了like "%a%",所以导致name和passwd都失效,只有id使用到了索引。
为了对比,如果把前面的%去掉,看看什么结果:

mysql> explain select * from test01 where id = 1 and name like "a%" and passwd = "123";
+----+-------------+--------+------------+-------+---------------+--------+---------+------+------+----------+-------------------
----+
| id | select_type | table  | partitions | type  | possible_keys | key    | key_len | ref  | rows | filtered | Extra
    |
+----+-------------+--------+------------+-------+---------------+--------+---------+------+------+----------+-------------------
----+
|  1 | SIMPLE      | test01 | NULL       | range | t_idx1        | t_idx1 | 129     | NULL |    1 |   100.00 | Using index condit
ion |
+----+-------------+--------+------------+-------+---------------+--------+---------+------+------+----------+-------------------
----+
1 row in set, 1 warning (0.00 sec)

可以看到,此时key_len = 129,说明三个字段都用到了。

5.尽量不要使用类型转换,包括显式的和隐式的

正常的索引应该是这样:

mysql> explain select * from test01 where id = 1 and name = "zz" and passwd = "123";
+----+-------------+--------+------------+------+---------------+--------+---------+-------------------+------+----------+-------
+
| id | select_type | table  | partitions | type | possible_keys | key    | key_len | ref               | rows | filtered | Extra
|
+----+-------------+--------+------------+------+---------------+--------+---------+-------------------+------+----------+-------
+
|  1 | SIMPLE      | test01 | NULL       | ref  | t_idx1        | t_idx1 | 129     | const,const,const |    1 |   100.00 | NULL
|
+----+-------------+--------+------------+------+---------------+--------+---------+-------------------+------+----------+-------
+
1 row in set, 1 warning (0.00 sec)

但是如果改一下,把passwd = "123"改成passwd = 123,让Mysql自己去做类型转换,将123转换成"123",那结果是怎样的呢?

mysql> explain select * from test01 where id = 1 and name = "zz" and passwd = 123;
+----+-------------+--------+------------+------+---------------+--------+---------+-------------+------+----------+-------------
----------+
| id | select_type | table  | partitions | type | possible_keys | key    | key_len | ref         | rows | filtered | Extra
          |
+----+-------------+--------+------------+------+---------------+--------+---------+-------------+------+----------+-------------
----------+
|  1 | SIMPLE      | test01 | NULL       | ref  | t_idx1        | t_idx1 | 68      | const,const |    1 |   100.00 | Using index
condition |
+----+-------------+--------+------------+------+---------------+--------+---------+-------------+------+----------+-------------
----------+
1 row in set, 2 warnings (0.00 sec)

发现key_len = 68,最后一个passwd失效了。

6.尽量不要使用or

or会使or前面的和后面的索引同时失效,这点比较变态,所以要特别注意:

mysql> explain select * from test01 where id = 1 or name = "zz";
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table  | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | test01 | NULL       | ALL  | t_idx1        | NULL | NULL    | NULL |    1 |   100.00 | Using where |
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

可以看到因为使用了or,导致索引为NULL,type级别为ALL。

如果一定要使用or,应该怎样补救呢?
补救的办法是尽量用到索引覆盖。比如我们把原来SQL中的select * 中的 * 号替换成具体的字段,这些字段能够覆盖索引,那么对索引优化也有一定的提升:

mysql> explain select id, name, passwd from test01 where id = 1 or name = "zz";
+----+-------------+--------+------------+-------+---------------+--------+---------+------+------+----------+-------------------
-------+
| id | select_type | table  | partitions | type  | possible_keys | key    | key_len | ref  | rows | filtered | Extra
       |
+----+-------------+--------+------------+-------+---------------+--------+---------+------+------+----------+-------------------
-------+
|  1 | SIMPLE      | test01 | NULL       | index | t_idx1        | t_idx1 | 129     | NULL |    1 |   100.00 | Using where; Using
 index |
+----+-------------+--------+------------+-------+---------------+--------+---------+------+------+----------+-------------------
-------+
1 row in set, 1 warning (0.00 sec)

从上例中可以看到,where条件没做任何改变,但是type级别已经提升到了index,也是用到了索引。

7.in经常会使索引失效,应该慎用

mysql> explain select id, name, passwd from test01 where id = 1 and name in ("zz", "aa");
+----+-------------+--------+------------+------+---------------+--------+---------+-------+------+----------+-------------------
-------+
| id | select_type | table  | partitions | type | possible_keys | key    | key_len | ref   | rows | filtered | Extra
       |
+----+-------------+--------+------------+------+---------------+--------+---------+-------+------+----------+-------------------
-------+
|  1 | SIMPLE      | test01 | NULL       | ref  | t_idx1        | t_idx1 | 5       | const |    2 |   100.00 | Using where; Using
 index |
+----+-------------+--------+------------+------+---------------+--------+---------+-------+------+----------+-------------------
-------+
1 row in set, 1 warning (0.00 sec)

从上例中,不难看出,key_len = 5,所以name索引失效了,原因就是name使用了in。
为什么说经常会使索引失效呢?因为in也不一定总使索引失效,如下面的例子:

mysql> explain select id, name, passwd from test01 where id in (1,2,3) and name = "zz";
+----+-------------+--------+------------+-------+---------------+--------+---------+------+------+----------+-------------------
-------+
| id | select_type | table  | partitions | type  | possible_keys | key    | key_len | ref  | rows | filtered | Extra
       |
+----+-------------+--------+------------+-------+---------------+--------+---------+------+------+----------+-------------------
-------+
|  1 | SIMPLE      | test01 | NULL       | index | t_idx1        | t_idx1 | 129     | NULL |    1 |   100.00 | Using where; Using
 index |
+----+-------------+--------+------------+-------+---------------+--------+---------+------+------+----------+-------------------
-------+
1 row in set, 1 warning (0.00 sec)

可以看到此时key_len = 129,说明用到了全部索引。以上情况之所以出现,其实还是索引失效了,但是虽然id索引失效,但是name索引并没有失效,所以上面的句子等价于:select id, name, passwd from test01 where name = "zz";。这与之前说的复合索引只要前面的失效,后面都失效并不太一致,所以对于in,应该谨慎使用。

对于关联表查询的情况,应该遵循“小表驱动大表”的原则

总而言之,就是左连接给左表建索引,右连接给右表建索引,内连接的话给数据量小的表建索引。

还需要说明一点的是,索引并不是越多越好

因为索引的数据结构是B树,毕竟要占内存空间,所以如果索引越多,索引越大,对硬盘空间的消耗其实是巨大的,而且如果表结构需要调整,意味着索引也要同步做调整,否则会导致不可预计的问题出现。
因此,在实际开发中,对于创建索引,应充分考虑到具体的业务情况,根据业务实现来创建索引,对于有些比较特殊的复杂SQL,建议在代码里进行一定的逻辑处理后再进行常规的索引查询。
举个例子,比如test01表中,需要判断inf字段是否包含“上海”字段,如果在SQL里实现,则必然是如下的逻辑:

select id,name,passwd,inf from test01 where id = 1 and name = "zz" and passwd = "123" and inf like "%上海%"

这条sql就比较恐怖了,且不说inf本来不是索引,而且有like "%上海%"这种糟糕的写法,所以我们完全可以使用下面的方法代替。
先使用下面的SQL查出所有字段:

select id,name,passwd,inf from test01 where id = 1 and name = "zz" and passwd = "123"

然后在代码里判断inf字段是否包含上海字段,如C语言实现如下:

if (strstr(inf, "上海") != NULL)
{
	//do something
}

这样一来,虽然只是多了一步简单的逻辑判断,但是对于SQL优化的帮助其实是巨大的。

您可能感兴趣的文档:

--结束END--

本文标题: MySQL优化之避免索引失效的方法

本文链接: https://www.lsjlt.com/news/5251.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • MySQL中索引失效的常见场景与规避方法
    前言 之前有看过许多类似的文章内容,提到过一些sql语句的使用不当会导致MySQL的索引失效。还有一些MySQL“军规”或者规范写明了某些sql不能这么写,否则索引失效。 绝大部分的内容笔者是认可的,不...
    99+
    2024-04-02
  • MySQL索引失效十种场景与优化方案
    目录1 数据准备1.1 新建数据表1.2 新增100万条数据2 基础知识2.1 explain type2.2 explain Extra3 索引失效场景3.1 查询类型错误3.1....
    99+
    2023-05-19
    MySQL索引失效场景 MySQL索引优化 MySQL索引失效与优化
  • Mysql索引失效的解决方法
    小编给大家分享一下Mysql索引失效的解决方法,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!背景6千万数据量的数据表出现了一个满...
    99+
    2024-04-02
  • mysql织梦索引优化之MySQL Order By索引优化
    在一些情况下,MySQL可以直接使用索引来满足一个ORDER BY 或GROUP BY 子句而无需做额外的排序。尽管ORDER BY 不是和索引的顺序准确匹配,索引还是可以被用到,只要不用的索引部分和所有的额外的ORDER B...
    99+
    2024-04-02
  • MySQL优化及索引的方法
    这篇“MySQL优化及索引的方法”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“MySQL优化及索引的方法”文章吧。索引简单介...
    99+
    2023-06-29
  • PHP与MySQL索引的失效情况及如何避免和解决
    引言:在开发Web应用程序时,PHP和MySQL往往是常用的编程语言和数据库。而在处理大量数据时,索引是提高查询性能的重要因素之一。然而,索引的失效情况可能会导致查询变慢,从而影响应用程序的性能。本文旨在介绍PHP与MySQL索引的失效情况...
    99+
    2023-10-21
    MySQL PHP 索引 避免 失效
  • 跳槽必备之你设计索引的原则是什么?怎么避免索引失效?
    目录主键索引为频繁查询的字段建立索引避免为"大字段"建立索引选择区分度大的列作为索引尽量为ORDER BY 和 GROUP BY 后面的字段建立索引不要在条件中使用函数不要建立太多的...
    99+
    2024-04-02
  • MySQL索引优化的方法是什么
    MySQL索引优化的方法有以下几种: 选择合适的列创建索引:根据查询的列和条件,选择最经常使用的列创建索引,以提高查询的效率。 ...
    99+
    2024-04-09
    MySQL
  • SQL索引的优化方法
    这篇文章主要讲解了“SQL索引的优化方法”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“SQL索引的优化方法”吧!SELECT TOP 50ROW_N...
    99+
    2024-04-02
  • MySQL索引管理优化的方法教程
    这篇文章主要讲解了“MySQL索引管理优化的方法教程”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“MySQL索引管理优化的方法教程”吧! ...
    99+
    2024-04-02
  • mysql索引失效的原因
    小编给大家分享一下mysql索引失效的原因,希望大家阅读完这篇文章后大所收获,下面让我们一起去探讨吧!索引对于MySQL而言,是非常重要的篇章。索引知识点也巨多,要想掌握透彻,需要逐个知识点一一击破,今天来...
    99+
    2024-04-02
  • MySQL索引失效之隐式转换的问题
    目录常见索引失效:一、常见索引失效场景1、条件字段函数操作2、条件字段运算操作3、隐式类型转换4、隐式字符编码转换二、类型转换1、字符串转整型2、时间类型转换常见索引失效: 1. 条...
    99+
    2024-04-02
  • MySQL数据库SQL及索引优化的方法
    MySQL数据库SQL及索引优化的方法?这个问题可能是我们日常学习或工作经常见到的。希望通过这个问题能让你收获颇深。下面是小编给大家带来的参考内容,让我们一起来看看吧!     ...
    99+
    2024-04-02
  • MySQL之主键索引排序失效问题
    目录主键索引排序失效现在初始化几行数据查一下所有记录查看一下执行计划总结主键索引排序失效 环境:mysql8 有一张用户信息表user_info,建表DDL如下: CREATE TABLE `user_info` ( ...
    99+
    2022-12-27
    MySQL主键索引 主键索引排序失效 MySQL索引失效
  • PHP与MySQL索引的原理及优化方法
    引言:在开发和维护一个功能强大的数据库应用程序时,索引是一个重要的概念,它可以显著提高数据库查询的效率。本文将介绍PHP与MySQL索引的原理和优化方法,并提供一些具体的代码示例。一、索引的原理索引是一种数据结构,它可以帮助数据库引擎快速定...
    99+
    2023-10-21
    PHP 优化方法 MySQL索引
  • MySQL调优之索引在什么情况下会失效详解
    目录前言【1】全值匹配我最爱【2】最佳左前缀法则【3】主键插入顺序【4】计算、函数、类型转换(自动或手动)导致索引失效【5】范围条件右边的列索引失效【6】不等于(!=或者 <>) 索引失效【7】is null...
    99+
    2024-04-02
  • Mysql进阶优化篇02——索引失效的10种情况及原理
    前 言 🍉 作者简介:半旧518,长跑型选手,立志坚持写10年博客,专注于java后端 🍌 专栏简介:mysql基础、进阶,主要讲解mysql数据库sql刷题、进...
    99+
    2023-08-31
    mysql 大数据 索引 数据库 sql调优
  • Mysql索引优化的方式有哪些
    MySQL索引优化的方式有以下几种:1. 选择正确的索引类型:MySQL支持多种索引类型,包括B-tree索引、哈希索引、全文索引等...
    99+
    2023-10-23
    Mysql
  • MySQL索引失效的原因及解决方法是什么
    MySQL索引失效的原因可能有以下几种:1. 数据分布不均匀:如果某个列的数据分布不均匀,索引可能无法有效地过滤掉大部分的数据,导致...
    99+
    2023-10-25
    MySQL
  • 索引失效的情况和解决方法
    1. 尽量避免在字段开头模糊查询,会导致数据库引擎放弃索引进行全表扫描 SELECT * FROM user WHERE name LIKE '%用%'​-- 优化方式:尽量在字段后面使用模糊查询SELECT * F...
    99+
    2023-09-16
    数据库 mysql java
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作