热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

mysql索引个数_曾经,我以为我很懂MySQL索引...

本文来源:51CTO技术栈腾讯云数据库负责人林晓斌说过:“我们面试MySQL同事时只考察两点,索引和锁”。言简意赅,MySQ
本文来源:51CTO技术栈

腾讯云数据库负责人林晓斌说过:“我们面试 MySQL 同事时只考察两点,索引和锁”。

言简意赅,MySQL 索引的重要性不言而喻。MySQL 索引历经了多个版本的迭代,从语法到底层数据结构都有很多改变。

MySQL 索引,我们真的了解么?好了,今天我们一起来看看 MySQL 索引的前世今生,一起聊聊索引的那些事儿。

1a17560e44ad0b5e4eba2397a2927c70.png

什么是索引?

在关系数据库中,索引是一种单独的、物理的对数据库表中一列或多列的值进行排序的一种存储结构,它是某个表中一列或若干列值的集合和相应的指向表中物理标识这些值的数据页的逻辑指针清单。

索引的作用相当于图书的目录,可以根据目录中的页码快速找到所需的内容。

当表中有大量记录时,若要对表进行查询:

  • 第一种搜索信息方式是全表搜索,是将所有记录一一取出,和查询条件进行一一对比,然后返回满足条件的记录,这样做会消耗大量数据库系统时间,并造成大量磁盘 I/O 操作。

  • 第二种就是在表中建立索引,然后在索引中找到符合查询条件的索引值,最后通过保存在索引中的 ROWID(相当于页码)快速找到表中对应的记录。

MySQL 5.5 以后 InnoDB 储引擎使用的索引数据结构主要用:B+Tree;本篇文章带大家以 B+Tree 前世今生为主线来聊一聊。

Mark:B+Tree 可以对 ,>=,BETWEEN,IN,以及不以通配符开始的 LIKE 使用索引。(MySQL 5.5 后)

这些事实或许会颠覆你的一些认知,比如在你读过的其他文章或书中。以上这些都属于“范围查询”,都是不走索引的!

没错,早在 5.5 以前,优化器是不会选择通过索引搜索的,优化器认为这样取出的行多与全表扫描的行,因为还要回表查一次嘛,可能会涉及 I/O 的行数更多,被优化器放弃。

经过算法(B+Tree)优化后,支持对部分范围类型的扫描(得利与 B+Tree 数据结构的有序性)。

该做法同时也违反了最左前缀原则,导致范围查询后的条件无法用到联合索引,我们在后面详细说明。

索引的优缺点

索引的优点如下:

  • 索引大大减小了服务器需要扫描的数据量。

  • 索引可以帮助服务器避免排序和临时表。

  • 索引可以将随机 I/O 变成顺序 I/O。

索引的缺点如下:

  • 虽然索引大大提高了查询速度,同时却会降低更新表的速度,如对表进行 INSERT、UPDATE 和 DELETE。因为更新表时,MySQL 不仅要保存数据,还要保存索引文件。

  • 建立索引会占用磁盘空间的索引文件。一般情况这个问题不算严重,但如果你在一个大表上创建了多种组合索引,且伴随大量数据量插入,索引文件大小也会快速膨胀。

  • 如果某个数据列包含许多重复的内容,为它建立索引就没有太大的实际效果。

  • 对于非常小的表,大部分情况下简单的全表扫描更高效。

因此应该只为最经常查询和最经常排序的数据列建立索引。(MySQL 里同一个数据表里的索引总数限制为 16 个)数据库存在的意义之一就是是解决数据存储和快速查找的。那么数据库的数据存在哪?没错,是磁盘,磁盘的优点是啥?便宜!缺点呢?相比内存访问速度慢。那么你知道 MySQL 索引主要使用的数据结构么?B+树!你脱口而出。那 B+树是什么样的数据结构?MySQL 索引又是为什么选择了 B+树呢?

其实最终选用 B+树是经历了漫长的演化:

二叉排序树 → 二叉平衡树 → B-Tree(B树) → B+Tree(B+树)
有小伙伴问我“B 树跟 B-树有什么区别”?这里普及一下,MySQL 数据结构只有B-Tree(B 树)和 B+Tree(B+树),多只是读法不同罢了,“B-Tree” 一般统称为 B 树,你叫他 B-树也行!还有小伙伴提到的红黑树,是编程语言中的存储结构,不是 MySQL 的;如 Java 的 HashMap 就是用的链表加红黑树。好了,今天就带着大家一起看一下演化成 B+树的过程吧。

