MySQL索引原理和优化

news2024/9/22 11:33:01

目录

  • 1 什么是索引?
    • 1.1 引言
    • 1.2 索引原理
    • 1.3 索引分类
      • 1.3.1 主键索引
      • 1.3.2 普通索引(单列索引)
      • 1.3.3 复合索引(组合索引)
      • 1.3.4 唯一索引
      • 1.3.5 全文索引
      • 1.3.6 索引的查询和删除
    • 1.4 索引的优缺点
  • 2 索引数据结构
    • 2.1 Hash表
    • 2.2 二叉树
    • 2.3 平衡二叉树
    • 2.4 B树:改造二叉树
    • 2.5 B+树:改造B树
  • 3 MySQL的索引实现
    • 3.1 InnoDB索引
      • 3.1.1 主键索引(聚簇索引)
      • 3.1.2 辅助索引
      • 3.1.3 组合索引
      • 3.1.4 最左匹配原则
      • 3.1.5 覆盖索引
    • 3.2 MyIsam索引
      • 3.2.1 根据主键等值查询数据
      • 3.2.2 辅助索引
  • 4 回表和联合索引的应用
    • 4.1 回表查询
    • 4.2 如何避免回表
    • 4.3 联合索引的使用


1 什么是索引?

在这里插入图片描述

1.1 引言

  • 官方上面说索引是帮助MySQL高效获取数据数据结构,通俗点的说,数据库索引好比是一本书的目录,可以直接根据页码找到对应的内容,目的就是为了加快数据库的查询速度
  • 索引是对数据库表中一列或多列的值进行排序的一种结构,使用索引可快速访问数据库表中的特定信息。
  • 一种能帮助mysql提高了查询效率的数据结构:索引数据结构

1.2 索引原理

索引的存储原理可以概括为一句话:以空间换时间

一般来说索引本身也很大,不可能全部存储在内存中,因此索引往往是存储在磁盘上的文件中的(可能存储在单独的索引文件中,也可能和数据一起存储在数据文件中)。

数据库在未添加索引进行查询的时候默认是进行全文搜索,也就是说有多少数据就进行多少次查询,然后找到相应的数据就把它们放到结果集中,直到全文扫描完毕。

1.3 索引分类

1.3.1 主键索引

  • 设定为主键后,数据库自动建立索引,InnoDB为聚簇索引,主键索引列值不能为空(Null)。
xxxxxxxxxx
# (1) 创建表添加主键索引
CREATE TABLE `table_name` (
    [...] ,
    PRIMARY KEY (`col_name`),
)
# (2) 添加主键索引
ALTER TABLE `table_name` ADD PRIMARY KEY (`col_name`);

1.3.2 普通索引(单列索引)

  • 普通索引(单列索引):单列索引是最基本的索引,它没有任何限制。
xxxxxxxxxx
#  (1) 直接创建索引
CREATE INDEX index_name ON table_name(`col_name`);
#  (2) 修改表结构的方式添加索引
ALTER TABLE `table_name` ADD INDEX index_name(`col_name`);
#  (3) 创建表的时候同时创建索引
CREATE TABLE `table_name` (
    [...] ,
    PRIMARY KEY (`id`),
    INDEX index_name (`col_name`)
)
#  (4) 删除索引
DROP INDEX index_name ON table_name;   
alter table `表名` drop index 索引名;

1.3.3 复合索引(组合索引)

  • 复合索引:复合索引是在多个字段上创建的索引。复合索引遵守“最左前缀”原则即在查询条件中使用了复合索引的第一个字段,索引才会被使用。因此,在复合索引中索引列的顺序至关重要。
xxxxxxxxxx
# (1)创建一个复合索引
create index index_name on table_name(`col_name1`,`col_name2`,...);
# (2)修改表结构的方式添加索引
alter table table_name add index index_name(`col_name1`,`col_name2`,...);

1.3.4 唯一索引

唯一索引:唯一索引和普通索引类似,主要的区别在于,唯一索引限制列的值必须唯一,但允许存在空值(只允许存在一条空值)

如果在已经有数据的表上添加唯一性索引的话:

  • 如果添加索引的列的值存在两个或者两个以上的空值,则不能创建唯一性索引会失败。(一般在创建表的时候,要对自动设置唯一性索引,需要在字段上加上 not null)
  • 如果添加索引的列的值存在两个或者两个以上的null值,还是可以创建唯一性索引,只是后面创建的数据不能再插入null值 ,并且严格意义上此列并不是唯一的,因为存在多个null值。
  • 对于多个字段创建唯一索引规定列值的组合必须唯一
