• Mysql 底层数据结构

    • 常见面试题
    • 索引
      • 数据结构

        • 二叉树
        • B-tree
        • B+tree
        • Hash
      • 存储引擎
        • MyISAM存储引擎索引实现

          • 非聚集索引
        • InnoDB存储引擎索引实现
          • 辅助索引
          • 联合索引
          • 如何支持千万级数据
    • 面试题答案
  • 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,

`namevarchar(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,

`namevarchar(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,

`namevarchar(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());

  1. 全值匹配
  2. 最左前缀法则
  3. 不在索引列上做任何操作(计算、函数、(自动or手动)类型转换)
  4. 存储引擎不能使用索引中范围条件右边的列
  5. 尽量使用覆盖索引(只访问索引的查询(索引列包含查询列)),减少select *语句
  6. 使用不等于(!=或者<>)的时候无法使用索引会导致全表扫描
  7. is null,is not null 一般情况下也无法使用索引
  8. like以通配符开头('$abc...')mysql索引失效会变成全表扫描操作
  9. 字符串不加单引号索引失效
  10. 少用or或in
  11. 用它查询时,mysql不一定使用索引,mysql内部优化器会根据检索比例、表大小等多个因素整体评估是否使用索引,详见范围查询优化
  12. 范围查询优化

没走索引原因: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详解相关推荐

  1. 【MySQL附录】A4:MySQL中执行计划explain详解

    MySql使用explain关键字可以模拟优化器执行sql语句,我们就能够知道MySql会如何处理咱们的sql, 可以根据explain的分析结果和MySql底层数据结构优化sql.文章内容基于MyS ...

  2. Mysql 性能优化神器Explain详解

    文章目录 Explain的作用 博文背景 Explain 使用方法 Explain之ID说明 1. ID值相同 2.ID值不同 3.ID值相同,ID值不同共存 Explain的select_type详 ...

  3. Redis底层数据结构(图文详解)

    目录 前言 Redis为什么要使用2个对象?两个对象的好处 redisObject对象解析 String 类型 1.int 整数值实现 2.embstr 3.raw List 类型 1.压缩链表:zi ...

  4. Mysql之查询优化器 EXPLAIN 详解

    了解查询优化器 查询瓶颈 CPU饱和(读取数据至内存 or 将数据持久化至硬盘) IO饱和(读取数据远大于内存容量) 硬件制约(top,free,iostat,vmstat等系统性能状态) 执行计划( ...

  5. mysql中%3cwhere%3e_MySQL EXPLAIN详解

    MySQL EXPLAIN命令是查询性能优化不可缺少的一部分,该文主要讲解explain命令的使用及相关参数说明. EXPLAIN Output Columns 列名说明id执行编号,标识select ...

  6. Mysql性能分析关键字Explain详解(附例子 )

    文章目录 Explain 1. 定义(是什么) 2. 作用(能干嘛) 3. 使用(怎么玩) 4. 各字段解释 Explain 1. 定义(是什么) 使用EXPLAIN关键字可以模拟优化器执行SQL查询 ...

  7. Mysql Explain 详解

    Mysql Explain 详解 一.语法 explain < table_name > 例如: explain select * from t3 where id=3952602; 二. ...

  8. JAVA中Explain注解用法,mysql之explain详解(分析索引最佳使用)

    mysql之explain详解(分析索引最佳使用) mysql explain用于分析sql 语句的执行及数据库索引的使用.本文将致力于帮助大家充分理解explain所返回的各项参数,从而使大家快速掌 ...

  9. MySQL高级之explain详解

    MySQL高级之explain详解 文章目录 MySQL高级之explain详解 一.expalin命令详解 1.使用方式 2.结果显示 3.主要的字段信息 4.作用 二.id字段 三.select_ ...

最新文章

  1. 2019编程语言最新排行榜!Python蝉联第一
  2. oracle 多条执行语句同时执行
  3. jQuery实现enter键登录
  4. android contentDescription的使用
  5. Java并发编程高级篇(十):分离任务的执行和结果的处理
  6. sql 动态写入数据库字段_批处理写入,动态SQL和参数化SQL,数据库的性能如何?...
  7. 选择适合自己的 OLAP 引擎,干货
  8. Windows 动态链接库
  9. Total Commander 使用技巧
  10. Rust或C#,Python 等如何封装C++的接口 (比如CTP)?
  11. enum java 判断相等_聊一聊Java的枚举enum
  12. python 清空文件_python:文件的读取、创建、追加、删除、清空
  13. Drool规则引擎介绍
  14. 华为荣耀盒子显示服务器忙,华为荣耀盒子m321连接后死机了怎么办?教你三大解决方法...
  15. ZoomIt使用教程
  16. Java课程设计之简易计算器:设计的计算器应用程序是基于AWT的,可以完成加法、减法、乘法、除法和取余运算。
  17. 微信小程序服务器向客户端发送通知消息,微信小程序消息推送
  18. 一个计算机专业女孩的求学之路——七年之痒,痒之感悟
  19. 西部数据硬盘 篇一:绿盘、红盘、蓝盘、紫盘、黑盘和金盘的区别
  20. 离散型最值的期望计算

热门文章

  1. QT的QPicture类的使用
  2. c++预处理命令#pragma 用法
  3. 和qc哪个发展更好_城西公司举办2020年度QC成果推广交流发布会
  4. 1、代码中设置编码、编辑器中设置Python的编码
  5. cocos2dx中关于Action动作的相关API的详细介绍
  6. 3.数据库操作相关术语,Oracle认证,insert into,批量插入,update tablename set,delete和truncate的区别,sql文件导入
  7. 斯坦福python中文分词stanza
  8. oracle的parameters怎么用,oracle普通用户使用show parameter方法
  9. 疯狂android源码中文乱码无gbk,我的Android进阶之旅------Android使用cmd窗口进行adb logcat时出现中文乱码问题的解决办法...
  10. caffe之特征图可视化及特征提取