B+Tree 索引的前世今生

①二叉排序树

理解 B+树之前,简单说一下二叉排序树,对于一个节点,它的左子树的孩子节点值都要小于它本身,它的右子树的孩子节点值都要大于它本身。

如果所有节点都满足这个条件,那么它就是二叉排序树。(此处可以串一下二分查找的知识点)

288865f084f1c557c7778e1470828200.png

上图是一颗二叉排序树,你可以尝试利用它的特点,体验查找 9 的过程:
  • 9 比 10 小,去它的左子树(节点 3)查找。

  • 9 比 3 大,去节点 3 的右子树(节点 4)查找。

  • 9 比 4 大,去节点 4 的右子树(节点 9)查找。

  • 节点 9 与 9 相等,查找成功。

一共比较了 4 次,那你有没有想过上述结构的优化方式?②AVL 树(自平衡二叉查找树)

ddb0376e8e2f2d11a594d108d52cc242.png

上图是 AVL 树,节点个数和值均和二叉排序树一摸一样。再来看一下查找 9 的过程:
  • 9 比 4 大,去它的右子树查找。

  • 9 比 10 小,去它的左子树查找。

  • 节点 9 与 9 相等,查找成功。

一共比较了 3 次,同样的数据量比二叉排序树少了一次,为什么呢?因为 AVL 树高度要比二叉排序树小,高度越高意味着比较的次数越多;不要小看优化的这一次,假如是 200w 条数据,比较次数会明显地不同。

你可以想象一下一棵 100 万节点的平衡二叉树,树高 20。一次查询可能需要访问 20 个数据块。在机械硬盘时代,从磁盘随机读一个数据块需要 10 ms 左右的寻址时间。也就是说,对于一个 100 万行的表,如果使用二叉树来存储,单独访问一个行可能需要 20 个 10 ms 的时间,这个查询可真够慢的!

③B 树(Balanced Tree)多路平衡查找树,多叉的

B 树是一种多路自平衡搜索树,它类似普通的二叉树,但是 B 树允许每个节点有更多的子节点。

B 树示意图如下:

8b2350972e5e4210e637f42e40ca6299.pngB 树的特点如下:
  • 所有键值分布在整个树中。

  • 任何关键字出现且只出现在一个节点中。

  • 搜索有可能在非叶子节点结束。

  • 在关键字全集内做一次查找,性能逼近二分查找算法。

为了提升效率,要尽量减少磁盘 I/O 的次数。实际过程中,磁盘并不是每次严格按需读取,而是每次都会预读。

磁盘读取完需要的数据后,会按顺序再多读一部分数据到内存中,这样做的理论依据是计算机科学中注明的局部性原理:
  • 由于磁盘顺序读取的效率很高(不需要寻址时间,只需很少的旋转时间),因此对于具有局部性的程序来说,预读可以提高 I/O 效率。预读的长度一般为页(page)的整倍数。

  • MySQL(默认使用 InnoDB 引擎),将记录按照页的方式进行管理,每页大小默认为 16K(可以修改)。

B-Tree 借助计算机磁盘预读机制:每次新建节点的时候,都是申请一个页的空间,所以每查找一个节点只需要一次 I/O;因为实际应用当中,节点深度会很少,所以查找效率很高。

那么最终版的 B+树是如何做的呢?

④B+Tree (B+树是 B 树的变体,也是一种多路搜索树)

ac94feaf221588b22d9fb1fe309e5f7b.png从图中也可以看到,B+树与 B 树的不同在于:
  • 所有关键字存储在叶子节点,非叶子节点不存储真正的 data,从而可以快速定位到叶子结点。

  • 为所有叶子节点增加了一个链指针,意味着所有的值都是按顺序存储的,并且每一个叶子页到根的距离相同,很适合查找范围数据。

因此,B+Tree 可以对 ,>=,BETWEEN,IN,以及不以通配符开始的 LIKE 使用索引。B+ 树的优点,比较的次数均衡,减少了 I/O 次数,提高了查找速度,查找也更稳定:
  • B+树的磁盘读写代价更低。

  • B+树的查询效率更加稳定。

