

engine 引擎名
support 是否支持
comment 描述
transactions 是否支持事务
XA 是否支持分布式事务处理规范 [就是事务相关的一种规范]
Savepoints 是否支持保存点[用于取消部分事务,结束事务时,会自动删除该事务中所定义的所有保存点]

create table my_myisam{
id int,
username varchar(10),
} engine = MyISAM;
InnoDB 存储引擎:
是一种兼顾高可靠性和高性能的通用存储引擎,在 MySQL 5.5 之后, InnoDB 是默认的 MySQL 存储引擎
(1)表空间文件
早期表结构 xxx.frm,在 MySQL8 中采用 xxx.sdi 的形式
默认是一张表对应一个表空间文件 [Value = ON]
通过 show variables like 'innodb_file_per_table' 指令可以查看配置情况



不支持事务和外键
支持表锁,但不支持行锁
访问速度快
xxx.sdi 存储表结构信息
xxx.MYD 存储数据信息
xxx.MYI 存储索引

Memory 存储引擎
内存存放、hash 索引(默认)
xxx.sdi 存储表结构信息

4.这三种引擎都有什么区别呢?

如何选择存储引擎呢?
根据应用系统的特点选择合适的存储引擎,复杂的应用系统还可以选择多种存储引擎组合使用
InnoDB 应用场景:
应用对事务的完整性有较高的要求,并发条件下要求数据的一致性
数据除了插入和查询外还有很多的更新、删除操作
MyISAM 应用场景:
以插入和查询操作为主,很少删除和更新操作,对事务完成性、并发要求不高
Memory 应用场景:
将数据保存到内存中,访问速度快,用于临时表和缓存功能的实现
对表的大小有限制,而且无法保证数据的安全性

索引是帮助 MySQL 高效获取数据的数据结构(有序)
正常我们要查询一条语句可能要扫描全表,使用索引可能遍历几个数
就可以获得结果,接下来通过案例简单说明:
假设我们要查询表中年龄为 45 的所有用户信息: select * from user where age = 45
在无索引情况下,就需要从第一行开始扫描,一直扫描到最后一行,我们称之为 全表扫描,性能很低
如果我们针对于这张表建立了索引,假设索引结构就是二叉树,那么也就意味着,会对age这个字段建 立一个二叉树的索引结构 【原理介绍示意图,并非索引真实数据结构】

因为这是一颗排序二叉树,所以我们只需要遍历三个数据就可以获得结果,极大的提高了查询效率
1.内容概述
MySQL 的索引是子啊存储引擎层实现的,不同的存储引擎有不同的索引结构,主要包含以下几种:

这几种是 MySQL 数据库所支持的所有的索引结构,下面是不同存储引擎对这几种索引的支持情况

2.二叉树
如果选择二叉树作为索引结构,会存在以下缺点:


但是大数据量情况下,还会产生较深的层级,检索速度还是很慢
3.B树
B-Tree (B树)是一种多叉路平衡查找树,相对于二叉树,B树每个节点可以有多个分支,我们称之为多叉
以一颗最大度数(max-degree) 为5的b树为例,那么这个 B树 每个结点最多可以存储 4个key,5个指针 【度数就是结点个数】

一旦结点存储的 key 数量达到5,就会裂变,中间元素向上分裂
在 B 树中,非叶子结点和叶子结点都会存放数据
4.B+ 树

B+树是B树的变种,我们可以看到
绿色的部分是索引部分,只具有索引作用,不存储任何数据
红色的部分是数据存储部分,在其叶子结点中存储具体的数据
B+树相对于B树有以下几个区别:
所有的数据都会存储到叶子结点上
叶子结点形成一个单向链表
非叶子结点仅仅起到索引数据作用
上面介绍的是标准的 B+ 树结构,MySQL对其进行了优化,增加了指向相邻叶子结点的链表指针,提高了区间访问的性能,有利于排序

5.Hash
哈希索引采用 hash 算法,将键值换成新的 hash 值,映射到对应的槽位上,然后存储在 hash 表中
如果多个键值映射到了同一个槽位,那么就用链表来解决 hash 冲突

