“MySQL合集-索引详解” 网站介绍
背景知识
- MySQL数据存储在磁盘里,影响查询速度的根本是磁盘的读写。
- 局部性原理:CPU访问存储器时,无论是存取指令还是存取数据,所访问的存储单元都趋于聚集在一个较小的连续区域中。
- 磁盘IO是非常高昂的操作,计算机操作系统做了一些优化,当一次IO时,不光把当前磁盘地址的数据,而是把相邻的数据也都读取到内存缓冲区内,因为局部预读性原理告诉我们,当计算机访问一个地址的数据的时候,与其相邻的数据也会很快被访问到。每一次IO读取的数据我们称之为一页(page)。具体一页有多大数据跟操作系统有关,一般为4k或8k,也就是我们读取一页内的数据时候,实际上才发生了一次IO。
为什么要使用索引
提高查询效率
- 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。
- 可以大大加快 数据的检索速度(大大减少的检索的数据量), 这也是创建索引的最主要的原因。
- 帮助服务器避免排序和临时表。
- 将随机IO变为顺序IO。
- 可以加速表和表之间的连接,特别是在实现数据的参考完整性方面特别有意义。
为什么索引能提高查询速度
先从 MySQL 的基本存储结构说起
MySQL的基本存储结构是页(记录都存在页里边):
编辑
编辑
- 各个数据页可以组成一个双向链表
- 每个数据页中的记录又可以组成一个单向链表
- 每个数据页都会为存储在它里的记录生成一个页目录,在通过主键查找某条记录的时候可以在页目录中使用二分法快速
定位到对应的槽,然后再遍历该槽对应分组中的记录即可快速找到指定的记录
- 以其他列(非主键)作为搜索条件:只能从最小记录开始依次遍历单链表中的每条记录。
所以说,如果我们写select * from user where name = 'xxx'这样没有进行任何优化的sql语句,默认会这样做:
- 定位到记录所在的页:需要遍历双向链表,找到所在的页
- 从所在的页内中查找相应的记录:由于不是根据主键查询,只能遍历所在页的单链表了
很明显,在数据量很大的情况下这样查找会很慢,这样的时间复杂度为O(n)。
使用索引之后
索引做了些什么可以让我们查询加快速度呢?其实就是将无序的数据变成有序(相对):
编辑
要找到id为8的记录简要步骤:
编辑
很明显的是:没有用索引我们是需要遍历双向链表来定位对应的页,现在通过 “目录” 就可以很快地定位到对应的页上了!(二分查找,时间复杂度近似为O(logn))
(存储引擎)MyISAM 和 InnoDB 的区别
区别:
1. InnoDB 支持事务,MyISAM 不支持事务。这是 MySQL 将默认存储引擎从 MyISAM 变成 InnoDB 的重要原因之一;
2. InnoDB 支持外键,而 MyISAM 不支持。对一个包含外键的 InnoDB 表转为 MYISAM 会失败;
3. InnoDB 是聚集索引,MyISAM 是非聚集索引。聚簇索引的文件存放在主键索引的叶子节点上,因此 InnoDB 必须要有主键,通过主键索引效率很高。但是辅助索引需要两次查询,先查询到主键,然后再通过主键查询到数据。因此,主键不应该过大,因为主键太大,其他索引也都会很大。而 MyISAM 是非聚集索引,数据文件是分离的,索引保存的是数据文件的指针。主键索引和辅助索引是独立的。
4. InnoDB 不保存表的具体行数,执行 select count(*) from table 时需要全表扫描。而MyISAM 用一个变量保存了整个表的行数,执行上述语句时只需要读出该变量即可,速度很快;
5. InnoDB 最小的锁粒度是行锁,MyISAM 最小的锁粒度是表锁。一个更新语句会锁住整张表,导致其他查询和更新都会被阻塞,因此并发访问受限。这也是 MySQL 将默认存储引擎从 MyISAM 变成 InnoDB 的重要原因之一;
如何选择:
1. 是否要支持事务,如果要请选择 InnoDB,如果不需要可以考虑 MyISAM;
2. 如果表中绝大多数都只是读查询,可以考虑 MyISAM,如果既有读写也挺频繁,请使用InnoDB。
3. 系统奔溃后,MyISAM恢复起来更困难,能否接受,不能接受就选 InnoDB;
4. MySQL5.5版本开始Innodb已经成为Mysql的默认引擎(之前是MyISAM),说明其优势是有目共睹的。如果你不知道用什么存储引擎,那就用InnoDB,至少不会差。
Mysql索引主要使用的数据结构
常见的MySQL主要有两种结构:Hash索引和B+ Tree索引,我们一般使用的是InnoDB引擎,默认的是B+树.
哈希
哈希索引在Mysql中确实存在,memory存储引擎下显示支持哈希索引
编辑
B树
B-Tree Visualization
B+树
B+ Tree Visualization
对比:
- 如果是等值查询,那么哈希索引明显有绝对优势,因为只需要经过一次算法即可找到相应的键值;当然了,这个前提是,键值都是唯一的。如果键值不是唯一的,就需要先找到该键所在位置,然后再根据链表往后扫描,直到找到相应的数据;
- Hash索引底层是哈希表,哈希表是一种以key-value存储数据的结构,所以多个数据在存储关系上是完全没有任何顺序关系的,所以,对于区间查询是无法直接通过索引查询的,就需要全表扫描。所以,哈希索引只适用于等值查询的场景。而B+ Tree是一种多路平衡查询树,所以他的节点是天然有序的(左子节点小于父节点、父节点小于右子节点),所以对于范围查询的时候不需要做全表扫描。
- 同理,哈希索引也没办法利用索引完成排序,以及like ‘xxx%’ 这样的部分模糊查询(这种部分模糊查询,其实本质上也是范围查询);
- 哈希索引也不支持多列联合索引的最左匹配规则;
- B+树索引的关键字检索效率比较平均,不像哈希那样波动幅度大,在有大量重复键值情况下,哈希索引的效率也是极低的,因为存在所谓的哈希碰撞问题。
单列索引 与 复合索引
只包含一个字段的索引叫做单列索引,包含两个或以上字段的索引叫做复合索引(或组合索引)。建立复合索引时,字段的顺序极其重要。
下面这个SQL语句在 列X,列Y,列Z 上建立了一个复合索引。
CREATE INDEX 索引名 ON 表名(列名X, 列名Y, 列名Z);
其实这相当于建立了三个索引,分别是:
1、单列索引(列X) 2、复合索引(列X, 列Y) 3、复合索引(列X,列Y,列Z)
聚簇索引、非聚簇索引
InnoDB的B+ Tree可能存储的是整行数据,也有可能是主键的值。索引B+ Tree的叶子节点存储了整行数据的是主键索引,也被称之为聚簇索引。而索引B+ Tree的叶子节点存储了主键的值的是非主键索引,也被称之为非聚簇索引。
聚簇索引查询会更快,主键索引树的叶子节点直接就是我们要查询的整行数据了。而非主键索引的叶子节点是主键的值,查到主键的值以后,还需要再通过主键的值再进行一次查询(回表)。
覆盖索引
如果一个索引包含(或者说覆盖)所有需要查询的字段的值,我们就称之为“覆盖索引”。我们知道InnoDB存储引擎中,如果不是主键索引,叶子节点存储的是主键+列值。最终还是要“回表”,也就是要通过主键再查找一次。这样就会比较慢覆盖索引就是把要查询出的列和索引是对应的,不做回表操作。
覆盖索引使用实例
创建了索引(username,age),我们执行下面的 sql 语句
select username , age from user where username = 'Java' and age = 22
在查询数据的时候:要查询出的列在叶子节点都存在!所以,就不用回表。
最左匹配原则
MySQL中的索引可以以一定顺序引用多列,这种索引叫作联合索引。如User表的name和age加联合索引就是(name,age),而最左前缀原则指的是,如果查询的时候查询条件精确匹配索引的左边连续一列或几列,则此列就可以被用到。如下:
select * from user where name=xx and age=xx ; //可以命中索引
select * from user where name=xx ; // 可以命中索引
select * from user where age=xx ; // 无法命中索引
select * from user where age=xx and name=xx ; //可以命中索引
查询的时候如果两个条件都用上了,但是顺序不同,如 age= xx and name =xx,那么现在的查询引擎会自动优化为匹配联合索引的顺序,这样是能够命中索引的。
如有索引(a, b, c, d),查询条件a = 1 and b = 2 and c > 3 and d = 4,则会在每个节点依次命中a、b、c,无法命中d。(会一直向右匹配直到遇到范围查询(>,<,BETWEEN,LIKE)就停止匹配)
由于最左前缀原则,在创建联合索引时,索引字段的顺序需要考虑字段值去重之后的个数,较多的放前面。ORDER BY子句也遵循此规则。
查询优化器
一条SQL语句的查询,可以有不同的执行方案,至于最终选择哪种方案,需要通过优化器进行选择,选择执行成本最低的方案。 在一条单表查询语句真正执行之前,MySQL的查询优化器会找出执行该语句所有可能使用的方案,对比之后找出成本最低的方案。这个成本最低的方案就是所谓的执行计划。 优化过程大致如下: 1、根据搜索条件,找出所有可能使用的索引 2、计算全表扫描的代价 3、计算使用不同索引执行查询的代价 4、对比各种执行方案的代价,找出成本最低的那一个
索引下推
索引条件下推优化可以减少存储引擎查询基础表的次数,也可以减少MySQL服务器从存储引擎接收数据的次数。
- 在开始之前先先准备一张用户表(user),其中主要几个字段有:id、name、age、address。建立联合索引(name,age)。
- 假设有一个需求,要求匹配姓名第一个为张的所有用户,sql语句如下:
SELECT * from user where name like '张%'
- 根据 "最佳左前缀" 的原则,这里使用了联合索引(name,age)进行了查询,性能要比全表扫描肯定要高。
- 问题来了,如果有其他的条件呢?假设又有一个需求,要求匹配姓名第一个字为张,年龄为20岁的用户,此时的sql语句如下:
SELECT * from user where name like '张%' and age=20
- 这条sql语句应该如何执行呢?下面对Mysql5.6之前版本和之后版本进行分析。
Mysql5.6之前的版本
- 5.6之前的版本是没有索引下推这个优化的,因此执行的过程如下图:
编辑
- 会忽略age这个字段,直接通过name进行查询,在(name,age)这课树上查找到了两个结果,id分别为2,1,然后拿着取到的id值一次次的回表查询,因此这个过程需要回表两次。
Mysql5.6及之后版本
- 5.6版本添加了索引下推这个优化,执行的过程如下图:
编辑
- InnoDB并没有忽略age这个字段,而是在索引内部就判断了age是否等于20,对于不等于20的记录直接跳过,因此在(name,age)这棵索引树中只匹配到了一个记录,此时拿着这个id去主键索引树中回表查询全部数据,这个过程只需要回表一次。
总结
- 索引下推在非主键索引上的优化,可以有效减少回表的次数,大大提升了查询的效率。
选择索引和编写利用这些索引的查询的原则
- 单行访问是很慢的。特别是在机械硬盘存储中(SSD的随机I/O要快很多,不过这一点仍然成立)。如果服务器从存储中读取一个数据块只是为了获取其中一行,那么就浪费了很多工作。最好读取的块中能包含尽可能多所需要的行。使用索引可以创建位置引,用以提升效率。
- 按顺序访问范围数据是很快的,这有两个原因。第一,顺序 I/O 不需要多次磁盘寻道,所以比随机I/O要快很多(特别是对机械硬盘)。第二,如果服务器能够按需要顺序读取数据,那么就不再需要额外的排序操作,并且GROUPBY查询也无须再做排序和将行按组进行聚合计算了。
- 索引覆盖查询是很快的。如果一个索引包含了查询需要的所有列,那么存储引擎就 不需要再回表查找行。这避免了大量的单行访问,而上面的第1点已经写明单行访 问是很慢的。
关于索引其他重要的内容补充
注意避免冗余索引
冗余索引指的是索引的功能相同,能够命中 就肯定能命中 ,那么 就是冗余索引如(name,city )和(name )这两个索引就是冗余索引,能够命中后者的查询肯定是能够命中前者的 在大多数情况下,都应该尽量扩展已有的索引而不是创建新索引。
MySQL 5.7 版本后,可以通过查询 sys 库的 schema_redundant_indexes 表来查看冗余索引
索引这么多优点,为什么不对表中的每一个列创建一个索引呢?
- 当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,这样就降低了数据的维护速度。
- 索引需要占物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要建立聚簇索引,那么需要的空间就会更大。
- 创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加。
使用索引的注意事项
- 在经常需要搜索的列上,可以加快搜索的速度;
- 在经常使用在WHERE子句中的列上面创建索引,加快条件的判断速度。
- 在经常需要排序的列上创 建索引,因为索引已经排序,这样查询可以利用索引的排序,加快排序查询时间;
- 对于中到大型表索引都是非常有效的,但是特大型表的话维护开销会很大,不适合建索引
- 在经常用在连接的列上,这 些列主要是一些外键,可以加快连接的速度;
- 避免 where 子句中对字段施加函数,这会造成无法命中索引。
- 在使用InnoDB时使用与业务无关的自增主键作为主键,即使用逻辑主键,而不要使用业务主键。
- 将某一列设置为default null,where 是可以走索引,另外索引列是否设置 null 是不影响性能的。 但是,还是不建议列上允许为空。最好限制not null,因为null需要更多的存储空间并且null值无法参与某些运算。
NULL 值的索引查找流程参考:https://juejin.im/post/5d5defc2518825591523a1db - 删除长期未使用的索引,不用的索引的存在会造成不必要的性能损耗 MySQL 5.7 可以通过查询 sys 库的 chema_unused_indexes 视图来查询哪些索引从未被使用
- 在使用 limit offset 查询缓慢时,可以借助索引来提高性能
- 最近发表
-
- 联想推出 IdeaPad 14s / 15s:均为 3399 元,运行 Win11 系统
- 顶配版ThinkPad X1 Carbon评测
- 联想ThinkPad X1 Carbon评测
- lenovo联想 拯救者-14 加装ssd、内存及win10转移到ssd经验谈
- 联想发布新款耳机鼠标等配件:专为ThinkPad X1设计
- 杜比全景音体验,联想K4 Note在印度正式发布
- 3099 元起,联想推出 IdeaPad 15:约10小时续航,预装 Win11系统
- 联想拯救者Y70、小新Pad Pro 2022发布丨拯救者Y70测评体验
- 千元可定制!联想K4 Note印度发布:杜比全景音体验
- 声临其境 联想TAB2 A10平板联手杜比
- 标签列表
-
- serv-u 破解版 (6)
- 极域电子教室2009 (6)
- 6300主题下载 (1)
- oracle11204下载 (1)
- c++论坛 (14)
- huaweiupdateextractor (4)
- thinkphp6下载 (7)
- 前端论坛 (11)
- mysql 时间索引 (13)
- mydisktest_v298 (35)
- unlocker208 (1)
- sql 日期比较 (33)
- document.appendchild (35)
- 头像打包下载 (35)
- 二调符号库 (23)
- oppoa5专用解锁工具包 (8)
- acmecadconverter_8.52绿色版 (25)
- oracle timestamp比较大小 (7)
- chm editor破解版 (7)
- throttlestop防止降频 (9)
- f12019破解 (16)
- 流星蝴蝶剑修改器 (18)
- pygame中文手册 (2)
- 联想杜比音效驱动下载 (10)