要知道的是,你每次创建表,系统会为你自动创建一个基于 ID 的聚集索引(上述 B+树),存储全部数据。

你每次增加索引,数据库就会为你创建一个附加索引(上述 B+树),索引选取的字段个数就是每个节点存储数据索引的个数,注意该索引并不存储全部数据。

为什么 MySQL 索引选择了 B+树而不是 B 树?

原因有如下两点:

  • B+树更适合外部存储(一般指磁盘存储),由于内节点(非叶子节点)不存储 data,所以一个节点可以存储更多的内节点,每个节点能索引的范围更大更精确。也就是说使用 B+树单次磁盘 I/O 的信息量相比较 B 树更大,I/O 效率更高。

  • MySQL 是关系型数据库,经常会按照区间来访问某个索引列,B+树的叶子节点间按顺序建立了链指针,加强了区间访问性,所以 B+树对索引列上的区间范围查询很友好。而 B 树每个节点的 key 和 data 在一起,无法进行区间查找。

程序员,你应该知道的索引知识点

①回表查询

比如你创建了 name, age 索引 name_age_index,查询数据时使用了:

select * from table where name ='陈哈哈' and age = 26;
由于附加索引中只有 name 和 age,因此命中索引后,数据库还必须回去聚集索引中查找其他数据,这就是回表,这也是你背的那条:少用 select * 的原因。

②索引覆盖

结合回表会更好理解,比如上述 name_age_index 索引,有查询:

select name, age from table where name ='陈哈哈' and age = 26;
此时 select 的字段 name,age 在索引 name_age_index 中都能获取到,所以不需要回表,满足索引覆盖,直接返回索引中的数据,效率高。是 DBA 同学优化时的首选优化方式。

③最左前缀原则

B+树的节点存储索引顺序是从左向右存储,在匹配的时候自然也要满足从左向右匹配。通常我们在建立联合索引的时候,也就是对多个字段建立索引,相信建立过索引的同学们会发现,无论是 Oracle 还是 MySQL 都会让我们选择索引的顺序。比如我们想在 a,b,c 三个字段上建立一个联合索引,我们可以选择自己想要的优先级,a、b、c,或者是 b、a、c 或者是 c、a、b 等顺序。为什么数据库会让我们选择字段的顺序呢?不都是三个字段的联合索引么?这里就引出了数据库索引的最左前缀原理。在我们开发中经常会遇到明明这个字段建了联合索引,但是 SQL 查询该字段时却不会使用索引的问题。

比如索引 abc_index:(a,b,c)是 a,b,c 三个字段的联合索引,下列 sql 执行时都无法命中索引 abc_index 的。

select * from table where c = '1';
select * from table where b ='1' and c ='2';

以下三种情况却会走索引:

select * from table where a = '1';
select * from table where a = '1' and b = '2';
select * from table where a = '1' and b = '2'  and c='3';
从上面两个例子大家是否阔以看出点眉目?是的,索引 abc_index:(a,b,c),只会在(a)、(a,b)、(a,b,c)三种类型的查询中使用。其实这里说的有一点歧义,其实(a,c)也会走,但是只走 a 字段索引,不会走 c 字段。

另外还有一个特殊情况说明下,下面这种类型的也只会有 a 与 b 走索引,c 不会走。

select * from table where a = '1' and b > '2'  and c='3';
像上面这种类型的 sql 语句,在 a、b 走完索引后,c 已经是无序了,所以 c 就没法走索引,优化器会认为还不如全表扫描 c 字段来的快。最左前缀:顾名思义,就是最左优先,上例中我们创建了 a_b_c 多列索引,相当于创建了(a)单列索引,(a,b)组合索引以及(a,b,c)组合索引。因此,在创建多列索引时,要根据业务需求,where 子句中使用最频繁的一列放在最左边。

④索引下推优化

还是索引 name_age_index,有如下 sql:

select * from table where name like '陈%' and age > 26;
该语句有两种执行可能:

  • 命中 name_age_index 联合索引,查询所有满足 name 以"陈"开头的数据, 然后回表查询所有满足的行。

  • 命中 name_age_index 联合索引,查询所有满足 name 以"陈"开头的数据,然后顺便筛出 age>20 的索引,再回表查询全行数据。