hash 索引有以下特点:
为什么 InnoDB 存储引擎选择 B+树索引结构?
索引分类
在 MySQL 数据库中,将索引分为以下几类:主键索引、唯一索引、常规索引、全文索引

而在 InnoDB 存储引擎中,根据索引的存储形式,又分为以下两种:

聚集索引选取规则:
我们来看一下聚集索引和二级索引的具体结构:


第一步,根据 name 字段进行查询,所以先根据 name = ‘Arm’ 到 name 字段的二级索引中进行匹配查找,但是二级索引中只能查找到 Arm 对应的主键值 10
第二步,因为我们要获得的是 Arm 的全部信息,所以根据主键值10,到聚集索引中查找,最终找到10对应的行 row
第三步,返回这一行的数据
此处采用的是回表查询:先到二级索引中查找数据,找到主键值,然后再到聚合索引中根据主键值,获取数据
我们来看两道题思考一下:
以下两条 SQL 语句,哪个执
// 将 id 设为主键,name 设置了二级索引
A. select * from user where id = 10;
B. select * from user where name = 'Arm';
答:A 选项的执行效率更高,因为 A 选项的语句直接走的聚集索引,然而B选项要先走二级索引,然后再走聚集索引,也就是我们常说的回表查询
InnoDB 主键索引的 B+树高度为多高呢?

