对于索引可以这样理解:
原本没有索引的表,是进行全表扫描,数据一行一行筛选,有了索引,数据有了结构,大大增加查询效率。
所以,索引可以大大提高我们的查询效率。(当然索引结构不是简单的二叉树,这里只是举个例子)
关于二叉树,对于数据检索有一个明显的缺点,就是当数据时顺序插入时(从大到小或从小到大逐一插入),会形成一个链表,导致查询效率大大降低。
MySQL的索引是在存储引擎层实现的,不同的存储引擎有不同的结构,主要包括以下几种:
我们平常说的索引,如果没有特别指明,一般说的都是B+树结构的索引。
这里推荐一个数据结构可视化网址: Data Structure Visualization (usfca.edu)
里面可以详细的看到数据结构的演变过程,包括B树、B+树;(好用,爱用)
Btree(B树结构)
又叫做多路平衡查找树,以一颗最大度数(max-degree)为5(5阶)的b-tree为例(每个节点最多存放4个key,5个指针)
节点中每次存放的数据大于4,就会取中间数向上分裂,具体演变过程可以参考数据可视化网站。对于B数,每个key下面都是一个数据。
MySQL索引数据结构对经典的B+Tree进行了优化。在原BTree的基础上,增加一个指向相邻叶子节点的链表指针,指针的B+Tree,提高区间访问的性能。且数据都存储在叶子节点中,父节点只是起到了一个指向作用。
具体演变过程参考如上。
Hash结构
哈希索引就是采用一定的哈希算法,将键值换成新的hash值,映射到对应的槽位上,然后存储在hash表中。如果不同数据映射到了同一个槽位上,会形成链表。
思考:为什么InnoDB引擎要使用B+tree作为索引结构?
1. 相对于二叉树,层级更少,搜索效率高;
2. 对于B-tree, 无论是叶子节点还是非叶子节点,都会保存数据,这样导致一页中存储的键值减少,指针跟着减少,要同样保存大量数据,只能增加树的高度,导致性能降低;
3.对于Hash,如上图。
索引主要包括四种:主键索引、唯一索引、常规索引和全文索引
在InnoDB引擎中,根据索引的形式,又可以分为聚集索引(聚簇索引)和二级索引 (非聚簇索引)
聚集索引选取规则:如果存在主键,主键索引就是聚集索引,如果不存在主键,将使用唯一索引作为聚集索引。如果没有主键且没有唯一索引,则InnoDB会自动生成一个rowid作为隐藏的聚集索引。
聚集索引下放的是行数据,而二级索引叶子节点下方的是代表这个数据的键。如下图所示
举个例子,当我们查询select * from user where id = 5时,走的就是聚集索引,而当我们查询select * from name = 'Kit'时,是先走二级索引拿到id值,然后在走聚集索引拿到行数据。这也就是为啥select * from user where id = ? 速度要快于select * from user where id where name = ?
补充:InnoDB引擎中的B+tree树有多高?
假设一行数据大小为1k,一页可以存储16行数据,InnoDB的指针需要占据6个字节的空间,主键即使为bigint,占用字节为8。则:
高度为2时 n * 8 +(n+1) * 6 = 16 * 1024 ,得到n = 1170;所以它能够存储的数据量为1171 * 16 = 18736
高度为3则可以存储的数据量为1171 * 1171 * 16 = 2193856;
创建索引
CREATE [UNIQUE|FULLTEXT] INDEX idx_name on table_name(idx_col_name)
查看索引
show index from tb_user;
删除索引
DROP INDEX idx_name on table_name;
-- name字段为姓名字段,该字段可能会重复(创建普通索引)
create index idx_user_name on tb_user(name);
-- phone为手机号,是非空且唯一的(创建唯一索引)
create unique idx_user_phone on tb_user(phone);
-- 为profession age status创建索引(联合索引)
create index idx_user_pro_age_sta on tb_user(profession,age,status);
-- 为email创建合适的索引
create index idx_user_email from tb_user(email);
查看sql语句执行评率
Mysql客户端连接成功后,可以通过执行show [session|global] status like 'Com______';来查看 select、insert等增删改查语句的执行频率。
show [session|global] status like 'Com_______';
慢查询日志
慢查询日志中纪录了执行时间超过指定参数的所有sql语句的日志。
MySQL默认的慢查询日志没有开启,需要在MySQL的配置文件中(/etc/my.cnf)中配置如下信息
生成出来的慢查询日志文件位置在/var/lib/mysql 中
profiles
show profiles能够在帮我们优化sql的时候了解时间都耗费到哪里去了,通过hava_profile参数,能够看到当前MySQL是否支持profiles
explain
explain可以查看当前sql语句的执行计划
最左前缀法则
如果索引了多列(联合索引),要遵守最左前缀法则。最左前缀法则指的是查询从索引的最左列开始,并且不跳过索引中的列。如果跳跃某一列,索引将部分失效(后面的字段索引失效)。
表中存在如下联合索引,最左索引为profession,其次是age,status
如果where条件中保留最左侧列(profession),联合索引始终存在。
否则,索引失效
索引失效情况
1. 不要在索引上进行函数运算,否则索引会失效
2. 如果查询字符串不加引号,索引失效
3.如果使用了模糊查询,如果只是尾部模糊查询,索引不会失效,如果是头部模糊查询,索引失效。
4. 用or分隔开的条件,如果or前的条件的列中有索引,而后面的列中没有索引,则涉及到的索引都不会生效。
5. 数据评估影响,如果MySQL 评估使用索引比全表更慢,索引会失效。
即添加的where条件,表中数据大部分满足此条件,索引会失效。
查询手机号(phone)> 17799990000时,索引失效
有时候一个字段涉及多个索引,MySQL会自动进行评估选择一个索引,有时候这个索引并不是我们本意使用的索引。而SQL提示,就是在SQL语句中加入一些提示来达到优化操作的目的。
explain select * from tb_user use index(index_user_pro) where prefession = '软件工程'
ignore index: 不允许用某个索引
explain select * from tb_user ignore index(index_user_pro) where prefession = '软件工程'
force index 强制使用某个索引
explain select * from tb_user force index(index_user_pro) where prefession = '软件工程'
当字段类型为字符串(varchar, text等 )时,有时候需要索引很长的字符串,这会让索引变得很大,查询时,浪费大量的磁盘IO,影响查询效率。此时可以只将字符串的一部分前缀建立索引,这样可以大大节约索引空间,从而提高索引效率。
前缀长度.
可以根据索引的选择性来决定,而选择性是指不重复的索引值(基数)和数据表的记录总数的比值,索引选择性越高则查询效率越高,唯一索引的选择性是1,这是最好的索引选择性,性能也是最好的。
可以用如下SQL查询选择性(不重复数/总数,为1时最优)
针对email字段创建索引
create index idx_user_email_5 from tb_user(email(5))