【MySQL】015-MySQL索引

news2024/11/22 16:25:04

【MySQL】015-MySQL索引

文章目录

  • 【MySQL】015-MySQL索引
  • 一、MySQL索引是什么
    • 1、概述
    • 2、好处
    • 3、缺点
    • 4、索引的使用场景
    • 5、常用命令
      • 查看数据表上建的索引
  • 二、索引的分类
    • 1、三大类
    • 2、为什么使用B+树作为索引
      • 在疑问为什么使用B+树这种数据结构之前,先想想什么样的数据结构才最适合MySQL?
      • 了解了MySQL对数据结构的需求后,来看看为什么B+树符合?
      • B+树的优点
      • 相比于其他数据结构
    • 3、聚簇索引和非聚簇索引的区别
      • 聚簇索引和非聚簇索引的区别
      • 主键索引
      • ★ MySQL主键索引的作用
      • ★ MySQL创建主键索引的常见方式
      • 二级索引
      • ★ MySQL创建唯一索引的常见方式
      • ★ MySQL创建普通索引的常见方式
      • ★ MySQL创建前缀索引的常见方式
    • 4、回表/覆盖索引/索引下推
      • 什么是回表?
      • 二级索引查找记录一定要进行回表查询吗?
      • 什么是索引下推
    • 5、联合索引
      • 概述
      • 联合索引是否存在失效情况呢?
      • 联合索引的最左前缀原则
  • 三、索引创建的时机和优化
    • 1、创建索引的时机
      • 适合创建索引的场景
      • 不适合创建索引的场景
    • 2、索引优化的方式
      • 使用前缀索引替换普通索引
      • 使用覆盖索引优化回表次数
      • 主键索引中主键设置为自增
  • 四、索引失效问题
    • 1、如何查看是否命中索引
      • `type` 连接类型
      • `key` 实际使用的索引
      • `rows` 扫描的行数
      • `filtered` 返回的行数
      • `Extra` 额外信息
    • 2、常见的索引失效问题
      • `WHERE` 子句出现 `OR`
      • 索引字段使用函数或进行表达式计算
    • 3、`like` 通配符模糊查询
    • 4、索引字段隐式转换问题
    • 5、联合索引不匹配问题

一、MySQL索引是什么

1、概述

MySQL的索引是一种帮助 MySQL 高效地查询和检索数据 的数据结构,可以看作是 数据的目录。(就像书籍的目录)

索引是一种用空间换时间的设计思想。

2、好处

  • 快速数据检索:索引允许数据库引擎快速定位和检索数据,而不必扫描整个表。这是通过使用树状数据结构(通常是B树或B+树)实现的,使数据库能够迅速定位所需的数据行。
  • 减少查询时间:索引大大减少了查询的时间复杂度,使数据库能够在大型数据集上执行快速的查找操作,因为它们不需要遍历整个表。
  • 提高排序性能:当对带有索引的列进行排序时,数据库引擎可以快速获取有序数据,而不必进行全表扫描,这提高了排序的性能。
  • 提高连接性能:在连接多个表时,索引可以加快连接操作的速度,尤其是在 JOIN 操作中。它们允许数据库引擎更有效地查找匹配的行。
  • 唯一性约束:通过在索引上添加唯一性约束,可以确保某一列的值在表中是唯一的。这对于维护数据的完整性非常有用。
  • 加速数据修改:尽管索引可以加快数据检索,但它们在数据插入、更新和删除时也有一定的好处。这是因为数据库引擎可以更快地维护索引,而不需要完全重建它们。

3、缺点

  • 额外存储空间开销:每个索引都需要额外的存储空间,通常以B树或B+树结构存储索引数据。这会增加数据库的磁盘空间占用
  • 降低插入、更新和删除性能:在插入、更新或删除记录时,索引需要维护,这会导致额外的I/O操作和处理时间。如果表有多个索引,这个开销会更加显著。
  • 复杂的查询优化:当查询涉及多个表和索引时,数据库查询优化器需要选择合适的索引,这可能会导致查询优化的复杂性增加。选择不当的索引可能导致性能下降。
  • 不适合所有列并非所有列都适合创建索引。对于某些列,如包含大量重复值的列或枚举值的列,创建索引可能不会提供显著的性能提升。
  • 索引失效问题:在某些情况下,索引可能不会被有效利用,导致查询性能不佳。这可能是由于数据分布不均匀、使用了不恰当的查询条件或连接操作等原因。
  • 索引维护成本:当数据库中的数据发生变化时,索引需要不断维护,这可能会引入锁和竞争条件,影响并发性能。
  • 选择不当的索引:选择不合适的列创建索引,或者创建过多的索引,可能导致索引失效,浪费存储空间,并且使查询变得更加复杂。
  • 内存占用:索引数据结构需要占用内存,如果内存不足,可能会导致性能下降,因为部分索引数据需要从磁盘读取。