假设:一行数据大小为 1k, 一页中可以存储 16 行, InnoDB 的指针占用 6 个字节的空间,主键最多占用 8 个字节 (bigint)
(1) 如果高度为 2,假设页中有n个key,那么就有 n + 1 个指针
n * 8 + (n - 1) * 6 = 16 * 1024, 解得 n 约为 1170
1171 * 16 = 18736 // 1171 个指针对应 1171 个子节点(页),1页存储16行
所以 如果树的高度为2,则可以存储 18000 多条记录
(2) 如果高度为 3
1171 * 1171 * 16 = 21939856 // 一层1171个指针,两层两个 1171 相乘,最后乘一个指针对应页的记录条数
所以 如果树的高度为3,则可以存储 2200w 左右的记录
在 MySQL 中与索引相关的基础语法:
单纯看命令有可能看不懂或记不住,所以通过案例来进行演示
1.准备数据
create table tb_user(
id int primary key auto_increment comment ‘主键’,
name varchar(50) not null comment ‘用户名’,
phone varchar(11) not null comment ‘手机号’,
email varchar(100) comment ‘邮箱’,
profession varchar(11) comment ‘专业’,
age tinyint unsigned comment ‘年龄’,
gender char(1) comment ‘性别 , 1: 男, 2: 女’,
status char(1) comment ‘状态’,
createtime datetime comment ‘创建时间’
) comment ‘系统用户表’;
INSERT INTO tb_user (name, phone, email, profession, age, gender, status,
createtime) VALUES (‘吕布’, ‘17799990000’, ‘lvbu666@163.com’, ‘软件工程’, 23, ‘1’,
‘6’, ‘2001-02-02 00:00:00’);
INSERT INTO tb_user (name, phone, email, profession, age, gender, status,
createtime) VALUES (‘曹操’, ‘17799990001’, ‘caocao666@qq.com’, ‘通讯工程’, 33,
‘1’, ‘0’, ‘2001-03-05 00:00:00’);
INSERT INTO tb_user (name, phone, email, profession, age, gender, status,
createtime) VALUES (‘赵云’, ‘17799990002’, ‘17799990@139.com’, ‘英语’, 34, ‘1’,
‘2’, ‘2002-03-02 00:00:00’);
INSERT INTO tb_user (name, phone, email, profession, age, gender, status,
createtime) VALUES (‘孙悟空’, ‘17799990003’, ‘17799990@sina.com’, ‘工程造价’, 54,
‘1’, ‘0’, ‘2001-07-02 00:00:00’);
INSERT INTO tb_user (name, phone, email, profession, age, gender, status,
createtime) VALUES (‘花木兰’, ‘17799990004’, ‘19980729@sina.com’, ‘软件工程’, 23,
‘2’, ‘1’, ‘2001-04-22 00:00:00’);
INSERT INTO tb_user (name, phone, email, profession, age, gender, status,
createtime) VALUES (‘大乔’, ‘17799990005’, ‘daqiao666@sina.com’, ‘舞蹈’, 22, ‘2’,
‘0’, ‘2001-02-07 00:00:00’);
INSERT INTO tb_user (name, phone, email, profession, age, gender, status,
createtime) VALUES (‘露娜’, ‘17799990006’, ‘luna_love@sina.com’, ‘应用数学’, 24,
‘2’, ‘0’, ‘2001-02-08 00:00:00’);
INSERT INTO tb_user (name, phone, email, profession, age, gender, status,
createtime) VALUES (‘程咬金’, ‘17799990007’, ‘chengyaojin@163.com’, ‘化工’, 38,
‘1’, ‘5’, ‘2001-05-23 00:00:00’);
INSERT INTO tb_user (name, phone, email, profession, age, gender, status,
createtime) VALUES (‘项羽’, ‘17799990008’, ‘xiaoyu666@qq.com’, ‘金属材料’, 43,
‘1’, ‘0’, ‘2001-09-18 00:00:00’);
INSERT INTO tb_user (name, phone, email, profession, age, gender, status,
createtime) VALUES (‘白起’, ‘17799990009’, ‘baiqi666@sina.com’, ‘机械工程及其自动
化’, 27, ‘1’, ‘2’, ‘2001-08-16 00:00:00’);
INSERT INTO tb_user (name, phone, email, profession, age, gender, status,
createtime) VALUES (‘韩信’, ‘17799990010’, ‘hanxin520@163.com’, ‘无机非金属材料工
程’, 27, ‘1’, ‘0’, ‘2001-06-12 00:00:00’);
INSERT INTO tb_user (name, phone, email, profession, age, gender, status,
createtime) VALUES (‘荆轲’, ‘17799990011’, ‘jingke123@163.com’, ‘会计’, 29, ‘1’,
‘0’, ‘2001-05-11 00:00:00’);
INSERT INTO tb_user (name, phone, email, profession, age, gender, status,
createtime) VALUES (‘兰陵王’, ‘17799990012’, ‘lanlinwang666@126.com’, ‘工程造价’,
44, ‘1’, ‘1’, ‘2001-04-09 00:00:00’);
INSERT INTO tb_user (name, phone, email, profession, age, gender, status,
createtime) VALUES (‘狂铁’, ‘17799990013’, ‘kuangtie@sina.com’, ‘应用数学’, 43,
‘1’, ‘2’, ‘2001-04-10 00:00:00’);
INSERT INTO tb_user (name, phone, email, profession, age, gender, status,
createtime) VALUES (‘貂蝉’, ‘17799990014’, ‘84958948374@qq.com’, ‘软件工程’, 40,
‘2’, ‘3’, ‘2001-02-12 00:00:00’);
INSERT INTO tb_user (name, phone, email, profession, age, gender, status,
createtime) VALUES (‘妲己’, ‘17799990015’, ‘2783238293@qq.com’, ‘软件工程’, 31,
‘2’, ‘0’, ‘2001-01-30 00:00:00’);
INSERT INTO tb_user (name, phone, email, profession, age, gender, status,
createtime) VALUES (‘芈月’, ‘17799990016’, ‘xiaomin2001@sina.com’, ‘工业经济’, 35,
‘2’, ‘0’, ‘2000-05-03 00:00:00’);
INSERT INTO tb_user (name, phone, email, profession, age, gender, status,
createtime) VALUES (‘嬴政’, ‘17799990017’, ‘8839434342@qq.com’, ‘化工’, 38, ‘1’,
‘1’, ‘2001-08-08 00:00:00’);
INSERT INTO tb_user (name, phone, email, profession, age, gender, status,
createtime) VALUES (‘狄仁杰’, ‘17799990018’, ‘jujiamlm8166@163.com’, ‘国际贸易’,
30, ‘1’, ‘0’, ‘2007-03-12 00:00:00’);
INSERT INTO tb_user (name, phone, email, profession, age, gender, status,
createtime) VALUES (‘安琪拉’, ‘17799990019’, ‘jdodm1h@126.com’, ‘城市规划’, 51,
‘2’, ‘0’, ‘2001-08-15 00:00:00’);
INSERT INTO tb_user (name, phone, email, profession, age, gender, status,
createtime) VALUES (‘典韦’, ‘17799990020’, ‘ycaunanjian@163.com’, ‘城市规划’, 52,
‘1’, ‘2’, ‘2000-04-12 00:00:00’);
INSERT INTO tb_user (name, phone, email, profession, age, gender, status,
createtime) VALUES (‘廉颇’, ‘17799990021’, ‘lianpo321@126.com’, ‘土木工程’, 19,
‘1’, ‘3’, ‘2002-07-18 00:00:00’);
INSERT INTO tb_user (name, phone, email, profession, age, gender, status,
createtime) VALUES (‘后羿’, ‘17799990022’, ‘altycj2000@139.com’, ‘城市园林’, 20,
‘1’, ‘0’, ‘2002-03-10 00:00:00’);
INSERT INTO tb_user (name, phone, email, profession, age, gender, status,
createtime) VALUES (‘姜子牙’, ‘17799990023’, ‘37483844@qq.com’, ‘工程造价’, 29,
‘1’, ‘4’, ‘2003-05-26 00:00:00’);