xxxxxxxxxx
“空值” 和”NULL”的概念: 
1:空值是不占用空间的 .
2: MySQL中的NULL其实是占用空间的.

长度验证:注意空值的之间是没有空格的。

> select length(''),length(null),length(' ');
+------------+--------------+-------------+
| length('') | length(null) | length(' ') |
+------------+--------------+-------------+
|          0 |         NULL |           1 |
+------------+--------------+-------------+
xxxxxxxxxx
-- (1)创建唯一索引
# 创建单个索引
CREATE UNIQUE INDEX index_name ON table_name(`col_name`);

# 创建多个索引
CREATE UNIQUE INDEX index_name on table_name(`col_name`,...);

-- (2)修改表结构
# 单个
ALTER TABLE table_name ADD UNIQUE index index_name(`col_name`);
# 多个
ALTER TABLE table_name ADD UNIQUE index index_name(`col_name`,...);

-- (3)创建表的时候直接指定索引
CREATE TABLE `table_name` (
    [...] ,
    PRIMARY KEY (`id`),
    UNIQUE index_name_unique(`col_name`)
)

1.3.5 全文索引

  • Full Text类型索引(FULLTEXT 索引在 MySQL 5.6 版本之后支持 InnoDB,而之前的版本只支持 MyISAM 表)。
  • 全文索引主要用来查找文本中的关键字,而不是直接与索引中的值相比较,目前只有char、varchar,text 列上可以创建全文索引。
xxxxxxxxxx
-- (1)创建表的适合添加全文索引
CREATE TABLE `table_name` (
    [...] ,
    PRIMARY KEY (`id`),
    FULLTEXT (`col_name`)
)

-- (2)修改表结构添加全文索引
ALTER TABLE table_name ADD FULLTEXT index_fulltext_content(`col_name`)

-- (3)直接创建索引
CREATE FULLTEXT INDEX index_fulltext_content ON table_name(`col_name`)

注意:

  • 默认 MySQL 不支持中文全文检索!
  • MySQL 全文搜索只是一个临时方案,对于全文搜索场景,更专业的做法是使用全文搜索引擎,例如 ElasticSearch 或 Solr。

1.3.6 索引的查询和删除

xxxxxxxxxx
#查看:
show indexes from `表名`;
#或
show keys from `表名`;
 
#删除
alter table `表名` drop index 索引名;

1.4 索引的优缺点

优点:

  • 大大提高数据查询速度。
  • 可以提高数据检索的效率,降低数据库的IO成本,类似于书的目录。
  • 通过索引列对数据进行排序,降低数据的排序成本降低了CPU的消耗。
  • 被索引的列会自动进行排序,包括【单例索引】和【组合索引】,只是组合索引的排序需要复杂一些。
  • 如果按照索引列的顺序进行排序,对order 不用语句来说,效率就会提高很多。

缺点:

  • 索引会占据磁盘空间。
  • 索引虽然会提高查询效率,但是会降低更新表的效率。比如每次对表进行增删改查操作,MySQL不仅要保存数据,还有保存或者更新对应的索引文件。
  • 维护索引需要消耗数据库资源。

综合索引的优缺点:

数据库表中不是索引越多越好,而是仅为那些常用的搜索字段建立索引效果最佳!

2 索引数据结构

MySQL索引使用的数据结构主要有BTree索引hash索引

对于hash索引来说,底层的数据结构就是哈希表,因此在绝大多数需求为单条记录查询的时候,可以选择哈希索引,查询性能最快;其余大部分场景建议选择BTree索引。

2.1 Hash表

Hash表,在Java中的HashMap,TreeMap就是Hash表结构,以键值对的形式存储数据。我们使用hash表存储表数据结构,Key可以存储索引列,Value可以存储行记录或者行磁盘地址。Hash表在等值查询时效率很高,时间复杂度为O(1);但是不支持范围快速查找,范围查找时只能通过扫描全表的方式,筛选出符合条件的数据。

显然这种方式,不适合我们经常需要查找和范围查找的数据库索引使用。

2.2 二叉树

在这里插入图片描述

上面这个图就是我们常说的二叉树:每个节点最多有两个分叉节点,左子树和右子树数据按顺序左小右大。

二叉树的特点就是为了保证每次查找都可以进行折半查找,从而减少IO次数。
但是二叉树不是一直保持二叉平衡,因为二叉树很考验根节点的取值,因为很容易在某个节点下不分叉了,这样的话二叉树就不平衡了,也就没有了所谓的能进行折半查找了,如下图:

在这里插入图片描述

