MySQL 索引 (只要能看完)(一篇就够了)

news2024/9/22 11:26:57

文章目录

  • 前言
  • 一、MySQL索引介绍
    • 1.1 索引的类别
    • 1.2 索引的创建原则
  • 二、索引的管理和使用
    • 2.1 制造实验数据
    • 2.2 explain 使用说明
    • 2.3 创建索引
      • 2.3.1 基于创建表时建立索引
      • 2.3.2 基于已创建好的表创建索引
    • 2.4 删除索引
    • 2.5 聚集索引和二级索引
      • 2.5.1 聚集索引
      • 2.5.2 二级索引(辅助索引)
    • 2.6 覆盖索引
    • 2.7 Multi-Range Read 优化
  • 三、索引实现的原理
    • 3.1 B+树的特征
    • 3.2 B+树的优势
    • 3.3 索引的设计思考
    • 3.4 索引的设计

前言

索引是对数据库表中一列或多列的值进行排序的一种结构。MySQL索引的建立对于MySQL的高效运行是很重要的,索引可以大大提高MySQL的检索速度。索引只是提高效率的一个因素,如果你的MySQL有大数据量的表,就需要花时间研究建立最优秀的索引,或优化查询语句。

我想很多人对mysql的认知可能就是CRUD(代表创建(Create)、更新(Update)、读取(Retrieve)和删除(Delete)操作),也不敢说自己会用和熟悉mysql,当然我就是其中一个,虽然知道mysql有很多东西,但是一直都没有深入的了解和掌握,最近想着好好的把Mysql原理学习下,这篇就是开胃菜吧,以后的慢慢道来。本篇文章内容主是基于mysql的InnoDB存储引擎。

一、MySQL索引介绍

索引是一个单独的、存储在磁盘上的数据库结构,它们包含着对数据表里所有记录的引用指针。使用索引用于快速找出在某个或多个列中有一特定值的行,所有MySQL列类型都可以被索引,对相关列使用索引是提高查询操作速度的最佳途径

MySQL索引的建立对于MySQL的高效运行是很重要的,索引可以大大提高MySQL的检索速度。比如我们在查字典的时候,前面都有检索的拼音和偏旁、笔画等,然后找到对应字典页码,这样然后就打开字典的页数就可以知道我们要搜索的某一个key的全部值的信息了。

创建索引时,你需要确保该索引是应用在 SQL 查询语句的条件(一般作为 WHERE 子句的条件),而不是在select的字段中,实际上,索引也是一张“表”,该表保存了主键与索引字段,并指向实体表的记录,虽然索引大大提高了查询速度,同时却会降低更新表的速度,如对表进行INSERT、UPDATE和DELETE。因为更新表时,MySQL不仅要保存数据,还要保存一下索引文件,建立索引会占用磁盘空间的索引文件。说白了索引就是用来提高速度的,但是就需要维护索引造成资源的浪费,所以合理的创建索引是必要的。

1.1 索引的类别

先去官网文档看看支持的索引类型,索引的实现方式如下图所示:
https://dev.mysql.com/doc/refman/8.0/en/create-index.html
在这里插入图片描述

由于本文是基于mysql的InnoDB存储引擎,索引我们主要看第一个表格,其他的表格可以自行的观看,都不难,从表格我们可以看出来,InnoDB存储引擎索引只支持BTREE类型的索引,索引的类别有Primary Key,Unique,Key,FULLTEXT和SPATIAL。当然也有其他的分法,按照索引列的数量分为单列索引和组合索引。

  1. Primary Key(聚集索引):InnoDB存储引擎的表会存在主键(唯一非null),如果建表的时候没有指定主键,则会使用第一非空的唯一索引作为聚集索引,否则InnoDB会自动帮你创建一个不 可见的、长度为6字节的row_id用来作为聚集索引。
  2. 单列索引:单列索引即一个索引只包含单个列
  3. 组合索引:组合索引指在表
  4. 的多个字段组合上创建的索引,只有在查询条件中使用了这些字段的左边字段时,索引才会被使用。使用组合索引时遵循最左前缀集合
    Unique(唯一索引):索引列的值必须唯一,但允许有空值。若是组合索引,则列值的组合必须唯一。主键索引是一种特殊的唯一索引,不允许有空值
  5. Key(普通索引):是MySQL中的基本索引类型,允许在定义索引的列中插入重复值和空值
  6. FULLTEXT(全文索引):全文索引类型为FULLTEXT,在定义索引的列上支持值的全文查找,允许在这些索引列中插入重复值和空值。全文索引可以在CHAR、VARCHAR或者TEXT类型的列上创建
  7. SPATIAL(空间索引):空间索引是对空间数据类型的字段建立的索引,MySQL中的空间数据类型有4种,分别是GEOMETRY、POINT、LINESTRING和POLYGON。MySQL使用SPATIAL关键字进行扩展,使得能够用于创建正规索引类似的语法创建空间索引。创建空间索引的列必须声明为NOT NULL

