简介
索引是在存储引擎用于快速找到记录的一种数据结构。在数据量越来越大的时代,索引对性能的影响越来越严重。所以建立适当的索引非常重要,想要建立适当的索引,就需要深入索引的原理。
分类
数据结构分类
HASH
简介
哈希索引使用了哈希算法,把值通过哈希算法计算出哈希值进行定位
优缺点
优点
检索的时间复杂度理论上是O(1),检索特别快
缺点
在数据量比较大的情况下,出现大量哈希碰撞,检索效率降低
不支持按照索引值的顺序存储,所以无法排序,无法进行范围查询
不支持最左匹配原则
BTREE
简介
mysql中默认的索引类型,通过多叉树的方式组织在一起
优缺点
优点
对比传统的二叉搜索树,当数据量比较大的时候,树的高度就会非常高,那么意味着I/O次数非常高,那么速度会很慢,B+TREE的树的高度非常低,寻找数据产生的I/O次数少
缺点
额外空间
添加,删除,修改索引列是,会伴随页分裂,页空洞等性能损耗
存储方式分类
聚簇索引(Clustered Index)
将索引与数据放在一起,并且在叶子结点存放数据(在Innodb中主键索引就是聚簇索引)
常见的聚簇索引
显示的主键列
第一个唯一索引
内置的6字节的ROWID
辅助索引(Secondary Index)
通过索引检索到行号,再通过行号找到数据(Innodb是找到对应的主键id,再通过主键id找到对应的数据)
功能分类
普通索引(INDEX)
这是最基本的索引类型,而且它没有唯一性之类的限制
唯一索引(UNIQUE INDEX)
索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一。
主键索引(PRIMARY KEY)
主键是一种唯一性索引,但它必须指定为“PRIMARY KEY”,且不允许有空值。
<strike>全文索引(FULLTEXT INDEX)</strike>
索引创建规范
单张表中索引数量不超过五个,单个索引中的字段数不超过五个
表必须有主见,推荐使用unsigned自增作为主键。唯一键由3以下字段组成,并且字段都是整形时,可使用唯一键作为主键
禁止冗余索引(<font color="#f44336">索引|(a,b,c),索引|(a,b)</font>),禁止重复索引(<font color="#f44336">索引|(a),索引|(a,主键ID)</font>)
联表查询时,join列的数据类型必须相同,并且要建立索引
不在低基数列上建立索引,如`性别`
选择区分度大的列建立索引,组合索引中,区分度大的字段放在最前面
合理创建组合索引,(a,b,c)相当于(a),(a,b),(a,b,c)
合理使用覆盖索引减少IO跟避免排序
索引优化分析
查看执行计划
执行方式
<font color="#f44336">explain</font> select * from xxx where xxxx=xxx
字段解释
id: 编号
id值相同,从上往下 顺序执行
id值越大越优先查询 (本质:在嵌套子查询时,先查内层 再查外层)
select_type :查询类型
PRIMARY
包含子查询SQL中的 主查询 (最外层)
SUBQUERY
包含子查询SQL中的 子查询 (非最外层)
SIMPLE
简单查询(不包含子查询、union)
table :输出结果集的表
type :访问类型
从左到右,性能由好到最差,system>const>eq_ref>ref>range>index>all (ref到range之间还有一些其他的不过不太常见)(要对type进行优化的前提:有索引)(一般优化后的type必须在range以上)
分类
system
只有一条数据的系统表 ;或 衍生表只有一条数据的主查询(基本不能达到可忽略)
const
仅仅能查到一条数据的SQL ,用于Primary key 或unique索引 (类型 与索引类型有关
eq_ref
唯一性索引:对于每个索引键的查询,返回匹配唯一行数据(有且只有1个,不能多 、不能0
ref
非唯一性索引,对于每个索引键的查询,返回匹配的所有行(0,多)
range
检索指定范围的行 ,where后面是一个范围查询(between ,> < >=, 特殊:in有时候会失效 ,从而转为 无索引all)
possible_keys :可能用到的索引
key :实际使用的索引
key_len :实际使用索引的长度
用于判断复合索引是否被完全使用 (a,b,c)
例子
在utf8编码中:1个字符占3个字节,如果索引的字段可以为null,则会使用一个字节用来标识 ,如果是varchar这种,会用两个字节表示可变<br><br>例如 :某个字段 name(varchar(20)),如果用到了这个字段的索引那么lenth就是 20*3+1(null)+2(可变字符) =63
ref :表之间的引用
rows: 被索引优化查询的 数据个数 (实际通过索引而查询到的 数据个数)
Extra :额外的信息
using index
性能提升; 索引覆盖(覆盖索引)。原因:不读取原文件,只从索引文件中获取数据 (不需要回表查询)只要使用到的列 全部都在索引中,就是索引覆盖using index
using where
表示进行了回表查询
using filesort
性能消耗大;需要“额外”的一次排序(查询) 。常见于 order by 语句中。
using temporary
性能损耗大 ,用到了临时表。一般出现在group by 语句中。
impossible where
where子句永远为false
避免索引失效的一些原则
复合索引,不能跨列使用(index(a,b,c),你的索引使用就不要 where a=x,c=x)
复合索引尽量使用全索引匹配
不要在索引上进行任何操作(计算,函数,类型转换),否则索引失效
复合索引不要使用不等(!= ,<>,>),这样会导致自身以及右侧的索引失效**(这个不是一定的有概率的**)
like尽量以“常量”开头,不要以'%'开头,否则索引失效
尽量不要使用or,否则索引失效(字段都是单值索引是可以的)
常见问题
经常在数据量很大的情况下我们要把主键用整数代替字符串,并且是增长的?
在使用BigInt的时候比UUID(为保证不重复一般都是UUID),占用的字节少,那么在B+Tree的结构中,就能存储更多的值。
比字符串好排序
如果你生成的是自增的(分布式id生成策略中都是增加的),在插入时,就可以尽可能减少页分裂
为什么我们经常被要求,只查询需要的字段?
在例如你查询只用到name时,select * from user where iphone=xxx,索引为(iphone,name)的时候,我们根据上面的innodb的索引结构学习到,你要查询索引中没有的字段的时候,需要先通过辅助索引找到主键索引的值,然后再找到对应的数据值(过程叫做回表),这样就多了I/O操作,如果你的sql改成select name from user where iphone=xxx,这个name值在辅助索引中已经有了,所以不需要再去主键索引中找数据,就提高了查询效率(这种情况叫做覆盖索引)