20200817-Mysql 底层数据结构及Explain详解
- Mysql 底层数据结构
- 常见面试题
- 索引
- 数据结构
- 二叉树
- B-tree
- B+tree
- Hash
- 存储引擎
- MyISAM存储引擎索引实现
- 非聚集索引
- InnoDB存储引擎索引实现
- 辅助索引
- 联合索引
- 如何支持千万级数据
- MyISAM存储引擎索引实现
- 数据结构
- 面试题答案
- Explain详解与索引最佳实践
- Explain工具介绍
- Explain分析示例
- Explain列说明
- id
- select_type
- simple
- primary
- subquery
- derived
- union
- table
- partitions
- type
- possible_keys
- key
- key_len
- 计算规则
- ref
- rows
- filtered
- extra
- 覆盖索引
- 索引最佳实践
- trace工具用法
Mysql 底层数据结构
常见面试题
- 为什么建议InnoDB表必须建主键,并且推荐使用整型的自增主键?
- 为什么非主键索引结构叶子节点存储的是主键值?
索引
帮助MySQL高效获取数据的排好序的数据结构
数据结构
数据结构演示:
Data Structure Visualization
二叉树
B-tree
- 叶节点具有相同的深度,叶节点的指针为空
- 所有索引元素不重复
- 节点中的数据索引从左到右递增排列
B+tree
- 非叶子节点不存储data,只存储索引(冗余),可以放更多的索引
- 叶子节点包含所有索引字段
- 叶子节点用指针连接,提高区间访问的性能
Hash
- 对索引的key进行一次hash计算就可以定位出数据存储的位置
- 很多时候Hash索引要比B+ 树索引更高效
- 仅能满足 “=”,“IN”,不支持范围查询
- hash冲突问题
存储引擎
MyISAM存储引擎索引实现
非聚集索引
索引文件和数据文件是分离的
InnoDB存储引擎索引实现
- 聚集索引
- 表数据文件本身就是按B+Tree组织的一个索引结构文件
- 聚集索引-叶节点包含了完整的数据记录
辅助索引
也叫二级索引:叶子节点中存储主键值,每次查找数据时,根据索引找到叶子节点中的主键值。
MYISAM存储引擎中,主键索引和辅助索引是同级别的,没有主次之分
联合索引
如何支持千万级数据
折叠源码
1 2 |
#查看mysql文件页大小(16K) SHOW GLOBAL STATUS like 'Innodb_page_size'; |
为什么mysql页文件默认16K?
假设我们一行数据大小为1K,那么一页就能存16条数据,也就是一个叶子节点能存16条数据;再看非叶子节点,假设主键ID为bigint类型,那么长度为8B,指针大小在Innodb源码中为6B,一共就是14B,那么一页里就可以存储16K/14=1170个(主键+指针)。
那么一颗高度为2的B+树能存储的数据为:1170*16=18720条,一颗高度为3的B+树能存储的数据为:1170*1170*16=21902400(千万级条)。
面试题答案
为什么建议InnoDB表必须建主键,并且推荐使用整型的自增主键?
排好序
不用平衡树结构
为什么非主键索引结构叶子节点存储的是主键值?
节省空间
数据一致性
Explain详解与索引最佳实践
Explain工具介绍
模拟优化器执行SQL语句,分析你的查询语句或是结构的性能瓶颈。
在 select 语句之前增加 explain 关键字,MySQL 会在查询上设置一个标记,执行查询会返回执行计划的信息,而不是执行这条SQL。
注意:如果 from 中包含子查询,仍会执行该子查询,将结果放入临时表中
Explain分析示例
折叠源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
DROP TABLE IF EXISTS `actor`; CREATE TABLE `actor` ( `id` int(11) NOT NULL, `name` varchar(45) DEFAULT NULL, `update_time` datetime DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; INSERT INTO `actor` (`id`, `name`, `update_time`) VALUES (1,'a','2017-12-22 15:27:18'), (2,'b','2017-12-22 15:27:18'), (3,'c','2017-12-22 15:27:18'); DROP TABLE IF EXISTS `film`; CREATE TABLE `film` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(10) DEFAULT NULL, PRIMARY KEY (`id`), KEY `idx_name` (`name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; INSERT INTO `film` (`id`, `name`) VALUES (3,'film0'),(1,'film1'),(2,'film2'); DROP TABLE IF EXISTS `film_actor`; CREATE TABLE `film_actor` ( `id` int(11) NOT NULL, `film_id` int(11) NOT NULL, `actor_id` int(11) NOT NULL, `remark` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`), KEY `idx_film_actor_id` (`film_id`,`actor_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; INSERT INTO `film_actor` (`id`, `film_id`, `actor_id`) VALUES (1,1,1),(2,1,2),(3,2,1); |
Explain列说明
EXPLAIN SELECT * FROM film;
id
- id列的编号是 select 的序列号
- 有几个 select 就有几个id
- id的顺序是按 select 出现的顺序增长的。
- id列越大执行优先级越高,id相同则从上往下执行,id为NULL最后执行
select_type
simple
简单查询。查询不包含子查询和union
primary
复杂查询中最外层的 select
subquery
包含在 select 中的子查询
derived
包含在 from 子句中的子查询。
union
在 union 中的第二个和随后的 select
折叠源码
1 2 3 4 5 6 7 8 9 |
#simple explain select * from film where id = 2; #primary、subquery、derived set session optimizer_switch='derived_merge=off'; #关闭mysql5.7新特性对衍生表的合并优化 explain select (select 1 from actor where id = 1) from (select * from film where id = 1) der; set session optimizer_switch='derived_merge=on'; #还原默认配置 explain select 1 union all select 1; |
table
访问的表。
- 当 from 子句中有子查询时,table列是 格式,表示当前查询依赖 id=N 的查询,于是先执行 id=N 的查询。
- 当有 union 时,UNION RESULT 的 table 列的值为<union1,2>,1和2表示参与 union 的 select 行id。
partitions
如果查询是基于分区表的话,会显示查询将访问的分区。
type
访问类型。依次从最优到最差分别为:system > const > eq_ref > ref > range > index > ALL
一般来说,得保证查询达到range级别,最好达到ref。
NULL:mysql能够在优化阶段分解查询语句,在执行阶段用不着再访问表或索引。
折叠源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
# null explain select min(id) from film; # system const explain extended select * from (select * from film where id = 1) tmp; show warnings; # eq_ref explain select * from film_actor left join film on film_actor.film_id = film.id; # ref explain select * from film where name = 'film1'; explain select film_id from film left join film_actor on film.id = film_actor.film_id; # range explain select * from actor where id > 1; # index explain select * from film; # all explain select * from actor; |
访问类型 |
说明 |
null |
mysql能够在优化阶段分解查询语句,在执行阶段用不着再访问表或索引 |
eq_ref |
primary key 或 unique key 索引的所有部分被连接使用 ,最多只会返回一条符合条件的记录 |
system |
system是const的特例,表里只有一条元组匹配时为system |
ref |
不使用唯一索引,而是使用普通索引或者唯一性索引的部分前缀,索引要和某个值相比较,可能会找到多个符合条件的行。 |
all |
全表扫描 |
const |
对查询的某部分进行优化并将其转化成一个常量,用于 primary key 或 unique key 的所有列与常数比较时,所以表最多有一个匹配行,读取1次,速度比较快。 |
index |
扫描全表索引 |
range |
范围扫描通常出现在 in(), between ,> ,<, >= 等操作中。使用一个索引来检索给定范围的行。 |
possible_keys
可能使用索引。
- possible_keys 有值,而 key 显示 NULL 的情况,这种情况是因为表中数据不多,mysql认为索引对此查询帮助不大,选择了全表查询。
- 如果该列是NULL,则没有相关的索引。
key
实际采用索引。如果想强制mysql使用或忽视possible_keys列中的索引,在查询中使用 force index、ignore index。
key_len
索引里使用的字节数。
折叠源码
1 |
explain select * from film_actor where film_id = 2; |
计算规则
类型 |
说明 |
char(n) |
n字节长度 |
varchar(n) |
utf8mb4=4,utf8=3,gbk=2,latin1=1。key_len=(表字符集长度) * 列长度 + 1(null) + 2(变长列)例:utf-8、不为null,则长度 3n + 2 |
tinyint |
1 |
smallint |
2 |
int |
4 |
bigint |
8 |
date |
3 |
timestamp |
4 |
datetime |
8 |
索引最大长度是768字节
ref
key列记录的索引中,表查找值所用到的列或常量,常见的有:const(常量),字段名。
rows
估计要读取并检测的行数.
filtered
半分比的值,rows * filtered/100 可以估算出将要和 explain 中前一个表进行连接的行数
extra
折叠源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# Using index explain select film_id from film_actor where film_id = 1; # Using where explain select * from actor where name = 'a'; # Using index condition explain select * from film_actor where film_id > 1; # Using temporary explain select distinct name from actor; explain select distinct name from film; # film.name建立了idx_name索引,此时查询时extra是using index,没有用临时表 # Using filesort explain select * from actor order by name; explain select * from film order by name; # film.name建立了idx_name索引 # Select tables optimized away explain select min(id) from film; |
值 |
说明 |
Using index |
使用覆盖索引 |
Using where |
使用 where 语句来处理结果,并且查询的列未被索引覆盖 |
Using index condition |
询的列不完全被索引覆盖,where条件中是一个前导列的范围 |
Using temporary |
需要创建一张临时表来处理查询。出现这种情况一般是要进行优化的,首先是想到用索引来优化 |
Using filesort |
将用外部排序而不是索引排序,数据较小时从内存排序,否则需要在磁盘完成排序。这种情况下一般也是要考虑使用索引来优化的。 |
Select tables optimized away |
使用某些聚合函数(比如 max、min)来访问存在索引的某个字段 |
覆盖索引
select后面查询的字段都可以从这个索引的树中获取,一般针对的是辅助索引,整个查询结果只通过辅助索引就能拿到结果,不需要通过辅助索引树找到主键,再通过主键去主键索引树里获取其它字段值。
索引最佳实践
工作中遇到的99%SQL优化,这里都能给你解决方案 - 云+社区 - 腾讯云
折叠源码
1 2 3 4 5 6 7 8 9 10 11 12 13 |
CREATE TABLE `employees` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(24) NOT NULL DEFAULT '' COMMENT '姓名', `age` int(11) NOT NULL DEFAULT '0' COMMENT '年龄', `position` varchar(20) NOT NULL DEFAULT '' COMMENT '职位', `hire_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '入职时间', PRIMARY KEY (`id`), KEY `idx_name_age_position` (`name`,`age`,`position`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8 COMMENT='员工记录表'; INSERT INTO employees(name,age,position,hire_time) VALUES('LiLei',22,'manager',NOW()); INSERT INTO employees(name,age,position,hire_time) VALUES('HanMeimei', 23,'dev',NOW()); INSERT INTO employees(name,age,position,hire_time) VALUES('Lucy',23,'dev',NOW()); |
- 全值匹配
- 最左前缀法则
- 不在索引列上做任何操作(计算、函数、(自动or手动)类型转换)
- 存储引擎不能使用索引中范围条件右边的列
- 尽量使用覆盖索引(只访问索引的查询(索引列包含查询列)),减少select *语句
- 使用不等于(!=或者<>)的时候无法使用索引会导致全表扫描
- is null,is not null 一般情况下也无法使用索引
- like以通配符开头('$abc...')mysql索引失效会变成全表扫描操作
- 字符串不加单引号索引失效
- 少用or或in
- 用它查询时,mysql不一定使用索引,mysql内部优化器会根据检索比例、表大小等多个因素整体评估是否使用索引,详见范围查询优化
- 范围查询优化
没走索引原因:mysql内部优化器会根据检索比例、表大小等多个因素整体评估是否使用索引。比如这个例子,可能是由于单次数据量查询过大导致优化器最终选择不走索引
优化方法:可以将大的范围拆分成多个小范围
折叠源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
# 全值匹配 EXPLAIN SELECT * FROM employees WHERE name= 'LiLei'; EXPLAIN SELECT * FROM employees WHERE name= 'LiLei' AND age = 22; EXPLAIN SELECT * FROM employees WHERE name= 'LiLei' AND age = 22 AND position ='manager'; # 最左匹配 EXPLAIN SELECT * FROM employees WHERE age = 22 AND position ='manager'; EXPLAIN SELECT * FROM employees WHERE position = 'manager'; EXPLAIN SELECT * FROM employees WHERE name = 'LiLei'; # 索引列上不做操作 EXPLAIN SELECT * FROM employees WHERE name = 'LiLei'; EXPLAIN SELECT * FROM employees WHERE left(name,3) = 'LiLei'; # 存储引擎不能使用索引中范围条件右边的列 EXPLAIN SELECT * FROM employees WHERE name= 'LiLei' AND age = 22 AND position ='manager'; EXPLAIN SELECT * FROM employees WHERE name= 'LiLei' AND age > 22 AND position ='manager'; # 使用覆盖索引 EXPLAIN SELECT name,age FROM employees ; EXPLAIN SELECT * FROM employees ; # 使用不等于(!=或者<>)的时候无法使用索引 EXPLAIN SELECT * FROM employees WHERE name != 'LiLei'; # is null,is not null 一般情况下也无法使用索引 EXPLAIN SELECT * FROM employees WHERE name is null; # like以通配符开头('$abc...')mysql索引失效 EXPLAIN SELECT * FROM employees WHERE name like '%Lei'; EXPLAIN SELECT name,age,position FROM employees WHERE name like '%Lei%'; # 使用覆盖索引 EXPLAIN SELECT * FROM employees WHERE name like 'Lei%'; # 字符串不加单引号 EXPLAIN SELECT * FROM employees WHERE name = '1000'; EXPLAIN SELECT * FROM employees WHERE name = 1000; # 少用 in or EXPLAIN SELECT * FROM employees WHERE name = 'LiLei' or name = 'HanMeimei'; # 范围查询优化 ALTER TABLE `employees` ADD INDEX `idx_age` (`age`) USING BTREE ; explain select * from employees where age >=1 and age <=2000; explain select * from employees where age >=1 and age <=1000; explain select * from employees where age >=1001 and age <=2000; |
trace工具用法
trace工具用法.md
20200817-Mysql 底层数据结构及Explain详解相关推荐
- 【MySQL附录】A4:MySQL中执行计划explain详解
MySql使用explain关键字可以模拟优化器执行sql语句,我们就能够知道MySql会如何处理咱们的sql, 可以根据explain的分析结果和MySql底层数据结构优化sql.文章内容基于MyS ...
- Mysql 性能优化神器Explain详解
文章目录 Explain的作用 博文背景 Explain 使用方法 Explain之ID说明 1. ID值相同 2.ID值不同 3.ID值相同,ID值不同共存 Explain的select_type详 ...
- Redis底层数据结构(图文详解)
目录 前言 Redis为什么要使用2个对象?两个对象的好处 redisObject对象解析 String 类型 1.int 整数值实现 2.embstr 3.raw List 类型 1.压缩链表:zi ...
- Mysql之查询优化器 EXPLAIN 详解
了解查询优化器 查询瓶颈 CPU饱和(读取数据至内存 or 将数据持久化至硬盘) IO饱和(读取数据远大于内存容量) 硬件制约(top,free,iostat,vmstat等系统性能状态) 执行计划( ...
- mysql中%3cwhere%3e_MySQL EXPLAIN详解
MySQL EXPLAIN命令是查询性能优化不可缺少的一部分,该文主要讲解explain命令的使用及相关参数说明. EXPLAIN Output Columns 列名说明id执行编号,标识select ...
- Mysql性能分析关键字Explain详解(附例子 )
文章目录 Explain 1. 定义(是什么) 2. 作用(能干嘛) 3. 使用(怎么玩) 4. 各字段解释 Explain 1. 定义(是什么) 使用EXPLAIN关键字可以模拟优化器执行SQL查询 ...
- Mysql Explain 详解
Mysql Explain 详解 一.语法 explain < table_name > 例如: explain select * from t3 where id=3952602; 二. ...
- JAVA中Explain注解用法,mysql之explain详解(分析索引最佳使用)
mysql之explain详解(分析索引最佳使用) mysql explain用于分析sql 语句的执行及数据库索引的使用.本文将致力于帮助大家充分理解explain所返回的各项参数,从而使大家快速掌 ...
- MySQL高级之explain详解
MySQL高级之explain详解 文章目录 MySQL高级之explain详解 一.expalin命令详解 1.使用方式 2.结果显示 3.主要的字段信息 4.作用 二.id字段 三.select_ ...
最新文章
- 2019编程语言最新排行榜!Python蝉联第一
- oracle 多条执行语句同时执行
- jQuery实现enter键登录
- android contentDescription的使用
- Java并发编程高级篇(十):分离任务的执行和结果的处理
- sql 动态写入数据库字段_批处理写入,动态SQL和参数化SQL,数据库的性能如何?...
- 选择适合自己的 OLAP 引擎,干货
- Windows 动态链接库
- Total Commander 使用技巧
- Rust或C#,Python 等如何封装C++的接口 (比如CTP)?
- enum java 判断相等_聊一聊Java的枚举enum
- python 清空文件_python:文件的读取、创建、追加、删除、清空
- Drool规则引擎介绍
- 华为荣耀盒子显示服务器忙,华为荣耀盒子m321连接后死机了怎么办?教你三大解决方法...
- ZoomIt使用教程
- Java课程设计之简易计算器:设计的计算器应用程序是基于AWT的,可以完成加法、减法、乘法、除法和取余运算。
- 微信小程序服务器向客户端发送通知消息,微信小程序消息推送
- 一个计算机专业女孩的求学之路——七年之痒,痒之感悟
- 西部数据硬盘 篇一:绿盘、红盘、蓝盘、紫盘、黑盘和金盘的区别
- 离散型最值的期望计算
热门文章
- QT的QPicture类的使用
- c++预处理命令#pragma 用法
- 和qc哪个发展更好_城西公司举办2020年度QC成果推广交流发布会
- 1、代码中设置编码、编辑器中设置Python的编码
- cocos2dx中关于Action动作的相关API的详细介绍
- 3.数据库操作相关术语,Oracle认证,insert into,批量插入,update tablename set,delete和truncate的区别,sql文件导入
- 斯坦福python中文分词stanza
- oracle的parameters怎么用,oracle普通用户使用show parameter方法
- 疯狂android源码中文乱码无gbk,我的Android进阶之旅------Android使用cmd窗口进行adb logcat时出现中文乱码问题的解决办法...
- caffe之特征图可视化及特征提取