这里在说一下组合索引的遵循最左前缀原则:

order by使用索引最左前缀
- order by a
- order by a,b
- order by a,b,c
- order by a desc, b desc, c desc 

如果where使用索引的最左前缀定义为常量,则order by能使用索引
- where a=const order by b,c
- where a=const and b=const order by c
- where a=const and b > const order by b,c

不能使用索引进行排序
- order by a , b desc ,c desc  --排序不一致
- where d=const order by b,c   --a丢失
- where a=const order by c     --b丢失
- where a=const order by b,d   --d不是索引的一部分
- where a in(...) order by b,c --a属于范围查询

创建一个简单的表:

CREATE TABLE my_test (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(50) DEFAULT NULL,
  `sex` varchar(5) DEFAULT NULL,
  `address` varchar(100) DEFAULT NULL,
  `birthday` datetime NOT NULL,
  `user_num` int(11) unique,
  PRIMARY KEY (`id`),
  index(username)
);

show index from my_test;
在这里插入图片描述

明明在建表的时候只创建了一个索引,查询出来的有三个,其实主键,唯一约束列,外键这些都自动会生成索引,至于外键大家可以去尝试下。

上表格中各个列的说明:

table #表名称
non_unique  #如果索引不能包括重复词,为0,如果可以,则为1
key_name  #索引的名称
seq_in_index #索引中的列序号
column_name  #列名称
collation  #列以什么方式存储在索引中,在mysql中,有值'A'(升序)或者NULL(无分类)
cardinality  #索引在唯一值的数据的估值,通过运行analyze table xxx_table;或者 myisamchk -a 可以更新,技术根据被存储为整数的统计数据来计数,所以即使对于小型表,该值也没必要是精确的,基数越大,当进行联合所饮食,mysql使用该索引的机会越大。myisam中,该值是准确的,INNODB中该值数据是估算的,存在偏差
sub_part  #如果列只是部分的编入索引 则为被编入索引的字符的数目,如果整列被编入索引,则为NULL
packed  #指示关键词如何被压缩,如果没有被压缩,则为NULL
NULL   #如果列含有NULL,则含有YES,如果没有,则该列为NO
index_type  #用过的索引方法(BTREE,FULLTEXT,HASH,RTREE)
comment  #备注
index_comment  #为索引创建时提供了一个注释属性的索引的任何评论

1.2 索引的创建原则

  1. 索引并非越多越好,一个表中如果有大量的索引,不仅占用磁盘空间,而且会影响INSERT、DELETE、UPDATE等语句的性能,因为在表中的数据更改的同时,索引也会进行调整和更新
  2. 避免对经常更新的表进行过多的索引,并且索引中的列尽可能少。而对经常用于查询的字段应该创建索引,但要避免添加不必要的字段。
  3. 数据量小的表最好不要使用索引,由于数据较少,查询花费的时间可能比遍历索引的时间还要短,索引可能不会产生优化效果。
  4. 在条件表达式中经常用到的不同值较多的列上建立索引,在不同值很少的列上不要建立索引。比如在学生表的“性别”字段上只有“男”与“女”两个不同值,因此就无须建立索引。如果建立索引,不但不会提高查询效率,反而会严重降低数据更新速度。
  5. 当唯一性是某种数据本身的特征时,指定唯一索引。使用唯一索引需能确保定义的列的数据完整性,以提高查询速度。
  6. 在频繁进行排序或分组(即进行group by或order by操作)的列上建立索引,如果待排序的列有多个,可以在这些列上建立组合索引。
  7. 搜索的索引列,不一定是所要选择的列。换句话说,最适合索引的列是出现在WHERE子句中的列,或连接子句中指定的列,而不是出现在SELECT关键字后的选择列表中的列。
  8. 使用短索引。如果对字符串列进行索引,应该指定一个前缀长度,只要有可能就应该这样做。例如,有一个CHAR(200)列,如果在前10个或20个字符内,多数值是唯一的,那么就不要对整个列进行索引。对前10个或20个字符进行索引能够节省大量索引空间,也可能会使查询更快。较小的索引涉及的磁盘 IO 较少,较短的值比较起来更快。更为重要的是,对于较短的键值,索引高速缓存中的块能容纳更多的键值,因此,MySQL 也可以在内存中容纳更多的值。这样就增加了找到行而不用读取索引中较多块的可能性。
  9. 利用最左前缀。在创建一个n列的索引时,实际是创建了MySQL可利用的n个索引。多列索引可起几个索引的作用,因为可利用索引中最左边的列集来匹配行。这样的列集称为最左前缀。
  10. 对于InnoDB存储引擎的表,记录默认会按照一定的顺序保存,如果有明确定义的主键,则按照主键顺序保存。如果没有主键,但是有唯一索引,那么就是按照唯一索引的顺序保存。如果既没有主键又没有唯一索引,那么表中会自动生成一个内部列,按照这个列的顺序保存。按照主键或者内部列进行的访问是最快的,所以InnoDB表尽量自己指定主键,当表中同时有几个列都是唯一的,都可以作为主键的时候,要选择最常作为访问条件的列作为主键,提高查询的效率。另外,还需要注意,InnoDB 表的普通索引都会保存主键的键值,所以主键要尽可能选择较短的数据类型,可以有效地减少索引的磁盘占用,提高索引的缓存效果

