首页 > 数据库 >mysql08--索引管理

mysql08--索引管理

时间:2022-10-25 16:23:23浏览次数:78  
标签:name -- s1 mysql08 索引 where id select

一、索引理论

ps:数据都是存在于硬盘上的,查询数据不可避免的需要进行IO操作

# 索引:就是一种数据结构,类似于书的目录。
    意味着以后在查询数据的应该先找目录再找数据,而不是一页一页的翻书,从而提升查询速度降低IO操作

# 索引在MySQL中也叫“键”,是存储引擎用于快速查找记录的一种数据结构
    primary key  # 主键索引   用途:加速查询  约束:唯一 且不为空
    unique key   # 唯一索引   用途:加速查询  约束:不为空
    index key    # 普通索引   用途:加速查询  约束:无

    # 注: foreign key 外键 不是用来加速查询用的,是用来限制表之间的关系的,不在索引的研究范围之内


# 添加索引的本质:       索引建立在表的列上(字段)的
  将某个字段添加成索引,就相当于依据该字段,建立了一颗b+树 从而加快查询速度
  如果某个字段没有添加索引 那么依据该字段查询数据会非常的慢(一行行查找)


# 索引的存放位置
  Innodb  两个文件  直接将主键存放在了s1.idb表中 
  MyIsam  三个文件  单独将索引存在一个索引文件table1.MYI


# 索引的原理:
  通过不断的缩小想要的数据范围筛选出最终的结果,
  同时将随机事件(一页一页的翻),变成顺序事件(先找目录、找数据),
  也就是说有了索引机制,我们可以总是用一种固定的方式查找数据


# 缺点:
  一张表中可以有多个索引(多个目录),索引虽然能够帮助你加快查询速度但是也有缺点
    1 当表中有大量数据存在的前提下 创建索引速度会很慢
    2 在索引创建完毕之后 对表的查询性能会大幅度的提升 但是写的性能也会大幅度的降低
    # 理解:本来只需要写数据,现在需要额外操作(建索引),数据越多,索引就越复杂(就越慢)
    
# 注意:索引不要随意的创建!!!

1. B+树

# B+树:
  是树状数据结构中的一种,其他树状的数据结构还有:二叉查找树、平衡二叉树、B树
  B+树是由二叉树、平衡二叉树、B树再到B+树一路演变过来的

# Mysql中的Innodb存储引擎 默认的索引结构为B+树


# B+树的特点:
  1.内部枝节点不保存数据的,只作索引作用,叶子节点才保存数据。
    # 只有叶子节点存放的是真实的数据 其他节点存放的是虚拟数据 仅仅是用来指路的

  2.树的层级越高,查询数据所需要经历的步骤就越多 (树有几层查询数据就需要几步)


# 为什么建议将id字段(自增主键)作为索引     int占4个字节,bigint占8个字节
  因为id字段占得空间少,一个磁盘块能够存储的数据多
  间接降低 B+Tree 的高度,提高搜索效率

  # 个人理解:
    B+树的各个节点,是存储在不同的磁盘块上
    而一个磁盘块存储是有大小限制的  
    数据项越小,每个节点(磁盘块) 存储的 元素数量就越多
    
    同样的数据元素,存放在的节点(磁盘块)就越少
    就降低了树的高度(层级),从而减少了磁盘块的IO读取次数

2. 聚集索引(Clustered Index)

# 聚集索引指的就是主键索引 PRIMARY KEY
  按照每张表的主键构造一棵B+树,同时 叶子节点中存放的即为整张表的行记录数据。

# InnoDB存储引擎:
   -若定义了主键,InnoDB会自动使用 主键 来创建聚集索引。
   -若没有定义主键,InnoDB会选择一个 唯一的非空索引 代替主键作为聚集索引。
   -若没有唯一的非空索引,InnoDB会 隐式定义一个主键 来作为聚集索引。

3. 辅助索引(Secondary Index)

# 前景:
  查询数据时不可能一直使用到主键,也有可能会用到name,password等其他字段
  那么此时不能利用聚集索引,就可以根据情况 给其他字段设置辅助索引(也是一个b+树)

# 辅助索引:也叫非聚集索引、二次索引 
  除主键索引之外,都是辅助索引,包含:唯一索引(unique key)、普通索引(index key)


# 与聚集索引的对比: 
  聚集索引的叶子结点存储的是 一行完整的数据
  辅助索引的叶子结点存储的是 数据的主键值


