MySQL 中这么多索引该怎么选择

目录
  • 前言
  • MySQL 单字段索引问题
  • 组合索引
  • 唯一索引和普通索引
  • 总结

前言

索引的本质是存储引擎用于快速查询记录的一种数据结构。特别是数据表中数据特别多的时候,索引对于数据库的性能就愈发重要。

在数据量比较大的时候,不恰当的索引对于数据库的性能的影响是非常大的。在实际的应用中常常会遇见使用错误的索引而导致一系列问题,所以,选择正确的索引对于 MySQL 数据库来说相当重要。

下面我们就来一起聊聊在 MySQL 数据库中该怎么选择正确的索引。

在了解怎么选择索引之前,我先给你举一个例子。如果我们在字典中用拼音查询某一个字,首先我们得根据拼音字母进而找到对应的页码。索引也是这个原理。

当我们查询一条数据的时候,我们首先在索引中查询到对应的值,然后根据匹配到的索引去找到对应数据。

例如:

mysql> select name from city where fid = 1;
+--------------+
| name         |
+--------------+
| 浦东新区      |
+--------------+
1 row in set (0.00 sec)

如果我们在fid字段上建立索引,那么 MySQL 数据库就会使用索引找到fid = 1的行,然后返回包含fid = 1的行中的所有数据。

对于 MySQL 数据库来说,索引是由存储引擎实现的,所以不同的存储引擎提供的索引也不一样。下面我们就来了解一下 MySQL 数据库中各种索引的优缺点。

MySQL 单字段索引问题

在 MySQL 数据库中,索引不能够使用表达式,具体如下:

mysql> explain select * from city where fid + 1 = 2;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | city  | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    5 |   100.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.01 sec)

mysql> explain select * from city where fid = 1;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | city  | NULL       | ALL  | index_1       | NULL | NULL    | NULL |    5 |   100.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

从结果上讲,select * from city where fid + 1 = 2; 和 select * from city where fid = 1;是完全一致的。

但是,在explain表达式中可以看出select * from city where fid + 1 = 2;是无法命中索引的。这是因为 MySQL 数据库无法解析fid + 1 = 2这个表达式,所以我们在使用索引时,索引的列不能够是一个表达式。

总之,通常情况下,对于单个字段的索引来说,必须直接使用,不能够使用一个表达式。

组合索引

我们经常会遇见这样一个场景,假设要求查询fid=1或者name='青浦区',这个时候我们查询的SQL语句如下:

select * from city where fid = 1 or name = '青浦区';

这个时候,我们如果要想提高查询速度,一般就会选择在fid字段和name字段上分别加上一个索引,但实际上这种做法是不恰当的。

具体如下:

mysql> explain select * from city where name = '青浦区' or fid = 1;
+----+-------------+-------+------------+------+-----------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys   | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------+------------+------+-----------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | city  | NULL       | ALL  | index_1,index_2 | NULL | NULL    | NULL |    5 |   100.00 | Using where |
+----+-------------+-------+------------+------+-----------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

我们可以看出,本次查询并没有使用到任何索引。

具体步骤如下:

  • 首先,根据name字段全表扫描查询出name = '青浦区'包含的所有结果;
  • 其次,再根据fid字段全表扫描查询出fid = 1包含的所有结果;
  • 最后,通过UNION ALL将所有的结果组合到一起并返回。

在这一过程中,MySQL 数据库需要通过全表扫描两次才能查询出结果。如果有更多的条件,查询的次数会更多。所以,在大多数情况下,多个条件查询在多个字段上建立索引并不能够提高MySQL的查询性能

为了解决多个字段同时需要索引的这一问题,MySQL 5.0之后的版本中提供了一个组合索引。它主要是将所有的字段组合建立一个索引,这样就可以直接利用索引匹配,而不需要全表扫描了。

具体如下:

mysql> explain select * from city where name = '青浦区' or fid = 1;
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+--------------------------+
| id | select_type | table | partitions | type  | possible_keys | key     | key_len | ref  | rows | filtered | Extra                    |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+--------------------------+
|  1 | SIMPLE      | city  | NULL       | index | index_3       | index_3 | 772     | NULL |    5 |    36.00 | Using where; Using index |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)

我们可以看出,利用了组合索引之后的查询是使用到了索引,具体如下:

  • 首先,根据索引匹配出name = '青浦区'的所有的内容;
  • 第二次查询仍然是根据fid字段全表扫描查询出fid = 1包含的所有结果;
  • 最后,通过UNION ALL将所有的结果组合到一起并返回。

在这一过程中,MySQL 数据库需要通过索引匹配两次就能查询出结果。所以,在大多数情况下,当有多个条件查询时,组合索引可以有效地提高MySQL的查询性能

讲完单字段索引和组合索引之后,下面我们可以聊一下唯一索引和普通索引的区别以及使用场景。