显然这种不稳定的情况,我们在选择存储数据结构的时候就会尽量避免这种的情况发生。

2.3 平衡二叉树

平衡二叉树采用的是二分法思维,平衡二叉查找树除了具备二叉树的特点,最主要的特征是树的左右两个子树的层级最多差1。在插入删除数据时通过左旋/右旋操作保持二叉树的平衡,不会出现左子树很高、右子树很矮的情况。

使用平衡二叉查找树查询的性能接近与二分查找,时间复杂度为O(log2n),查询id=6,只需要两次IO。

在这里插入图片描述

就上述平衡二叉树的特点来看,其实是我们理想的状态下,然而其实内部还是存在一些问题:

  • 时间复杂度和树的高度有关。树有多高就需要检索多少次,每个节点的读取,都对应一次磁盘的IO操作。树的高度就等于每次查询数据时磁盘IO操作的次数。磁盘每次寻道的时间为10ms,在数据量大时,查询性能会很差。(1百万的数据量,log2n约等于20次磁盘IO读写,时间消耗约等于:20*10=0.2S)。
  • 平衡二叉树不支持范围查询快速查找,范围查询需要从根节点多次遍历,查询效率不高。

2.4 B树:改造二叉树

MySQL的数据是存储在磁盘文件中的,查询处理数据时,需要先把磁盘中的数据加载到内存中,磁盘IO操作非常耗时,所以我们优化的重点就是尽量减少磁盘的IO操作。访问二叉树的每个节点都会发生一次IO,如果想要减少磁盘IO操作,就需要尽量降低树的高度。

那如何降低树的高度呢?

假如key为bigint=8字节,每个节点有两个指针,每个指针为4个字节,一个节点占用的空间为(8+4*2=16)。

因为在MySQL的InnoDB引擎的一次IO操作会读取一页的数据量(默认一页大小为16K),而二叉树一次IO操作的有效数据量只有16字节,空间利用率极低。为了最大化的利用一次IO操作空间,一个解决方法就是在一个节点处存储多个元素,在每个节点尽可能多的存储数据。每个节点可以存储1000个索引(16k/16=1000),这样就将二叉树改造成了多叉树,通过增加树的分叉树,将树的体型从高瘦变成了矮胖。构建1百万条数据,树的高度需要2层就可以(1000*1000=1百万),也就是说只需要两次磁盘IO操作就可以查询到数据,磁盘IO操作次数变少了,查询数据的效率整体也就提高了。

这种数据结构我们称之为B树,B树是一种多叉平衡查找树,如下图主要特点:

  • B树的节点中存储这多个元素,每个内节点有多个分叉。
  • 节点中的元素包含键值和数据,节点中的键值从大到小排列。也就是说,在所有的节点中都存储数据。
  • 父节点当中的元素不会出现在子节点中。
  • 所有的叶子节点都位于同一层,叶子节点具有相同的深度,叶子节点之间没有指针连接。

在这里插入图片描述

举个简单的例子,在B树中查询数据的情况:

xxxxxxxxxx
假如我们要查询key等于10对应的数据data,根据上图我们可知在磁盘中的查询路径是:磁盘块1->磁盘块2->磁盘块6
  • 第一次磁盘IO:将磁盘块1加载到内存中,在内存中从头遍历比较,10<15,走左子树,到磁盘中寻址到磁盘块2。
  • 第二次磁盘IO:将磁盘块2加载到内存中,在内存中从头遍历比较,10>7,走右子树,到磁盘中寻址到磁盘块6。
  • 第三次磁盘IO:将磁盘块6加载到内存中,在内存中从头遍历比较,10=10,找到key=10的位置,取出对应的数据data,如果data存储的是行记录,直接取出数据,查询结束;如果data存储的是行磁盘地址,还需要根据磁盘地址到对应的磁盘中取出数据,查询结束。
xxxxxxxxxx
相比较二叉平衡查找树,在整个查找过程中,虽然数据的比较次数并没有明显减少,但是对于磁盘IO的次数会大大减少,同时,由于我们是在内存中进行的数据比较,所以比较数据所消耗的时间可以忽略不计。B树的高度一般2至3层就能满足大部分的应用场景,所以使用B树构建索引可以很好的提升查询的效率。

过程如图:

在这里插入图片描述

看到上面的情况,觉得B树已经很理想了,但是其中还是存在可以优化的地方:

  • B树不支持范围查询的快速查找,例如:仍然根据上图,我们想要查询10到35之间的数据,查找到10之后,需要回到根节点重新遍历查找,需要从根节点进行多次遍历,查询效率有待提高。
  • 如果data存储的是行记录,行的大小随着列数的增加,所占空间会变大,这时一页中可存储的数据量就会减少,树相应就会变高,磁盘IO次数就会随之增加,有待优化。