# 根据 辅助索引 查到完整数据的流程:  
  eg: select * from user where username='javaboy'

  1.先去辅助索引的B+树,拿到数据的主键值
  2.再去主键索引的B+树,查询行的全部数据

4. 覆盖索引(Covering index)

# 覆盖索引:
  如果一个索引包含(或者说覆盖)所有需要查询的字段的值,就称之为“覆盖索引”。
  # 即在辅助索引的叶子节点,就已经拿到了需要的数据,而不需要再次查询聚集索引中的记录。


# InnoDB存储引擎支持覆盖索引

# 覆盖索引的好处
  可以减少大量的IO操作


# 给name设置辅助索引后  查询name字段数据,能直接从name索引B+树 获取
select name from user where name='jason';  

# 非覆盖索引
select age from user where name='jason';

5. 前缀索引

# 前缀索引: 按照该列数据的前n个字母创建索引

# 好处:
  如果索引字段的值很长,最好使用值的前缀来索引
  例如,TEXT和BLOG类型的字段,进行全文检索会很浪费时间
  如果只检索字段的前面的若干个字符,这样可以提高检索速度

6. 联合索引

# 联合索引:指对表上的多个列进行索引  专门用于组合搜索

# 种类:
  PRIMARY KEY(id,name) : 联合主键索引 
  UNIQUE(id,name)      : 联合唯一索引 
  INDEX(id,name)       : 联合普通索引

# 好处:
  是在第一个键相同的情况下,已经对第二个键进行了排序处理

7. 测试索引是否有效的代码

感兴趣就自己试一试 不感兴趣直接忽略

**准备**

# 1. 准备表
create table s1(
id int,
name varchar(20),
gender char(6),
email varchar(50)
);

# 2. 创建存储过程,实现批量插入记录
delimiter $$   # 声明存储过程的结束符号为$$
create procedure auto_insert1()
BEGIN
    declare i int default 1;
    while(i<3000000)do
        insert into s1 values(i,'jason','male',concat('jason',i,'@oldboy'));
        set i=i+1;
    end while;
END$$  # $$结束
delimiter ; #重新声明分号为结束符号

# 3. 查看存储过程
show create procedure auto_insert1\G 

# 4. 调用存储过程
call auto_insert1();



# 表没有任何索引的情况下
select * from s1 where id=30000;
# 避免打印带来的时间损耗
select count(id) from s1 where id = 30000;
select count(id) from s1 where id = 1;

# 给id做一个主键
alter table s1 add primary key(id);  # 速度很慢

select count(id) from s1 where id = 1;  # 速度相较于未建索引之前两者差着数量级
select count(id) from s1 where name = 'jason'  # 速度仍然很慢


"""
范围问题
"""
# 并不是加了索引,以后查询的时候按照这个字段速度就一定快   
select count(id) from s1 where id > 1;  # 速度相较于id = 1慢了很多
select count(id) from s1 where id >1 and id < 3;
select count(id) from s1 where id > 1 and id < 10000;
select count(id) from s1 where id != 3;

alter table s1 drop primary key;  # 删除主键 单独再来研究name字段
select count(id) from s1 where name = 'jason';  # 又慢了

create index idx_name on s1(name);  # 给s1表的name字段创建索引
select count(id) from s1 where name = 'jason'  # 仍然很慢!!!

"""
再来看b+树的原理,数据需要区分度比较高,而我们这张表全是jason,根本无法区分
那这个树其实就建成了“一根棍子”
"""
select count(id) from s1 where name = 'xxx';  
# 这个会很快,我就是一根棍,第一个不匹配直接不需要再往下走了
select count(id) from s1 where name like 'xxx';
select count(id) from s1 where name like 'xxx%';
select count(id) from s1 where name like '%xxx';  # 慢 最左匹配特性

# 区分度低的字段不能建索引
drop index idx_name on s1;

# 给id字段建普通的索引
create index idx_id on s1(id);
select count(id) from s1 where id = 3;  # 快了
select count(id) from s1 where id*12 = 3;  # 慢了  索引的字段一定不要参与计算

drop index idx_id on s1;
select count(id) from s1 where name='jason' and gender = 'male' and id = 3 and email = 'xxx';
# 针对上面这种连续多个and的操作,mysql会从左到右先找区分度比较高的索引字段,先将整体范围降下来再去比较其他条件
create index idx_name on s1(name);
select count(id) from s1 where name='jason' and gender = 'male' and id = 3 and email = 'xxx';  # 并没有加速

drop index idx_name on s1;
# 给name,gender这种区分度不高的字段加上索引并不难加快查询速度