唯一索引和普通索引

说起唯一索引和普通索引,有朋友可能就非常熟悉。普通索引的主要特征就是提高了查询的速度,唯一索引的主要特征除了提高查询的速度外就是所有字段的值唯一。

那么,我现在提一个问题,唯一索引和普通索引都应该在什么场景下使用呢?一定是需要唯一值的场景下才使用唯一索引吗?下面我们就来对比着聊一下普通索引和唯一索引。

为了加强了解,我们从读写性能方面来聊一下普通索引和唯一索引。

假设现在我们有一个订单系统,订单号唯一,那么我们看一下订单号在使用唯一索引和普通索引的情况下读的性能。

具体如下:

mysql> select * from sp_order where order_id = 52355096;
+----+----------+---------+--------------+-------------+-----------+------------+-------------+-------------+
| id | order_id | user_id | order_number | order_price | order_pay | pay_status | create_time | update_time |
+----+----------+---------+--------------+-------------+-----------+------------+-------------+-------------+
|  1 | 52355096 |     410 | DD52355096   |      332.44 | 2         | 1          |  1509051984 |  1507411372 |
+----+----------+---------+--------------+-------------+-----------+------------+-------------+-------------+
1 row in set (0.00 sec)

在 order_id 字段上设置唯一索引时,具体步骤如下:

  • MySQL 首先会在B-Tree的子树上查询 order_id = 52355096
  • 再根据查询到的索引值,通过主键索引查询出对应的记录;
  • 组装结果并返回。

在 order_id 字段上设置普通索引时,具体步骤如下:

  • MySQL 首先会在B-Tree的子树上查询 order_id = 52355096
  • 继续向下匹配,直至匹配到 order_id 不等于 52355096时;
  • 再根据查询到的索引值,通过主键索引查询出对应的记录;
  • 组装结果并返回。

唯一索引与普通索引之间对比之后,可以发现:普通索引比唯一索引多了一个步骤,就是唯一索引匹配成功之后直接返回,而普通索引还需要往下继续匹配直至条件不符合为止。

那么,在这个过程当中,普通索引与唯一索引之间的性能差多少呢?其实是微乎其微的。这是因为B-Tree算法将相邻或相近的数据都放在相邻的子树之中,索引查询性能相差无几。

聊完普通索引与唯一索引读的性能之后,我们再来聊一下写的性能。

具体如下:

mysql> update sp_order set order_price = '888' where order_id = 52355096;

对于MySQL来说,写的过程如下。

  • 首先判断需要修改的数据是否在Buffer Pool之中。

    • 如果该数据在Buffer Pool之中,则直接修改逻辑记录到Buffer Pool中的数据。
    • 如果该数据不在Buffer Pool之中,MySQL 会将这一修改的过程记录在Change Buffer之中。之后如果该条数据被查询到,则会将该修改过程mergeBuffer Pool之中,确保数据一致性。
  • 之后,再统一写入磁盘。

那么对于普通索引来说,完全适用于这一过程;但是对于唯一索引来说,按着这种方式修改数据则会影响 MySQL 数据库的性能。这是因为唯一索引在修改数据之前,还需要判断该条数据是否唯一,这样的话就需要将所有的数据全部扫描一遍,进而达到数据唯一。那么这样就不需要使用Change Buffer了,因为在修改之前,唯一索引会将所有的数据全部读取到Buffer Pool之中,直接在内存修改即可。但是不可避免的是,唯一索引会将所有的数据全部独到内存之中,无异于一次全表扫描。

于是,我们可以得出:唯一索引和普通索引都适用于读的场景,而唯一索引不适用于写的场景。

总结

本次我从根本上给你介绍了各种索引的情况。

  • 对于单个字段的索引来说,要直接使用,而不能写成一个表达式,写成表达式将会无法命中索引。
  • 对于多个字段需要索引来说,一般需要创建组合索引,这样有利于命中索引,但是一定要注意组合索引的前缀性。
  • 对于索引的类型,我还给你介绍了唯一索引和普通索引,在读的场景比较多的情况下普通索引和唯一索引都能胜任,不过在写场景比较多的情况下,普通索引的性能要优于唯一索引。

在实际应用中,我们通常建议使用普通索引,对于需要唯一的字段,我们一般在代码的层面去控制其唯一性。

