MySQL系列:索引(B+Tree树、构建过程、回表、基本操作、执行计划、应用)
介绍
https://dev.mysql.com/doc/refman/5.7/en/optimization-indexes.html
作用
优化查询
算法
索引的算法包括
BTreeHashRTreeFullTextGIS
B+Tree结构
BTree查找算法图
B+Tree查找算法图(在叶子节点上加上双向指针)
B*Treee查找算法图(在枝节点上也使用双向指针)
官方图
分类
构建过程
聚簇索引构建过程
作用:
有了聚簇索引,将来插入的数据行,在同一个区内,都会按照ID值得顺序,有序在磁盘存储数据。
MySQL InnoDB 表 通过聚簇索引组织存储数据表。
前提:
1.建表时指定了主键列,MySQL InnoDB会将主键自动作为聚簇索引列,比如
Id not null primary key2.没有指定主键,自动选择唯一键的列作为聚簇索引列
3.以上都没有,生成隐藏聚簇索引
辅助索引构建过程
作用:
优化非聚簇索引列
回表
介绍
MySQL用来存储数据行的逻辑结构,表的数据行最终存储到了很多个Page上
InnoDB存储引擎,会按照聚簇索引,有序的组织存储表数据到各个区的连续的页中上
这些连续的数据页,成为了聚簇索引的叶子节点,可以认为聚簇所以就是原表数据
所以回表就是回聚簇索引
辅助索引:将辅助索引列值+ID主键值,构建辅助索引BTree结构
用户使用辅助索引作为条件查询时,首先扫描辅助索引的B树
1.如果辅助索引能够完全覆盖到查询结构时,就不需要回表
2.如果不能完全覆盖到,只能通过得到的ID主键值,回到聚簇索引(回表)扫描,最终得到想要的结果
影响
1.IO量级变大2.IOPS会增大3.随机IO会增大
减少回表
1.将查询尽可能用ID主键查询2.设计合理的联合索引3.更精确的查询条件+联合索引4.基于优化器对索引的算法(MRR)
基本操作
建议将索引建立在,经常where ,group by ,order by ,join on ...的条件
查询索引
desc test;
key :
PRI 聚簇索引(主键索引)
MUL 辅助索引
UNI 唯一索引
(root@localhost) [test]> desc test;
+-------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(20) | NO | MUL | NULL | |
| sex | varchar(2) | YES | | NULL | |
| age | int(11) | YES | | NULL | |
| phone | varchar(11) | YES | UNI | NULL | |
+-------+-------------+------+-----+---------+----------------+
5 rows in set (0.00 sec)
查询索引详细信息
show index from test;
(root@localhost) [test]> show index from test;
+-------+------------+-----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+-----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| test | 0 | PRIMARY | 1 | id | A | 0 | NULL | NULL | | BTREE | | |
| test | 0 | ix_unique_phone | 1 | phone | A | 0 | NULL | NULL | YES | BTREE | | |
| test | 1 | ix_name | 1 | name | A | 0 | NULL | NULL | | BTREE | | |
| test | 1 | ix_prifix_phone | 1 | phone | A | 0 | 3 | NULL | YES | BTREE | | |
| test | 1 | ix_name_sex | 1 | name | A | 0 | NULL | NULL | | BTREE | | |
| test | 1 | ix_name_sex | 2 | sex | A | 0 | NULL | NULL | YES | BTREE | | |
+-------+------------+-----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
6 rows in set (0.00 sec)
创建索引
创建表时创建索引
create table test(
id int primary key auto_increment , # 主键索引
name varchar(20) not null,
sex varchar(2) ,
age int ,
phone varchar(11),
unique index ix_unique_phone(phone asc), # 唯一索引
index ix_name(name), # 单列索引
index ix_prefix_phone(phone(3)), # 前缀索引
index ix_name_sex(name,sex) # 联合索引
);
修改表时创建索引
alter table test add primary key(id); # 添加主键索引
alter table test modify id integer auto_increment; # 自增
alter table test add unique ix_unique_phone(phone asc); # 唯一索引 #
alter table test add index ix_name(name); # 单列索引
alter table test add index ix_prifix_phone(phone(3)); # 前缀索引
alter table test add index ix_name_sex(name,sex); # 联合索引
删除索引
alter table test modify id int; # 取消自增
alter table test drop primary key; # 删除主键索引
# 删除其它索引
alter table test drop index ix_unique_phone;
alter table test drop index ix_name;
alter table test drop index ix_prefix_phone;
alter table test drop index ix_name_sex;
执行计划
查看
explain select * from test;
desc select * from test;
(root@localhost) [test]> insert into test(name,sex,age,phone) values('张三','男',18,'13744551221');
Query OK, 1 row affected (0.00 sec)
(root@localhost) [test]> insert into test(name,sex,age,phone) values('李四','男',18,'13744531221');
Query OK, 1 row affected (0.01 sec)
(root@localhost) [test]> insert into test(name,sex,age,phone) values('王五','女',19,'13744531421');
Query OK, 1 row affected (0.01 sec)
(root@localhost) [test]> explain select * from test where id=1;
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| 1 | SIMPLE | test | NULL | const | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | NULL |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
(root@localhost) [test]> desc select * from test where id=1;
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| 1 | SIMPLE | test | NULL | const | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | NULL |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
分析
| 列名 | 说明 |
|---|---|
| id | 标识符,表示执行顺序 |
| select_type | 查询类型 1. SIMPLE:简单select,不使用union和子查询 2. PRIMARY:查询中包含任何复杂的子部分,最外层的select被标记为PRIMARY 3. UNION:union中第二个后面的select语句 4. EPENDENT UNION:一般是子查询中的第二个select语句(取决于外查询,mysql内部也有些优化) 5. UNION RESULT:union的结果 6. SUBQUERY:子查询中的第一个select 7. DEPENDENT SUBQUERY:子查询中第一个select,取决于外查询(在mysql中会有些优化,有些dependent会直接优化成simple) 8. DERIVED:派生表的select(from子句的子查询) |
| table | 显示数据来自于哪个表,有时不是真实的表的名字(虚拟表),虚拟表最后一位是数字,代表id为多少的查询。 |
| partitions | 使用的哪个分区,需要结合表分区才可以看到 |
| type | 类型:全表扫描(all),索引扫描(index,range,ref,eq_ref,const(system)) 1. system:表只有一行记录,这个是const的特例,一般不会出现,可以忽略 2. const:表示通过索引一次就找到了,const用于比较primary key或者unique索引。因为只匹配一行数据,所以很快 3. eq_ref:唯一性索引扫描,表中只有一条记录与之匹配。一般是两表关联,关联条件中的字段是主键或唯一索引 4. ref:非唯一行索引扫描,返回匹配某个单独值的所有行 5. range:检索给定范围的行,一般条件查询中出现了>、<、in、between等查询 6. index:遍历索引树。通常比ALL快,因为索引文件通常比数据文件小。all和index都是读全表,但index是从索引中检索的,而all是从硬盘中检索的 7. all:遍历全表以找到匹配的行 |
| possible_keys | 可能会用到的索引 |
| key | 实际使用的索引 |
| key_len | 表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度。一般来说,索引长度越长表示精度越高,效率偏低;长度越短,效率高,但精度就偏低。并不是真正使用索引的长度,是个预估值。 |
| ref | 表示哪一列被使用了,常数表示这一列等于某个常数 |
| rows | 大致找到所需记录需要读取的行数 |
| filtered | 表示选取的行和读取的行的百分比,100表示选取了100%,80表示读取了80% |
| Extra | 额外的信息 1. Using filesort:使用外部的索引排序,而不是按照表内的索引顺序进行读取。(一般需要优化) 2. Using temporary:使用了临时表保存中间结果。常见于排序order by和分组查询group by(最好优化) 3. Using index:表示select语句中使用了覆盖索引,直接冲索引中取值,而不需要回行(从磁盘中取数据) 4. Using where:使用了where过滤 5. Using index condition:5.6之后新增的,表示查询的列有非索引的列,先判断索引的条件,以减少磁盘的IO 6. Using join buffer:使用了连接缓存 7. impossible where:where子句的值总是false |
应用规范
必须建表一定要有主键,一般是个无关列
选择唯一性索引:唯一性索引的值是唯一的,可以更快速的通过该索引来确定某条记录
限制索引的数目
Percona-toolkit中有个工具:专门分析索引是否有用
尽量少在经常更新值得列建索引
Innodb_index-stats
Innodb_table-stats
Optimize table city;
函数,隐式转换不走索引
<,>,in等也可能不走索引,和结果集有关 超过15%~30%就不走索引
优化器针对索引的算法
思考
更新数据时,会对索引有影响吗?索引是否实时更新?
比如insert,delete,update一行数据
对于聚簇索引会立即更新
对于辅助索引,不是实时更新的
在InnoDB内存结构中,加入了insert buffer(会话),现在版本叫change buffer
change buffer功能是临时缓冲辅助索引需要的数据更新
当我们需要查询新的insert数据,会在内存中进行merge(合并)操作,此时辅助索引就是最新的
B+Tree索引树高度影响因素?
1.索引字段较长: 前缀索引
2.数据行过多:分区表,归档表(pt-archive),分布式架构
3.数据类型:选择合适的数据类型*
为什么不能乱建索引?
如果冗余索引过多,表的数据变化的时候,很有可能会导致索引频繁更新。会阻塞很多业务更新的请求
索引过多,会导致优化器选择出现偏差
MySQL系列:索引(B+Tree树、构建过程、回表、基本操作、执行计划、应用)的更多相关文章
- MySQL 树形索引结构 B树 B+树
MySQL 树形索引结构 B树 B+树 如何评估适合索引的数据结构 索引的本质是一种数据结构 内存只是临时存储,容量有限且容易丢失数据.因此我们需要将数据放在硬盘上. 在硬盘上进行查询时也就产生了 ...
- MySQL数据库索引之B+树
一.B+树是什么 B+ 树是一种树型数据结构,通常用于数据库和操作系统的文件系统中.B+ 树的特点是能够保持数据稳定有序,其插入与修改操作拥有较稳定的对数时间复杂度.B+ 树元素自底向上插入,这与二叉 ...
- 为什么mysql innodb索引是B+树数据结构
1.文件很大,不可能全部存储在内存中,所以要存在磁盘上 2.索引的组织结构要尽量减少查找过程中磁盘I/O的存取次数(为什么用B-/+Tree,还跟磁盘存取原理有关) 3.B+树所有的data域在叶子节 ...
- 为什么MySQL索引使用B+树
为什么MySQL索引使用B+树 聚簇索引与非聚簇索引 不同的存储引擎,数据文件和索引文件位置是不同的,但是都是在磁盘上而不是内存上,根据索引文件.数据文件是否放在一起而有了分类: 聚簇索引:数据文件和 ...
- Mysql高级操作学习笔记:索引结构、树的区别、索引优缺点、创建索引原则(我们对哪种数据创建索引)、索引分类、Sql性能分析、索引使用、索引失效、索引设计原则
Mysql高级操作 索引概述: 索引是高效获取数据的数据结构 索引结构: B+Tree() Hash(不支持范围查询,精准匹配效率极高) 树的区别: 二叉树:可能产生不平衡,顺序数据可能会出现链表结构 ...
- MySQL 系列(三)你不知道的 视图、触发器、存储过程、函数、事务、索引、语句
第一篇:MySQL 系列(一) 生产标准线上环境安装配置案例及棘手问题解决 第二篇:MySQL 系列(二) 你不知道的数据库操作 第三篇:MySQL 系列(三)你不知道的 视图.触发器.存储过程.函数 ...
- mysql之索引组织表
1.二叉树/平衡树.B-tree.B+tree.B*tree 树:最上一层是根节点.最底下一层是叶子节点.(一般左边节点小于右边节点) 二叉树:每个节点最多只能有两个分支,一般只用于教材.二叉树的深度 ...
- DOM 操作成本究竟有多高,HTML、CSS构建过程 ,从什么方向出发避免重绘重排)
前言: 2019年!我准备好了 正文:从我接触前端到现在,一直听到的一句话:操作DOM的成本很高,不要轻易去操作DOM.尤其是React.vue等MV*框架的出现,数据驱动视图的模式越发深入人心,jQ ...
- MySQL for OPS 03:索引和执行计划
写在前面的话 啥是索引?以一本书为例,如果想要找到某一指定章节的某一小节,书薄还好,如果书厚,可能就会找的头皮发麻.于是便出现了目录,让用户更容易查找到自己所需要的东西.索引就类似一张表的目录.其存在 ...
- mysql中的回表查询与索引覆盖
了解一下MySQL中的回表查询与索引覆盖. 回表查询 要说回表查询,先要从InnoDB的索引实现说起.InnoDB有两大类索引,一类是聚集索引(Clustered Index),一类是普通索引(Sec ...
随机推荐
- DICOM PS3.7 2021a - Message Exchange
PS3.7 DICOM PS3.7 2021a - Message Exchange DICOM Standards Committee Copyright 2021 NEMA A DICOM pub ...
- DPDK-22.11.2 [四] Virtio_user as Exception Path
因为dpdk是把网卡操作全部拿到用户层,与原生系统驱动不再兼容,所以被dpdk接管的网卡从系统层面(ip a/ifconfig)无法看到,同样数据也不再经过系统内核. 如果想把数据再发送到系统,就要用 ...
- 【信创】 JED on 鲲鹏(ARM) 调优步骤与成果
项目背景 基于国家对信创项目的大力推进,为了自主可控的技术发展,基础组件将逐步由国产组件替代,因此从数据库入手,将弹性库JED部署在 国产华为鲲鹏机器上(基于ARM架构)进行调优,与Intel (X8 ...
- 基于matomo实现业务数据埋点采集上报
matomo是一款Google-analytics数据埋点采集上报的平替方案,可保护您的数据和客户的隐私:正如它官网的slogan: Google Analytics alternative that ...
- 使用 Kubernetes 简化平台工程
平台工程在现代应用程序开发和部署中发挥的作用至关重要.随着软件应用程序变得越来越复杂和分散,对稳健且可扩展的基础设施的需求变得越来越重要.这就是平台工程的作用所在,它是支持整个软件开发生命周期的支柱. ...
- 如何去掉桌面快捷方式左下角的小箭头(Win11)
在对系统重命名之后,在快捷方式的左下角莫名的出现了小图标 如果想要去掉这个小图标 (1)首先在桌面上创建一个txt文件 (2)打开后输入指令 reg add "HKEY_LOCAL_MACH ...
- JUC并发编程学习笔记(六)Callable(简单)
Callable(简单) callable接口和runnable接口类似,都是为了执行另外一条线程而设计的,区别是Runnable不会返回结果也不会抛出异常. 1.可以有返回值 2.可以抛出异常 3. ...
- 聊聊魔塔社区MGeo模型的部署与运行
从现今与今后的发展来看,单一的业务不再仅仅依靠于传统的技术开发,而是应该结合AI模型来应用.实践.只有这样,才能更数智化,更高效化,更贴合时代的发展. 魔塔 社区就类似国外的Hugging Face, ...
- 2022.7.16 lhm_ 讲课纪要
前言 啊好的,这节课又是对牛弹琴课...... 虽说题给的不难,以黄绿为主,,穿插了一个蓝一个紫,但是给一群不知道什么是树什么是DAG的人讲树形dp和dag上dp有点.... 顺便讲了讲拓扑排序和记忆 ...
- vivado生成Bitstream报错[Vivado 12-1345] Error(s) found during DRC. Bitgen not run(Vivado 2017.4)。
写了一个很简单的程序,2-4译码器. module decoder2to4( input in1, in0, output reg [3:0]out ); always @ (*) begin if ...