create index idx_id on s1(id);
select count(id) from s1 where name='jason' and gender = 'male' and id = 3 and email = 'xxx';  # 快了  先通过id已经讲数据快速锁定成了一条了
select count(id) from s1 where name='jason' and gender = 'male' and id > 3 and email = 'xxx';  # 慢了  基于id查出来的数据仍然很多,然后还要去比较其他字段

drop index idx_id on s1

create index idx_email on s1(email);
select count(id) from s1 where name='jason' and gender = 'male' and id > 3 and email = 'xxx';  # 快 通过email字段一剑封喉 


#### 联合索引

mysql

select count(id) from s1 where name='jason' and gender = 'male' and id > 3 and email = 'xxx';  

# 如果上述四个字段区分度都很高,那给谁建都能加速查询

# 给email加 然而不用email字段
select count(id) from s1 where name='jason' and gender = 'male' and id > 3; 

# 给name加然而不用name字段
select count(id) from s1 where gender = 'male' and id > 3; 

# 给gender加然而不用gender字段
select count(id) from s1 where id > 3; 

# 带来的问题是所有的字段都建了索引然而都没有用到,还需要花费四次建立的时间

create index idx_all on s1(email,name,gender,id);  # 最左匹配原则,区分度高的往左放
select count(id) from s1 where name='jason' and gender = 'male' and id > 3 and email = 'xxx';  # 速度变快


总结:上面这些操作,你感兴趣可以敲一敲,不感兴趣你就可以不用敲了,权当看个乐呵。理论掌握了就行了

二、索引操作

https://www.yuque.com/nidhogg14/wssb4n/gevr3z#nU4u0

# 查看表结构
desc 表名;
 
# 查看生成表的SQL
show create table 表名 \G;  # \G表示美化输出
 
# 查看索引
show index from  表名;
 
# 查看执行时间   了解SQL执行的线程的状态及消耗的时间。
set profiling = 1;  # 默认是关闭的  开启 show profile分析
SQL...
show profiles;

2.1 创建索引

# 几种索引的创建时间:  
  主键索引   # 创建表时
  唯一索引  # 创建表时
  普通索引   # 创建表时 和 表已创建  ***


##### 创建表时
# 语法: 
  1.单独字段设置索引:主键、唯一、普通索引  可直接写在字段的 约束条件里
  2.也可以类似联合索引一样  单独设置索引

CREATE TABLE 表名 (
    字段名1  数据类型 [完整性约束条件…],
    字段名2  数据类型 [完整性约束条件…],
    
    # 单独设置索引时:
    [PRIMARY | UNIQUE | FULLTEXT | SPATIAL] # 主键、唯一、全文和空间 后两者几乎不用
    INDEX | KEY  
    [索引名]  (字段名[(长度)]  [ASC |DESC]) 
);

# eg:
create table t1(
    id int primary key,  # 约束条件里 设置主键索引
    name char,
    age int,
    sex enum('male','female'),
    
    unique key uni_id(id),  # 给id字段建 名叫uni_id 的唯一索引
    index ix_age(age),      # 普通索引 index没有key
    index ix_name(name(8))  # 前缀索引 name字段的前8个字符
);


##### 表已创建
# 语法1:
CREATE [UNIQUE] INDEX  索引名  
ON
表名(字段名[(长度)] [ASC | DESC]);

# eg:
create index ix_age on t1(age);


# 语法2:
ALTER TABLE 表名 ADD [UNIQUE] INDEX
索引名(字段名[(长度)] [ASC | DESC]);

# eg:
alter table t1 add UNIQUE index ix_sex(sex);

2.2 删除索引

# 删除索引
    
# 语法:
DROP INDEX 索引名 on 表名
或 
alter table 表名 drop index 索引名;
    
# eg:
DROP INDEX index_emp_name on emp1;

alter table emp1 drop index index_emp_name;

三、索引原则

3.1 设计原则

# 背景
  为了使索引的使用效率更高,在创建索引时必须考虑
  在哪些字段上创建索引   创建什么类型的索引


# 索引设计原则

# 1.唯一性字段 建立索引
  唯一性索引的值是唯一的,可以更快速的通过该索引来确定某条记录
  # 主键索引和唯一键索引,在查询中使用是效率最高的 !!!

  # eg:
  学生表中学号是具有唯一性的字段。为该字段建立唯一性索引可以很快的确定某个学生的信息
  如果使用姓名的话,可能存在同名现象,从而降低查询速度

  