显然第 2 种方式回表查询的行数较少,I/O 次数也会减少,这就是索引下推。所以不是所有 like 都不会命中索引。

使用索引时的注意事项

①索引不会包含有 null 值的列

只要列中包含有 null 值都将不会被包含在索引中,复合索引中只要有一列含有 null 值,那么这一列对于此复合索引就是无效的。所以我们在数据库设计时建议不要让字段的默认值为 null。

②使用短索引

对串列进行索引,如果可能应该指定一个前缀长度。

例如,如果有一个 char(255)的列,如果在前 10 个或 20 个字符内,多数值是惟一的,那么就不要对整个列进行索引。短索引不仅可以提高查询速度而且可以节省磁盘空间和 I/O 操作。

③索引列排序

查询只使用一个索引,因此如果 where 子句中已经使用了索引的话,那么 order by 中的列是不会使用索引的。因此数据库默认排序可以符合要求的情况下不要使用排序操作;尽量不要包含多个列的排序,如果需要最好给这些列创建复合索引。

④like 语句操作

一般情况下不推荐使用 like 操作,如果非使用不可,如何使用也是一个问题。like “%陈%” 不会使用索引而 like “陈%”可以使用索引。

⑤不要在列上进行运算

这将导致索引失效而进行全表扫描,例如:

SELECT * FROM table_name WHERE YEAR(column_name)<2017;

⑥不使用 not in 和 <> 操作

这不属于支持的范围查询条件&#xff0c;不会使用索引。

我的体会

曾经&#xff0c;我一度以为我很懂 MySQL。

刚入职那年&#xff0c;我还是个孩子&#xff0c;记得第一个需求是做个统计接口&#xff0c;查询近两小时每隔 5 分钟为一时间段的网站访问量&#xff0c;JSONArray 中一共返回 24 个值&#xff0c;当时菜啊&#xff0c;写了个接口循环二十四遍&#xff0c;发送 24 条 SQL 去查(捂脸)。

由于那个接口&#xff0c;被技术经理嘲讽表示他写的 SQL 比我吃的米都多。虽然我们山东人基本不吃米饭&#xff0c;但我还是羞愧不已。

然后经理通过调用一个 dateTime 函数分组查询处理一下&#xff0c;就 OK 了&#xff0c;效率是我的几十倍吧。

从那时起&#xff0c;我就定下目标&#xff0c;深入 MySQL 学习&#xff0c;万一日后有机会嘲讽回去&#xff1f;筒子们&#xff0c;MySQL 路漫漫&#xff0c;其修远兮。永远不要眼高手低&#xff0c;一起加油&#xff0c;希望本文能对你有所帮助。

END

推荐阅读&#xff1a;

Nginx 为什么这么快&#xff1f;

one thread one loop 思想

业务数据处理一定要单独开线程吗

后端服务中的定时器设计

侵入式服务与非侵入式程序结构

网络通信中收发数据的正确姿势

更多的服务器文章请点击这里

为防止失联&#xff0c;我开了一个备份小号——程序员小方&#xff0c;欢迎扫码关注。

f457795dec9d9eb27093af3bdfa4d9be.png

点赞是最大的支持 e402b9a891eb8f0addadebd7698c9553.gif




