索引
1.索引概述
索引是帮助MySQL高效获取数据的数据结构(有序)。在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法,这种数据结构就是索引。
优点 | 缺点 |
---|---|
提高数据检索效率,降低IO成本 | 索引需要占有一定空间 |
通过索引列对数据进行排序,降低数据排序的成本,降低CPU的消耗 | 降低了更新表的速度 |
2.索引结构
MySQL的索引实在存储引擎层实现的,不同的存储引擎有不同的索引结构
索引结构 | 描述 |
---|---|
B+Tree索引 | 最常见的索引类型,大部分引擎都支持B+索引 |
Hash索引 | 底层是Hash表实现的,只支持精确匹配,不支持范围查询和排序 |
R- Tree(空间索引) | 空间索引是MyISAM引擎的一个特殊索引类型,主要用于地理空间数据类型 |
Full-text(全文索引) | 是一种通过建立倒排索引,快速匹配文档的方式,类似于Lucene,Solr, ES |
索引 | InnoDB | MyISAM | Memory |
---|---|---|---|
B+索引 | 支持 | 支持 | 支持 |
Hash索引 | 不支持 | 不支持 | 支持 |
R-tree索引 | 不支持 | 支持 | 不支持 |
Full-text | 5.6之后支持 | 支持 | 不支持 |
2.1 B-Tree (多路平衡查找树)
2.2 B+Tree
B+Tree展示网站
和B-Tree的区别:
1.数据都存在叶子节点
2.叶子节点形成一个单向链表
Mysql
优化:叶子节点是双向链表
2.3 Hash索引
经过hash算法将键值换成新的hash值,映射到对应的槽位上,然后存储在hash表中
特点:
1.Hash索引只能用于对等比较(=,in),不支持范围查询(between,>,<,…)
2.无法利用索引完成排序操作
3.查询效率高,通常只需要一次检索就可以了,效率通常要高于B+Tree索引
为什么InnoDB存储引擎使用B+Tree索引结构?
1.相对于二叉树,层级更少,搜索效率高
2.对于B树,无论是叶子节点非叶子节点都需要存储数据,这样导致一页中可以存储的键减少,指针数减少,要想保存大量的数据,只能增加树的高度,降低了性能。
3.相对于Hash索引,B+Tree支持范围以及排序操作
3.索引分类
分类 | 含义 | 特点 | 关键字 |
---|---|---|---|
主键索引 | 针对表中主键所创建的索引 | 默认自动创建,只能有一个 | PRIMARY |
唯一索引 | 避免同一个表中某数据列中的值重复 | 可以有多个 | UNIQUE |
常规索引 | 快速定位特定数据 | 可以有多个 | |
全文索引 | 全文索引查找文本中的关键字,而不是比较索引中的值 | 可以有多个 | FULLTEXT |
根据索引的存储形式分类
分类 | 含义 | 特点 |
---|---|---|
聚集索引(Clustered Index) | 将数据和索引放在一起,索引结构的叶子节点保存了行数据 | 必须有,只能有一个 |
二级索引(Secondary index) | 将数据和索引分开存储,索引结构的叶子节点关联的是对应的主键 | 可以存在多个 |
聚集索引选取规则
- 如果存在索引,主键索引就是聚集索引
- 如果不存在主键,将使用第一个唯一索引(UNIQUE)作为聚集索引
- 如果没有主键和唯一索引,则InnoDB会自动生成一个rowid作为隐藏的聚集索引
回表查询
4.索引语法
4.1 创建索引
CREATE [UNIQUE|FULLTEXT] INDEX index_name ON table_name (index_col_name,...);
4.2 查看索引
SHOW INDEX FROM table_name;
4.3 删除索引
DROP INDEX index_name ON table_name;
5.SQL性能分析
5.1 SQL执行频率
MySQL客户端连接成功后,通过命令可以提供服务器状态信息。通过以下指令,可以查看当前数据库INSERT、UPDATE、DELETE、SELECT执行频率
SHOW GLOBAL STATUS LIKE 'Com_______';
5.2 慢查询日志
慢查询日志记录了所有执行时间超过指定参数(long_query_time, 单位:秒,默认:10秒)的所有SQL语句的日志
MSQL的慢查询日志默认没有开启
查看慢查询日志是否开启:
show variables like 'slow_query_log';
开启需要在MySQL的配置文件(/etc/my.cnf)中配置如下信息:
# 开启MySQL慢查询日志的开关
slow_query_log=1
# 设置慢日志的时间为2秒,SQL语句执行时间超过2秒,就会被视为慢查询,记录慢查询日志
long_query_time=2
当设置开启慢查询日志后,在 /var/lib/mysql/ 文件夹下会生成一个 ****-slow.log 的日志文件(****取决于你服务器)
5.3 profile
show profiles;
show profiles 能够在做优化时帮助我们了解时间都耗费到哪了。
have_profiling 参数,能够看到当前MySQL是否支持 profile操作
select @@have_profiling;
默认profiling是关闭的,可以通过set语句在session/global级别开启profiling
set profiling = 1;
查询所有语句耗费的时间
show profiles;
查询具体语句的耗时
show profiles for query 1;
5.4 explain执行计划
explain + sql语句 显示sql的执行计划
explain select....
explain执行计划各字段的含义
- id:
select查询的序列号,表示查询中执行select子句或者是操作表的顺序(id相同,执行顺序从上到下;id不同,值越大,越先执行) - select_type
表示select的类型,常见的取值有simple(简单表,即不使用表连接和子查询)、primary(主查询,即外层查询)、union(union中的第二个或者后面的查询语句)、subquery(select/where之后包含子查询)等 - type
表示连接类型,性能有好到差的连接类型为NULL、system、const、eq_ref、ref、range、index、all。 - possible_keys
表中可能用到的索引 - key
用到的索引 - key_len
索引的长度 - rows
MySQL认为必须要执行查询的行数,在innoDB引擎中,是一个预估值,可能并不总是准确的 - filtered
表示返回结果的行数站总读取行数的百分比,filtered的值越大越好
6.索引的使用
6.1 最左前缀法则
如果索引了多列(联合索引),要遵守最左前缀法则。最左前缀法则指的是查询从索引的最左列开始,并且不跳过索引中的列
如果跳跃某一列,索引将部分失效(后面的索引失效)
这里有一个联合索引,分别是由real_name, phone, email组成,我们看下面几个sql
(1)最左前缀法则
select * from sys_user where real_name = 'xzx' and phone = '19826520135' and email = '1493012739@qq.com';
这个SQL走了联合索引,索引长度为777
(2)最左前缀法则
select * from sys_user where real_name = 'xzx' and phone = '19826520135';
因为没有跳过某一列,依然遵守最左匹配法则,用到了联合索引,索引长度变成518,长度减少
(3)最左前缀法则
select * from sys_user where real_name = 'xzx';
依然遵守最左匹配法则,用到了联合索引,索引长度变成259,长度依旧减少
(4)最左前缀法则
select * from sys_user where phone = '19826520135' and email = '1493012739@qq.com';
最左边的列没有出现,不满足最左匹配法则,全表扫描
(5)最左前缀法则
select * from sys_user where email = '1493012739@qq.com';
不满足最左匹配法则,全表扫描
(6)最左前缀法则
select * from sys_user where real_name = 'xzx' and email = '1493012739@qq.com';
走索引,但不完全,索引长度259, 只有real_name走索引,因为phone没有出现,所以email失效
(7)索引跟顺序的关系
select * from sys_user where phone = '19826520135' and email = '1493012739@qq.com' and real_name = 'xzx';
索引全部生效,跟创建顺序有关,跟查询顺序无关
(8)> 和 >= 的区别
select * from sys_user where real_name = 'xzx' and phone = '19826520135' and email = '1493012739@qq.com';
select * from sys_user where real_name = 'xzx' and phone >= '19826520135' and email = '1493012739@qq.com';
两个sql对比,>范围查询会使后面的索引失效,>=则不会
(9)对索引字段进行函数运算
这里对phone有一个单列索引
select * from sys_user where substring(phone, 10, 2) = '35';
对phone进行截取函数
未命中索引,函数运算不触发索引
(9)字符串不加单引号
select * from sys_user where phone = '35';
索引失效,字符串类型不加单引号
(10)%模糊搜索
select * from sys_user where phone like '%35';
select * from sys_user where phone like '35%';
前面加%索引失效,后面加%索引命中
(11)or连接的条件
用 or 分割开的条件,如果or前的条件中的列没有索引,那么设计的索引都不会用到
select * from sys_user where id = '1' or phone = '19826520135';
select * from sys_user where id = '1' or email ='1493012739@qq.com';
id主键索引,phone单列索引,email没有索引
要么都有索引,都生效,要么都没有,有一个都不生效
(12)mysql自己评估
有时候失效,mysql认为没必要用索引
7.SQL提示
SQL提示,是优化数据库的一个重要手段,简单来说,就是在SQL语句中加入一些人为的提示来达到优化操作的目的。
说白了,就是给sql提示
use index 用哪个索引 (只是建议mysql,mysql可以不听)
select * from sys_user use index(index_real) where real_name = 'xzx';
ignore index 不用哪个索引
select * from sys_user ignore index(index_real_phone_email) where real_name = 'xzx';
force index 必须用哪个索引
select * from sys_user force index(index_real) where real_name = 'xzx';
8.覆盖索引
尽量使用覆盖索引(查询使用了索引,并且需要返回的列,在该索引中已经全部能找到),减少select *
9.前缀索引
当字段类型为字符串(varchar,text等)时,有时候需要索引很长的字符串,这会让索引变得很大,查询时,浪费大量的磁盘IO,
影响查询效率。此时可以只将字符串的一部分前缀,建立索引,这样可以大大节约索引空间,从而提高索引效率。
语法
create index 索引名称 on 表名(列名(长度))
前缀长度
可以根据索引的选择性来决定,而选择性是指不重复的索引值(基数)和数据表的记录总数的比值,索引选择性越高则查询效率越高,唯一索引的选择性是1,这是最好的索引选择性,性能也是最好的。
select count(distinct substring(计算字段,截取点,截取长度))/count(*) from 表名;