– A. name字段为姓名字段,该字段的值可能会重复,为该字段创建索引。
create unique index name_index on tb_user (name);
show index from tb_user;
– B. phone手机号字段的值,是非空,且唯一的,为该字段创建唯一索引。
create unique index phone_index on tb_user (phone);
– C. 为profession、age、status创建联合索引。
create index pas on tb_user (profession, age,status);
– D. 为email建立合适的索引来提升查询效率。
create unique index email_index on tb_user (email);
– 查看所有索引
show index from tb_user;

SQL性能分析
1.SQL 执行频率分析
在 MySQL 客户端连接成功后,通过 show [session | global] status 命令可以提供服务器状态信息
mysql复制代码-- 插入次数
show global status like ‘Com_insert’;
– 删除次数
show global status like ‘Com_delete’;
– 查询次数
show global status like ‘Com_select’;
– 更新次数
show global status like ‘Com_update’;
主要的作用就是可以让我们知道当前数据库是以查询为主还是增删改为主,从而为数据库优化提供参考依据,要是以查询为主就要对数据库索引进行优化了
2.慢查询日志
我们可以借助慢查询日志去定义对哪些查询语句进行优化,慢查询日志记录了所有执行时间超过指定参数(long_query_time,单位:秒,默认 10 秒)的所有 SQL语句的日志
mysql复制代码–
我使用的是 Linux 中 Docker 运行的 MySQL8 容器
– 进入容器内部 >> 进入 MySQL >> 查看慢查询相关内容
show variables like ‘slow%’;

– 如果没有开启慢查询,可以通过下面的指令开启
set global slow_query_log = ON;
– 设置慢 sql 阈值time,超过阈值就会被记录为慢 sql
set global slow_launch_time = time;
– 因为我不知道我的mysql容器数据卷,让我挂载到哪里去了
docker inspect mysql8
– 找到 Mounts 部分,上面是Linux中的目录,下面的MySQL容器中的目录
“/mydata/mysql/conf”
“/etc/mysql/conf.d”
“/mydata/mysql/data”
“/var/lib/mysql”
“/mydata/mysql/log”
“/logs”
– 根据上图的 slow_query_log_file 我找到了我慢查询文件位置
/mydata/mysql/data/cea8370ff66d-slow.log
– 为了模拟慢查询,我创建了一个表
create table test(
id int,
name varchar(10)
);
– 网上搜了一段通过存储过程模拟循环插入大量数据
CREATE PROCEDURE insert_data(IN start INT, IN end INT)
BEGIN
DECLARE i INT DEFAULT start;
WHILE i <= end DO
INSERT INTO test
VALUES (i, “test_datas”);
SET i = i + 1;
END WHILE;
END $$
DELIMITER ;
– 执行下面这个方法开始插入数据,本来想插入 100w 条数据,实在太慢了就插入了 15w 条
CALL insert_data(1, 10000000);
– 执行查询,没让我失望还是达到了慢查询的标准
select * from test;

