深入了解Mysql索引
csdh11 2024-11-30 19:55 20 浏览
github.com/zhangpanqin/MFlyYou 收集技术文章及我的系列文章,欢迎 Star。
前言
数据库中的索引是为了提高查询效率的,将像字典的目录一样。
当我们了解索引的原理之后,就没有必要去死记硬背所谓的 Mysql 军规之类的东西了。
本文内容
- 索引的类型:UNIQUE,FULLTEXT,SPATIAL,NORMAL(普通索引)
- 索引为什么会采用 B+ 树结构,为什么不是二叉树、B- 树
- Mysql 中 B+ 树索引 和 Hash 索引应该选哪个
- 为什么索引的使用需要遵循 最左匹配原则
- 联合索引、聚簇索引 和 覆盖索引 分别是什么
- 索引添加的判断依据是什么
索引
Mysql 中常见的索引类型有:
- 普通索引
- 唯一索引
- 全文索引
- 空间索引
Mysql 中索引的数据结构有:
- B+Tree ,存储引擎 InnoDB 和 MyISAM 都支持。因为我们一般都是使用存储引擎 InnoDB 和 MyISAM,我们都是使用 B+Tree 数据结构的索引。
- HASH,存储引擎 MEMORY 支持,存储引擎 InnoDB 和 MyISAM 不能手动定义 HASH 索引。
因此,我们详细了解 B+Tree 就行了。
我们先来介绍一下两种索引的数据结构的区别,感受一些各自的使用场景。
Hash 数据结构的索引
Hash 数据结构,我们可以理解为 Java 中的 Map,存储的都是 key-value 键值对的数据,但我们没有办法进行范围查找。但是它的等值查找比较快,时间复杂度 O(1)。
创建一张表,字段有 id 和 description,并且在 description 上添加 HASH 索引。存储引擎使用 MEMORY
DROP TABLE IF EXISTS `index_hash_test`;
CREATE TABLE `index_hash_test` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
KEY `inx_descrption` (`description`(100)) USING HASH
) ENGINE=MEMORY AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC;
INSERT INTO `index_hash_test` VALUES (1, 'a');
INSERT INTO `index_hash_test` VALUES (3, 'b');
INSERT INTO `index_hash_test` VALUES (2, 'c');
INSERT INTO `index_hash_test` VALUES (4, 'd');
INSERT INTO `index_hash_test` VALUES (5, 'e');
HASH 索引范围查找不生效
-- 查看执行计划看到,全表扫描,没有走索引
EXPLAIN SELECT * FROM index_hash_test WHERE description >= 'b'
HASH 等值查找生效
EXPLAIN SELECT * FROM index_hash_test WHERE description = 'b'
B+Tree 数据结构的索引
B+Tree 数据结构的索引是可以进行范围查询的。
DROP TABLE IF EXISTS `index_test`;
CREATE TABLE `index_test` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`description` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `inx_descrption` (`description`)USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
INSERT INTO `index_test` VALUES (1, 'a');
INSERT INTO `index_test` VALUES (3, 'b');
INSERT INTO `index_test` VALUES (2, 'c');
INSERT INTO `index_test` VALUES (4, 'd');
INSERT INTO `index_test` VALUES (5, 'e');
在 B+Tree 数据结构的索引表上执行查询计划,可以看到在查询的时候,索引可以使用。
EXPLAIN SELECT * FROM index_test WHERE description >= 'b';
EXPLAIN SELECT * FROM index_test WHERE description > 'b';
EXPLAIN SELECT * FROM index_test WHERE description = 'b';
分别查看执行计划可以看到,等值查找和范围查找都使用到了索引,但是这三者性能上会有所差别 (以后会详细介绍这部分内容)。
B+Tree 索引数据结构
实际开发中,我们使用的存储引擎是 InnoDB 和 MyISAM ,因此主要研究 B+Tree 索引
感兴趣的话可以在这个网站,看数据结构是怎样运行的。比如说 B+ 树插入、删除和查询
https://www.cs.usfca.edu/~galles/visualization/Algorithms.html
相较于二叉树,B+ 树子节点会更多,树的高度会更低,在查找数据的时候,减少了遍历的次数以达到可以减少 Io 次数 (从磁盘加载数据到内存中)。
B+ 树相较于 B- 树,叶子节点是有序的,并且只有叶子节点会存数据。
比如查询大于 3 的数据的时候,找到了 3 直接遍历 3 上的链表就可以查询大于3 的数据。
一个表上索引也不是越多越好,通常推荐不超过 5 个索引。
因为我们修改数据的时候,数据库为了维护索引的数据结构也会产生计算和 io 从而影响数据的性能。当索引多的时候,数据量大的时候,这部分的影响就可以体现出来了。
当因为业务需要,添加的索引超过了 5 个,并且通过压测确定是索引过多影响了数据库的性能。可以考虑对表进行垂直拆分,将一部分业务字段拆分到另一个表中去。
索引相关名词
CREATE TABLE `my_test` (
`id` int(11) NOT NULL,
`name` varchar(255) DEFAULT NULL,
`age` varchar(200) DEFAULT NULL,
`phone` varchar(200) DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
KEY `index_a_b` (`name`(20),`age`(5)) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
INSERT INTO `my_test` VALUES (1, 'a', '1');
INSERT INTO `my_test` VALUES (2, 'a', '2');
INSERT INTO `my_test` VALUES (3, 'a', '3');
INSERT INTO `my_test` VALUES (4, 'b', '1');
INSERT INTO `my_test` VALUES (5, 'b', '2');
关于索引的图只是示意性展示,重在理解。
聚簇索引
聚簇索引(也可以简单理解为主键)的叶子节点存的是整行数据,而非聚簇索引的叶子节点存的是索引数据和主键。
聚簇索引非叶子节点都是主键,叶子节点中既有主键又有对应的行数据。
当使用主键查询数据的时候,实际就是查询聚簇索引,然后从其中把数据读出来返回。
联合索引
联合索引:就是多个列组成一个索引。比如 name 和 age 组成一个索引
我们看到联合索引是按照 name,age 进行排序的,当 name 一样的时候,按 age 排序,并且叶子节点会有 id 的数据。
通过查询这个索引就可以得到对应数据行的数据主键,然后根据主键 id 查询聚簇索引得到整行数据。
覆盖索引
覆盖索引,也是联合索引。只是我们查询的数据就在索引中,不用再去 回表 查找数据了。
SELECT `name`,age FROM my_test WHERE `name` = b;
上述 sql 执行就利用了覆盖索引,查询的结果就在索引中。
SELECT * FROM my_test WHERE `name` = b;
索引中的数据只有 id,name,age,差 phone。这个数据只能通过回表去查询数据。
先查询 (name,age) 这个索引拿到主键 id,在通过主键 id 去聚簇索引中查询数据,这个过程也叫回表。
也有人经常推荐说,查询的时候要查询需要的字段,不要使用 select * ,这样做的好处一是减少 io,另一个就是避免回表。
索引的使用和添加
了解了 B+ 树索引数据结构,我们也就差不多知道怎样使用索引了,也可以理解使用索引的一些规则。
通常说的索引失效,一部分是可以从数据结构来推算出来的;另一部分就是 mysql 通过自身查询统计的数据判断不走索引性能会更高而导致索引失效而去全表扫描。
索引是为了我们从表中检索出少量数据才使用的。
如果你添加了索引,当查询的时候还需要扫描表中绝大数的数据,就不用在这个字段添加索引了,因为这对你的查询没有任何提高,反而因为数据的修改需要维护索引,可能还降低了查询性能。
通常我们会选择区分度比较高的字段添加索引(如果这个字段和查询业务没有关系也没有必要添加索引)。
比如说性别这个字段,只有男、女两个选项,当你有 1000 w 的数据的时候,700w 是 男,300w 是女,就没必要添加索引了,没有意义。性别这个字段区分度较低。
SHOW INDEX FROM index_test;
当我们创建索引之后,可以通过查看 Cardinality 来判断索引添加是否合理。Cardinality/表总行数 值越接近 1 查询性能越好。
Cardinality 代表的是这个索引的数据唯一值的个数。现在表中有 5 行数据,并且 description 的值没有重复的。所以这个数据是 5。
数据库也会根据 Cardinality 进行优化查询,但这个值又不是实时更新的,我们需要每过一段时间,在业务不忙的时候来维护表。
ANALYZE [NO_WRITE_TO_BINLOG | LOCAL] TABLE tbl_name [, tbl_name] ...
mysql> analyze table index_test;
+-----------------------+---------+----------+----------+
| Table | Op | Msg_type | Msg_text |
+-----------------------+---------+----------+----------+
| index_blog.index_test | analyze | status | OK |
+-----------------------+---------+----------+----------+
1 row in set (0.00 sec)
最左匹配规则
我们在使用索引的时候,只需要包含索引的最左边就可以匹配索引(name,age)
SELECT * FROM my_test WHERE `name` = 'b';
SELECT * FROM my_test WHERE `name` = 'b' AND age = 1;
当我们执行下面的 sql 的时候,就用不到索引 (name,age)
-- 通过查看执行计划,看查询的性能:全表扫描
EXPLAIN SELECT * FROM my_test WHERE age=1;
最左匹配原则 是由 Mysql 的索引的数据结构决定的。
联合索引 (name,age) 的 B+Tree 数据结构中叶子节点是按照 name 排序再按照 age 排序。age 实际是乱序的,没有办法进行范围查找。
如果你还想在 age 进行索引查找,就需要在 age 上建立一个新索引。
-- 全表扫描
EXPLAIN SELECT * FROM my_test WHERE NAME LIKE '%a';
-- 索引范围查找
EXPLAIN SELECT * FROM my_test WHERE NAME LIKE 'a%';
索引的建立,不会整个字段值都参与索引的建立,一般会指定多长的字段(从值开头部分的长度)参与索引的建立。
当你需要关键字查找的时候,可以使用全文索引,或者是增加一个 ES 用于检索。
github.com/zhangpanqin/MFlyYou 收集技术文章及我的系列文章,欢迎 Star。
- 上一篇:你还不知道的MySQL数据库索引
- 下一篇:MySQL索引详解
相关推荐
- 探索Java项目中日志系统最佳实践:从入门到精通
-
探索Java项目中日志系统最佳实践:从入门到精通在现代软件开发中,日志系统如同一位默默无闻却至关重要的管家,它记录了程序运行中的各种事件,为我们排查问题、监控性能和优化系统提供了宝贵的依据。在Java...
- 用了这么多年的java日志框架,你真的弄懂了吗?
-
在项目开发过程中,有一个必不可少的环节就是记录日志,相信只要是个程序员都用过,可是咱们自问下,用了这么多年的日志框架,你确定自己真弄懂了日志框架的来龙去脉嘛?下面笔者就详细聊聊java中常用日志框架的...
- 物理老师教你学Java语言(中篇)(物理专业学编程)
-
第四章物质的基本结构——类与对象...
- 一文搞定!Spring Boot3 定时任务操作全攻略
-
各位互联网大厂的后端开发小伙伴们,在使用SpringBoot3开发项目时,你是否遇到过定时任务实现的难题呢?比如任务调度时间不准确,代码报错却找不到方向,是不是特别头疼?如今,随着互联网业务规模...
- 你还不懂java的日志系统吗 ?(java的日志类)
-
一、背景在java的开发中,使用最多也绕不过去的一个话题就是日志,在程序中除了业务代码外,使用最多的就是打印日志。经常听到的这样一句话就是“打个日志调试下”,没错在日常的开发、调试过程中打印日志是常干...
- 谈谈枚举的新用法--java(java枚举的作用与好处)
-
问题的由来前段时间改游戏buff功能,干了一件愚蠢的事情,那就是把枚举和运算集合在一起,然后运行一段时间后buff就出现各种问题,我当时懵逼了!事情是这样的,做过游戏的都知道,buff,需要分类型,且...
- 你还不懂java的日志系统吗(javaw 日志)
-
一、背景在java的开发中,使用最多也绕不过去的一个话题就是日志,在程序中除了业务代码外,使用最多的就是打印日志。经常听到的这样一句话就是“打个日志调试下”,没错在日常的开发、调试过程中打印日志是常干...
- Java 8之后的那些新特性(三):Java System Logger
-
去年12月份log4j日志框架的一个漏洞,给Java整个行业造成了非常大的影响。这个事情也顺带把log4j这个日志框架推到了争议的最前线。在Java领域,log4j可能相对比较流行。而在log4j之外...
- Java开发中的日志管理:让程序“开口说话”
-
Java开发中的日志管理:让程序“开口说话”日志是程序员的朋友,也是程序的“嘴巴”。它能让程序在运行过程中“开口说话”,告诉我们它的状态、行为以及遇到的问题。在Java开发中,良好的日志管理不仅能帮助...
- OS X 效率启动器 Alfred 详解与使用技巧
-
问:为什么要在Mac上使用效率启动器类应用?答:在非特殊专业用户的环境下,(每天)用户一般可以在系统中进行上百次操作,可以是点击,也可以是拖拽,但这些只是过程,而我们的真正目的是想获得结果,也就是...
- Java中 高级的异常处理(java中异常处理的两种方式)
-
介绍异常处理是软件开发的一个关键方面,尤其是在Java中,这种语言以其稳健性和平台独立性而闻名。正确的异常处理不仅可以防止应用程序崩溃,还有助于调试并向用户提供有意义的反馈。...
- 【性能调优】全方位教你定位慢SQL,方法介绍下!
-
1.使用数据库自带工具...
- 全面了解mysql锁机制(InnoDB)与问题排查
-
MySQL/InnoDB的加锁,一直是一个常见的话题。例如,数据库如果有高并发请求,如何保证数据完整性?产生死锁问题如何排查并解决?下面是不同锁等级的区别表级锁:开销小,加锁快;不会出现死锁;锁定粒度...
- 看懂这篇文章,你就懂了数据库死锁产生的场景和解决方法
-
一、什么是死锁加锁(Locking)是数据库在并发访问时保证数据一致性和完整性的主要机制。任何事务都需要获得相应对象上的锁才能访问数据,读取数据的事务通常只需要获得读锁(共享锁),修改数据的事务需要获...
- 一周热门
- 最近发表
- 标签列表
-
- mydisktest_v298 (34)
- document.appendchild (35)
- 头像打包下载 (61)
- acmecadconverter_8.52绿色版 (39)
- word文档批量处理大师破解版 (36)
- server2016安装密钥 (33)
- mysql 昨天的日期 (37)
- parsevideo (33)
- 个人网站源码 (37)
- centos7.4下载 (33)
- mysql 查询今天的数据 (34)
- intouch2014r2sp1永久授权 (36)
- 先锋影音源资2019 (35)
- jdk1.8.0_191下载 (33)
- axure9注册码 (33)
- pts/1 (33)
- spire.pdf 破解版 (35)
- shiro jwt (35)
- sklearn中文手册pdf (35)
- itextsharp使用手册 (33)
- 凯立德2012夏季版懒人包 (34)
- 反恐24小时电话铃声 (33)
- 冒险岛代码查询器 (34)
- 128*128png图片 (34)
- jdk1.8.0_131下载 (34)