2.5 B+树:改造B树

B+树,作为B树的升级版,MySQL在B树的基础上继续进行改造,使用B+树构建索引。B+树和B树最主要的区别在于非叶子节点是否存储数据的问题。

  • B树:叶子节点和非叶子节点都会存储数据。
  • B+树:只有叶子节点才会存储数据,非叶子节点只存储键值key;叶子节点之间使用双向指针连接,最底层的叶子节点形成了一个双向有序链表。

B+树的大致数据结构:

在这里插入图片描述

xxxxxxxxxx
B+树的最底层叶子节点包含了所有的索引项。从图上可以看到,B+树在查找数据的时候,由于数据都存放在最底层的叶子节点上,所以每次查找都需要检索到叶子节点才能查询到数据。所以在需要查询数据的情况下每次的磁盘的IO跟树高有直接的关系,但是从另一方面来说,由于数据都被放到了叶子节点,所以放索引的磁盘块锁存放的索引数量是会跟这增加的,所以相对于B树来说,B+树的树高理论上情况下是比B树要矮的。也存在索引覆盖查询的情况,在索引中数据满足了当前查询语句所需要的全部数据,此时只需要找到索引即可立刻返回,不需要检索到最底层的叶子节点。

举例:等值查询

假如我们查询值等于9的数据。查询路径磁盘块1->磁盘块2->磁盘块6。

  • 第一次磁盘IO:将磁盘块1加载到内存中,在内存中从头遍历比较,9<15,走左路,到磁盘寻址磁盘块2。
  • 第二次磁盘IO:将磁盘块2加载到内存中,在内存中从头遍历比较,7<9<12,到磁盘中寻址定位到磁盘块6。
  • 第三次磁盘IO:将磁盘块6加载到内存中,在内存中从头遍历比较,在第三个索引中找到9,取出data,如果data存储的行记录,取出data,查询结束。如果存储的是磁盘地址,还需要根据磁盘地址到磁盘中取出数据,查询终止。(这里需要区分的是在InnoDB中Data存储的为行数据,而MyIsam中存储的是磁盘地址。)

过程如图:

在这里插入图片描述

举例:范围查询

假如我们想要查找9和26之间的数据,查找路径为:磁盘块1->磁盘块2->磁盘块6->磁盘块7

  • 前三次磁盘IO:首先查找到键值为9对应的数据(定位到磁盘块6),然后缓存大结果集中。这一步和前面等值查询流程一样,发生了三次磁盘IO。
  • 继续查询,查找到节点15之后,底层的所有叶子节点是一个有序列表,我们从磁盘块6中的键值9开始向后遍历筛选出所有符合条件的数据。
  • 第四次磁盘IO:根据磁盘块6的后继指针到磁盘中寻址定位到磁盘块7,将磁盘块7加载到内存中,在内存中从头遍历比较,9<25<26,9<26<=26,将数据data缓存到结果集中。
  • 逐渐具备唯一性(后面不会再有<=26的数据),不需要再向后查找,查询结束,将结果集返回给用户。

在这里插入图片描述

可以看到B+树可以保证等值和范围查询的快速查找,MySQL的索引就采用了B+树的数据结构。

3 MySQL的索引实现

介绍完了索引数据结构,那肯定是要带入到Mysql里面看看真实的使用场景的,所以这里分析Mysql的两种存储引擎的索引实现:MyISAM索引InnoDB索引

3.1 InnoDB索引

3.1.1 主键索引(聚簇索引)

每个InnoDB表都有一个聚簇索引 ,聚簇索引使用B+树构建,叶子节点存储的数据是整行记录。一般情况下,聚簇索引等同于主键索引,当一个表没有创建主键索引时,InnoDB会自动创建一个ROWID字段来构建聚簇索引。

InnoDB创建索引的具体规则如下:

  • 在表上定义主键PRIMARY KEY,InnoDB将主键索引用作聚簇索引。
  • 如果表没有定义主键,InnoDB会选择第一个不为NULL的唯一索引列用作聚簇索引。
  • 如果以上两个都没有,InnoDB 会使用一个6 字节长整型的隐式字段 ROWID字段构建聚簇索引。该ROWID字段会在插入新行时自动递增。

除聚簇索引之外的所有索引都称为辅助索引。在中InnoDB,辅助索引中的叶子节点存储的数据是该行的主键值都。 在检索时,InnoDB使用此主键值在聚簇索引中搜索行记录。

