目录
为啥要写这篇?
一、索引到底是啥?
二、MySQL有哪些索引,使用索引有什么优势?
1、索引类型
2、索引的优点
三、 B+ Tree 索引
1、B+ Tree数据结构
2、B+ Tree索引特点
3、实验案例
四、哈希索引
1、哈希索引数据结构
2、实验测试
3、哈希索引的特点
五、聚簇索引
1、聚簇索引优点
2、缺点
六、覆盖索引
今天我们从最基础的方面来聊聊MySQL索引,我相信应该有不少小伙伴都掌握了基本的数据库操作,增(insert)删(delete)查(select)改(update)。如果学得更深入的话,对于数据库的复杂查询SQL语句也有所了解,索引的概念因此略知一二,因为我们平时做的项目相对较小,比如就简单的一个Web系统,我之前做的这个《口罩预约管理系统——数据库设计(前端+PHP+MySQL)》(有兴趣可以瞧瞧,完整项目源码和教程已上传CSDN),数据库中的数据也不多,所以会感觉索引是否使用的效果差别不大,最多就是在一个表中定义了主键(Primary Key)。但是对于大型在线系统来说,数据库中的数据量亿万级别以上,索引对数据库查询的效率就很重要了,这明显是关乎用户体验的事情。所以,来认识一下MySQL索引,为之后遇到优化SQL查询打基础!
索引,其实之前学数据库原理的时候,用“键”(key)表达的比较多。它是存储引擎用来快速找到目标记录的一种数据结构。工作原理就像,我们去看一本书,会先看目录(索引)部分,然后看到感兴趣的章节就根据页码翻看。在MySQL中,存储引擎(MylSAM、InnoDB等)使用索引,就会现在索引中找到对应值,然后根据匹配到的索引回表去找数据行。
MySQL中的索引类型有很多,比如B+树索引、哈希索引、聚簇索引、覆盖索引,这几个比较常见而且使用较多的,还有空间数据索引、全文索引等。
索引可以让存储引擎快速定位到表中的指定位置,当然还有其他索引的功能。简单概况有这几个优点:
最常见的B+树索引,按照顺序存储数据,在MySQL中可以用作order by和group by的操作实现。
B+ Tree的数据结构大家应该有所了解,B+ Tree中,非叶子结点结构:
- 本结点所含关键字的个数。
- 指向父节点的指针。
- 关键字。
- 指向子结点的指针。
所有叶子节点(存放数据)之间是一种链式环结构。
- 非叶子节点只存储键值信息。
- 所有叶子节点之间都有一个链指针。
- 数据记录都存放在叶子节点中。
高性能MySQL书中给出,B+树存储数据的结构如图:
B+树适用于全键值、键值范围和键前缀查找(最左前缀)。
特点:叶子到根结点距离相等,索引树是有序的。
限制:
举个栗子,现在有一张表,各个字段如下:
CREATE TABLE `user`(
`id` int(11) NOT NULL,
`username` varchar(255) NOT NULL,
`password` varchar(255) NOT NULL,
`sex` varchar(255) DEFAULT NULL,
`address` varchar(32) DEFAULT NULL,
`info` varchar(255) DEFAULT NULL,
KEY `id` (`id`,`username`,`password`)
) ENGINE=InnoDB;
增加几条记录:
索引包括了id、username、password三个列,存储在B+树结构中,会先按照三个字段依次进行排序,我们尝试查询,并查看执行计划。
mysql> explain select * from user where id=1001 and name='A'\G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: user
partitions: NULL
type: const
possible_keys: username,id
key: username
key_len: 1022
ref: const
rows: 1
filtered: 100.00
Extra: NULL
1 row in set, 1 warning (0.00 sec)
根据前面查询是否走索引的特点,可以看到如果从左到右是会使用索引的。
接下来测试无法使用索引的情况:
【哈希索引】:基于哈希表的数据结构实现的,需要精确匹配索引所有列的查询才有效,存储引擎会对每一行数据的每一列计算一个哈希码。
解决哈希冲突:如果多个列的哈希值相同,索引会以链表的方式存放多个记录指针到同一个哈希条目。索引只需存储对应的哈希值,其结构十分紧凑,查找速度快。
创建测试哈希索引的表,在name字段加上哈希索引。
CREATE TABLE `testhash` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`crc` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `hash` (`name`)
) ENGINE=InnoDB
也可以自定义哈希索引,下面我们使用CRC32作为哈希函数,创建伪哈希索引,进行一个简单的试验测试:
创建触发器,当有数据插入或者更新时,自动计算哈希码存放到crc字段:
create trigger crc_in before insert on testhash for each row begin set new.crc=crc32(new.name);
create trigger crc_up before update on testhash for each row begin set new.crc=crc32(new.name);
首先需要将分隔符定义为其他符号,这样才能在创建触发器时使用分号。
mysql> delimiter //
mysql> create trigger crc_in before insert on testhash for each row begin set new.crc=crc32(new.name);
-> end;
-> //
mysql> create trigger crc_up before update on testhash for each row begin set new.crc=crc32(new.name);
-> end;
-> //
插入记录:
insert into testhash (name) values('A');
可以发现触发器维护了每一行的哈希值。
更新记录:
update testhash set name='BB' where name='B';
id为2的记录哈希码也会重新计算。
- 索引只包含哈希值和行指针,不存储字段值,所以不能使用索引值来避免读取行(回表查询),因为访问内存中的行速度快,这一点影响倒不大。
- 数据并不是按索引值顺序存储,所以无法用于排序。
- 不支持部分索引列值匹配查找,因为是使用索引列的全部内容计算哈希值的。比如,在列(A,B)上建立哈希索引,若只查询A,则无法使用索引。
- 只支持等值比较查询,包括=、in()、<=>.
- 当出现hash冲突,存储引擎必须遍历链表中所有行指针,逐行比较直到符合条件。
- 当某列的哈希冲突很多时候,如果要从表中删除一行,需要遍历对应哈希值的链表的每一行,找到并删除其引用,代价很大。
【聚簇索引】:实际是一种数据存储方式。聚簇:表示数据行和相邻的键值紧凑地存储在一起。InnoDB的聚簇索引是在同一结构中保存了B-Tree索引和数据行。一个表只能有一个聚簇索引。
- 可以把相关的数据保存在一起,比如实现电子邮箱,可以根据用户ID来聚集数据,减少每封邮件都要进行一次IO的操作。
- 数据访问更快,因为聚簇索引将索引和数据保存在同一个B-Tree中。
- 使用覆盖索引扫描查询可以直接使用叶节点中的主键值。
- 聚簇数据提高了IO密集型应用的性能,但如果数据全部放在内存中,访问顺序就不重要了,因此没有优势。
- 插入速度严重依赖插入顺序,按主键顺序插入速度最快。
- 更新聚簇索引代价很高,因为强制InnoDB将每个被更新的行移动到新的位置。
- 在3中,移动行可能导致页分裂问题,导致表占用更多的磁盘空间。
- 可能 导致全表扫描变慢,尤其在行稀疏或页分裂情况下数据存储不连续的时候。
- 二级索引可能更大,因为其叶子结点包含了引用行的主键列。且访问需要两次索引查找(因为二级索引行叶子结点保存的是行的主键值而不是指向物理位置的指针)
在InnoDB存储引擎中,二级索引优点是无需更新索引中存储的指针,减少行移动或页分裂时二级索引的维护工作,虽然主键值会占用更多的空间 。
【覆盖索引】:如果一个索引包含(覆盖)所有需要查询的字段的值,这个索引成为覆盖索引。Mysql只能使用B-Tree做覆盖索引。
索引可以用来提高查询效率,那如果能够直接通过索引获取数据,就不用回表查询,更加高效。因此覆盖索引就提供了这样的性能。
覆盖索引的好处:
- 无需回表查询数据,在索引的叶子节点可以获取数据。索引条目远小于数据行大小,可以极大减少数据 访问量。
- 索引是按照列值顺序存储的,对于密集IO的范围查询比锁机读取行数据的IO少的多。
- InnoDB的二级索引保存行的主键值,如果二级主键覆盖查询,可以避免主键索引的二次查询。
当我写到后面的时候,发现索引的知识点真的很多,而且理解难度不断增加,博主也正在学习这部分内容中,如果你能够坚持读到这里,说明你很认真,我相信至少掌握了2/3的内容。在学习MySQL数据库时,不断深入愈加觉得掌握的只是冰山一角。欢迎大家一起交流讨论学习心得 : -)
如果觉得不错欢迎“一键三连”哦,点赞收藏关注,评论提问建议,欢迎交流学习!一起加油进步!
本篇内容首发我的CSDN博客:https://csdn-czh.blog.csdn.net/article/details/116596657