二、索引的管理和使用

2.1 制造实验数据

这里的实验数据采用的是一个github的一个开源项目,具体的操作流程查看:
https://github.com/wuda0112/mysql-tester
数据制造完成后会有四个数据库:
在这里插入图片描述
数据可以根据参数进行生成,很简单,根据步骤来就好了

2.2 explain 使用说明

使用实验数据我们进行explain的查询:explain SELECT store_id,count(1) from foundation_item.item group by store_id limit 10;
在这里插入图片描述
EXPLAIN 语句的基本语法如下:

explain select select_option

select_options是SELECT语句的查询选项,包括FROMWHERE子句等

id: SELECT识别符。这是SELECT的查询序列号,表示查询中执行select子句或操作表的顺序,id相同,执行顺序从上到下,id不同,id值越大执行优先级越高
select_type:表示SELECT语句的类型。它可以是以下几种取值:
    SIMPLE:表示简单查询,其中不包括连接查询和子查询;
    PRIMARY:表示主查询,或者是最外层的查询语句,最外层查询为PRIMARY,也就是最后加载的就是PRIMARYUNION:表示连接查询的第2个或后面的查询语句, 不依赖于外部查询的结果集
    DEPENDENT UNION:连接查询中的第2个或后面的SELECT语句,依赖于外面的查询;
    UNION RESULT:连接查询的结果;
    SUBQUERY:子查询中的第1SELECT语句;不依赖于外部查询的结果集
    DEPENDENT SUBQUERY:子查询中的第1SELECT,依赖于外面的查询;
    DERIVED:导出表的SELECTFROM子句的子查询),MySQL会递归执行这些子查询,把结果放在临时表里。
    DEPENDENT DERIVED:派生表依赖于另一个表
    MATERIALIZED:物化子查询
    UNCACHEABLE SUBQUERY:子查询,其结果无法缓存,必须针对外部查询的每一行重新进行评估
    UNCACHEABLE UNION:UNION中的第二个或随后的 select 查询,属于不可缓存的子查询
table:表示查询的表
partitions:查询将从中匹配记录的分区。该值适用NULL于未分区的表
type:表示表的连接类型
    system:该表是仅有一行的系统表。这是const连接类型的一个特例
    const: 数据表最多只有一个匹配行,它将在查询开始时被读取,并在余下的查询优化中作为常量对待。const表查询速度很快,因为只读取一次,const用于使用常数值比较PRIMARY KEYUNIQUE索引的所有部分的场合。
    eq_ref:对于每个来自前面的表的行组合,从该表中读取一行,可以用于使用=运算符进行比较的索引列 。比较值可以是常量,也可以是使用在此表之前读取的表中列的表达式
    ref:对于来自前面的表的任意行组合,将从该表中读取所有匹配的行,ref可以用于使用“=”或“<=>”操作符的带索引的列。
    fulltext:使用FULLTEXT 索引执行联接
    ref_or_null:这种连接类型类似于ref,但是除了MySQL还会额外搜索包含NULL值的行。此联接类型优化最常用于解析子查询
    index_merge:此联接类型指示使用索引合并优化。在这种情况下,key输出行中的列包含使用的索引列表,并key_len包含使用的索引 的最长键部分的列表
    unique_subquery:类型替换 以下形式的eq_ref某些 IN子查询,unique_subquery 只是一个索引查找函数,它完全替代了子查询以提高效率。
    index_subquery:连接类型类似于 unique_subquery。它代替IN子查询,但只适合子查询中的非唯一索引
    range:只检索给定范围的行,使用一个索引来选择行。key列显示使用了哪个索引。key_len包含所使用索引的最长关键元素。当使用=、<>、>、>=、<、<=、IS NULL、<=>、BETWEEN或者IN操作符用常量比较关键字列时,类型为range
    index:该index联接类型是一样的 ALL,只是索引树被扫描。这发生两种方式:1、如果索引是查询的覆盖索引,并且可用于满足表中所需的所有数据,则仅扫描索引树。在这种情况下,Extra列显示为 Using index,2、使用对索引的读取执行全表扫描,以按索引顺序查找数据行。 Uses index没有出现在 Extra列中。
    ALL:对于前面的表的任意行组合进行完整的表扫描    