这里以user_innodb为例,user_innodb的id列为主键,age列为普通索引。

xxxxxxxxxx
CREATE TABLE `user_innodb`
(
  `id`       int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(20) DEFAULT NULL,
  `age`      int(11)     DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE,
  KEY `idx_age` (`age`) USING BTREE
) ENGINE = InnoDB;

在这里插入图片描述

  • InnoDB的数据和索引存储在t_user_innodb.ibd文件中,InnoDB的数据组织方式,是聚簇索引。
  • 主键索引的叶子节点会存储数据行,辅助索引的叶子节点只会存储主键值。

在这里插入图片描述

等值查询数据:

xxxxxxxxxx
select * from user_innodb where id = 28;
  1. 先在主键树中从根节点开始检索,将根节点加载到内存,比较28<75,走左路。(1次磁盘IO)
  2. 将左子树节点加载到内存中,比较16<28<47,向下检索。(1次磁盘IO)
  3. 检索到叶节点,将节点加载到内存中遍历,比较16<28,18<28,28=28。查找到值等于28的索引项,直接可以获取整行数据。将改记录返回给客户端。(1次磁盘IO)

磁盘IO数量:3次。

在这里插入图片描述

3.1.2 辅助索引

除聚簇索引之外的所有索引都称为辅助索引,InnoDB的辅助索引只会存储主键值而非磁盘地址。

以表user_innodb的age列为例,age索引的索引结果如下图。

在这里插入图片描述

  • 辅助索引的底层叶子节点是按照(age,id)的顺序排序,先按照age列从小到大排序,age相同时按照id列从小到大排序。
  • 使用辅助索引需要检索两遍索引:首先检索辅助索引获得主键,然后根据主键到主键索引中检索获得数据记录。

辅助索引等值查询的情况:

xxxxxxxxxx
select * from t_user_innodb where age=19;

在这里插入图片描述

根据在辅助索引树中获取的主键id,到主键索引树检索数据的过程称为回表查询。

磁盘IO数:辅助索引3次+获取记录回表3次

3.1.3 组合索引

  • 以表abc_innodb为例,id列为主键索引,创建一个联合索引idx_abc(a,b,c)
xxxxxxxxxx
CREATE TABLE `abc_innodb`
(
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `a`  int(11)     DEFAULT NULL,
  `b`  int(11)     DEFAULT NULL,
  `c`  varchar(10) DEFAULT NULL,
  `d`  varchar(10) DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE,
  KEY `idx_abc` (`a`, `b`, `c`)
) ENGINE = InnoDB;

组合索引的数据结构:

在这里插入图片描述

组合索引的查询过程:

xxxxxxxxxx
select * from abc_innodb where a = 13 and b = 16 and c = 4;

在这里插入图片描述

3.1.4 最左匹配原则

最左前缀匹配原则和联合索引的索引存储结构和检索方式是有关系的。

在组合索引树中,最底层的叶子节点按照第一列a列从左到右递增排序,但是b列和c列是无序的,b列只有在a列值相等的情况下小范围内有序递增;而c列只能在a和b两列值相等的情况下小范围内有序递增。

就像上面的查询,B+ 树会先比较a列来确定下一步应该检索的方向,往左还是往右。如果a列相同再比较b列,但是如果查询条件中没有a列,B+树就不知道第一步应该从那个节点开始查起。

可以说创建的idx_(a,b,c)索引,相当于创建了(a)、(a,b)、(a,b,c)三个索引。

组合索引的最左前缀匹配原则:

xxxxxxxxxx
使用组合索引查询时,mysql会一直向右匹配直至遇到范围查询(>、<、between、like)等就会停止匹配。

3.1.5 覆盖索引

覆盖索引并不是一种索引结构,覆盖索引是一种很常用的优化手段。因为在使用辅助索引的时候,我们只可以拿到相应的主键值,想要获取最终的数据记录,还需要根据主键通过主键索引再去检索,最终获取到符合条件的数据记录。

在上面的abc_innodb表中的组合索引查询时,如果我们查询的结果只需要a、b、c这三个字段,那我们使用这个idx_index(a,b,c)组合索引查询到叶子节点时就可以直接返回了,而不需要再次回表查询,这种情况就是覆盖索引。

未使用索引覆盖的情况:

在这里插入图片描述

索引覆盖的情况

在这里插入图片描述

3.2 MyIsam索引

以一个简单的user表为例。user表存在两个索引,id列为主键索引,age列为普通索引