推荐阅读
  • 本文介绍了数据库的存储结构及其重要性,强调了关系数据库范例中将逻辑存储与物理存储分开的必要性。通过逻辑结构和物理结构的分离,可以实现对物理存储的重新组织和数据库的迁移,而应用程序不会察觉到任何更改。文章还展示了Oracle数据库的逻辑结构和物理结构,并介绍了表空间的概念和作用。 ... [详细]
  • eclipse学习(第三章:ssh中的Hibernate)——11.Hibernate的缓存(2级缓存,get和load)
    本文介绍了eclipse学习中的第三章内容,主要讲解了ssh中的Hibernate的缓存,包括2级缓存和get方法、load方法的区别。文章还涉及了项目实践和相关知识点的讲解。 ... [详细]
  • 在Oracle11g以前版本中的的DataGuard物理备用数据库,可以以只读的方式打开数据库,但此时MediaRecovery利用日志进行数据同步的过 ... [详细]
  • 本文介绍了使用哈夫曼树实现文件压缩和解压的方法。首先对数据结构课程设计中的代码进行了分析,包括使用时间调用、常量定义和统计文件中各个字符时相关的结构体。然后讨论了哈夫曼树的实现原理和算法。最后介绍了文件压缩和解压的具体步骤,包括字符统计、构建哈夫曼树、生成编码表、编码和解码过程。通过实例演示了文件压缩和解压的效果。本文的内容对于理解哈夫曼树的实现原理和应用具有一定的参考价值。 ... [详细]
  • MySQL中的MVVC多版本并发控制机制的应用及实现
    本文介绍了MySQL中MVCC的应用及实现机制。MVCC是一种提高并发性能的技术,通过对事务内读取的内存进行处理,避免写操作堵塞读操作的并发问题。与其他数据库系统的MVCC实现机制不尽相同,MySQL的MVCC是在undolog中实现的。通过undolog可以找回数据的历史版本,提供给用户读取或在回滚时覆盖数据页上的数据。MySQL的大多数事务型存储引擎都实现了MVCC,但各自的实现机制有所不同。 ... [详细]
  • 一、Hadoop来历Hadoop的思想来源于Google在做搜索引擎的时候出现一个很大的问题就是这么多网页我如何才能以最快的速度来搜索到,由于这个问题Google发明 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • C语言注释工具及快捷键,删除C语言注释工具的实现思路
    本文介绍了C语言中注释的两种方式以及注释的作用,提供了删除C语言注释的工具实现思路,并分享了C语言中注释的快捷键操作方法。 ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • Day2列表、字典、集合操作详解
    本文详细介绍了列表、字典、集合的操作方法,包括定义列表、访问列表元素、字符串操作、字典操作、集合操作、文件操作、字符编码与转码等内容。内容详实,适合初学者参考。 ... [详细]
  • Oracle优化新常态的五大禁止及其性能隐患
    本文介绍了Oracle优化新常态中的五大禁止措施,包括禁止外键、禁止视图、禁止触发器、禁止存储过程和禁止JOB,并分析了这些禁止措施可能带来的性能隐患。文章还讨论了这些禁止措施在C/S架构和B/S架构中的不同应用情况,并提出了解决方案。 ... [详细]
  • 如何提高PHP编程技能及推荐高级教程
    本文介绍了如何提高PHP编程技能的方法,推荐了一些高级教程。学习任何一种编程语言都需要长期的坚持和不懈的努力,本文提醒读者要有足够的耐心和时间投入。通过实践操作学习,可以更好地理解和掌握PHP语言的特异性,特别是单引号和双引号的用法。同时,本文也指出了只走马观花看整体而不深入学习的学习方式无法真正掌握这门语言,建议读者要从整体来考虑局部,培养大局观。最后,本文提醒读者完成一个像模像样的网站需要付出更多的努力和实践。 ... [详细]
  • MySQL语句大全:创建、授权、查询、修改等【MySQL】的使用方法详解
    本文详细介绍了MySQL语句的使用方法,包括创建用户、授权、查询、修改等操作。通过连接MySQL数据库,可以使用命令创建用户,并指定该用户在哪个主机上可以登录。同时,还可以设置用户的登录密码。通过本文,您可以全面了解MySQL语句的使用方法。 ... [详细]
  • EzPP 0.2发布,新增YAML布局渲染功能
    EzPP发布了0.2.1版本,新增了YAML布局渲染功能,可以将YAML文件渲染为图片,并且可以复用YAML作为模版,通过传递不同参数生成不同的图片。这个功能可以用于绘制Logo、封面或其他图片,让用户不需要安装或卸载Photoshop。文章还提供了一个入门例子,介绍了使用ezpp的基本渲染方法,以及如何使用canvas、text类元素、自定义字体等。 ... [详细]
  • svnWebUI:一款现代化的svn服务端管理软件
    svnWebUI是一款图形化管理服务端Subversion的配置工具,适用于非程序员使用。它解决了svn用户和权限配置繁琐且不便的问题,提供了现代化的web界面,让svn服务端管理变得轻松。演示地址:http://svn.nginxwebui.cn:6060。 ... [详细]
author-avatar
小帅哥小羊儿_309
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有