# 2.常排序、分组和联合操作的字段 建立索引
  经常需要ORDER BY、GROUP BY、DISTINCT和UNION等操作的字段
  排序操作会浪费很多时间,如果为其建立索引,可以有效地避免排序操作


# 3.常查询条件的字段 建立索引
  如果某个字段经常用来做查询条件,那么该字段的查询速度会影响整个表的查询速度
  但 是不是真的适合去做索引,根据区分度去判断  # 简单讲:该列的值 越不重复 越适合
  
  # 区分度: count(distinct col)/count(*)  表示字段不重复的比例  比例越大 越适合
  统计该列去重之后的行数 和 总行数对比  # 唯一键的区分度是1

  # eg: 性别  就不合适作为索引列
  select count(DISTINCT population ) from city;
  select count(*) from city;

    
# 4.短索引原则:尽量使用前缀来索引
  如果索引字段的值很长,最好使用值的前缀来索引,节省大量索引空间
  
  # eg: TEXT和BLOG类型的字段,进行全文检索会很浪费时间。
  备注 列字段VARCHAR(200) 如果该列设置为索引列,查询效率不很高
  因为索引字段长度过大,索引节点树高增加,I/O次数也会增加
  将 备注 字段值得前10个字符设置为索引,节省索引空间


# 5.限制索引的数目
  设置索引时要考虑设置合适的列,不要造成“过多的索引列”
   
  因为每个索引都需要占用磁盘空间,并降低写操作的性能
  并且修改表时,对索引的重构和更新很麻烦。索引列越多,会使更新表变得很浪费时间
  
  # 对于长时间不再使用或者很少使用的索引要进行删除操作

3.2 优化原则

# 索引的优化原则:
  就是避免查询 出现不走索引查询(索引未命中)的情况,从而导致全表扫描数据


# 索引未命中(失效)的情况:  重点关注

# 1.没有查询条件 或者 查询条件没有建立索引 

  # 注:在业务数据库中 特别是数据量比较大的表,是没有全表扫描这种需求
    1.对用户查看是非常痛苦的
    2.对服务器来讲毁灭性的
    
# eg:
1.select * from tab; # 没有查询条件  直接全表扫描
改?
  select * from tab order by price limit 10  # 需要在price列上建立索引

2.select * from tab where name='zhangsan' # 查询条件name列 没有索引
改?
  1.换成有索引的列作为查询条件
  2.或 将name列建立索引
    
    
# 2.查询结果集是原表中的大部分数据,应该是30%以上
  查询的结果集,超过了总数行数30%,优化器觉得就没有必要走索引了
    
# eg:
1.tab表(id, name)  id:1-100w , id列有索引
select * from tab  where id>500000;  # 此时结果集>30%, 不会走索引了

改 ?
  1.如果业务允许,可以使用limit控制
  2.结合业务判断,有没有更好的方式
    如果没有更好的改写方案,尽量不要在mysql存放这个数据了  放到redis里面。

    
# 3.查询条件使用函数在索引列上 或者 对索引列进行运算 包括(+ - * / !等) 

# eg: 
1.select * from test where id-1=9; 
改?
  select * from test where id=10;
    
2.select * from test where from_unixtime(create_time) = ’2014-05-29’;
改?
  select * from test where create_time = unix_timestamp(’2014-05-29’);

    
# 4.隐式转换    开发中经常会犯的错误 !!!
  数据类型出现隐式转换的时候不会命中索引
  特别是当列类型是字符串,一定要将字符常量值用引号引起来 

# eg: tu_mdn为varchar2(20)  传递了number类型
1.select * from test where tu_mdn=13333; 
改?
  select * from test where tu_mdn='1333'; 


# 5.负向条件查询 !=、<>、not in、not exists、not like等
  即:优化为in查询
  # 注:
  1.单独的>,<,in 可能走,跟结果集和版本有关,尽量结合业务添加limit
  2.union、in、or都能够命中索引  # or或in 尽量改成union

# eg:
EXPLAIN  SELECT * FROM teltab WHERE telnum IN ('110','119');
改?
  EXPLAIN SELECT * FROM teltab WHERE telnum='110'
  UNION ALL
  SELECT * FROM teltab WHERE telnum='119'


# 6.like "%_" 左模糊匹配(百分号在最前面)    

# eg:
EXPLAIN SELECT * FROM teltab WHERE telnum LIKE '31%'  # 走range索引扫描
EXPLAIN SELECT * FROM teltab WHERE telnum LIKE '%110' # 不走索引
改?
  %linux%类的搜索需求 可以使用elasticsearch