– 然后我们去 Linux 服务器中查看一下慢查询日志
tail -f /mydata/mysql/data/cea8370ff66d-slow.log

通过慢查询日志,就可以定位到执行效率较低的 SQL,从而有针对性的进行优化
3.profile 详情
show profiles 能够帮助我们看到 SQL 语句时间主要消耗在哪块,有助于我们进行 SQL 优化
可以看到当前 MySQK 是否支持 profile 操作 [1 YES(开启) 0 NO(关闭)]
select @@have_profiling;
– @@profiling 用于控制查询分析的详细程度
– 值为0代表不会记录任何信息、值为1代表仅记录主要查询的信息、值为2还会记录所有子查询的信息
set profiling = 1;

接下来通过案例介绍 profiles 的用法
select * from tb_user;
select * from tb_user where id = 1;
select * from tb_user where name = ‘白起’;
select count(*) from tb_user;
– 查看每一条 SQL 的耗时情况
show profiles;
– 查看指定 query_id 的 SQL 语句各个阶段执行耗时情况
show profile for query 94;
– 查看指定 query_id 的 SQL 语句 CPU 的使用情况
show profile cpu for query 94;
4.explain
explain 或者 desc 命令获取 MySQL 如何执行 select 语句的信息,包括在 select 语句执行过程中表如何连接和连接的顺序
直接在 select 语句前使用关键字 explain | desc
explain select 字段列表 from 表名 where 条件列表;


如果使用了联合索引(索引了多个列),要遵守最左前缀法则
接下来通过案例演示与分析:
show index from table tb_user;

有一组聚合索引 (profession、age、status)
– 完整索引执行计划
explain select * from tb_user where profession = ‘软件工程’ and age = 31 and status = ‘0’;
– 舍弃末尾的字段
explain select * from tb_user where profession = ‘软件工程’ and age = 31;
– 只保留最左边的字段
explain select * from tb_user where profession = ‘软件工程’;

以上的这三组测试中,我们发现只要联合索引最左边的字段 profession存在,索引就会生效,只不 过索引的长度不同。 而且由以上三组测试,我们也可以推测出profession字段索引长度为47、age 字段索引长度为2、status字段索引长度为5
– 没有最左边的列
explain select * from tb_user where age = 31 and status = ‘0’;
explain select * from tb_user where status = ‘0’;
explain select * from tb_user where age = 31;
– 中间跳过一列,索引部分生效
explain select * from tb_user where profession = ‘软件工程’ and status = ‘0’;
上面三组测试,因为不满足最左前缀法则,聚合索引最左边的列不存在,所以索引未生效
下面的这组测试,有最左边的那列,但是跳过了一列,所以后面的索引未生效 >> 索引长度就为 47

最左前缀法则指的是在执行 DQL 语句时,where 条件部分联合索引最左边的字段必须存在,与条件的先后顺序无关。
联合索引中,出现范围查询 (>,<),范围查询右侧的列索引失效
– 范围查询使用了 > | <,范围查询右侧的索引失效
explain select * from tb_user where profession = ‘软件工程’ and age > 30 and status = ‘0’;
– 范围查询使用了 >=、<= 时,是不会出现索引失效的
explain select * from tb_user where profession = ‘软件工程’ and age >= 30 and status = ‘0’;

(1)在索引列上进行运算操作,索引将失效
– 当根据 phone 字段进行等值匹配查询时,索引生效
explain select * from tb_user where phone = ‘17799990015’;
– 当根据 phone 字段进行函数运算操作之后,索引失效
explain select * from tb_user where substring(phone, 12, 2) = ‘15’;