4、索引的使用场景

  1. 加速数据检索最常见的使用场景是加速数据检索。通过在查询涉及的列上创建索引,数据库引擎能够快速定位和检索满足查询条件的数据,而不必扫描整个表。
  2. 唯一性约束:索引可以用来确保某一列或列组的值在表中是唯一的,从而维护数据的完整性。这是通过创建唯一性索引实现的。
  3. 加速排序操作:当对包含索引的列进行排序时,数据库可以快速获取有序数据,而不必进行全表扫描,从而提高排序操作的性能。
  4. 加速连接操作:在执行连接操作时,索引可以大幅提高性能,特别是在多表连接的情况下。它们使数据库引擎更有效地查找匹配的行。
  5. 加速聚合操作:当执行聚合函数(如SUM、COUNT、AVG等)时,索引可以用于快速定位和处理相关的数据,从而提高聚合操作的性能。
  6. 加速检查外键约束外键列通常会被索引,以确保引用完整性。这有助于在执行插入或更新操作时快速验证外键引用
  7. 加速子查询:索引可以加速子查询的执行,使其更高效。
  8. 加速全文搜索:对于包含文本数据的列,全文搜索索引可以用于更快速的文本搜索操作。

5、常用命令

查看数据表上建的索引

SHOW INDEX FROM your_table_name;

二、索引的分类

1、三大类

MySQL的索引按照不同的类型和特点可以从三个角度进行分类:

  • 数据结构 分类可分为:B+树索引、Hash索引、全文索引等
  • 物理存储 分类可分为:聚簇索引(主键索引)、非聚簇索引(二级索引)等
  • 逻辑特性 分类可分为:主键索引、普通索引、唯一索引、前缀索引、联合索引(多个字段)等

2、为什么使用B+树作为索引

MySQL索引从数据结构的角度可以分为B+树索引、Hash索引、全文索引等。

在这里只详细说一下B+树索引,因为InnoDB、MyISAM等常见的MySQL存储引擎都支持B+树索引,并且MySQL5.5后将InnoDB设为默认存储引擎,B+树索引也成为了MySQL存储引擎使用最多的所有类型。

在疑问为什么使用B+树这种数据结构之前,先想想什么样的数据结构才最适合MySQL?

MySQL的数据是 持久化 的,也就是说数据是存储在磁盘中的(即索引和记录是保存在磁盘中的)。

那么在需要使用索引进行高效检索数据时,就必须访问磁盘取得索引读入内存,再根据索引再次访问磁盘检索数据后读入内存,所以每次检索数据都需要进行多次的 I/O操作,I/O操作耗费大量时间。

读索引到内存 => 根据索引读数据到内存!

因此应该选择一种数据结构 使得 I/O操作 次数尽可能的少

此外还需要注意的是MySQL是支持范围查询的,也就意味着挑选的数据结构必须能够 支持高效地范围查询

从磁盘读取索引到内存 => 根据索引再次从磁盘读取到目标数据到内存。 (先看目录,再找目标数据)

了解了MySQL对数据结构的需求后,来看看为什么B+树符合?

MySQL中的B+树结构图:
image-20231023090054997

B+树的优点

B+树是一种基于磁盘的 平衡多路查找树,它的高度通常很低(3~4层),这意味着访问效率很高,从千万或上亿数据里查询一条数据,只用 3到4 次 I/O操作

B+树的 非叶子节点不存储实际的数据,只存储索引,相比于非叶子节点又存储索引又存储数据的B树而言,B+树的非叶子节点可以存放更多的索引,所以查询底层节点的磁盘I/O次数会更少。

B+树的所有叶子节点都存储了数据,并且叶子节点之间用双向链表连接,所以数据是有序的,范围查询通过链表可以快速实现,这样可以方便地进行范围查询和排序操作。

B+树每个节点都有大量的数据或索引(有大量冗余的节点),这些冗余的数据或索引可以保证B+树在插入和删除时效率更高(因为不会因为某个数据导致需要进行复杂的树变化,即B+树的树层结构很稳定不会经常变化)。

相比于其他数据结构

  • 相对于 数组 来说,虽然使用数组+二分查找的方式实现线性排序更为简单,但是相比于B+树,B+树具备更高效的插入或删除元素的能力。
  • 相对于 二叉查找树或平衡二叉树 来说,虽然平衡二叉树具备平衡树高和高效地维护元素地能力,但是相比于B+树,由于二叉树始终只能二分,随着元素地增加,树高会越来越大,B+树具备更矮的树高,即对I/O操作的次数更少,所以具备更高效地查找效率。
  • 相对于 B树 来说,因为B树需要使用中序遍历的方式进行范围查询,增加了I/O次数。而由于B+树的叶子节点使用双向链表所以可以更高效地进行范围查询,具备给更高的范围查询效率。

所以B+树更适合作为MySQL索引的数据结构。

3、聚簇索引和非聚簇索引的区别

聚簇索引和非聚簇索引的区别

  • 聚簇索引是指将 数据存储与索引放到了一块,叶子节点存储索引和索引对应的数据;聚簇索引的顺序就是数据的物理存储顺序,聚簇索引可以把相关数据保存在一起,提高查询效率;但也会增加插入和更新的开销,以及占用更多的空间。
  • 非聚簇索引是指将 数据与索引分开存储,索引结构的叶子节点指向了数据对应的位置;非聚簇索引的顺序与数据物理排列顺序无关

MySQL的 InnoDB 存储引擎中每张表只能有一个聚簇索引,通常是主键索引;而非聚簇索引都是二级索引,可以有多个

主键索引

主键索引是建立在主键字段上的索引,主键索引的B+树结构中的叶子节点存放的是实际数据(所有完整的记录)。