possible_keys:指出MySQL能使用哪个索引在该表中找到行。若该列是NULL,则没有相关的索引。在这种情况下,可以通过检查WHERE子句看它是否引用某些列或适合索引的列来提高查询性能。如果是这样,可以创建适合的索引来提高查询的性能。
kye:表示查询实际使用的索引,如果没有选择索引,该列的值是NULL。要想强制MySQL使用或忽视possible_keys列中的索引,在查询中使用FORCE INDEXUSE INDEX或者IGNORE INDEX
key_len:表示MySQL选择的索引字段按字节计算的长度,若键是NULL,则长度为NULL。注意,通过key_len值可以确定MySQL将实际使用一个多列索引中的几个字段
ref:表示使用哪个列或常数与索引一起来查询记录。
rows:显示MySQL在表中进行查询时必须检查的行数。
Extra:表示MySQL在处理查询时的详细信息

更详细说明见官网:https://dev.mysql.com/doc/refman/8.0/en/explain-output.html

2.3 创建索引

创建索引的语法(如下都是默认的innodb存储引擎):https://dev.mysql.com/doc/refman/8.0/en/create-index.html

CREATE [UNIQUE | FULLTEXT | SPATIAL] INDEX index_name
    [index_type]
    ON tbl_name (key_part,...)
    [index_option]
    [algorithm_option | lock_option] ...

key_part: {col_name [(length)] | (expr)} [ASC | DESC]

index_option: {
    KEY_BLOCK_SIZE [=] value
  | index_type
  | WITH PARSER parser_name
  | COMMENT 'string'
  | {VISIBLE | INVISIBLE}
  | ENGINE_ATTRIBUTE [=] 'string'
  | SECONDARY_ENGINE_ATTRIBUTE [=] 'string'
}

index_type:
    USING {BTREE | HASH}
algorithm_option:
    ALGORITHM [=] {DEFAULT | INPLACE | COPY}
lock_option:
    LOCK [=] {DEFAULT | NONE | SHARED | EXCLUSIVE}

可以在创建的表的时候建立索引,也可以对创建好的表建立索引。

2.3.1 基于创建表时建立索引

CREATE TABLE创建表时,除了可以定义列的数据类型,还可以定义主键约束、外键约束或者唯一性约束,而不论创建哪种约束,在定义约束的同时相当于在指定列上创建了一个索引。
创建普通索引

CREATE TABLE test.`user2` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(50) DEFAULT NULL,
  `sex` varchar(5) DEFAULT NULL,
  `address` varchar(100) DEFAULT NULL,
  `birthday` datetime NOT NULL,
  PRIMARY KEY (`id`),
  index idx1(username)
);

show index from test.‘user2’;
在这里插入图片描述

创建唯一索引

CREATE TABLE test.`user2` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(50) DEFAULT NULL,
  `sex` varchar(5) DEFAULT NULL,
  `address` varchar(100) DEFAULT NULL,
  `birthday` datetime NOT NULL,
  `score_num` int(11) NOT NULL UNIQUE,
  PRIMARY KEY (`id`),
  unique index idx1(username)
);

show index from test.user2;
在这里插入图片描述
前面两个索引都是通过主键和唯一约束自动创建的

创建组合索引
注意:最左前缀原则

CREATE TABLE test.`user2` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(50) DEFAULT NULL,
  `sex` varchar(5) DEFAULT NULL,
  `address` varchar(100) DEFAULT NULL,
  `birthday` datetime NOT NULL,
  `score_num` int(11) NOT NULL UNIQUE,
  PRIMARY KEY (`id`),
  index idx1(id,score_num,username)
);

在这里插入图片描述
创建全文索引
FULLTEXT全文索引可以用于全文搜索,并且只为CHAR、VARCHAR和TEXT列创建索引。索引总是对整个列进行,不支持局部(前缀)索引

CREATE TABLE test.`user2` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(50) DEFAULT NULL,
  `sex` varchar(5) DEFAULT NULL,
  `address` varchar(100) DEFAULT NULL,
  `birthday` datetime NOT NULL,
  `score_num` int(11) NOT NULL UNIQUE,
  PRIMARY KEY (`id`),
  fulltext index idx1(username)
);

在这里插入图片描述
创建空间索引

create table test.test(
    position geometry not null,
    spatial index idx1(position)
);

show index from test.test;
在这里插入图片描述
创建前缀索引