(2)字符串类型字段使用时,不加引号,索引将失效
– 使用单引号和不使用单引号的聚合索引
explain select * from tb_user where profession = ‘软件工程’ and age = 31 and status = ‘0’;
explain select * from tb_user where profession = ‘软件工程’ and age = 31 and status = 0;
– 使用单引号和不使用单引号的常规索引
explain select * from tb_user where phone = ‘17799990015’;
explain select * from tb_user where phone = 17799990015;

对于字符串不加单引号,对于查询结果没有什么影响,但是数据库存在隐式类型转换,索引将失效。
(3)如果仅仅是尾部模糊查询,索引不会失效,如果是头部模糊查询匹配,则索引失效
– 模糊匹配,首部模糊失效、尾部模糊索引不失效
explain select * from tb_user where profession like ‘软件%’;
explain select * from tb_user where profession like ‘%工程’;
explain select * from tb_user where profession like ‘%工%’;

在 like 模糊查询中,在关键字后面加 %,索引可以生效。但是在关键字前面加了%,索引将会失效
(4)用 or 分割的条件,如果 or 前面的列有索引,而后面的列中没有索引,那么设计的索引都不会被用到
explain select * from tb_user where id = 10 or age = 23;
– 特意试一下让没有索引的字段放在 or 前面,有索引的字段放在 or 后面
explain select * from tb_user where age = 23 or id = 10;
explain select * from tb_user where phone = ‘17799990017’ or age = 23;

– 我们可以对 age 字段建立索引测试:
create index age_index on tb_user(age);
– 再次测试
explain select * from tb_user where id = 10 or age = 23;
总结,当 or 连接的条件,左右两侧字段都有索引时,索引才会生效

(5)如果 MySQL评估使用索引比全表更慢,则不使用索引
– MySQL 评估使用索引比全表搜索更慢的情况下,那么就不会使用索引
explain select * from tb_user where phone >= ‘17799990005’;
explain select * from tb_user where phone >= ‘17799990015’;

经过测试我们发现,相同的SQL语句,只是传入的字段值不同,最终的执行计划也完全不一样,这是为 什么呢? 就是因为MySQL在查询时,会评估使用索引的效率与走全表扫描的效率,如果走全表扫描更快,则放弃 索引,走全表扫描。 因为索引是用来索引少量数据的,如果通过索引查询返回大批量的数据,则还不 如走全表扫描来的快,此时索引就会失效
– 查看 is null 与 is not null 操作是否走索引
explain select * from tb_user where profession is null;
explain select * from tb_user where profession is not null;

查询时MySQL会评估,走索引快,还是全表扫描快,如果全表 扫描更快,则放弃索引走全表扫描。 is null 、is not null是否走索引,得具体情况具体 分析,并不是固定的
mysql复制代码-- 查询 profession = ‘软件工程’ 的执行计划会用哪个索引
explain select * from tb_user where profession = ‘软件工程’;
– 为 profession 列创建常规索引
create index profession_index on tb_user(profession);
– 再次进行测试
explain select * from tb_user where profession = ‘软件工程’;

测试结果,我们可以看到,possible_keys中 pas,profession_index 这两个索引都可能用到,最终MySQL选择了 pas 索引。这是MySQL自动选择的结果
如果我们想指定使用哪个索引,或者不想用哪个索引就要用到 MySQL 中的 SQL 提示啦
– use index: 建议 MySQL 使用哪一个索引完成此次查询[建议不一定生效,mysql 内部会进行评估]
explain select * from tb_user use index(profession_index) where profession = ‘软件工程’;
– ignore index: 忽略指定的索引
explain select * from tb_user ignore index(profession_index) where profession = ‘软件工程’;
– force index: 强制使用索引
explain select * from tb_user force index(profession_index) where profession = ‘软件工程’

