目录
索引概述
介绍
索引是一种能够帮数据库高效查询数据的数据结构。数据库不仅仅需要维护,还需要维护满足特定查找算法的数据结构。
演示
select *from user where age = 45;
使用约束函数进行查找时,当找到第一个45时,查询并不会停止,因为数据库并不知道,表中是不只有一个45,所以他要进行全表查询,效率极低。
所以数据库确实需要一种数据结构来进行高效的查询,现在我们试着使用数据结构中的二叉树来储存数据,并且比较一下查找效率:
优缺点
优点:
- 使数据的查询更加高效,降低的数据库的IO成本
- 通过索引进行排序,降低了排序的成本,降低了CPU的消耗
缺点:
- 索引列是要占用空间的
- 降低了更新表的速度,要进行INSERT、UPDATE等(也就是不太方便维护)
索引结构
索引是在储存引擎层实现的,不同的储存引擎有不同的结构。
主要的索引结构
- BTree索引:最常见的索引,大部分的储存引擎都支持B+Tree索引
- Hash索引:他的底层数据结构使用Hash实现的,但是只有精确匹配的索引列才能查询有效,不支持范围查询
- R+Tree索引(空间索引):是MyISAMD的特有的索引结构,通常用于地理数据类型,不经常使用
- Full-Text(全文索引):是引种建立倒排索引,快速快速匹配文档的方式
在分析索引结构之前,我们先回顾一下数据结构中的二叉树:
如图是一个二叉排序树,他确实能提高查询的速率,但是我们知道,当插入的数据有序时,二叉树就会退化成为链表:
不难看出,当输入数据有序时,二叉树会退化成链表,导致查找效率大大降低;并且,当数据量特别大时,二叉树的层数会很深。
所以才有了后面的红黑树(RBT):
红黑树可以解决退化成链表的情况,但是还是不能避免数据量大时,层数太深的问题。
BTree
BTree又称多路平衡查找树:
每个节点可以有多个子节点(事先规定好最大度数)
假设max-degree=5,就说明每个节点最多存4个key,每个节点会有5个指针
五个指针分别指向,值分别在该节点4个key之间的区间,大家可以使用这个数据结构可视化网站进行体验一下Btree的构建过程。
B+Tree
B+Tree与BTree优有点像,但是不同点在于当一个节点值满后,向上分裂时,B+Tree的节点始终会保留在叶子结点上,最终叶子结点会形成一个链表。
B+Tree
数据库索引中的B+Tree在经典数据结构的B+Tree的基础上进行了优化,加上一个指向相邻叶子结点的指针,从而形成了带有顺序指针的B+Tree,提高了访问速率。
非叶子结点其实并不储存数据,只是起到索引的作用
Hash
Hash的逻辑和经典数据结构中的处理相似,通过Hasn算法,将键值映射成Hash值,然后储存到Hash表对应的槽位中,而当多个键值对应一个槽位时,就需要进行Hash冲突处理,也就是后面接链表解决。
Hash的特点:
- Hash只能进行对等比较(=、IN),不能进行范围查询(<、>、BETWEEN)
- 不能通过索引完成排序操作
- 能够提高查询效率
一般来说,Memory储存引擎是支持Hasn索引结构的,但是Innodb储存引擎,会根据B+Tree的索引在指定条件下转换成为Hash索引。
思考
为什么InnoDB要使用B+Tree索引结构呢?而不是红黑树或者BTree又或是Hasn?
- 因为B+Tree相较于红黑树而言,层级更少,查找效率更高
- 相较于BTree而言,B+Tree只有叶子结点储存数据,非叶子结点不储存数据,只是起到索引作用。而我们知道,每一个结点在数据库中使用块/页储存的,这样不储存数据的话,能够存放的结点和指针就会更多,能够减少层数,提高查询效率
索引的分类
在InnoDB储存引擎中,根据索引的储存形式,将索引分为了:
注意:
- 当表中有主键时,主键索引就是聚焦索引
- 当没有主键时,将第一个唯一索引作为聚焦索引
- 没有主键,切没有合适的唯一索引时,InnoDB会自动生成ROWID作为隐藏的聚焦索引
下面使用一个例子来比较清晰地解释下聚焦索引和二级索引:
这张表中有三个字段,其中id是主键,主键将会作为聚焦索引:
聚焦索引的叶子结点是一行数据(包括所有字段),而不难想到,二级索引的叶子结点肯定就不是存放行数据的所有字段,因为这样就会有数据冗余。下面是二级索引:
它存放的是id号,所以使用二级索引查找信息时,应该是先通过name,找到id号,然后在通过聚集索引查找这一行所有字段的数据,这个过程叫做回表查询。
索引语法
查看索引
show index from users;
创建索引
CREATR [ 约束 ] INDEX 索引名 ON 表名(字段名);
create unique index idx_user_contact on users(user_contact);
删除索引
drop index idx_user_name on users;
SQL性能分析
查看执行频次
查询数据库增、删、改、查命令的频次:
show global status like 'Com_______';
根据系统的命令执行频次,来选择对那些操作进行优化。
慢查询日志
查看是否开启慢查询日志
show variables like 'slow_query_log';
如果没有开启,需要在mysql的配置文件中添加配置(注意是在Linux中输入语句,不是mysql):
vim /etc/my.cnf
然后在配置文件中添加下面两条语句:
#开启mysql慢查询日志开关
slow_query_log=1
#设置慢查询的时间为2秒
long_query_time=2
然后我们可以看一下,日志中的信息:
先进入文件所在位置
cd /var/lib/mysql
然后查看一下文件内容
cat localhost-slow.log
显示信息如下
这里要注意的是,在配置文件时,明确了慢查询的时间,也就是说只有查询时间大于这个值才会被日志记录下来,而相反小于这个时间的查询就不会记录下来。
profile详情
查询当前数据库是否支持profile详情
select @@have_profiling;
查看profile是否打开
select @@profiling;
使用set语句,将profile打开
set profiling = 1;
查看每一条SQL语句的耗时情况
show profiles ;
查看指定query_id的SQL语句每个阶段的耗时情况
show profile for query query_id;
查看指定query_id的SQL语句的CPU的使用情况
show profile cpu for query query_if;
explain执行计划
查看执行计划
explain select *form users;
将会出现一系列信息:
执行计划各字段义
ID
select查询的序列号(ID相同,执行计划从上到下;ID不同,值越大越先执行)
SELECT_Type
表示select的类型,常见的有:SIMP(简单表,即不使用表连接或者子查询),PRIMARY(主查询,即外层的查询),UNION(union中的第二个查询语句或者后面的查询语句),SUBQUERY(select或where后面有子查询)等
TYPE
连接性能从好到坏:NULL、system、const(根据主键或者唯一索引查询时)、eq_ref、ref、range、index、all
possible_key
查询可能出现的索引,可以是一个,可以是多个
KEY
实际用到的索引,如果没有索引就显示NULL
KEY_len
表示索引的字节数,是索引的最大可能长度,所以可能与实际的数据不同(在不会减少精确度的情况下,长度越短越好)
rows
是MySQL认为必须要查询的行数,这一般是一个估计值
filtered
表示返回行数占实际读取行数的百分比(当然是百分比越大越好)
Extra
在查询过程中没有出现的字段,会显示在Extra中
索引使用
最左前缀法则
当要使用联合索引时,必须满足最左前缀法则,在查询时必须包含联合索引最左边的列(不包含的话,索引全部失效),如果跳过联合索引的某列就会导致部分索引失效(后面的索引失效)
假设有一个联合索引,它包含了name、age、status三个字段,此时我进行查询:
select *from user where name='张三' and age='24' and status='0';
是能够按照索引进行查询的,可以通过执行计划中的select_type查看,select_type是联合索引
但是如果不包含最左边的列,那么select_type就会是ALL(也就是全表查询):
select *from user where age='24' and status='0';
此时我们还可以验证一下,如果 跳过了某个索引,如下就是跳过了字段age:
select *from user where name='张三' and status='0';
导致的结果就是,虽然依旧是使用联合索引查询,但是会导致跳过的索引age后面的索引status失效,也就是说在匹配最后一个索引的条件时并没有使用索引查询。
索引失效情况
范围索引
当联合索引中出现了范围索引( < 或者 > ),范围索引右边的所有索引失效(索引一般情况下,使用 >= 或者 <= 比较好)
索引列运算
不要对索引列进行运算操作,否则索引列会失效
字符串索引
如果在使用字符串索引列时没有使用引号,虽然能够查询成功,但是索引是失效的
模糊匹配
如果是模糊匹配后面部分,能够使用索引查询;但如果是模糊匹配前面部分,无法使用索引:
explain select *from users where user_contact like '13%';
explain select *from users where user_contact like '%00000000';
or连接的条件
在条件中,如果or前面的条件有索引,or后面的字段没有索引,那么所有涉及的索引都不会被用到
数据分布影响
如果MySQL评价使用索引查询比比全表还慢,那么就不会使用索引
SQL提示
如果有多个索引存在,可以加入人为的提示,告诉MySQL进行一定的优化
常见的索引SQL提示:
- use是建议MySQL使用你给的索引,但是MySQL还需要评估是不是最优的
- ignore是限制MySQL不使用某个索引
- force是强制MySQL使用某个索引
覆盖索引
Extra的信息提示:
using index condition(使用了索引,但是需要回表查询)
using where ;using index(同样使用了索引,并且数据全在索引列中,不需要回表查询)
前缀索引
当字段类型是字符串时,建立索引时,可能出现索引太长的情况。这时就可以使用前缀索引,也就是截取该字段数据的部分前缀,建立索引。能够大大减少索引空间,提高索引效率。
语法
create index idx_xxxx on 表名(字段名(n))
--n表示截取的字段前缀长度
索引的选择性
选择性表示,所建立的不重复的索引值与数据表的记录总数的比值,这个比值越大代表索引的选择性越好,主键的选择性为1,性能是最好的
查询流程
如上表,邮件字段是字符串类型,并且比较长,所以在建立索引时会比较占用空间,我们可以考虑使用前缀索引,比如我想实现下面的查询:
select *from users where email = 'lvbu666@163.com';
我们发现其实只需要email的前5位就可以将需要的信息查询出来,并且选择性为1,所以查询流程就会是下面这样:
建立一个前缀索引作为辅助索引,然后找到匹配的数据,然后找到数据的行号,在通过row进行回表查询:
思考题
最优的方案应该是,建立一个username和password的联合索引,zhegelianhesuoyin是一个二级索引,而二级索引的B+Tree的叶子结点下面挂的是id