# 7.联合索引时,违背最左前缀匹配原则
  即:联合索引 保证查询条件中存在最左索引
  # 强调:
  1.组合索引(a,b,c)的情况下,实际上相当于建立了(a), (a,b), (a,b,c)共三个索引
  2.最左匹配原则并不是指查询条件的顺序,而是指查询条件中是否包含索引最左列字段
    
# eg:
CREATE TABLE t1 (
    id INT,
    NAME VARCHAR(20),
    age INT ,
    sex ENUM('m','f'),
    money INT
);
# 复合索引:money,age,sex
ALTER TABLE t1 ADD INDEX t1_idx(money,age,sex);


1.走索引的情况: 跟查询条件的顺序无关
EXPLAIN SELECT NAME,age,sex,money FROM t1 WHERE money=30 AND age=30  AND sex='m';
EXPLAIN SELECT NAME,age,sex,money FROM t1 WHERE money=30 AND age=30  ;
EXPLAIN SELECT NAME,age,sex,money FROM t1 WHERE money=30 AND sex='m'; # 部分走索引

2.不走索引的情况: 未包含最左列字段
EXPLAIN SELECT NAME,age,sex,money FROM t1 WHERE age=20
EXPLAIN SELECT NAME,age,sex,money FROM t1 WHERE age=30  AND sex='m';
EXPLAIN SELECT NAME,age,sex,money FROM t1 WHERE sex='m';


# 8.索引本身失效,统计数据不真实 
  索引有自我维护的能力
  对于表内容变化比较频繁的情况下,有可能会出现索引失效

标签:name,--,s1,mysql08,索引,where,id,select
From: https://www.cnblogs.com/Edmondhui/p/16825274.html

相关文章

  • typora的简单使用
    一级标题#+空格或者快捷键ctrl+1二级标题##+空格或者快捷键ctrl+2三级标题###+空格或者快捷键ctrl+3调试模式开启调试模式:快捷键shift+F12  字体HelloW......
  • 植被指数第二弹
    植被指数第二弹—窄带绿度(NarrowbandGreenness)窄带绿度指数对叶绿素含量、叶子表面冠层、叶聚丛、冠层结构非常敏感。它的计算,采用的是红色与近红外区域部分—红边,红......
  • 面试篇之HR问什么是静态代理?什么是动态代理?
    面试篇之HR问什么是静态代理?什么是动态代理?加入交流群前往:CN.ITLTT.COM何为代理?Java中的代理,开源理解为通过代理去访问实际的目标对象,比如呢?我们平常买卖二手车的中间......
  • 常用git插件
    Chinese(Simplified)(简体中文)LanguagePackforVisualStudioCodeGitHistoryGitLensImagepreviewNGA-MoFishPowerModeQQTailwindCSSIntelliSenseuni-......
  • Caddy File Server Docker部署
    CaddyFileServerDocker部署1.切换到应用的文件夹下面$cd/alidata1/admin/app/caddy2.编辑docker-compose.ymlversion:"3.7"services:caddy:image:......
  • Centos7防火墙开放端口基本命令
    一、防火墙的开启、关闭、禁用命令(1)设置开机启用防火墙:systemctlenablefirewalld.service(2)设置开机禁用防火墙:systemctldisablefirewalld.service(3)启动防火墙:system......
  • ASEMI代理的安森美车规级mos管NVHL040N65S3F怎么选型
    编辑-Z随着车规级mos管的应用得到全面推广,在不同工作环境中广泛应用的普及,这导致生产加工行业受到发展的带动,各种不同类型的品种层出不穷,为了工作中具有强大的优势和特点,......
  • SpringBoot使用WebUploader做大文件的分块和断点续传
    ​ 在Web应用系统开发中,文件上传和下载功能是非常常用的功能,今天来讲一下JavaWeb中的文件上传和下载功能的实现。先说下要求:PC端全平台支持,要求支持Windows,Mac,Linux......
  • 高斯消元
    高斯消元是求解线性方程组的方法。对于一个\(m\)个等式\(n\)个未知数的方程组,我们可以将其写成\(m\times(n+1)\)的增广矩阵的形式:对于这个矩阵我们可以进行三......
  • 父组件监听到子组件的生命周期
    一、问题引入父组件监听到子组件的生命周期二、$emit手动触发//Parent.vue<Child@mounted="doSomething"/>//Child.vuemounted(){this.$emit("mounted"......