xxxxxxxxxx
CREATE TABLE `user`
(
  `id`       int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(20) DEFAULT NULL,
  `age`      int(11)     DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE,
  KEY `idx_age` (`age`) USING BTREE
) ENGINE = MyISAM
  AUTO_INCREMENT = 1
  DEFAULT CHARSET = utf8;

在这里插入图片描述

MyISAM的数据文件和索引文件是分开存储的。MyISAM使用B+树构建索引树时,叶子节点中存储的键值为索引列的值,数据为索引所在行的磁盘地址。

主键ID列索引:

在这里插入图片描述

表user的索引存储在索引文件user.MYI中,数据文件存储在数据文件 user.MYD中。

简单分析下查询时的磁盘IO情况:

3.2.1 根据主键等值查询数据

xxxxxxxxxx
select * from user where id = 28
  • 第一次磁盘IO:先在主键索引树中从根节点开始检索,将根节点加载到内存中,比较28<75,所以走左子树。
  • 第二次磁盘IO:将左子树节点加载到内存中,比较16<28<47,向下检索。
  • 第三次磁盘IO:检索到叶子节点,将节点加载到内存中遍历,从16<28,18<28,28=28,查找到键值等于28的索引项。
  • 第四次磁盘IO:从索引项中获取磁盘地址,然后到数据文件user.MYD中获取对应整行记录。
  • 将记录返回给客户端。

磁盘IO次数:3次索引检索+记录数据检索。

在这里插入图片描述

根据主键范围查询数据:

xxxxxxxxxx
select * from user where id between 28 and 47;
    1. 先在主键树中从根节点开始检索,将根节点加载到内存,比较28<75,走左路。(1次磁盘IO)
    1. 将左子树节点加载到内存中,比较16<28<47,向下检索。(1次磁盘IO)
    1. 检索到叶节点,将节点加载到内存中遍历比较16<28,18<28,28=28<47。查找到值等于28的索引项。
    1. 根据磁盘地址从数据文件中获取行记录缓存到结果集中。(1次磁盘IO)
    1. 我们的查询语句时范围查找,需要向后遍历底层叶子链表,直至到达最后一个不满足筛选条件。
    1. 向后遍历底层叶子链表,将下一个节点加载到内存中,遍历比较,28<47=47,根据磁盘地址从数据文件中获取行记录缓存到结果集中。(1次磁盘IO)
    1. 最后得到两条符合筛选条件,将查询结果集返给客户端。

磁盘IO次数:4次索引检索+记录数据检索。

在这里插入图片描述

3.2.2 辅助索引

在MyISAM存储引擎中,辅助索引和主键索引的结构是一样的,没有任何区别,叶子节点中data阈存储的都是行记录的磁盘地址。 主键列索引的键值是唯一的,而辅助索引的键值是可以重复的。

查询数据时,由于辅助索引的键值不唯一,可能存在多个拥有相同的记录,所以即使是等值查询,也需要按照范围查询的方式在辅助索引树种检索数据。

4 回表和联合索引的应用

4.1 回表查询

在InnoDB的存储引擎中,使用辅助索引查询的时候,因为辅助索引叶子节点保存的数据不是当前数据记录,而是当前数据记录的主键索引。如果需要获取当前记录完整的数据,就必须要再次根据主键从主键索引中继续检索查询,这个过程我们称之为回表查询。

由此可见,在数据量比较大的时候,回表必然会消耗很多的时间影响性能,所以我们要尽量避免回表的发生。

4.2 如何避免回表

  • 使用索引覆盖

举例:

