索引分类
分类 含义 特点 关键字
主键索引 针对表中主键创建的索引 默认自动创建,只能有一个 PRIMARY
唯一索引 避免同一个表中某数据列中的值重复 可以有多个 UNIQUE
常规索引 快速定位特定数据 可以有多个
全文索引 全文索引查找的是文本中的关键词 可以有多个 FULLTEXT
在InnoDB存储引擎中,根据索引的存储形式,又可分为以下两种:
分类 含义 特点
聚焦索引 将数据存储与索引放到了一块,索引结构地 叶子节点保存了行数据 有且仅能有一个
二级索引 将数据与索引分开存储,索引结构的叶子节点关联的是对应的主键 可以存在多个
聚焦索引选取规则:
如果存在主键,主键索引就是聚焦索引
如果不存在主键,将使用第一个唯一索引作为聚焦索引
如果表没有主键,或没有合适的唯一索引,则InnoDB会自动生成一个rowid作为隐藏的聚焦索引
select * form user where name=“Arm”;
利用二级索引找到对应聚焦索引(回表查询)然后找到对应数据
思考:
1、以下SQL语句,哪个执行效率高?为什么?
select * from user where id =10;
select * from user where name =‘arm’;
备注:id为主键,name字段创建的有索引
答:第一个语句执行效率高,因为执行第二个语句name字段是二级索引,还需要回表检索到聚焦索引,才能找到对应的数据。
2、InnoDB主键索引的B+Tree高度有多高?
假设:
一行数据的大小为1KB,一页(16K)中可以存储16行这样的数据。InnoDB的指针占用6个字节的空间,主键即使是bigint,占用的字节数为8。
树的高度为2可以存储的:
n*8+(n+1)*6=1024*16
主键:n = 1170
指针:n+1=1171
存储量(KB)1171*16=18736KB
树的高度为3可以存储:
1171*1171*16=21939856KB=21426MB=21GB
索引语法
创建索引
语法:
CREATE [UNIQUE|FULLTEXT]INDEX 索引名 ON 表名(被索引列名...);
案例
1、name字段为姓名字段,该字段的值可能会重复,为该字段创建索引
CREATE INDEX ON user1(name1);
2、phone手机号字段的值,是非空,且唯一,为该字段创建唯一索引
CREATE UNIQUE INDEX index_to_phone ON user1(phone);
3、为profession、age、status创建联合索引
CREATE INDEX idx_user_pro_age_sta ON user1(profession,age,status);
4、为Email建立合适的索引来提升查询的效率
CREATE INDEX index_user_email ON user1(email);
查看索引
查看当前表所具有的索引:
show index from 表名;
删除索引
删除索引:
DROP INDEX 索引名 ON 表名;
SQL性能分析
SQL执行频率
MYSQL客户端连接成功后,通过show[session|global]status命令可以提供服务器状态信息。
通过如下指令可以查看当前数据库的insert,update,delete,select的访问频次:
SHOW GLOBAL STATUS LIKE 'Com_______';
慢查询日志
慢查询日志记录所有执行时间超过指定参数(long_query_time,单位:秒,默认10秒)的所有SQL语句的日志。MYSQL的慢查询日志默认并没有开启,需要在MYSQL的配置文件:
C:\ProgramData\MySQL\MySQL Server 8.0\my.ini
找到指定文件内,修改图下信息:
配置完成之后,通过以下指令重启MYSQL服务器进行测试,查看慢日志文件中记录的信息:C:\ProgramData\MySQL\MySQL Server 8.0\Data\**-slow.log
profiles详情
show profiles能够在做SQL优化时帮助我们了解时间都耗费到哪里去了。通过have_profiling参数,能够看到是否支持profiles,yes是支持。
select @@have_profiling;
默认profiling默认是关闭的,通过set语句在session/global级别开启profiling
查询是否开启:select
0关闭1开启
设置开启
set @@profiling=1;
然后执行一系列sql语句之后
通过
show profiles;
查看各个sql语句的耗时。
查询SQL语句在各个阶段执行耗时情况
show profiles for query query_id;
查询指定query_id的SQL语句CPU的使用情况
show profiles cpu for query query_id;
explain执行计划
explain执行计划各字段的含义:
explain sql语句
索引的使用
验证索引效率
在未创建索引之前,执行SQL语句查看SQL的耗时
select * from tb_sku where sn ='10000002258';
耗时20.03s
针对字段创建索引
create index idx_sku_sn on tb_sku(sn);
然后再次执行相同的SQL语句,再次查看SQL的耗时情况。
select * from tb_sku where sn ='10000002258';
耗时0.00s
使用原则
索引失效
最左前缀法则
如果索引了多列(联合索引),要遵守最左前缀法则指的是查询从索引的最左列开始,并且不跳过索引中的列。如果跳过某一列,索引将部分失效(后面的字段索引失效)。
create full_text index idx_pro_age_sta on 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="软件工程";
# 走索引 status失效
explain select * from tb_user where profession="软件工程" and status="0";
# 不走索引
explain select * from tb_user where age=31 and status="0";
# 不走索引
explain select * from tb_user where status="0";
范围查询
联合索引中,出现范围查询(>,<),范围查询右侧的列索引失效(规避办法加上>=)
# age后索引失效
explain select * from tb_user where profession="软甲工程" and age>60 and status='0';
# 规避办法
explain select * from tb_user where profession="软甲工程" and age>=60 and status='0';
索引列运算操作
不要在索引列进行运算操作,索引将失效
explain select * from tb_user where substring(phone,10,2)='15';
字符串类型不加引号
字符串类型的字段使用时,不加引号,索引将失效
explain select * from tb_user where profession="软甲工程" and age>=60 and status='0';
# status索引失效
explain select * from tb_user where profession="软甲工程" and age>=60 and status=0;
模糊查询
如果仅仅是尾部模糊匹配,索引不会失效,如果是头部模糊匹配,索引失效。
# 走索引
explain select * from tb_user where profession="软甲工程";
# 走索引
explain select * from tb_user where profession like "软甲%" ;
# 不走索引
explain select * from tb_user where profession like "%工程";
or连接的条件
用or分隔开的条件,如果or前的条件中的列有索引,而后面的列中没有索引,那么涉及的索引豆瓣不会被用到。
解决办法:针对or条件后的字段建立索引
数据分布影响
如果MYSQL评估使用索引比全表更慢,则不使用索引
# 走全表扫描
select * from tb_user where phone>='190000005'
# 走索引
select * from tb_user where phone>='190000015'
SQL提示
SQL提示,是优化数据库的一个重要手段,简单来说,就是在SQL语句中加入一些人为的提示来达到优化操作的目的。
use index(给个建议)
explain select * from tb_user use index(idx_user_pro) where profession="软件工程";
ignore index(忽略)
explain select * from tb_user ignore index(idx_user_pro) where profession="软件工程";
force index(强制)
explain select * from tb_user force index(idx_user_pro) where profession="软件工程";
覆盖索引
尽量使用覆盖索引(查询使用了索引,并且要返回的列能在该索引中全部找到),减少select*(要回表查询,使性能下降)
注意:
using index condition:查找使用了索引,但需要回表查询数据
usingwhere;using index:查找使用了索引,但需要的数据在索引列能找到,不需要回表查询
思考题:
建立一个包含 username和password的联合索引
前缀索引
单列索引和联合索引
单列索引:一个索引包含一个列
联合索引:一个索引包含多个列
在业务场景中,如果存在多个查询条件,考虑针对查询字段建立索引时,建立联合索引,而非单列索引。
单列索引可能会回表查询
联合索引情况