CREATE TABLE test.t1 (
  col1 VARCHAR(10),
  col2 VARCHAR(20),
  INDEX (col1, col2(10))
);

在这里插入图片描述

前缀索引的目的主要是减少字符作为索引占用空间,提高查询效率

2.3.2 基于已创建好的表创建索引

在已经存在的表中创建索引,可以使用ALTER TABLE语句或者CREATE INDEX语句创建索引。
使用ALTER TABLE 语句创建索引

ALTER TABLE table_name ADD [UNIQUE|FULLTEXT|SPATIAL] [INDEX|KEY] [index_name] (col_name[length],...) [ASC|DESC]

使用CREATE INDEX 创建索引

CREATE [UNIQUE|FULLTEXT|SPATIAL] INDEX index_name ON  table_name (col_name[length],...) [ASC|DESC]

2.4 删除索引

使用ALTER TABLE 删除索引

ALTER TABLE table_name DROP INDEX index_name
CREATE TABLE test.t1 (
  id int AUTO_INCREMENT primary key,
  col1 VARCHAR(10),
  col2 VARCHAR(20),
  INDEX (col1, col2(10))
);

在这里插入图片描述
alter table test.t1 drop index col1;

alter table test.t1 drop index PRIMARY; #会报错:添加AUTO_INCREMENT约束字段的唯一索引不能被删除
在这里插入图片描述
使用DROP INDEX语句删除索引

DROP INDEX index_name ON table_name;

2.5 聚集索引和二级索引

2.5.1 聚集索引

InnoDB存储引擎表是索引组织表,即表中数据按照主键顺序存放。而聚集索引(clustered index)就是按照每张表的主键构造一棵B+树,同时叶子节点中存放的即为整张表的行记录数据,也将聚集索引的叶子节点称为数据页。聚集索引的这个特性决定了索引组织表中数据也是索引的一部分。同B+树数据结构一样,每个数据页都通过一个双向链表来进行链接。

由于实际的数据页只能按照一棵B+树进行排序,因此每张表只能拥有一个聚集索引。由于定义了数据的逻辑顺序,聚集索引能够特别快地访问针对范围值的查询。查询优化器能够快速发现某一段范围的数据页需要扫描。

聚集索引的存储并不是物理上连续的,而是逻辑上连续的。这其中有两点:一是前面说过的页通过双向链表链接,页按照主键的顺序排序;另一点是每个页中的记录也是通过双向链表进行维护的,物理存储上可以同样不按照主键存储。

2.5.2 二级索引(辅助索引)

对于辅助索引(Secondary Index),叶子节点并不包含行记录的全部数据。叶子节点除了包含键值以外,每个叶子节点中的索引行中还包含了一个书签(bookmark)。该书签用来告诉InnoDB存储引擎哪里可以找到与索引相对应的行数据。由于InnoDB存储引擎表是索引组织表,因此InnoDB存储引擎的辅助索引的书签就是相应行数据的聚集索引键。

当通过辅助索引来寻找数据时,InnoDB存储引擎会遍历辅助索引并通过叶级别的指针获得指向主键索引的主键,然后再通过主键索引来找到一个完整的行记录

2.6 覆盖索引

InnoDB存储引擎支持覆盖索引(covering index,或称索引覆盖),即从辅助索引中就可以得到查询的记录,而不需要查询聚集索引中的记录。使用覆盖索引的一个好处是辅助索引不包含整行记录的所有信息,故其大小要远小于聚集索引,因此可以减少大量的IO操作。

CREATE TABLE `item` (
  `item_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `store_id` bigint(20) unsigned NOT NULL COMMENT '所属店铺ID',
  `type` tinyint(3) unsigned NOT NULL COMMENT '商品类型 . 不同类型的商品, 保存到各自不同的表中. 参考 https://learnwoo.com/woocommerce-different-product-types/',
  `state` tinyint(3) unsigned NOT NULL COMMENT '状态',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `create_user_id` bigint(20) unsigned NOT NULL,
  `last_modify_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `last_modify_user_id` bigint(20) unsigned NOT NULL,
  `is_deleted` bigint(20) unsigned NOT NULL DEFAULT '0',
  PRIMARY KEY (`item_id`),
  KEY `fk_store_id` (`store_id`)
) ENGINE=InnoDB AUTO_INCREMENT=332604631475558863 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='代表所有的物品,之前有把用户ID放进来,表示该物品所属的用户,但是考虑到如果有子账号的情况,物品难道属于这个子账号所属的用户吗?而且记录了创建人用户ID,考虑这两个因素,因此不设置用户ID列'

