目录
1.介绍
索引是帮助MySQL高效获取数据的数据结构(有序)。在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法,这种数据结构就是索引
2.索引概述
2.1.优缺点
优势:
-
提高数据检索的效率,降低数据库的IO成本
-
通过索引列对数据进行排序,降低数据排序的成本,降低CPU的消耗
劣势:
-
索引列占用空间(磁盘中的ibd文件、MYI文件)
-
索引提高查询效率的同时也降低更新表的速度,如对表进行INSERT、UPDATE、DELETE时,效率降低
3.索引结构
索引是在第三层,存储引擎层实现的。根据存储引擎的不同,索引的结构也会有所不同
-
B+Tree索引
最常见的索引类型,大部分引擎都支持B+树索引
-
Hash索引
底层数据结构是用哈希表实现的,只有精确匹配索引列的查询才有效,不支持范围查询
-
R-tree(空间索引)
空间索引是MyISAM引擎的一个特殊索引类型,主要用于地理空间数据类型,通常使用较少
-
Full-text(全文索引)
是一种通过建立倒排索引,快速匹配文档的方式。类似于Lucene、Solr、ES
3.1.B+Tree索引
我们重点研究InnoDB引擎的B+树存储结构
这是经典的的B+Tree
相对于B-Tree的区别是:
-
所有的数据都会出现在叶子节点
-
叶子节点形成一个单向链表
B+Tree
MySQL索引数据结构对经典的B+Tree进行了优化。在原B+Tree的基础上,增加一个指向相邻叶子节点的链表指针,就形成了带有顺序指针的B+Tree,提高区间访问的性能
3.2.Hash索引
哈希索引就是采用一定的hash算法,将键值换算成新的hash值,映射到对应的槽位上,然后存储在hash表中
如果多个键值映射到相同的槽位上,它们就产生了hash冲突(hash碰撞),可以通过链表解决
Hash索引的特点:
-
Hash索引只能用于对等比较(=、in),不支持范围查询(between、>、<)
-
无法利用索引完成排序操作
-
查询效率高,通常只需要一次检索就可以了,效率通常要高于B+Tree索引
不同存储引擎对hash索引的支持情况:
在MySQL中,仅Memory引擎支持hash索引。但InnoDB具有自适应hash功能,hash索引是存储引擎根据B+Tree索引在指定条件下自动构建的
4.索引分类
在InnoDB存储引擎中,根据索引的存储形式,又可以分为以下两种
聚集索引的存取规则:
-
如果存在主键,主键索引就是聚集索引
-
如果不存在主键,将使用第一个唯一(UNIQUE)索引作为聚集索引
-
如果表没有主键,或没有合适的唯一索引,则InnoDB会自动生成一个rowid作为隐藏的聚集索引
聚集索引的叶子节点下面挂着这一行的数据
二级索引的叶子节点下面挂着id
回表查询
先到二级索引查找对应的主键值,再回到聚集索引根据主键值查找对应的行数据数据
5.索引语法
5.1.创建索引
CREATE [UNIQUE|FULLTEXT] INDEX index_name ON table_name (index_col_name, ...);
括号中的内容为表中的字段,一个索引是可以关联多个字段的,如果一个索引只关联一个字段,这种索引称为单列索引,如果关联多个字段,这种索引称为联合索引或组合索引
5.2.查看索引
SHOW INDEX FROM table_name
5.3.删除索引
DROP INDEX index_name ON table_name;
6.SQL性能分析
为了进行SQL优化(主要优化查询语句),我们学习SQL性能分析
SQL执行频率
SQL优化对增删改的作用微乎其微,我们首先要分析增删改查在当前数据库所占的比例
通过如下指令,我们可以查看当前数据库的INSERT、UPDATE、DELETE、SELECT的访问频次
SHOW [session][global] STATUS LIKE 'Com_______';
一个下划线就代表一个字符
操作与对应次数
如果发现当前数据库的操作中查询占据了绝大部分,我们就要对当前数据库进行优化
我们要优化哪些语句呢?
可以通过慢查询日志定位SQL语句
6.1.慢查询日志
慢查询日志记录了所有执行时间超过指定参数(long_query_time,单位:秒,默认10秒)的所有SQL语句的日志
MySQL的慢查询日志默认没有开启,需要在MySQL的配置文件(/etc/my.cnf)中配置信息
-
查询慢查询日志开启状态
SHOW VARIABLES LIKE 'slow_query_log';
-
添加配置信息
打开my.cnf配置文件,添加如下配置
#开启MySQL慢日志查询开关
slow_query_log=1
#设置慢日志的时间为2秒,SQL语句执行时间超过2秒,就被视为慢查询,记录慢查询日志
long_query_time=2
配置完毕之后,通过以下指令重新启动MySQL服务器进行测试
systemctl restart mysqld
查看慢日志文件中记录的信息 /var/lib/mysql/local-slow.log
6.2.profile详情
在慢查询中记录的SQL语句是执行过程中耗时超过了预设的指定时间之后才会记录,比如预设的指定时间是2秒,超过了2秒才会记录在慢查询日志当中。加入某些SQL语句耗时1.8秒,这样的语句是不会记录在慢查询日志当中的。加入一些语句的业务很简单,但是执行达到了1.9秒,那这样的SQL性能也是较低的,我们需要定位到这样的SQL。
可以借助profile详情定位
SHOW PROFILES;
SHOW PROFILES能够在做SQL优化时帮助我们了解时间都耗费到哪里去了。通过have_profiling参数,能够看到当前MySQL是否支持profile操作:
SELECT @@have_profiling;
默认profiling是关闭的,可以通过set语句在session/global级别开启profiling;
SET profiling=1;
6.3.explain执行计划
EXPLAIN或者DESC命令获取MySQL如何执行SELECT语句的信息,包括在SELECT语句执行过程中表如何连接和连接的顺序
语法:
#直接在select语句之前加上关键字explain/desc
EXPLAIN SELECT 字段列表 FROM 表名 WHERE 条件;
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_key
显示可能应用在这张表上的索引,一个或多个
-
Key
实际使用的索引,如果为NULL,则没有使用索引
-
Key_len
表示索引中使用的字节数,该值为索引字段最大可能长度,并非实际使用长度,在不损失精确性的前提下,长度越短越好
-
rows
MySQL认为必须要执行查询的行数,在InnoDB引擎的表中,是一个估计值,可能并不总是准确的
-
filtered
表示返回结果的行数占需读取行数的百分比,filtered的值越大越好
7.索引使用
7.1索引使用原则
7.1.1.最左前缀法则
如果索引了多列(联合索引),要遵守最左前缀法则。最左前缀法则指的是查询从索引的最左列开始,并且不跳过索引中的列
如果跳过某一列,索引将部分失效(后面的字段索引失效)
7.1.2.索引失效情况
-
范围查询
联合索引中,出现范围查询(>、<),范围查询右侧的列索引失效。如果使用>=、<=则右侧不会失效
-
索引列运算
不要在索引列上进行运算操作,这将导致索引失效
-
字符串不加引号
字符串类型字段使用时不加引号,这将导致索引失效
-
模糊查询
如果仅仅是尾部模糊匹配,索引不会失效。如果是头部模糊匹配,索引失效
-
or连接的条件
用or分隔开的条件,如果or的一侧的列有索引,而另一侧的列没有索引,那么涉及的索引都不会被用到
-
数据分布影响
如果MySQL评估使用索引比全表更慢,则不使用索引
7.2.SQL提示
SQL提示是优化数据库的一个重要手段,简单来说,就是在SQL语句中加入一些人为的提示来达到优化的目的
use index:建议使用
EXPLAIN SELECT * FROM tb_user USE index(idx_user_pro) WHERE profession='软件工程';
ignore index:禁止使用
EXPLAIN SELECT * FROM tb_user IGNORE index(idx_user_pro_age_sta) WHERE profession='软件工程';
force index:强制使用
EXPLAIN SELECT * FROM tb_user FORCE index(idx_user_pro_age_sta) WHERE profession='软件工程';
7.3.覆盖索引
尽量使用覆盖索引(查询过程中使用了索引,并且查询需要返回的列在该索引中已经全部能够找到字段值),减少select *的使用
select *极易出现回表查询的情况
using index condition:查找使用了索引,但是需要回表查询数据
using where; using index:查找使用了索引,但是需要的数据都在索引列中能找到,所以不需要回表查询数据
7.4.前缀索引
当字段类型为字符串(varchar,text等)时,有时候需要索引很长的字符串,这会让索引变得很大,查询时,浪费大量的磁盘IO,影响查询效率。此时可以只将字符串的一部分前缀建立索引,这样可以大大节约索引空间,从而提高索引效率
CREATE INDEX idx_xxxx ON table_name(colum(n));
n为10则提取前10个字符
-
前缀长度
可以根据索引的选择性来决定,而选择性是指不重复的索引值(基数)和数据表的记录总数的比值,索引选择性越高则查询效率越高,唯一索引的选择性是1,这是最好的索引选择性,性能也是最好的
SELECT COUNT(distinct email)/COUNT(*) FROM tb_user;
SELECT COUNT(distinct substring(email,1,5))/COUNT(*) FROM tb_user;
7.5.单列索引与联合索引
单列索引:一个索引只包含单个列
联合索引:一个索引包含了多个列
在业务场景中,如果存在多个查询条件,考虑针对于查询字段建立索引时,建议建立联合索引,而非单列索引
多条联合查询时,MySQL优化器会评估哪个字段的索引效率更高,会选择该索引完成本次查询
8.索引设计原则
-
针对数据量较大,且查询比较频繁的表建立索引
-
针对于常作为查询条件(where)、排序(order by)、分组(group by)操作的字段建立索引
-
尽量选择区分度高的列作为索引,尽量建立唯一索引,区分度越高,使用索引的效率越高
-
如果是字符串类型的字段,字段的长度较长,可以针对于字段的特点,建立前缀索引
-
尽量使用联合索引,减少单列索引,查询时联合索引很多时候可以覆盖索引,节省存储空间,避免回表,提高查询效率
-
要控制索引的数量,索引并不是多多益善,索引越多,维护索引结构的代价也就越大,会影响增删改的效率
-
如果索引列不能存储NULL值,请在创建表时使用NOT NULL约束它。当优化器知道每一列是否包含NULL值时,它可以更好地确定哪个索引最有效地用于查询