一. 索引的作用
类似于书的目录,起到优化查询的功能
二. 索引类型
- B树索引:BTREE
- R树索引:RTREE
- HASH索引
- 全文索引
三. BTREE索引细分类(算法)
3.1 B-TREE
普通索引,现在基本不适用,由根节点,枝节点,叶节点三部分组成,顺序由根到叶依次往下查询,每次都要从新查询一遍,不包括范围查询
3.2 B+TREE
和B-TREE组成结构一样,但是此索引在叶节点部分增加了左右双向的范围查询方式
3.3 B*TREE
是现在mysql数据库默认的查询方式,也是目前最通用的,组成与上述一样,但是此索引在B+TREE的基础上在枝节点增加了左右双向的范围查询方式(有时也被称为B+TREE,归位了一类)
四. BTREE索引的功能分类
- 聚集索引(集群索引)
- 辅助索引(二级索引)
五. BTREE索引是如何构建的
5.1 索引构建BTREE结构过程
1. 索引是基于表中,列(索引键)的值生成的B树结构
2. 首先提取此列的所有值,进行自动排序
3. 将排好序的值均匀分布到索引树的叶子节点中(16k)
4. 依次生成枝节点与根节点,根据数据量级和索引键长度,生成合适的索引树高度
5.2 辅助索引(s)构建B树结构的过程
1. 从表中的列中提取此列的所有值,进行自动排序
2. 将排好序的值,均匀分布到索引树的叶子节点(16k)
3. 生成此索引键指向对应的后端数据页(整行数据)的指针
4. 生成枝节点和根节点,根据数据量级和索引键长度,生成合适的索引树高度
---
辅助索引会发生跳跃状态查找,无序的查找,辅助索引的(叶节点)存储的是索引键所对应的整列数
据的值与指向此行整个数据的一个指向信息。而(枝节点)与(根节点)存储的是索引键下所对应数据
的值的信息。查找顺序由根---枝---叶依次查找。一般配合聚集索引使用
5.3 聚集索引(c)构建B树结构的过程
1. 默认按照主键列(ID列)生成聚集索引;如果没有主键的情况下存储引擎会使用唯一键;如果都没有
则会自动生成隐藏的聚集索引 #注意:主键列是前提,一般建表时都会构建
2. 聚集索引在存储数据时就会按照其主键列(ID列)的顺序存储到磁盘的数据页(我们称之为聚集索引组织表)
3. 因为它本身已经排序,所以构建B树结构时不用再次排序
4. 将排好序的整行数据生成叶子节点,可以理解为磁盘的数据页就是叶子节点
5. 枝节点与根节点依次调用下层节点主键的最小值(16k)
---
聚集索引的叶子节点把其对应的整行数据进行存储,可以直接获取到整行数据信息,所以不需要指向。
大小为16kb,而聚集索引的枝节点与根节点存储的确实下层节点主键的最小值(方便存储,不占用太大空
间)大小都为16kb,查找顺序由根---枝---叶进行依次查找,到叶子节点时可以直接获取到此行所有数据
5.4 辅助索引和聚集索引区别
辅助索引:
1. 叶子节点只保存主键值+索引键值的有序存储以及指向信息
2. 对索引键值会自动排序
3. 需要手动进行创建
4. 辅助索引可以有多个
5. 任何列都可以创建辅助索引
聚集索引:
1. 只能在主键列生成,唯一且非空
2. 数据存储时,就是按照(主键列)聚集索引顺序进行有序存储
3. 叶子节点保存的是整个有序的数据行
4. 叶子节点不需要单独生成
六. 辅助索引细分
6.1 单列辅助索引
- select * from t1 where name=' ';
6.2 多列辅助索引(联合索引)
- select * from t1 where a and b and c;
6.3 唯一索引
- 索引键值唯一的列进行索引创建
七. 索引树高度原因(影响IO,越低越好)
7.1 数据量级过大
解决:
- (1)分区表解决---把数据分开放到同一台服务器上,对服务器压力过大
- (2)分库分表---目前普遍的分布式架构,分开数据到不同的服务器上,减轻单台服务器压力
7.2 索引键值长度过长
解决:
- 尽可能选择列值短的列创建索引
- 采用浅醉创建索引
7.3 数据类型不同(索引键值长度不一,比如name)
解决:
- 选择合适的数据类型(char,varchar,enum...)
八. 索引的基本管理
8.1 索引管理命令
8.1.1 查询索引
use school;(进入要查询索引的库)
desc student;(查询整个学生表的索引,key中查看)
show index from student\G;(这种查看结果更加详细)
key列显示:
PRI(主键) UNI(唯一索引) MUL(辅助索引)
8.1.2 创建索引
1. 单列索引创建:
alter table student add index idx_name(sname); #给student表中的sname列创建索引叫idx_name
desc student; #进行查看
2. 联合索引创建:
alter table student add index idx_sname_sage_ssex(sname,sage,ssex);
#给student表中的sname列,sage,ssex多列创建联合索引
idx_a_b_c----idx_a idx_a_b idx_a_b_c
3. 前缀索引创建
alter table student add index idex(sname(5));
#给student表中的sname创建前缀索引,取前五个字符
4. 唯一索引创建
alter table student add telnum char(11) not null;
alter table student add unique index idx_tel(telnum);
8.1.3 删除索引
alter table student drop index idx;
alter table student drop index idx_name;
alter table student drop index idx_sname_sage_ssex;
8.2 索引压力测试
8.2.1 压力测试环境准备
- t100w文档导入数据库
8.2.2 无索引进行并发测试
- mysqldump --defaults-file=/etc/my.cnf --concurrency=100 --iterations=1 --create-schema='test' --query="select * from test.t100w where k2='LMMN'" engine=innodb --number-of-queries=2000 -uroot -p123 -verbose(此时测试结果显示时间会很长,在1000s左右)
8.2.3 为test库中t100w这个表的测试列k2列创建索引
- use test;(进入test库)
- desc t100w;(查看t100w表,此时这个表中没有索引)
- alter table t100w add index idx_k2(k20;(创建索引)
8.2.4 再次进行测试
- 依然是上边那条命令,此时会快很多,大概10s左右,这就是索引优化
8.3 索引执行计划管理(desc/explain优化)
8.3.1 执行计划获取(获取优化器选择后的执行计划)
- desc select * from test.t100w where k2='LMMN';
-
explain select * from test.t100w where k2='LMMN';
8.3.2 执行计划重要项分析(上述desc命令结果)
table项(涉及到的表)
指desc的SQL语句涉及到的表(这里指t100w)
type项(指查询类型)
(1)type---ALL:(全表扫描)
含义:
---不走任何索引,所有的表随机扫描,性能最差
可能性:
---查询条件本身查询的列项就没有创建索引
---有索引但不走索引(如下)
1. desc select * from t100w where k2 != 'asdf';
(不等于的应用不走索引,若查询的是主键列则走的是range)
2. desc select * from t100w where k2 like '%aa%';
(like语句在前面加了%不走索引)
3. desc select * from t100w where k2 not in ('asda','asas');
(not in子句应用不走索引,若为主键列则走range)
4. desc select * from t100w;
(目的就是全表扫描,不走索引)
(2)type---index:(全索引扫描)
含义:
---查询的此表中所有列的索引都要进行扫描,要获取的数据适应用于整个索引条件(不太实用)
可能性:
desc select k2 from t100w;
(3)type---range:(索引范围查询)
含义:
应用于索引进行查询项(列)的范围查询
可能性:
1. (>,<,>=,<=,like,between and...等)
desc select * from city where id<10;
desc select * from city where countrycode like 'CH%';
---上述范围查询能够被B+TREE额外优化到
2. (in(), or)
desc select * from city where countrycode in ('CHN','USA');
---这两项一般享受不到B+TREE的额外优化,一般会进行如下优化改写
desc select * from city where countrycode='CHN' union all select * from city where countrycode='USA';
(4)type---ref:(辅助索引等值查询)
desc select * from city where countrycode = 'CHN';
(5)type---eq_ref:(多表连接查询中,非驱动表on的条件列是主键或唯一键的查询)
desc select a.name,b.name from city as a join country as b on a.countrycode=b.code where a.population<100;
(6)type---const(system):(主键或唯一键的等值查询)
desc select * from city where id=10;
(7)type---NULL:(获取不到数据)
possible_keys项(可能用到的索引,一个列可能有多个索引)
(1)possible_keys---NULL
---没有和查询体检匹配的索引条目
(2)possible_keys---有值
---有和查询条件匹配的索引条目,但是他走索引。大部分原因是语句查询方式不符合索引应用条目,语句错误
key项(指具体使用到的索引)
指具体使用到的索引,最终所使用的,可以帮助我们判断是否使用了合适的索引
key_len项(按数据类型占最大字节计算,指索引的覆盖长度)
(1)计算方式:字符集
---utf8:一个字符在char与varchar类型中占3个字节
数据类型 not null null(+1为存储它是空还是非空)
int 4 4+1=5 #对于int类型,utf8字符集一个字符占4个字节
tinyint 1 1+1=2 #对于tinyint类型,utf8一个字符占1个字节
char(2) 2*3=6 2*3+1=7 #对于char类型,utf8一个字符占3个字节
varchar(2) 2*3+2=8 2*3+2+1=9 #对于varchar类型,utf8一个字符占3个字节
##注意:varchar类型有两个字节是用来存储字符长度的
---utf8mb4:一个字符在char与varchar类型中占4个字节
数据类型 not null null(+1为存储它是空还是非空)
int 4 4+1=5 #对于int类型,utf8mb4字符集一个字符占4个字节
tinyint 1 1+1=2 #对于tinyint类型,utf8mb4一个字符占1个字节
char(2) 2*4=8 2*4+1=9 #对于char类型,utf8mb4一个字符占4个字节
varchar(2) 2*4+2=10 2*4+2+1=11 #对于varchar类型,utf8mb4一个字符占4个字节
(2)对于单列索引(key_len)
---一般不使用,略
(3)对于联合索引(key_len)
---key_len普遍用于联合索引,主要是优化选择
1. 完美查询过程
含义:
按照索引排序方式进行所有索引的查询(等值查询)
索引:
idx(id,num,k1,k2,k3,k4)
创建:
alter table t1 add index idx(id,num,k1,k2,k3,k4);
查询:
desc select * from t1 where id=1 and num=1 and k1='a' and k2='a' and k3='a' and k4='a';
结论:
当查询条件中包含了索引列中所有的列条件,并且都是等值查询,name无关排列顺序,都可以走全联合
索引优化;原因是优化器会自动调整顺序,选择最佳的优化效果。所以,我们重点关注的是联合索引建
立的顺序(从左到右),唯一值越多的列越靠左(查询时字符要带引号)
2. 影响key_len查询长度的条件
--按照索引建立顺序在查找条件中,少了任意一个中间列,后续都无法走索引
--在条件查询中,出现了不等值查询,从不等值列开始,所有列都无法走索引(将不等值列放句末)
--若有多子句的条件查询(必须是联合索引)按照子句的执行顺序建立联合索引
Extra项(指出现的额外信息)
using filesort的出现:
原因:
--查询子句中出现group by,order by,distinct等子句的查询条件
优化:
上述子句条件与前方where条件建立联合索引(按顺序)
九. 建立索引的原则(DBA运维规范)
- 建立索引时要有主键,如果没有可以找能够作为主键条件的列,创建无关列。
- 为经常需要where,order by,group by,join on,distinct等排序操作的条件建立索引,优化查询(若是经常作为条件的列,重复值特别多,可以建立联合索引)
- 最好使用唯一值多的列作为索引列(若是索引列重复值较多,可以考虑建立联合索引;如果非使用重复值多的列作为查询条件(比如男女),可以将表逻辑拆分)
- 列值长度较长的索引列,建议使用前缀索引
- 限制索引数目
——每个索引都占用磁盘空间,索引越多,占用磁盘空间越大
——修改表时,对索引的重构和更新很麻烦,索引越多,耗时越长
——优化器负担会很重,有可能影响优化器选择- 删除不再使用或很少使用的索引(percona toolkit)。表中数据被大量更新,或者数据使用方式被改变后,原有的一些索引可能不再需要。数据库管理员应当定期找出这些索引并将其删除,从而减少索引对更新操作的影响。
- 大表加索引,要在业务不繁忙期间操作(pt-osc)
- 尽量少在经常更新值的列上建立索引
十. 查询条件不走索引的情况(开发规范)
- 没有查询条件,或查询条件中没有建立索引(生产中不建议进行全表扫描)
- 查询结果集是原表中大部分数据,应该是25%以上(查询结果太多,优化器感觉没有走索引的必要)
- 索引本身失效,统计数据不真实(表中内容变化频繁出现索引失效,删除原索引重建)
- 查询条件使用函数在索引列上,或者对索引列进行计算(+,-,*,/,!)
——select * from test where id-1=9;(错误案例)- 隐式转换导致索引失效(索引列的数据类型,是字符集还是字符串,在查询时一定要按照数据类型查看)
- <,>,not in不走索引(辅助索引)
——单独的<,>,in有可能走索引,也有可能不走,与结果集有关。尽量添加limit取少量数据行
——or或in尽量改为union all联合子句- like子句"%_"百分号在前也不走索引