查看:explain select store_id,create_time from item where store_id > 332604504321036698 ;
在这里插入图片描述
这里使用了全表扫描,没有走索引,然后我们把查询语句改为:explain select store_id from item where store_id > 332604504321036698 ;
在这里插入图片描述
这样就变成了范围查询,走索引,因为索引中包含了需要查询的全部值,所以不需要再查询聚集索引,减少磁盘IO,这样就可以提高速度。

2.7 Multi-Range Read 优化

Multi-Range Read优化的目的就是为了减少磁盘的随机访问,并且将随机访问转化为较为顺序的数据访问,这对于IO-bound类型的SQL查询语句可带来性能极大的提升。Multi-RangeRead优化可适用于range,ref,eq_ref类型的查询。

Multi-Range Read的好处:

  1. MRR使数据访问变得较为顺序。在查询辅助索引时,首先根据得到的查询结果,按照主键进行排序,并按照主键排序的顺序进行书签查找。
  2. 减少缓冲池中页被替换的次数。
  3. 批量处理对键值的查询操作

MRR的工作方式如下:

  1. 将查询得到的辅助索引键值存放于一个缓存中,这时缓存中的数据是根据辅助索引键值排序的。
  2. 将缓存中的键值根据RowID进行排序。
  3. 根据RowID的排序顺序来访问实际的数据文件。

三、索引实现的原理

innodb存储的索引是基于B+树实现的,从1.1节中的表格可以看出,不支持hash的实现方式。首先来了解下B+树的特点;

3.1 B+树的特征

  1. 有k个子树的中间节点包含有k个元素(B树中是k-1个元素),每个元素不保存数据,只用来索引,所有数据都保存在叶子节点。
  2. 所有的叶子结点中包含了全部元素的信息,及指向含这些元素记录的指针,且叶子结点本身依关键字的大小自小而大顺序链接。
  3. 所有的中间节点元素都同时存在于子节点,在子节点元素中是最大(或最小)元素。

3.2 B+树的优势

  1. 单一节点存储更多的元素,使得查询的IO次数更少。
  2. 所有查询都要查找到叶子节点,查询性能稳定。
  3. 所有叶子节点形成有序链表,便于范围查询。

在B+树中,所有记录节点都是按键值的大小顺序存放在同一层的叶子节点上,由各叶子节点指针进行连接。先来看一个B+树,其高度为2,每页可存放4条记录,扇出(fan out)为5,如下图所示:

在这里插入图片描述

3.3 索引的设计思考

  • 索引是一种存储方式,最相关的硬件就是磁盘,索引磁盘的性能会直接影响到数据库的查询效率
  • 磁盘的性能和读写的顺序有关,普通磁盘顺序读写比随机读写快很多,所以尽量避免随机读写。
  • 数据都是以行为单位一行一行的存储的,每一行都包括了所有的列,多行可以连续存储。
  • 每一行数据中,一般都有一个键,其他的列可以称为值,可以理解为键值对。innodb必须有唯一非空的主键,就是默认的键。
  • 在键值对中,键值可以排序,还可以组合键值。

3.4 索引的设计

  • 磁盘空间会划分为许多个大小相等的块或者页,一个页中可以存储多行数据,这样就可以符合磁盘的顺序读写,这样一次IO就可以读取很多数据到内存,可以减少磁盘IO。
  • 在一个页内,所有的数据可能会经常变动,并且大小也是相对固定的,所以内部通过链表或者数组管理。
  • 每个键值可以排序,所以在一个块内的所有数据也可以是有序的,这样通过二分法查找可以很快的在一个页内找到指定键对应的数据
  • 一个页设计好之后,可以把页作为B+树的节点,通过页来承载数据,通过B+数来组织不同页之间的关系
  • B+树的特点是在内节点存储键来提高搜索的性能,所以很自然的,内节点用来存储数据行的键,叶子节点存储所有数据行,可以很好的提升性能

接下来在结合2.5节的聚集索引和二级索引来说:

表中数据按照主键顺序存放。而聚集索引(clustered index)就是按照每张表的主键构造一棵B+树,同时叶子节点中存放的即为整张表的行记录数据,也将聚集索引的叶子节点称为数据页。聚集索引的这个特性决定了索引组织表中数据也是索引的一部分。同B+树数据结构一样,每个数据页都通过一个双向链表来进行链接。如下图所示:
在这里插入图片描述
上图所示的是一个深度为2的B+树,也是我们所称的索引,这里假设页有随机唯一的编号,根页号为20。这里只有一个内节点(根节点),其他的都是叶子节点,也是数据节点,对于内节点来说,存有key和pageno的指针信息,对于叶子节点来说,只存有完整的数据。对于聚集索引,data部分存有除主键外的其他列的组合,如果是二级索引,则这里存放就是这行记录对应主键的组合,用于回表。