到此这篇关于MySQL 中这么多索引该怎么选择的文章就介绍到这了,更多相关MySQL 索引选择内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • MySQL索引介绍及优化方式

    目录 一.导致sql执行慢的原因 二.分析原因时,一定要找切入点 三.什么是索引? 四.Explain分析 1.id 2.select_type 3.table 4.type(★) 5.possible_key 6.key(★) 7.key_len 8.ref(★) 9.rows(★) 10.extra 五.优化案例 六.是否需要创建索引? 一.导致sql执行慢的原因 硬件条件限制: io吞吐量小,形成瓶颈(读取磁盘数据) 网络传输速度慢 内存不足(读取磁盘数据加载到内存) 程序设计方面: 没有

  • MySql索引原理与操作

    目录 1. 什么是索引 2. 索引的实现原理 3. 添加索引的条件 4. 索引的操作 1. 创建索引 2. 删除索引 3. 查看一个sql语句是否使用了索引进行检索 5. 索引的失效 6. 索引的类型 1. 什么是索引 索引是在数据库表的字段上添加的,是为了提高查询效率存在的一种机制. 一张表的一个字段可以添加一个索引,当然,多个字段联合起来也可以添加索引. 索引相当于一本书的目录,是为了缩小扫描范围而存在的一种机制. 索引相当于一本书的目录 通过索引查询的方式被称为索引查询. 在 mysql

  • 详解MySQL单列索引和联合索引

    目录 一.简介 二.单列索引 三.最左前缀原则 四.同时存在联合索引和单列索引(字段有重复) 五.联合索引本质 六.索引失效 七.其它知识点 八.MySQL存储引擎简介 九.索引结构(方法.算法) 一.简介 利用索引中的附加列,可以缩小搜索的范围,但使用一个具有两列的索引不同于使用两个单独的索引. 联合索引的结构与电话簿类似,人名由姓和名构成,电话簿首先按姓氏进行排序,然后按名字对有相同姓氏的人进行排序.如果您知道姓,电话簿将非常有用,如果您知道姓和名,电话簿则更为有用,但如果您只知道名不知道姓

  • MySQL中索引的定义以及操作新手教程

    目录 索引的定义 索引的类型 普通.唯一.主键和全文索引 普通索引(INDEX) 唯一索引(UNIQUE INDEX) 主键索引(PRIMARY KEY) 全文索引(FULLTEXT) 单列索引和组合索引 聚集索引和非聚集索引 索引的创建原则 索引操作 创建索引 查看索引 删除索引 总结 索引的定义 数据库中的索引就像一本书的目录,可以据此快速定位数据库中相关数据的所在位置. 在数据库中,索引被定义为一种特殊的数据结构,由数据库中的一列或多列组合而成,可以用来快速查询数据表中某一特定值的记录.

  • 一文搞懂什么是MySQL前缀索引

    目录 一.什么是前缀索引 二.为什么要用前缀索引 三.怎么创建前缀索引 四.使用前缀索引需要注意的事项 五.小结 一.什么是前缀索引 所谓前缀索引,说白了就是对文本的前几个字符建立索引(具体是几个字符在建立索引时去指定),比如以产品名称的前 10 位来建索引,这样建立起来的索引更小,查询效率更快! 有点类似于 Oracle 中对字段使用 Left 函数来建立函数索引,只不过 MySQL 的这个前缀索引在查询时是内部自动完成匹配的,并不需要使用 Left 函数. 二.为什么要用前缀索引 可能有的同

  • MySQL索引详细解析

    目录 1. MySQL 索引的最左前缀原则 2. 前缀索引 3. 索引下推(ICP——Index Condition Pushdown) 4. 查看 MySQL 语句是否用到索引 5. 为什么官方建议用自增长主键作为索引 6. 如何创建索引 7. 创建索引注意事项 8. 使用索引一定可以提高查询性能嘛 9. 索引失效 1. MySQL 索引的最左前缀原则 左前缀原则是联合索引在使用时要遵循的原则,查询索引可以使用联合索引的一部分,但是必须从最左侧开始.在创建联合索引时,要根据业务需求,where

  • MySQL 中这么多索引该怎么选择

    目录 前言 MySQL 单字段索引问题 组合索引 唯一索引和普通索引 总结 前言 索引的本质是存储引擎用于快速查询记录的一种数据结构.特别是数据表中数据特别多的时候,索引对于数据库的性能就愈发重要. 在数据量比较大的时候,不恰当的索引对于数据库的性能的影响是非常大的.在实际的应用中常常会遇见使用错误的索引而导致一系列问题,所以,选择正确的索引对于 MySQL 数据库来说相当重要. 下面我们就来一起聊聊在 MySQL 数据库中该怎么选择正确的索引. 在了解怎么选择索引之前,我先给你举一个例子.如果

  • mysql中关于覆盖索引的知识点总结

    如果一个索引包含(或覆盖)所有需要查询的字段的值,称为'覆盖索引'. 覆盖索引是一种非常强大的工具,能大大提高查询性能,只需要读取索引而不需要读取数据,有以下优点: 1.索引项通常比记录要小,所以MySQL访问更少的数据. 2.索引都按值得大小存储,相对于随机访问记录,需要更少的I/O. 3.数据引擎能更好的缓存索引,比如MyISAM只缓存索引. 4.覆盖索引对InnoDB尤其有用,因为InnoDB使用聚集索引组织数据,如果二级索引包含查询所需的数据,就不再需要在聚集索引中查找了. 限制: 1.

  • Python如何识别 MySQL 中的冗余索引

    目录 前言 脚本介绍 表结构 MySQL 元数据 DEMO 演示 SQL 查询冗余索引 后记 前言 最近在搞标准化巡检平台,通过 MySQL 的元数据分析一些潜在的问题.冗余索引也是一个非常重要的巡检目,表中索引过多,会导致表空间占用较大,索引的数量与表的写入速度与索引数成线性关系(微秒级),如果发现有冗余索引,建议立即审核删除. PS:之前见过一个客户的数据库上面竟然创建 300 多个索引!?当时的想法是 “他们在玩排列组合呢” 表写入非常慢,严重影响性能和表维护的复杂度. 脚本介绍 表结构

  • MySQL中的唯一索引的简单学习教程

    mysql 唯一索引UNIQUE一般用于不重复数据字段了我们经常会在数据表中的id设置为唯一索引UNIQUE,下面我来介绍如何在mysql中使用唯一索引UNIQUE吧. 创建唯一索引的目的不是为了提高访问速度,而只是为了避免数据出现重复.唯一索引可以有多个但索引列的值必须唯一,索引列的值允许有空值.如果能确定某个数据列将只包含彼此各不相同的值,在为这个数据列创建索引的时候就应该使用关键字UNIQUE. 把它定义为一个唯一索引. 创建表时直接设置: DROP TABLE IF EXISTS `st

  • MySQL中主键索引与聚焦索引之概念的学习教程

    主键索引 主键索引,简称主键,原文是PRIMARY KEY,由一个或多个列组成,用于唯一性标识数据表中的某一条记录.一个表可以没有主键,但最多只能有一个主键,并且主键值不能包含NULL. 在MySQL中,InnoDB数据表的主键设计我们通常遵循几个原则: 采用一个没有业务用途的自增属性列作为主键: 主键字段值总是不更新,只有新增或者删除两种操作: 不选择会动态更新的类型,比如当前时间戳等. 这么做的好处有几点: 新增数据时,由于主键值是顺序增长的,innodb page发生分裂的概率降低了:可以

  • MySQL中的联合索引学习教程

    联合索引又叫复合索引.对于复合索引:Mysql从左到右的使用索引中的字段,一个查询可以只使用索引中的一部份,但只能是最左侧部分.例如索引是key index (a,b,c). 可以支持a | a,b| a,b,c 3种组合进行查找,但不支持 b,c进行查找 .当最左侧字段是常量引用时,索引就十分有效. 两个或更多个列上的索引被称作复合索引. 利用索引中的附加列,您可以缩小搜索的范围,但使用一个具有两列的索引 不同于使用两个单独的索引.复合索引的结构与电话簿类似,人名由姓和名构成,电话簿首先按姓氏

  • MySQL中NULL对索引的影响深入讲解

    前言 看了很多博客,也听过很多人说,包括我们公司的DBA,说MySql中如果某一列中含有null,那么包含该列的索引就无效了. 翻了下<高性能MySQL第二版>和<MySQL技术内幕--InnoDB存储引擎第2版>,并没有看到关于这个的介绍.但在本地试了下,null列是可以用到索引的,不管是单列索引还是联合索引,但仅限于is null,is not null是不走索引的. 后来在官方文档中找到了说明,如果某列字段中包含null,确实是可以使用索引的,地址:https://dev.m

  • 正确理解Mysql中的列索引和多列索引

    Mysql数据库提供两种类型的索引,如果没正确设置,索引的利用效率会大打折扣却完全不知问题出在这. 复制代码 代码如下: CREATE TABLE test (    id         INT NOT NULL,    last_name  CHAR(30) NOT NULL,    first_name CHAR(30) NOT NULL,    PRIMARY KEY (id),    INDEX name (last_name,first_name)); 以上创建的其实是一个多列索引,

  • MySQL中B树索引和B+树索引的区别详解

    目录 1.多路搜索树 2.B树-多路平衡搜索树 3.B树索引 4.B+树索引 总结 如果用树作为索引的数据结构,每查找一次数据就会从磁盘中读取树的一个节点,也就是一页,而二叉树的每个节点只存储一条数据,并不能填满一页的存储空间,那多余的存储空间岂不是要浪费了?为了解决二叉平衡搜索树的这个弊端,我们应该寻找一种单个节点可以存储更多数据的数据结构,也就是多路搜索树. 1. 多路搜索树 1.完全二叉树高度:O(log2N),其中2为对数,树每层的节点数: 2.完全M路搜索树的高度:O(logmN),其

随机推荐