什么是覆盖索引?
– 推荐使用覆盖索引,不用回表
explain select id, profession from tb_user where profession = ‘软件工程’ and age = 31 and status = ‘0’ ;
explain select id,profession,age, status from tb_user where profession = ‘软件工程’ and age = 31 and status = ‘0’ ;
explain select id,profession,age, status, name from tb_user where profession = ‘软件工程’ and age = 31 and status = ‘0’ ;
explain select * from tb_user where profession = ‘软件工程’ and age = 31 and status= ‘0’;

我们可以看到四条 SQL 语句的执行计划前面所有的指标都是一样的,只有 Extra 部分是不同的

为,在tb_user表中有一个联合索引 pas,该索引关联了三个字段 profession、age、status,而这个索引也是一个二级索引,所以叶子节点下面挂的是这一行的主 键id。 所以当我们查询返回的数据在 id、profession、age、status 之中,则直接走二级索引 直接返回数据了。 如果超出这个范围,就需要拿到主键id,再去扫描聚集索引,再获取额外的数据了,这个过程就是回表。 而我们如果一直使用select * 查询返回所有字段值,很容易就会造成回表查询(除非是根据主键查询,此时只会扫描聚集索引)
如果查询中使用了索引,同时 Extra 列为空,可能有以下几种原因:
1.索引的选择性较低:当需要从表中查询的记录数量很多时,索引选择性会降低,此时 MySQL 仍会使用索引,但会放弃使用索引的排序功能,因此 Extra 列会显示为空。
2.查询的结果集太小:如果查询的结果集很小,MySQL 可能会选择全表扫描而不使用索引,因为全表扫描可能比使用索引更快。在这种情况下,Extra 为 NULL 是正常的。
3.没有使用索引的特殊功能:如果查询使用的索引支持某些特殊的功能,如覆盖索引(Covering Index)、索引合并(Index Merge)等,MySQL 仍然会使用索引,但 Extra 列不会显示特殊功能的相关信息,因此会显示为空。
如果对覆盖索引和回表查询不是很理解,可以看看下面几张图




我们思考一个问题一张表, 有四个字段(id, username, password, status), 由于数据量大, 需要对 以下SQL语句进行优化, 该如何进行才是最优方案:
select id,username,password from tb_user where username = ‘zhangsan’;
答:针对 username、password 建立联合索引,create index sp on user(username, password);因为这样做可以避免上述 SQL 在查询的过程中出现回表查询
当字段类型为字符串(varchar, text, longtext等)时,有时候需要索引很长的字符串,索引空间变大意味着查询时浪费大量的磁盘 IO,影响查询效率,所以我们可以将字符串的一部分前缀建立索引,这样可以大大节省索引空间,从而提升索引效率
(1) 语法格式
create index index_name on table_name(column(n))
– 为 email 设置前缀索引
create index email_index on tb_user(email(5));
– 查看当前 tb_user 的所有索引
show index from tb_user;

(2)前缀长度
可以根据索引的选择性来决定,而选择性是指不重复的索引值(基数)和数据表的记录总数的比值, 索引选择性越高则查询效率越高, 唯一索引的选择性是1,这是最好的索引选择性,性能也是最好的 [一列去重后记录数 / 总记录数]
select count(distinct email) / count() from tb_user;
select count(distinct substring(email, 1, 5)) / count() from tb_user;
(3)前缀索引的查询流程

在and连接的两个字段 phone、name上都是有单列索引的,但是 最终mysql只会选择一个索引,也就是说,只能走一个字段的索引,此时是会回表查询的
但是,我们再来创建一个phone和name字段的联合索引,查询时,就走了联合索引,而在联合索引中包含 phone、name的信息,在叶子节点下挂的是对 应的主键id,所以查询是无需回表查询的,这样就可以提升查询效率

最后,给大家分享一个超棒的网络安全学习路线图(文末有高清图和XMIND文件)

还有一些学习中收集的视频、文档资源,有需要的可以自取:
每个成长路线对应板块的配套视频:


当然除了有配套的视频,同时也为大家整理了各种文档和书籍资料&工具,并且已经帮大家分好类了。