最左边的MIN为了很好的组织树形结构的指针,和其他的内节点一样,主要用来标记它是最小记录Min,还有就是一个pageno指针指向下层最左边的Min记录,其他节点的Min记录用于判断搜索是否到了边界。每个页都有页头页尾用来管理和标记页面的状态,页面中的数据是如何存储,有没有空闲的空间,以什么样的顺序存储等。

上图中所有的叶子节点从左到右都是从小到大的顺序以双向链表的方式存储的,所以当我们需要遍历全部的数据,只需要通过B+树找到最小的位置,然后通过遍历链表则可以查询到所有的数据,还有就是10,16,25这三条记录在内节点和叶子节点均存在,这既是B+数的特点,叶子节点会存有所有的key和值。而内节点只存储了key,不存储其他的数据,只有用来索引。叶子节点除了第一条记录会有上一层重复的存储,其他数据不会有这样的现象,所以浪费的空间也不大,由于每一个页的大小是固定的(16k),在内节点上只存储key,不存储其他数据,一个页就可以存储更多的key,这样检索也能减少磁盘的IO,由于页存储Key增多,这样就可以使得B+树的深度减少,这样也可以减少磁盘的IO,提高查询性能。

例如一个三层的B+数,每一个页能存1000个key,所以第二层就有1000*(1+1000)个key,第三层就可以有100010011001=1002001000(十亿级别),一个简单的三层B+数据就可以存十亿级别的数据,很强大。

上面说到的“回表”其实就是在使用二级索引进行搜索时,因为二级索引只保存了部分列的数据,如果需要获取键值不包括的列的数据时,需要通过二级索引的指针(书签:用于指向聚集索引的指针)来找到聚集索引的全部数据,然后返回需要查询的列的值。如果使用二级索引不能找到需要的值(需要回表),称为非覆盖索引,否则为2.6节介绍的覆盖索引。非覆盖索引需要回表,增加IO,所以性能会差一些。所以可以根据业务需求创建组合索引来避免回表。但是也要权衡索引带来的利是否大于弊。所以在统计行总数的时候可以通过二级索引来统计,这样速度会快一些。大概图形如下:
  在这里插入图片描述
这里附带的说一些不能走索引的情况,但是不多说,因为优化这个东西太多,后期准备写一两篇优化的文章,所以这里只是提一下,走索引的强大;虽然可能创建了很多索引,很多情况都不走索引,比如:like ‘%query_name%’ ,where端使用or条件连接,where端使用函数等,在group by和order by使用的时候要注意组合索引的最左前缀原则。

基于索引的优化可以看:mysql优化篇(基于索引)

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

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

相关文章

【python知识】win10下如何用python将网页转成pdf文件

一、说明 本篇记录一个自己享用的简单工具。在大量阅读网上文章中,常常遇到一个专题对应多篇文章,用浏览器的收藏根本不够。能否见到一篇文章具有搜藏价值,就转到线下,以备日后慢慢消化吸收。这里终于找到一个办法,将在…

【IIC子系统之读取温湿度】

IIC子系统之读取温湿度IIC总线协议主机读取一个字节主机发送一个字节设备树编写IIC设备驱动层API编写程序读取温湿度应用层驱动读取温湿度函数解析头文件IIC总线协议 1.I2C总线是PHLIPS公司在八十年代初推出的一种串行的半双工同步总线,主要用于连接整体电路。 1&a…

面试准备知识点与总结——(基础篇)

目录Java基础Java面向对象有哪些特征ArrayList和LinkedList有什么区别高并发的集合有哪些问题迭代器的fail-fast和fail-safeArrayList底层扩容机制HashMap面试合集解答设计模式单例设计模式哪些地方体现了单例模式Java基础 Java面向对象有哪些特征 Java面向对象有三大特征&am…

Win10显示dds及tga缩略图

整理之前做游戏MOD时收集的模型资源,3D游戏模型的贴图文件格式基本都是dds或tga的,毕竟无损压缩、支持嵌入MipMap、带透明通道、可以被GPU硬解balabala...道理我都懂但这俩玩意系统根本直接查看不了,就算装上专门的看图软件或插件,文件夹视图下也没有缩略图预览,只能一个个点开…

SQL查询的优化:如何调整SQL查询的性能

查询优化是在合理利用系统资源和性能指标的基础上,定义最有效、最优化的方式和技术,以提高查询性能的过程。查询调整的目的是找到一种方法来减少查询的响应时间,防止资源的过度消耗,并识别不良的查询性能。 在查询优化的背景下&a…

CF1795D Triangle Coloring (组合数学)

链接 题意: 给定一个 n 个点 n 条边的图 ,n 为 6 的倍数,每条边都有边权。 这个图是由 n/3 个三元环构成的 [1,2,3],[4,5,6],[7,8,9],[10,11,12]...... 一个 n12 的图长成这个(唯一): 现在你需要给图染上红…

