MySQL索引的学习(二)
摘要:本文详细介绍了MySQL索引的相关知识,包括索引概述、优缺点、结构分类(B+树、哈希等)、语法操作及性能分析方法。重点讲解了B+树索引的特性、聚集索引选取原则、索引使用规则(如最左前缀原则、范围查询影响等)以及SQL性能分析工具(慢查询日志、PROFILE、EXPLAIN)。文章还提供了索引创建、查看和删除的具体语法,并分析了可能导致索引失效的常见场景,为数据库优化提供了实用指导。
·
一、索引
1.1、概述:
索引是一种数据结构(有序),帮助MySQL高效获取数据
1.2、优缺点:
| 优点 | 缺点 |
|---|---|
| 提高数据的查找效率 | 索引也要占用空间 |
| 通过索引列对数据进行排序,降低数据的排序成本,降低CPU的消耗 | 索引会降低插入、删除和修改的性能 |
1.3、索引结构:
索引是在存储引擎层实现的,不同的存储引擎支持的索引类型也不一样。主要包含以下几种:
| 索引结构 | 描述 |
|---|---|
| B+树索引 | 最常见的索引类型,大部分引擎都支持B+树索引 |
| hash索引 | 底层数据结构是用哈希表实现的,只有精确匹配索引列的查询才有效,不支持范围查询 |
| R-Tree(空间索引) | 空间索引是MyISAM引擎的一个特殊索引类型,主要用于地理空间数据类型,可以支持存储经纬度信息,使用较少 |
| Full-text(全文索引) | 是一种通过建立倒排索引,快速匹配文档的方式,类似于ElasticSearch,主要用于搜索引擎 |
| 索引 | InnoDB | MyISAM | Memory |
|---|---|---|---|
| B+树索引 | 支持 | 支持 | 支持 |
| hash索引 | 不支持 | 不支持 | 支持 |
| R-Tree索引 | 不支持 | 支持 | 不支持 |
| Full-text索引 | 支持 | 支持 | 不支持 |


B+树和B树的区别:
-
- B+树中,只有叶子节点存放数据信息,非叶子节点只存放键值信息
-
- B+树中,所有叶子节点之间,形成了一个单链表