xxxxxxxxxx
CREATE TABLE `user`
(
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name`  int(11)     DEFAULT NULL,
  `sex`  char(3)     DEFAULT NULL,
  `address`  varchar(10) DEFAULT NULL,
  `hobby`  varchar(10) DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE,
  KEY `i_name` (`name`)
) ENGINE = InnoDB;

如果有一个场景:

xxxxxxxxxx
select id,name,sex from user where name = 'zhangsan';

这个语句在业务上频繁使用到,而user表中的其他字段使用频率远低于这几个字段,在这个情况下,如果我们在建立name字段的索引时,不是使用单一索引,而是使用联合索引(name,sex),这样的话再执行这个查询语句,根据这个辅助索引(name,sex)查询到的结果就包括了我们所需要的查询结果的所有字段的完整数据,这样就不需要再次回表查询去检索sex字段的数据了。

  • 以上就是一个典型的使用覆盖索引的优化策略减少了回表查询的情况。

4.3 联合索引的使用

联合索引:

xxxxxxxxxx
在建立索引的时候,尽量在多个单列索引上判断下是否可以使用联合索引。联合索引的使用不仅可以节省空间,还可以更容易的使用到索引覆盖。

节省空间:

xxxxxxxxxx
试想一下,索引的字段越多,是不是更容易满足查询需要返回的数据呢。比如联合索引(a_b_c),是不是等于有了索引:a,a_b,a_b_c三个索引,这样是不是节省了空间,当然节省的空间并不是三倍于(a,a_b,a_b_c)三个索引,因为索引树的数据没变,但是索引data字段的数据确实真实的节省了。

联合索引的创建原则:

xxxxxxxxxx
在创建联合索引的时候因该把频繁使用的列、区分度高的列放在前面,频繁使用代表索引利用率高,区分度高代表筛选粒度大,这些都是在索引创建的需要考虑到的优化场景,也可以在常需要作为查询返回的字段上增加到联合索引中。
xxxxxxxxxx
如果在联合索引上增加一个字段而使用到了覆盖索引,那建议这种情况下使用联合索引。

联合索引的使用:

  • 考虑当前是否已经存在多个可以合并的单列索引,如果有,那么将当前多个单列索引创建为一个联合索引。
  • 当前索引存在频繁使用作为返回字段的列,这个时候就可以考虑当前列是否可以加入到当前已经存在索引上,使其查询语句可以使用到覆盖索引。

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

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

相关文章

做题遇见的PHP函数汇总

mb_substr函数 mb_substr() 函数返回字符串的一部分&#xff0c;之前学过 substr() 函数&#xff0c;它只针对英文字符&#xff0c;如果要分割的中文文字则需要使用 mb_substr() 语法&#xff1a; mb_substr ( $str ,$start [, $length NULL [, $encoding mb_encoding() ]] …

改进版简化路径。

美图 在原有的基础上增加对 cd - 的处理。 在 Unix 命令中&#xff0c;cd - 表示返回上一次所在的目录。我们可以使用一个变量来记录上一次所在的目录&#xff0c;在遇到 cd - 时将当前目录设置为上一次所在的目录。 以下是增加对 cd - 的处理后的代码&#xff1a; 4 cd /…

016 - STM32学习笔记 - SPI读写FLASH(一)

016 - STM32学习笔记 - SPI访问Flash&#xff08;一&#xff09; 之前csdn的名称是宥小稚&#xff0c;后来改成放学校门口见了&#xff0c;所以前面内容看到图片水印不要在意&#xff0c;都是自己学习过程中整理的&#xff0c;不涉及版权啥的。 1、什么是SPI&#xff1f; SP…

Linux项目自动化构建工具-make/Makefile以及git三板斧

目录 一、关于make/makefile的背景知识二、依赖关系和依赖方法三、make/makefile如何书写&#xff1f;四、文件的三个时间(Access、Modify、Change)五、Linux下倒计时和进度条代码的书写5.1 回车换行5.2 缓冲区5.3 倒计时代码实现5.4 进度条代码实现 六、git三板斧6.1 什么是gi…

10.15资源加载

定义&#xff1a; 1.直接属性引用 生成一个actor和声音&#xff1a; 运行时就会产生一个Myactor中设置的Actor并且播放Myactor中设置的声音。 音频&#xff1a;class USoundCue&#xff1b; 纹理&#xff1a;class UTexture&#xff1b; 材质&#xff1a; class UMaterial 模…

TCP/IP出现的背景及其历史【图解TCP/IP(笔记八)】

文章目录 TCP/IP出现的背景及其历史从军用技术的应用谈起ARPANET的诞生TCP/IP的诞生UNIX系统的普及与互联网的扩张商用互联网服务的启蒙 TCP/IP出现的背景及其历史 从军用技术的应用谈起 20世纪60年代&#xff0c;很多大学和研究机构都开始着力于新的通信技术。其中有一家以美…

jmeter列表数据断言

在jmeter接口请求中&#xff0c;通常需要根据接口data列表有无返回的数据断言是接口请求成功&#xff0c;如图1&#xff0c; 通常有这么几种方法&#xff1a; beanshell断言 json断言 响应断言 图1&#xff1a; 失败请求&#xff1a;{"code":0,"msg"…

小甲鱼- python -洗牌算法 —— Fisher-Yates

练习1 自己的原始代码 &#xff08;比较复杂&#xff09; 1.没有把字符串转为列表&#xff0c;所以不能利用pop # 打乱的次数 for i in range(1,4):s "ABCDEF"n 4list []l len(s)while l > 0:k random.randint(1, l)list.append(s[k-1])s s.replac…

电子设备电池容量与充电器功率的关系

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhang.cn] 目录 抛出问题 手机的工作电压 手机的工作电流 手机的电池容量 电能转换公式 充电器功率 充电时间计算 总结 抛出问题 你是否也想过&#xff0c;你的手机电池容量是5000mAh&#xff0c;手机充电器是120W快…

基于低代码平台的项目设计的一般流程及低代码平台(基于iVX)与MVC的关系

基于低代码平台的项目设计的一般流程及低代码平台&#xff08;基于iVX&#xff09;与MVC的关系 1.基于低代码平台的项目设计的一般流程a.流程图b.MVC架构应用于iVX项目的各分层排序&#xff1a;&#xff08;1&#xff09;第一步&#xff1a;写M&#xff08;2&#xff09;第二步…

LeetCode[912]排序数组

难度&#xff1a;Medium 题目&#xff1a; 给你一个整数数组 nums&#xff0c;请你将该数组升序排列。 示例 1&#xff1a; 输入&#xff1a;nums [5,2,3,1] 输出&#xff1a;[1,2,3,5]示例 2&#xff1a; 输入&#xff1a;nums [5,1,1,2,0,0] 输出&#xff1a;[0,0,1,1,2,…

Openlayers实战:显示海量数据

Openlayers地图中通常的加载方式是canvas,另外还一种加载方式是webGL,在绘制海量数据时,使用GPU进行绘制可有效减少CPU的负载,提升绘制时的速度在浏览器中,可以使用WebGL的方式与GPU交互。 在本实战中,使用WebGLPoints显示海量数据。 效果图 源代码 /* * @Author: 大剑…

表单标签from

七、表单标签 form text name属性必须添加&#xff0c;否则后端不知道这个值是什么意思。且name不能重复 添加label主要是方便程序员&#xff0c;一看到用户名称这个label就是为username的&#xff0c;添加或者不添加页面效果一样 2、possword 用户密码显式出来&#xff0c;所…

与一款医疗仪器的往事

这是一款比较冷门的医疗仪器&#xff0c;SOD型多普勒脐动脉血流检测仪。 大约是20年前&#xff0c;有个朋友找到了俺&#xff0c;让俺给他写一个医疗仪器的软件。当时这个仪器是这样子的。这是唯一还能找到的当时的图片。朋友要求用C写。俺就选择了C Builder&#xff0c;比用VC…

vue源码阅读之什么是虚拟dom

前面简单说过数据响应式原理&#xff0c;大体是个怎么流程&#xff0c;数据发生变化&#xff0c;我们界面如何更新。 依赖收集收集的是watcher&#xff0c;然后当数据发生变化的时候dep通知watcher&#xff0c;然后watcher负责updateComponent。 那么更新组件过程中&#xff…

PostgreSQL查询引擎——上拉子链接SubLink

子查询是查询语句中经常出现的一种类型&#xff0c;是比较耗时的操作。优化子查询对查询效率的提升有直接的影响。从子查询出现在SQL语句的位置看&#xff0c;它可以出现在目标列、FROM子句、WHERE子句、JOIN/ON子句、GROUPBY子句、HAVING子句、ORDERBY子句等位置。子查询出现在…

c语言指针进阶(一)

大家好&#xff0c;我是c语言boom成家宝。今天为大家分享的是c语言中很重要的一个知识点------指针的深入讲解。 目录 指针 指针数组 数组指针 函数指针 什么是指针&#xff1f; 首先&#xff0c;指针的本质是一个地址&#xff0c;指针在32位机器上的大小是4个字节&a…

python_day3_str

字符串str 按索引下标查找 str Hi, world, follow, admin print(str[0]) print(str[-1])index() str Hi, world, follow, admin print(str.index(world)) #首字母下标 print(str.index(w))字符串.replace&#xff08;字符串1&#xff0c;字符串2&#xff09;&#xff1a;…

【Linux】高级IO(二)

文章目录 高级IO&#xff08;二&#xff09;I/O多路转接之pollpoll服务器 I/O多路转接之epollepoll相关函数epoll工作原理epoll回调机制epoll服务器epoll的优点 高级IO&#xff08;二&#xff09; I/O多路转接之poll poll也是系统提供的一个多路转接接口 poll系统调用也可以…

ruoyi若依 组织架构设计--[ 部门管理 ]

ruoyi若依 组织架构设计--[ 部门管理 ] 部门管理部门查询部门新增部门修改部门删除 部门管理 部门查询 需要注意的是&#xff0c;部门管理也有数据权限&#xff0c;比如A用户分配的数据权限(通过角色分配)是深圳总公司&#xff0c;那么A用户登录后看到的部门也是深圳总公司&am…