厂商推送限制说明及极光的适配方案

背景 自2023年起,各个厂商逐步对营销类消息做数量管控,具体如下: 华为 自2023年1月5日起,华为PUSH通道将根据应用类型对资讯营销类消息的每日推送数量进行上限管理,服务与通讯类消息每日推送数量不受限制。详情请参考推…

使用MMDetection进行目标检测、实例和全景分割

MMDetection 是一个基于 PyTorch 的目标检测开源工具箱,它是 OpenMMLab 项目的一部分。包含以下主要特性: 支持三个任务 目标检测(Object Detection)是指分类并定位图片中物体的任务实例分割(Instance Segmentation&a…

服务间调用方式 OpenFeign 的介绍和使用

文章目录前言1、 RestTemplate1.1、通用方法 exchange1.2、RestTemplate 和 OpenFeign 的区别2、RPC 和 HTTP2.1、RPC 是什么2.2、RPC 调用过程示意图2.3、HTTP 是什么2.4、HTTP 调用过程示意图2.5、对比3 、OpenFeign3.1 、OpenFeign 常用注解3.2 、案例前言 Feign 停更了&am…

空间复杂度与时间复杂度

1、时间复杂度和空间复杂度 (1)时间复杂度、空间复杂度是什么? 算法效率分析分为两种:第一种是时间效率,第二种是空间效率。时间效率被称为时间复杂度,空间效率被称作空间复杂度时间复杂度主要衡量的是一…

Python空间分析| 02 利用Python计算空间局部自相关(LISA)

局部空间自相关 import esda import numpy as np import pandas as pd import libpysal as lps import geopandas as gpd import contextily as ctx import matplotlib.pyplot as plt from geopandas import GeoDataFrame from shapely.geometry import Point from pylab im…

LeetCode 1139. 最大的以 1 为边界的正方形

原题链接 难度:middle\color{orange}{middle}middle 2023/2/17 每日一题 题目描述 给你一个由若干 000 和 111 组成的二维网格 gridgridgrid,请你找出边界全部由 111 组成的最大 正方形 子网格,并返回该子网格中的元素数量。如果不存在&am…

Transformer论文阅读:ViT算法笔记

标题:An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale 会议:ICLR2021 论文地址:https://openreview.net/forum?idYicbFdNTTy 文章目录Abstract1 Introduction2 Related Work3 Method3.1 Vision Transformer3.2…

【已解决】关于 luckysheet 设置纯文本,解决日期格式回显错误的办法

目录 一、现象 二、分析 三、思考过程 五、解决 六、参考链接 一、现象 在excel里面输入内容,如 2023-2-17 12:00 保存后,传回后端的数据被转化成了 数值类型,这显然是一种困扰。 如图所示 二、分析 查阅了文档和一些博客发现 Lucky…

2023美赛ICM F题 详细思路

对于本次美赛F题来说,核心之处就是综合评价模型,但考察我们这个模型的角度较以往常规的制定指标,计算得分给出排名然后分析结果这一套常规流程不同,这次美赛F题出题更贴合实际,整体聚焦在“制定完一个新指标后会带来的…

2019蓝桥杯真题数列求值(填空题) C语言/C++

题目描述 本题为填空题,只需要算出结果后,在代码中使用输出语句将所填结果输出即可。 给定数列 1,1,1,3,5,9,17,⋯,从第 4 项开始,每项都是前 3 项的和。 求第 20190324 项的最后 4 位数字。 运行限制 最大运行时间&#xff1a…

三分钟学会用Vim

Vim知识点 目录Vim知识点一:什么是vim二:vim常用的三种模式三:vim的基本操作一:什么是vim vim最小集 vim是一款多模式的编辑器—各种模式—每种模式的用法有差别—每种模式之间可以互相切换 但是我们最常用的就是3~5个模式 vi…

更换ssl证书

更换ssl证书常用证书查看以及转换网址阿里云判断流量以及配置证书判断接入点阿里云控制台配置证书WAFAzure判断流量以及配置证书:判断接入点Azure配置证书CDNAPP GateWay常用证书查看以及转换网址 https://www.chinassl.net/ssltools/convert-ssl.htmlhttps://myss…

【java 高并发编程之JUC】高阶JUC特性总结

1 线程中断机制 1.1 什么是中断? 首先 一个线程不应该由其他线程来强制中断或停止,而是应该由线程自己自行停止。所以,Thread.stop, Thread.suspend, Thread.resume 都已经被废弃了。 其次 在Java中没有办法立即停止一条线程,然…

TCP简单说明

前言 提示:这里可以添加本文要记录的大概内容: 例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。 提示:以下是本篇文…