关于InnoDB引擎确定 聚簇索引(主键索引) 的方式按照如下次序:

  1. 如果表中有主键,默认使用 主键 作为聚簇索引(主键索引)的索引键;
  2. 如果表中没有主键,则选择 第一个不包含NULL值的唯一列 作为聚簇索引(主键索引)的索引键;
  3. 如果2的情况也不存在,则存储引擎会 自动生成一个隐式自增id 列作为聚簇索引(主键索引)的索引键。

★ MySQL主键索引的作用

  1. 唯一性约束:主键索引确保了表中的每一行数据都具有唯一的主键值。这意味着不允许在主键列中存在重复的数值或空值。这有助于维护数据的一致性和完整性,防止数据冗余和错误。
  2. 数据检索效率:主键索引通常是高效的数据访问路径。当您根据主键进行数据检索时,数据库可以快速定位到目标行,而不必扫描整个表。这大大提高了查询性能,尤其是对于大型数据集。
  3. 表关联:主键索引用于在表之间建立关系。在关系型数据库中,外键通常引用其他表的主键。这种关联关系便于查询多个表的数据并进行连接操作。
  4. 数据完整性:主键索引有助于确保数据的完整性。它可以防止不良数据插入或删除,并强制执行数据的一致性规则。
  5. 索引优化:数据库管理系统(DBMS)可以使用主键索引来进行优化,例如在执行连接操作时,它可以更有效地选择连接策略。这有助于改善查询性能。

★ MySQL创建主键索引的常见方式

一张表,只能创建一个主键索引,这个主键索引可以包含一个或多个列。

001 在创建表时定义主键

使用CREATE TABLE语句时,可以在列的定义后面添加PRIMARY KEY来指定主键。例如:

CREATE TABLE students (
    student_id INT PRIMARY KEY,
    name VARCHAR(50),
    age INT
);

002 使用ALTER TABLE语句添加主键

如果表已经存在,可以使用ALTER TABLE语句来添加主键。例如:

ALTER TABLE orders
ADD PRIMARY KEY (order_id);

003 创建表的同时定义自动递增主键

通常,可以使用AUTO_INCREMENT属性创建自动递增的主键。例如:

CREATE TABLE products (
    product_id INT AUTO_INCREMENT,
    product_name VARCHAR(50),
    price DECIMAL(10, 2),
    PRIMARY KEY (product_id)
);

004 使用CREATE TABLE时定义复合主键

如果需要复合主键,可以在CREATE TABLE语句中同时指定多个列作为主键。例如:

CREATE TABLE orders (
    order_id INT,
    customer_id INT,
    order_date DATE,
    PRIMARY KEY (order_id, customer_id)
);

二级索引

二级索引又称为辅助索引,二级索引的B+树结构中的叶子节点 存放的数据是主键,索引分类中的唯一索引、普通索引、前缀索引等都属于二级索引。

  • 唯一索引(UNIQUE):唯一索引的属性列不能出现重复的数据,但是允许数据为NULL,一张表可以允许创建多个唯一索引
  • 普通索引(INDEX):建立在普通字段上的索引,一张表可以允许多个普通索引并允许数据重复和数据为NULL
  • 前缀索引:前缀索引只能建立在字符串类型(charvarcharbinaryvarbinary)等类型上。创建方式可以是CREATE INDEX 索引名 ON 表名(列名(指定前缀长度)); ,前缀索引的目的是为了减少索引占用的存储空间,提高查询效率。

★ MySQL创建唯一索引的常见方式

001 在CREATE TABLE语句中创建唯一索引

在创建表的同时定义唯一索引,使用UNIQUE关键字确保列中的值是唯一的。

CREATE TABLE users (
    id INT PRIMARY KEY,
    email VARCHAR(255) UNIQUE,
    username VARCHAR(50) UNIQUE
);

在上面的示例中,我们在users表中创建了两个唯一索引,分别用于emailusername列,以确保这两列中的值不重复。

建表语句末写法:

CREATE TABLE `sys_dict` (
  `id` varchar(36) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '唯一ID',
  `dict_name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT '' COMMENT '字典名称',
  `dict_code` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '字典编码',
  `status` int DEFAULT '0' COMMENT '状态(0正常 1停用)',
  `remark` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '备注',
  `create_by` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '创建用户',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `update_by` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '更新用户',
  `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE KEY `dict_code_index` (`dict_code`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='字典表';

-- 方法一:等效于方法二
UNIQUE INDEX dict_code_index (dict_code) USING BTREE
-- 方法二:等效于方法一
UNIQUE KEY `dict_code_index` (`dict_code`) USING BTREE

002 使用ALTER TABLE语句添加唯一索引

如果表已经存在,可以使用ALTER TABLE语句添加唯一索引。例如,如果要为已存在的表mytable添加唯一索引:

ALTER TABLE mytable
ADD UNIQUE (username);

003 使用CREATE INDEX语句创建唯一索引

使用CREATE INDEX语句,您可以为表的一个或多个列创建唯一索引。例如:

CREATE UNIQUE INDEX idx_username ON mytable (username);

004 通过PRIMARY KEY创建唯一索引(主键索引)

在定义表结构时,通常使用PRIMARY KEY来创建主键索引,它也是唯一的。例如:

CREATE TABLE mytable (
    id INT PRIMARY KEY,
    username VARCHAR(50)
);

这将为 id 列创建一个唯一索引,并将其定义为主键。

005 多列组合建立唯一索引(联合索引)

这个唯一索引要求三个列(column1column2column3)的组合是唯一的,而不是每个列都唯一。

UNIQUE INDEX unique_index_name (column1, column2, column3) USING BTREE

★ MySQL创建普通索引的常见方式

001 CREATE INDEX语句

使用CREATE INDEX语句可以为表中的一个或多个列创建索引。例如,要为名为example_table的表的column_name列创建索引,可以使用以下语句:

CREATE INDEX index_name ON example_table (column_name);

这将创建一个名为index_name的索引,用于提高column_name列的检索性能。

002 ALTER TABLE语句

使用ALTER TABLE语句也可以添加索引。例如,要为已经存在的表example_tablecolumn_name列添加索引,可以使用以下语句:

ALTER TABLE example_table ADD INDEX index_name (column_name);

这将在example_table表上添加一个名为index_name的索引。

003 建表语句中写法

CREATE TABLE `sys_dict_item` (
  `id` varchar(36) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '唯一ID',
  `dict_id` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '字典ID',
  `dict_code` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '字典编码',
  `dict_label` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT '' COMMENT '字典标签',
  `dict_value` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT '' COMMENT '字典键值',
  `value_type` tinyint(1) DEFAULT '0' COMMENT '值类型(0字符 1数字 2布尔)',
  `dict_sort` int DEFAULT '0' COMMENT '字典排序',
  `color` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '配色(用于前端显示)',
  `status` int DEFAULT '0' COMMENT '状态(0正常 1停用)',
  `remark` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '备注',
  `create_by` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '创建用户',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `update_by` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '更新用户',
  `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`) USING BTREE,
  KEY `dict_code_index` (`dict_code`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='字典项';

-- 方法一:等效于方法二
INDEX dict_code_index (dict_code) USING BTREE
-- 方法二:等效于方法一
KEY `dict_code_index` (`dict_code`) USING BTREE

004 多列组合建立普通索引(联合索引)

INDEX index_name (column1, column2, column3) USING BTREE

★ MySQL创建前缀索引的常见方式

001 使用CREATE INDEX语句

你可以使用CREATE INDEX语句来创建前缀索引。例如,如果你有一个名为text_column的文本列,你可以创建一个前缀索引,仅索引文本列的前几个字符,如下所示:

CREATE INDEX prefix_index ON your_table (text_column(10)); -- 这将创建一个索引,仅索引前10个字符

002 使用ALTER TABLE语句

你也可以使用ALTER TABLE语句来添加前缀索引到已存在的表中。例如,如果你有一个名为your_table的表,你可以执行以下操作来添加前缀索引:

ALTER TABLE your_table ADD INDEX prefix_index (text_column(10)); -- 这将添加一个索引,仅索引前10个字符

003 建表语句中写法

CREATE TABLE your_table (
    id INT AUTO_INCREMENT PRIMARY KEY,
    text_column VARCHAR(255), -- 假设你有一个文本列
    -- 创建一个前缀索引,仅索引text_column的前10个字符
    INDEX prefix_index (text_column(10)) USING BTREE
);


-- 方法一:等效于方法二
INDEX prefix_index (text_column(10)) USING BTREE
-- 方法二:等效于方法一
KEY `prefix_index` (`text_column(10)`) USING BTREE

4、回表/覆盖索引/索引下推

什么是回表?

正如上面所说的二级索引的 B+树 结构中的叶子节点存放的数据是主键,那么使用二级索引查找记录的过程是怎么样的呢?

二级索引查找记录的过程为:先获得二级索引中的B+树的索引值,检索 二级索引的B+树 找到对应的叶子节点后获取到对应的主键值,再通过主键值检索 主键索引的B+树 找到对应的叶子节点即可获得对应的数据(记录)。第一次检索获取主键值,再通过主键值再次检索获得记录的这个过程叫做 回表。

举个例子:

select * from products where name = 'iPhone 14';

image-20231023092036800

如上面的代码,id为主键索引列,name为唯一索引列(二级索引)。执行这条语句时,会先拿'iPhone 14'作为索引值检索二级索引的B+树找到对应的叶子节点,这里的叶子节点存储的是要找的这条记录的主键值1,然后再拿1作为索引值检索主键索引的B+树找到对应的叶子节点,此时这里的叶子节点存储的就是整条记录了。

二级索引查找记录一定要进行回表查询吗?

答案是并不一定。

修改上面的例子:

select id from products where name = 'iPhone 14';

查询的数据能在二级索引的B+树的叶子节点上直接查询到 时,就不再需要进行回表查询,直接返回数据即可。正如这里例子中,因为需要查询的是 id,二级索引的B+树的叶子节点上可以直接获取,这时就不需要回表操作了。

这种在二级索引的B+树的叶子节点能直接查询到数据的过程就叫做 覆盖索引

扩展:如何确保或者如何实现:二级索引的叶子节点包含了 id 列的值?

要确保或实现二级索引的叶子节点包含了 id 列的值,你可以按照以下步骤操作:

1、创建合适的二级索引:首先,你需要创建一个二级索引,确保该索引包含了你希望包含的列。在这种情况下,你希望 id 列的值包含在二级索引中。创建索引的SQL语句如下:

CREATE INDEX idx_name_id ON products (name, id);

这将创建一个名为 idx_name_id 的二级索引,其中包含 name 列和 id 列的值。

2、查询优化:确保查询语句中包含了需要的列。在你的查询中,你需要选择 id 列,以便数据库引擎知道你想要从索引中检索这一列的值。示例查询如下:

SELECT id FROM products WHERE name = 'iPhone 14';

这个查询将从包含 name 列和 id 列的二级索引 idx_name_id 中检索 id 列的值。

3、维护索引:确保在插入、更新和删除操作后,二级索引保持一致。MySQL通常会自动处理索引的维护,但在大批量插入或更新数据时,可能需要手动重建索引以确保其正确性。

什么是索引下推

索引下推是在MySQL5.6之后引进的索引优化功能,可以让存储引擎在非聚簇索引检索数据时,对索引中包含的字段先做判断是否符合条件,过滤掉不符合条件的记录,在返回给MySQL数据库(Server层),减少回表次数,减少不必要的数据传输,提高查询效率和性能。

索引下推我的理解就是把 原本应该在MySQL数据库(Server层)进行判断是否符合条件的工作下推到了存储引擎层中进行,这样的好处就是存储引擎返回给Server层的数据少了,减少了不必要的数据传输同时也就减少了回表的次数。

这里需要注意的是 存储引擎层 只是判断 索引中包含的字段 是否符合条件,对于那些 没有索引的字段但又需要判断的 会返回到 Server层 之后再进行判断。

5、联合索引

概述

联合索引:由 多个字段 组合成一个索引。

语法:CREATE INDEX 索引名 ON 表名(列名1, 列名2, ...);

当前数据有:

image-20231102154239344

通过 CREATE INDEX index_stockNo_stock ON products(stockNo, stock); 创建 stockNostock联合索引

此时联合索引的B+树是这样的:

image-20231102154314441

可以看到 B+树 是先按照 stockNo 字段进行排序,再 stockNo 字段相同的情况况下再按照 stock 字段进行排序。

所以 stockNo 字段是 全局有序 的,而 stock 字段是 全局无序,但局部有序 的。也就是说在创建联合索引时,创建顺序为(a, b, c),那么对应的就是 a 全局有序,bc 局部相对有序。

联合索引是否存在失效情况呢?

image-20231102154425559

联合索引的最左前缀原则

联合索引的最左前缀原则指的是 在MySQL建立联合索引时,会遵守最左前缀原则,在检索数据时从联合索引的最左边开始匹配。

通俗一点讲:好比现在创建了一个联合索引 (a, b, c),相当于建立了三个索引 (a)(a, b)(a, b, c), 也就是说当需要检索的是 where a = XXX; 或者是 where a = XXX and b = XXX; 或者是 where a = XXX and b = XXX and c = XXX都是可以命中索引,也就是利用索引进行检索数据的。

但是如果需要检索的是 where b = XXX and c = XXX 那就无法命中索引了也就是无法利用索引检索数据。也就是说必须从最左边开始匹配,只要匹配不到了就无法使用索引(注意这里 where a = XXX and c = XXX是可以匹配到的,具体在上面索引失效中介绍了)。

三、索引创建的时机和优化

1、创建索引的时机

适合创建索引的场景

  1. 不为NULL的字段:对于数据为NULL的字段,数据库较难优化,字段被频繁查询时,避免不了为NULL。
  2. 频繁用于where子句查询的字段:经常用于条件查询的字段可以建立索引,这样能提高查询的速度。
  3. 经常排序的字段:经常被oreder by 或者 group by 使用的字段可以建立索引,这样在查询时就无需再做一次排序。
  4. 被经常用于连接的字段:经常用于连接的字段涉及到表与表的关系,对于频繁被连接查询的字段,可以建立索引,提高多表连接查询的效率。
  5. 尽可能地创建联合索引:相对于创建的单一索引来说,如果适合创建联合索引应当选择建立联合索引,因为联合索引多个字段在一个索引上,可以很大程度地减少磁盘占用的空间,提高维护索引效率。
  6. 尽可能地创建前缀索引:对于字符串类型的字段,创建前缀索引相对于创建普通索引来说,能节约磁盘占用空间。

不适合创建索引的场景

  • 频繁需要更新的字段:对于需要经常更新的字段不适合建立索引,因为字段频繁被修改,而B+树需要维护索引键的有序性,所以频繁更新的字段会增加维护索引的成本,很影响数据库的性能。
  • 表中数据量过少:表数据太少时无需创建索引,因为全表扫描也不会很慢,无需以空间换时间。
  • 冗余索引:比如已经有联合索引(a, b, c),则自然无需为字段 a 单独建立索引,此时单独建立字段 a 索引就是冗余索引。
  • 较低区分度(重复数据过多)的字段:较低区分度的字段很容易导致表中出现过多的重复数据,此时建立索引已经无法起到优化作用了,因为查询优化器在对比成本后会直接跳过索引选择全表扫描(索引失效)。
  • 需要参与计算的字段:索引列不能参与计算,因为B+树中存储的都是原始的字段值,如果需要参与计算的话,可能会导致索引失效(具体后面介绍)。虽然可以把 a + 1 = 5 这样的表达式变为 a = 5 - 1使得索引不失效,但针对一些较为复杂的表达式,无疑会增加查询成本,所以不建议在需要计算的字段上建立索引。

2、索引优化的方式

使用前缀索引替换普通索引

对于字符串类型的字段建立索引时,可以使用前缀索引替换普通索引,这样可以减少索引字段的大小,从而增大数据也存储索引的数量,可以提高索引的查询效率。

使用覆盖索引优化回表次数

对于查询记录不要求全部的,可以考虑建立联合索引,这样使得二级索引的B+树中的叶子节点能找到所有需要查询的数据,这样就不再需要通过主键索引查询整条记录,也就是避免了回表操作,减少I/O次数,从而提高查询效率。

主键索引中主键设置为自增

对于主键索引的主键最好可以设置为自增。

对于使用自增的主键值,在索引的B+树插入新数据时,都是顺序的追加操作,无需移动节点调整树结构,这样的插入效率会变得更高

而对于使用非自增的主键值,在B+树插入新数据时,可能需要移动其他节点来满足新节点的插入,也可能出现页分裂(当前数据页由于新数据的插入需要将数据页的数据隔开,所以需要新建一张数据页并且把当前数据页的一部分数据复制进新数据页),页分裂可能造成大量的内存碎片从而导致索引结构不稳定,影响查询效率

四、索引失效问题

建立了索引,未必每次查询都能用到索引,也可能全表扫描!

建立了索引,是否意味着 所有查询都可以使用索引进行检索数据 呢?

当然不是,如果无法命中索引(索引失效)时,将会进行全表扫描,此时建立的索引就派不上用场了。

出现索引失效这种情况将会导致性能大大降低,所以需要了解 如何查看索引是否命中 以及一些 常见的索引失效问题,避免出现索引失效这样才能有效地利用索引提高查询效率。

1、如何查看是否命中索引

MySQL中通过 explain 查看SQL的执行计划,通过执行计划可以查看是否命中索引。

语法:EXPLAIN + sql 语句

如下图这样:

image-20231102171856382

执行计划中只需要重点关注 typekeyrowsfilteredExtra 即可。

type 连接类型

可以看成是扫描方式

type如下表,扫描方式的 速度(性能)从慢到快

image-20231102171953644

key 实际使用的索引

表示实际使用到的索引(显示的是索引名,也就是在创建索引时给索引命名的名字)。

rows 扫描的行数

表示MySQL估算找到所需的记录需要扫描的行数,注意这个值只是估算值,并不是一定准确。

filtered 返回的行数

表示符合条件的记录数的百分比(注意这里是百分比值),百分比为 经过过滤后满足条件的记录数 / 存储引擎返回的记录数

Extra 额外信息

image-20231102172128079

2、常见的索引失效问题

WHERE 子句出现 OR

如果在查询语句的 WHERE 子句中出现 OR 并且 OR 的左右两边的字段中有一个字段不是索引列时,会出现索引失效,进行全表扫描。

select * from products where name = 'Apple Watch' or price = 999.99;语句中 name字段为唯一索引列,而 price 只是普通字段时,执行计划如下:

image-20231102172340985

可以看到执行计划中的 type 为 ALL,说明进行了全表扫描,也就说明了对于 name 字段的唯一索引并没有使用,即 索引失效

如何避免这种情况下的索引失效呢?

避免这种情况的方法是 OR 的左右两边的字段都需要是索引列

select * from products where name = 'Apple Watch' or id = 1; 就使用了主键索引和唯一索引,这里的执行计划 type 为 index_merge,表示利用两个索引分别检索后合并得到结果。

image-20231102172438784

索引字段使用函数或进行表达式计算

如果在查询语句中 对索引列使用函数 或者 对索引列进行表达式计算,都会导致索引失效。

select * from products where LENGTH(name) = 5; 语句中看似使用了 name 的唯一索引,但是实际上并没有命中索引,而是进行了全表扫描。

image-20231102172529570

为什么会出现这种情况呢?

因为 索引的B+树结构中索引键是原始的索引值(没有经过计算或函数的),如果经过函数或者表达式计算之后自然就无法在B+树结构找到对应的索引键了,那么就自然无法通过索引来检索到记录了。

如何避免这种情况呢?

其实只需要对代码做一些转换,像 id + 2 = 5 转换为 id = 5 - 2from_unixtime(create_time) = '2023-3-22'转换为 create_time = unix_timestamp('2023-3-22') 这样即可实现索引命中,也就是让索引列比较时变得“干净些”

select * from products where id = 5 - 2; 即可避免索引失效问题。

image-20231102172611844

但是还是比较建议如果 需要进行计算的字段就不必再建立索引 了,因为当面对一些较为复杂的函数时无法做到的两边转换。

3、like 通配符模糊查询

如果在查询语句中使用 like 模糊匹配时,对于 like %XXXlike %XXX% 这样的形式模糊匹配时是无法命中索引(索引失效)的。而对于 like% 这样的形式是可以命中索引进行索引检索的。

select * from products where name like '%Pro';语句中执行计划的 type 为 ALL,表明这次执行是全表扫描。

image-20231102172738137

select * from products where name like 'Apple%';语句中执行计划的 type 为 range,表明这次执行是索引范围查询。

image-20231102172750785

为什么会出现这样的情况呢?

由于索引的B+树的索引键是有序的,根据指定的值和索引键比较,最后找到所需的记录,在模糊查询时会根据指定的值 按照前缀的方式 比较索引键来找到所需的记录。

对于像 Apple% 这样的值因为前面有明确的 A,所以完全可以使用索引快速缩小范围,进行索引范围查询。

而对于像 %Pro 或者 %Pro% 这类的值因为前面不具有明确的值,可以是任意值,这样索引就不知道从哪里开始检索,无法快速缩小范围,只能选择全表扫描,也就会导致索引失效。

4、索引字段隐式转换问题

如果在查询语句时,对字符串类型的索引列的输入参数类型为整型时,会导致索引失效。

select * from products where stockNo = 1001; 中将为 varchar 类型的 stockNo比较时输入参数为整型 1001 时,虽然可以找出记录,但是使用的却是全表扫描,说明索引失效。

image-20231102172956269

出现这种情况的原因

MySQL的隐式转换:当遇到字符串和数字进行比较时,会自动将字符串转为数字进行比较

知道这个原则后,就比较好解释了,上面的例子中在 字符串类型的 stockNo 和数字类型的 1001比较时,会自动把 stockNo转换成数字,但是因为 隐式转换仍然是需要使用函数进行转换的,也就是说需要对 stockNo 索引列使用函数,所以自然无法使用索引检索了,这就导致索引失效。

也就是说如果例子为 select * from products where id = '2'; 时,因为 id 是主键索引并且是整型,此时输入参数是字符串类型,但是通过隐式转换后会变为 id = 2,所以并不影响主键索引的使用,也就不会导致索引失效。

image-20231102173019603

5、联合索引不匹配问题

如果在查询数据时使用联合索引进行检索数据时,需要注意按照最左优先的方式进行索引的匹配(遵循最左前缀原则)。

现在创建一个联合索引 (a, b, c) ,此时的索引结构是以 a 为全局有序, bc 为局部相对有序。

如果此时的查询为:where b = 2 and c = 3 或者 where b = 2 或者 where c = 3 会出现索引失效的问题

image-20231102173047644

之所以出现这样的情况是因为查询中并没有遵守最左前缀原则,无法匹配到 a 字段,所以无法命中联合索引,导致索引失效问题。

对于 where a = 1 and c = 3 这种情况会出现索引失效吗?

答案是不会。

在没有出现 索引下推 前,这条语句的执行过程是 先去匹配 a 字段的索引键找到所有符合条件的主键后,通过回表操作将这些主键值经过主键索引找到对应的数据行,并且把这些数据都返回给Server层,然后再通过Server层的 where 比较 c 的值,过滤掉不符合的条件。

在出现 索引下推 后,这条语句的执行过程是 先去匹配 a 字段的索引键找到所有符合条件的主键时,因为 c字段也在索引中,所以可以在存储引擎层就对 c 字段进行比较,过滤掉不符合 c字段的值,然后再将剩余符合条件的主键进行回表操作,通过主键索引找到对应的数据返回给Server层。

通过这个例子也可以巩固之前文章中 索引下推 的定义,索引下推减少了回表的数量,提高了查询效率。

image-20231102173157937

为什么不遵守最左前缀原则就无法命中索引呢?

因为在联合索引中,数据是按照索引第一列排序,在第一列相同的情况下才按照第二列进行排序,也就是最左边的列是全局有序的,而其余的列都是局部相对有序的,查询时如果连最左的列都无法匹配到,那么自然无法使用索引。

想要更多的列的使用到 联合索引,必须保证从最左边开始能连续匹配到对应列,像 where a = XXX and c = XXX这种使用索引只能缩小范围到 a 字段,无法快速检索 c 字段,只能通过遍历进行 c 字段的比较。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1168382.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

网络编程——以太网协议

一,以太网格式 1.1目的地址 这个地址不是IP地址,而是网络中的另一套地址体系,mac 地址(物理地址),这个地址的作用与IP地址有一定的重合。因为网络层协议与应用层协议是各自独立被开发的,所以地…

黄仁勋:英伟达预言 2 年内行业将面目全非 一个词形容AI:Unbelievable

本心、输入输出、结果 文章目录 黄仁勋:英伟达预言 2 年内行业将面目全非 一个词形容AI:Unbelievable前言【访谈内容】一个词形容AI:Unbelievable创立英伟达“比想象中难一百万倍”相关图片传送门弘扬爱国精神 黄仁勋:英伟达预言 …

贷款行业极难获客,怎么获取实时客户数据?

我们能想象当客户有贷款需求时会发生什么吗? 客户可能会打开手机搜索“如何借款”、“哪笔贷款利息低”、“最多能借多少钱”、“贷款需要什么条件”等关键词,然后,客户点击进入第一个链接,然后填写他们的姓名和电话号码来测试他…

浅谈无源供电无线测温在线监测系统应用方案

安科瑞 崔丽洁 摘要:无源供电无线测温在线监测系统是一种基于声表面波技术的测温技术,在变电站监测方面得到了很好的技术实践应用。本文对无源供电无线测温在线监测系统研究应用进行分析研究。 关键词:设备检测;无线测温。 引言 在…

vcruntime140.dll无法继续执行代码修复教程

在计算机的世界里,我们经常会遇到各种各样的问题,其中之一就是“vcruntime140.dll缺失”。这个问题可能会影响到我们的正常使用,但是别担心,今天我就来给大家分享一下关于vcruntime140.dll缺失的4种修复方案。 首先,我…

C#完成XML文档节点的自动计算功能

一个项目涉及XML文档中节点的自动计算,就是XML文档的每个节点都参与运算,要求: ⑴如果节点有计算公式则按照计算公式进行; ⑵如果节点没有计算公式则该节点的值就是所有子节点的值之和; ⑶节点有4种类型,计…

内网穿透实现远程访问,如何满足信息安全需求?

内网穿透技术是一种将内网服务映射到外网,从而实现远程访问的技术。它的出现为企业及个人带来了便利,但同时也带来了一定的安全风险。因此,确保内网穿透远程访问内网服务的安全性显得尤为重要。 贝锐旗下内网穿透兼动态域名解析品牌花生壳&am…

【漏洞库】XXL-JOB executor 未授权访问漏洞导致RCE

文章目录 漏洞描述漏洞编号漏洞评级影响版本漏洞复现- EXP 编写 漏洞挖掘修复建议 漏洞描述 XXL-JOB是一个分布式任务调度平台,其核心设计目标是开发迅速、学习简单、轻量级、易扩展。现已开放源代码并接入多家公司线上产品线,开箱即用。XXL-JOB分为adm…

TCP四次挥手过程解密:为什么不止三次挥手或更少次挥手?

🎬 江城开朗的豌豆:个人主页 🔥 个人专栏 :《 VUE 》 《 javaScript 》 📝 个人网站 :《 江城开朗的豌豆🫛 》 ⛺️ 生活的理想,就是为了理想的生活 ! 目录 ⭐ 专栏简介 📘 文章引言 一、三…

使用Postman工具做接口测试 —— 断言与参数提取

引言 下面我会以登录为例,来讲如何利用postman提取上一个接口返回数据,并且放到当前接口来使用。 接口关联(参数提取) 下面以登录接口为例,讲一下postman如何使用参数提取,可以通过Fiddler工具对系统登录操作进行抓包分析&#…

linux报错汇总

1. linux 安装pytorch报错 CondaHTTPError: HTTP 000 CONNECTION FAILED for url <https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/linux-64/current_repodata.json> Elapsed: -An HTTP error occurred when trying to retrieve this URL. HTTP errors are o…

基于 Amazon EC2 和 Amazon Systems Manager Session Manager 的堡垒机的设计和自动化实现

文章目录 1. 背景2. 云上堡垒机设计2.1 安全设计2.2 高可用和弹性设计2.3 监控告警设计2.4 自动化部署设计2.4.1 堡垒机代码设计2.4.2 Session Manager 配置设计2.4.3 堡垒机 IAM 角色设计 3. 部署堡垒机3.1 堡垒机部署架构图3.2 堡垒机自动化部署 4. 堡垒机使用场景4.1 堡垒机…

[python 刷题] 2866 Beautiful Towers II

[python 刷题] 2866 Beautiful Towers II 题目如下&#xff1a; You are given a 0-indexed array maxHeights of n integers. You are tasked with building n towers in the coordinate line. The ith tower is built at coordinate i and has a height of heights[i]. A co…

Wi-Fi还可以做什么?柯南解释IOT应用

大会报告&#xff1a;无线人工智能技术正在改变世界 Wi-Fi还可以做什么&#xff1f;随着带宽的提升&#xff0c;无线终端可以识别出更多的多径&#xff0c;每条多径都可以视作一个虚拟传感器&#xff0c;以感知周边环境。基于此&#xff0c;越来越多的无线感知产品应运而生。20…

Leetcode1122. 数组的相对排序

Every day a Leetcode 题目来源&#xff1a;1122. 数组的相对排序 解法1&#xff1a;哈希 用集合 set 存储 arr2 中的元素。 遍历数组 arr1 &#xff0c;设当前元素为 num&#xff1a; 如果 num 在 set 中出现&#xff0c;用哈希表 hash 记录 num 和它出现的次数。否则&a…

【移远QuecPython】EC800M物联网开发板的内置GNSS定位的恶性BUG(目前没有完全的解决方案)

【移远QuecPython】EC800M物联网开发板的内置GNSS定位的恶性BUG&#xff08;目前没有完全的解决方案&#xff09; GNSS配置如下&#xff1a; 【移远QuecPython】EC800M物联网开发板的内置GNSS定位获取&#xff08;北斗、GPS和GNSS&#xff09; 测试视频&#xff08;包括BUG复…

大数据毕业设计选题推荐-热门旅游景点数据分析-Hadoop-Spark-Hive

✨作者主页&#xff1a;IT研究室✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…

第二章 Python字符串处理

系列文章目录 第一章 Python 基础知识 第二章 python 字符串处理 第三章 python 数据类型 第四章 python 运算符与流程控制 第五章 python 文件操作 第六章 python 函数 第七章 python 常用内建函数 第八章 python 类(面向对象编程) 第九章 python 异常处理 第十章 python 自定…

『亚马逊云科技产品测评』活动征文|搭建基础运维环境

授权声明&#xff1a;本篇文章授权活动官方亚马逊云科技文章转发、改写权&#xff0c;包括不限于在 Developer Centre, 知乎&#xff0c;自媒体平台&#xff0c;第三方开发者媒体等亚马逊云科技官方渠道 目录 1、什么是容器化部署 2、连接到控制台 3、安装docker 3.1 更新…

学习Opencv(蝴蝶书/C++)代码——1.macOS下安装OpenCV4.8.0和QT5.15(C++)

文章目录 1.前置条件-cmake和c2. opencv2.1 opencv安装2.2 opencv测试2.2.1 基本测试2.2.2 opencv里的自带测试图像 2.3 报错2.3.1 MacOSX10.15.sdk/usr/include/sys/cdefs.h:807:2: error: Unsupported architecture2.2.2 电脑上没有安装java(Unable to locate a Java Runtime…