MySQL中,B+树在叶子节点这,还存储了指向下一节点的指针,形成一个双向链表
hash索引:
- 1. hash索引只能用于对等比较(=,in),不支持范围查询(>, <, between)
- 2. 无法利用索引完成排序
- 3. 检索效率非常高,通常一次检索就可以找到数据,通常高于B+树索引
1.4、索引分类:
| 分类 | 含义 | 特点 | 关键字 |
|---|---|---|---|
| 主键索引 | 针对表中主键创建的索引 | 默认自动创建,只能有一个 | PRIMARY |
| 唯一索引 | 针对表中某个字段创建的唯一索引,可以重复多个 | 可以有多个 | UNIQUE |
| 常规索引 | 最基本的索引,没有任何限制 | 可以有多个 | |
| 全文索引 | 针对文本的内容创建的索引 | 可以有多个 | FULLTEXT |
| 根据索引的存储形式,可以分为: | |||
| 分类 | 含义 | 特点 | |
| – | – | – | |
| 聚集索引 | 将数据存储与索引放在一块,索引结构的叶子节点保存了行数量 | 必须有,而且只有一个 | |
| 二级索引 | 将数据与索引分开,索引结构的叶子节点关联的是对应的主键 | 可以有多个 | |
聚焦索引选取原则:
-
- 如果存在主键,则主键索引就是聚集索引
-
- 如果不存在主键,则第一个唯一索引就是聚集索引
-
- 如果1、2都不存在,则InnoDB内部会生成一个隐藏的主键(rowid)作为聚集索引
- 如果1、2都不存在,则InnoDB内部会生成一个隐藏的主键(rowid)作为聚集索引
1.5、索引的语法
-
- 创建索引
CREATE [UNIQUE|FULLTEXT] INDEX 索引名 ON 表名(字段名[长度],...); -- 创建索引,是否时聚焦索引,就看括号里面是一个值还是多个值 -- 比如: CREATE UNIQUE INDEX index_name ON tb_sanguo(name); -
- 查看索引
SHOW INDEX FROM 表名; -- 查看表的索引信息 -
- 删除索引
DROP INDEX 索引名 ON 表名; -- 删除表的索引
1.6、SQL性能分析:
-
- 查看SQL执行频次
SHOW [session | global] STATUS LIKE 'Com_select'; -- 查看SQL执行频次,Com_xxx表示每种类型的命令的次数,session表示当前会话,global表示全局 SHOW STATUS LIKE 'Com_select'; -- 查看SQL执行频次,Com_xxx表示每种类型的命令的次数 -
- 慢查询日志:
慢查询日志记录了所有执行时间超过long_query_time值的SQL,默认的阈值是10秒。
慢查询日志默认是关闭的,可以通过配置文件/etc/my.cnf开启,或者在MySQL中执行以下命令:
SHOW VARIABLES LIKE 'slow_query_log'; -- 查看慢查询日志是否开启 SET [GLOBAL|SESSION] slow_query_log = 1; -- 设置慢查询日志开启配置文件中,修改参数
show_query_log = 1; -- 开启慢查询日志 long_query_time = 2; -- 设置慢查询阈值为2秒查看慢查询日志,路径在
/var/lib/mysql/下,文件名通常是localhost-slow.log
- 慢查询日志:
-
- profile分析SQL执行详情
之前是通过慢查询日志来查看哪些SQL语句超过预设阈值,但对于未超过阈值的某些SQL语句,比如某条查询语句执行1.5S,但需要对其进行优化,慢启动日志就看不了
profile能够在做SQL优化时,帮助了解时间都耗哪里去了,通过have_profiling参数,能够看到当前MySQL是否支持profile功能
SELECT @@have_profiling; -- 查看是否支持profile SET [GLOBAL|SESSION] profiling = 1; -- 设置开启profile, 默认是关闭的 SHOW PROFILES; -- 查看所有SQL的执行时间 SHOW PROFILE FOR QUERY 查询编号; -- 查看指定query_id的SQL的执行详情 SHOW PROFILE FOR QUERY 查询编号, TYPE; -- 查看指定query_id的SQL的详细执行信息,TYPE可以是ALL、BLOCK IO、CONTEXT SWITCHES、CPU、IPC、MEMORY、PAGE FAULTS、SOURCE、SWAPS - profile分析SQL执行详情
-
- explain分析SQL执行计划
前面都是粗略的估算,通过EXPLAN或者DESC命令获取MySQL如何执行SQL语句,包括是否走索引、扫描行数、时间等
-- 在任意的SQL语句前加EXPLAIN或者DESC命令,就可以获取MySQL如何执行该语句的信息 EXPLAIN SELECT * FROM 表名 WHERE 条件; -- 分析SQL执行计划
EXPLAIN个字段解释:
字段 含义 id 查询的序列号,表示查询中执行顺序的标识,ID越大越先执行,id相同,执行顺序从上至下 select_type 查询的类型,比如SIMPLE表示简单查询,PRIMARY表示主查询,SUBQUERY表示子查询等 type 表示连接类型,性能由好到差依次是:NULL、const、eq_ref、ref、range、index、all possible_keys 显示可能应用在这张表上的索引,一个或多个。查询涉及到的字段若存在索引,则该索引将被列出,但不一定被查询实际使用 key 实际使用的索引,如果没有使用索引则为NULL。如果想强制MySQL使用或忽视不使用某个索引,可以在查询中使用FORCE INDEX、IGNORE INDEX key_len 表示索引中使用的字节数,为索引字段最大可能长度,在不损失精确性的情况下,长度越短越好 rows MySQL认为必须要执行查询的行数,是个估算值 filtered 表示返回结果的行数占需要读取行数的百分比,值越大越好 当使用主键或者唯一索引查询时,type是const。
- explain分析SQL执行计划
1.7、使用规则:
- 最左前缀原则:
如果索引了多列(联合索引)时,查询条件必须包含索引最左侧的列,才能触发索引匹配. - 范围查询:
在联合索引中,出现范围查询(>,<)时,范围查询右侧的列表索引失效
反而>=和<=不会失效 - 索引列运算
不要再索引列上进行运算操作,否则索引将失效 - 字符串不加引号
字符串类型字段使用时,不加引号,会导致索引失效 - 模糊查询
如果仅仅时尾部模糊查询,索引不会失效,如果是头部模糊查询,索引会失效 - or连接的条件:
使用or连接的条件,如果一侧有索引,另一侧没有索引,那么涉及的索引都会失效. - 数据分布影响:
如果MySQL评估使用索引比全表扫描还慢,则不会使用索引;比如id >= 10,全表大部分数据都满足这个条件 - SQL提示:
在SQL语句中加入一些人为的提示来达到优化操作的目的
-- use index:告诉MySQL使用哪个索引 explain select * from user use index(idx_user_pro) where id = 1; -- 使用id索引 -- ignore index:告诉MySQL不使用哪个索引 explain select * from user ignore index(idx_user_pro) where id = 1; -- 不使用id索引 -- force index:强制使用哪个索引 explain select * from user force index(idx_user_pro) where id = 1; -- 强制使用id索引 - 覆盖索引:
查询使用了索引,并且返回的列正是索引是包含的全部列(即覆盖索引),减少select *。
Tips:using index condition表示使用了索引,但是需要回表查询数据using where; using index表示使用了索引,需要的数据在索引列中能够找到,不需要回表查询数据 - 前缀索引:
当字段类型为字符串(varchar, text)时,有时候需要前缀索引,这会让索引变得很大,查询时,浪费大量的磁盘IO,影响查询效率。
解决方案:将字符串的一部分前缀,建立索引,减少索引大小,提高查询效率
CREATE INDEX idx_xxx on table_name(字段名(n)); -- 前缀索引,n表示前缀长度 -- 以email字段为例,可以通过以下两条语句来判断,值越大越好,1是最好 SELECT COUNT(DISTINCT(email)) / COUNT(*) FROM tb_user; -- 计算唯一邮箱占比 SELECT COUNT(DISTINCT(SUBSTRING(email, 1, 5))) / COUNT(*) FROM tb_user; -- 计算唯一邮箱前缀占比 - 单列索引和联合索引
- 单列索引:即一个索引只包含单列
- 联合索引:即一个索引包含多个列,根据前面最左前缀原则,在创建联合索引时,应该考虑列的顺序。
1.8、索引设计原则:
-
- 对于数据量较大(百万条记录),并且查询操作比较频繁的表建立索引
-
- 针对于常作为查询条件(where),排序(order by)和分组(group by)操作的字段建立索引
-
- 尽量选择区分度高的列作为索引,尽量建立唯一索引,区分度越高,使用索引的效率越高。
-
- 如果时字符串类型的字段,字段的长度较长,可以建立前缀索引。
-
- 尽量使用联合索引,减少单列索引;查询时,联合索引可以覆盖索引,减少回表查询。
-
- 要控制索引的数量,索引越多,维护索引结构的代价也就越大,会影响增删改的效率。
-
- 如果索引列不能存储NULL值,那么在创建表时使用NOT NULL约束,便于MySQL优化器优化。
二、小结:
- 索引概述:索引是高效获取数据的数据结构。
- 索引结构:B+Tree和hash
- 索引分类:主键索引、唯一索引、普通索引、全文索引;聚集索引、二级索引
- SQL性能分析:执行频次,慢查询日志、profile、explain
三、问题:
3.1、一张表,有四个字段(id, username, password, status),由于数据量大,需要对以下SQL语句进行优化,该如何进行才是最优方案?
select id, username, password from tb_user where username = 'zhangsan'; -- 优化这条SQL语句
方案一:在username上建立索引,但是会触发回表查询,性能较差
方案二:在username和password上建立联合索引, 成立二级索引,二级索引下面就是id,不需要回表查询
3.2、 为什么InnoDB默认使用B+树索引结构?
-
- 相对于二叉树,层级更少,查询效率更高
-
- 对于B树,无论是叶子节点还是非叶子节点,都会保存数据(即key-value),这样导致一页中存储的键值对会相对更少,树的高度会增加,因此检索效率不如B+树高
-
- 相对于hash索引,B+树支持范围匹配及排序操作(双向链表)
- 相对于hash索引,B+树支持范围匹配及排序操作(双向链表)
更多推荐


所有评论(0)