精华内容
下载资源
问答
  • 什么索引提起索引,大家都知道,建立索引可以让数据库查询更快,那么索引究竟是什么?我想这就不是每个人都说得出来了。索引,是数据库管理系统中一个排序的数据结构,并用以协助快速查询、 更新数据库表中数据...

    声明

    本文所述的各种数据结构(二叉树等),均不考虑重复值的情况,本文简述各种数据结构的区别仅仅只是为了理解MySQL索引的需要而做的铺垫。

    什么是索引

    提起索引,大家都知道,建立索引可以让数据库查询更快,那么索引究竟是什么?我想这就不是每个人都能说得出来了。

    索引,是数据库管理系统中一个排序的数据结构,并用以协助快速查询、 更新数据库表中数据。

    是的,索引是一种数据结构,但是那么多的数据结构中为何MySQL要选择B+树呢?接下来就让我们一起来了解下B+树相对于其他数据结构有何独特之处!

    二分查找法(Binary Search)

    首先让我们自己想一想,如果让我们去设计,我们会怎么去存储?我想大部分人想到就是用链表或者数组去存储数据,然后再按默认的顺序排好,再去查找,而一个排好顺序的链表我们就可以通过二分查找法来高效查询。

    二分查找也称折半查找,是一种效率较高的查找方法。比如有1-10十个数,我们要找到8,先从中间开始找5,然后发现8比5大,可以把5左边的数去掉,剩下6-10,再从中间开始找,依次类推,直到找到8为止。但是这种查找法有一个前提是数据必须是有序的,而且这种属于链表式的存储,我们一但要插入或者修改一个数据,可能会伴随着大量的下标移动,比如我们把1-10放在数组里面,下标分别对应0-9,然后现在要插入一个0,为了保证有序,0必须排在第一位,那么1-10所有的数据下标都要往后移动一位,这种就有点大动干戈了,所以为了解决这个问题,我们就有了二叉树。

    二叉查找树(BST)

    二叉查找树简称二叉树(BST),英文全称:Binary Search Tree,这是一种什么样的数据结构呢?请看下图

    487015a3b336ec21ce89503f787ffd05.png

    在上面这棵树中,我们要找到8,先从根节点6开始比较,发现8比6大,就往右边走,就可以找到8

    二叉树的特点

    二叉树有两个特点:

    1、左子树所有的节点都小于父节点

    2、右子树所有的节点都大于父节点

    二叉树存在的问题

    二叉树有一个严重的问题,那就是它的查找耗时是和这棵树的深度相关的,在最坏的情况下时间复杂度会退化成 O(n)。

    如下图:

    a1bd0a1fc04bc85a1230cf0f608e247d.png

    上面就是一种极端情况下的二叉树,会退化成线性链表,这种如果要找到最后一个数6,就要从1开始遍历完整棵树,效率就会非常低。那么有没有一种相对平衡一点,不要出现这种极端情况的数据结构呢,所以就有了平衡二叉树。

    平衡二叉树(AVL Tree)

    平衡二叉树,英文全名叫做 Balanced binary search trees,简称AVL树,这个AVL并不是英文名的简称,而是发明者(G. M. Adelson-Velsky和E. M. Landis)两个人的人名缩写,请看下图一个平衡二叉树示例:

    c2a0adebd3ca3f04c5191360a4e1b450.png

    上图中也是从1开始插入6,如果是二叉树就会变成一种线性结构,但是平衡二叉树就会通过左旋和右旋操作,最终会生成上图所示的结构,感兴趣的可以进入网站自己操作观察旋转过程.

    平衡二叉树的特点

    平衡二叉树相比较二叉树具有一个特点就是:左右子树深度差绝对值不能超过 1,当然,平衡二叉树首先是一颗二叉树,只不过通过左旋和右旋实现左右子树深度差不超过1,避免了二叉树的极端情况的出现。

    MySQL为何不选择平衡二叉树

    既然平衡二叉树解决了普通二叉树的问题,那么mysql为何不选择平衡二叉树作为索引呢?

    索引需要存储什么

    让我们想一想,如果我们要把索引存起来,那么应该存哪些信息呢,它应该存储三块信息:

    • 索引的值:就是表里面索引列对应的值。
    • 数据的磁盘地址(通过磁盘地址找到当前数据)或者直接存储整条数据。
    • 子节点的引用:我们需要从根节点往下走,所以需要知道左右子节点的地址。

    根据这三点,可以有如下大致的一个简单的结构图:

    df464cc5c4a6f74f0ff53701cb7911d9.png

    上图中数字表示的是索引的值,0x开头的表示磁盘地址,根节点中存了左右节点的引用。

    AVL树用来存储索引存在什么问题

    我们知道,页(Page)是 Innodb 存储引擎用于管理数据的最小磁盘单位,页的默认大小为16KB。页也就是上图中的节点,每查询一次节点就需要进行一次IO操作,IO操作是一种非常耗时的操作,很多业务系统的瓶颈都是卡在IO操作上,所以如果我们需要提高查询效率的办法之一就是减少IO次数,那么问题就来了,AVL树一个节点上只存了一个关键字(索引值)+一个磁盘地址+左右节点的引用,这是远远达不到16KB的,会浪费了大量的空间。

    上图中如果我们要找到6这条数据,需要进行3次IO(获取一个节点就是一个IO操作),如果这棵树很高的话,就会进行大量的IO操作,所以说AVL树存在的最大问题就是空间利用不足,浪费了大量空间,数据量大的时候就会成为一颗瘦高的树。

    那么我们可以怎么改进呢?答案很明显了,那就是每个磁盘块多存一点东西,也就是说每个磁盘多存几个关键字,因为关键字越多,路数越多;路数越多,树也就越矮越胖,相应的操作IO次数就会越少。

    多路平衡树(Balanced Tree)

    多路平衡树简称B树,又称B-树,和AVL树一样,B树在枝节点和叶子节点存储键值、磁盘地址、左右节点引用。请看下图的一个多路平衡树的示例:

    ed0d0fecf14101d5024f34bf77c9a0ed.png

    B树的特点

    相比较AVL树,B树一个磁盘上可以存多个关键字(值),而且有一个特点就是:

    • 分叉数(路数)永远比关键字数多1。

    我们可以画出如下简图(下图中只画了3路,即两个关键字,实际取决于一页能存储多少个关键字):从上图可以很明显的看出,同样高度的树,B树能存的数据远远大于平衡二叉树。

    9db4b285ef5741df8e2163c345a4d5da.png

    B树是如何查找数据的

    以上图为例,假如我们要找key=32这个数字,首先获取到根节点,发现18小于key,所以往右边走,获取到右边的数据,54和76,这时候遵循以下原则:

    • key<54,命中最左边分叉;
    • key=54,直接命中,返回数据;
    • 54<key<76,走中间的一个分叉;
    • key=76,直接命中,返回数据;
    • key>76,命中右边分支;

    这里因为key=32,所以走得是第1条,命中左边分支,这时候再去获取左边分支,获取到32和50,比较发现key=32,命中,返回数据。

    从上面我们可以看出B树效率相对于AVL树,在数据量大的情况效率已经提高了很多,那么为什么MySQL还是不选择B树作为索引呢?

    那么接下来让我们先看看改良版的B+树,然后再下结论吧!

    B+树

    B+树由B树改良而来,属于改良版的多路平衡查找树。

    首先让我们来看看B+树到底长什么样呢:

    7a6b4f092d16024e8025da248f5d4647.png

    对比B+树,我们可以发现一个很明显的区别就是叶子节点有一个箭头指引而且从左到右是有序的。

    InnoDB中使用的B+树相比较于传统B+树,改进之后的B+树具有以下特点

    InnoDB中B+树的特点

    • 它的关键字的数量是跟路数相等的。
    • B+树的根节点和枝节点中都不会存储数据,只有叶子节点才存储数据。而搜索到关键字不会直接返回,会到最后一层的叶子节点。
    • B+树的每个叶子节点增加了一个指向相邻叶子节点的指针,它的最后一个数据会指向下一个叶子节点的第一个数据,形成了一个有序链表的结构。
    • 它是根据左闭右开的区间来检索数据的

    按照B+树的特点,我们可以画出一个存储数据的简图,如下:

    41652e2b0d5f5c1a607f41641d6cab6d.png

    B+树是如何查找数据的

    假设我们现在要找一个key=66,遵循如下步骤:

    1、获取到根节点,依据左闭右开有如下区间:[1,28),[28,66),[66,+∞),命中了最后一个区间,虽然66在根节点,但是因为根节点不存储数据,所以是会往下继续搜索右边的节点

    2、获取到右边节点,依据左闭右开有如下区间:[66,78),[78,89),[89,+∞),命中左边的范围。

    3、获取到第三排倒数第二块磁盘,找到66,返回数据。

    B+树相对于B树的改进点

    B+树是由B树改进而来的,所以B树能解决的问题,B+树都能解决,那么B+树能解决哪些B树所不能解决的问题呢?

    1、扫库、扫表能力更强:如果我们要对表进行全表扫描,只需要遍历叶子节点就可以 了,不需要遍历整棵B+Tree

    2、B+Tree 的磁盘读写能力相对于 B Tree 来说更强:根节点和枝节点不保存数据区, 所以一个节点可以保存更多的关键字,一次磁盘加载(IO操作)能获取到相对更多的关键字。

    3、天然具备排序能力:叶子节点上有下一个数据区的指针,数据形成了链表。

    4、效率稳定:B+Tree 永远是在叶子节点拿到数据,所以 IO 次数是稳定的,而B树运气好根节点就拿到数据,运气不好就要到叶子节点才能拿到数据,所花费的时间会有差异。

    总结

    本文简述了从二叉树到B+树之前的演进过程,并大致讲解了各种数据结构之间的差异以及MySQL为何最终会选择了B+树来作为索引。

    点关注,不迷路

    好了各位,以上就是这篇文章的全部内容了,能看到这里的人呀,都是人才。之前说过,PHP方面的技术点很多,也是因为太多了,实在是写不过来,写过来了大家也不会看的太多,所以我这里把它整理成了PDF和文档,如果有需要的可以

    点击进入暗号: PHP+「平台」

    633adc31d6764c4d0530ccd85bff793c.png

    e00d6c2138664b037f0cc1c89dc10f17.png

    更多学习内容可以访问

    阿布阿布:【对标大厂】精品PHP架构师教程目录大全,只要你能看完保证薪资上升一个台阶(持续更新)​zhuanlan.zhihu.com
    zhihu-card-default.svg

    以上内容希望帮助到大家,很多PHPer在进阶的时候总会遇到一些问题和瓶颈,业务代码写多了没有方向感,不知道该从那里入手去提升,对此我整理了一些资料,包括但不限于:分布式架构、高可扩展、高性能、高并发、服务器性能调优、TP6,laravel,YII2,Redis,Swoole、Swoft、Kafka、Mysql优化、shell脚本、Docker、微服务、Nginx等多个知识点高级进阶干货需要的可以免费分享给大家,需要的可以加入我的 PHP技术交流群

    展开全文
  • = 这些条件时便不能使用索引查询,只能使用全表扫描。这种说法愈演愈烈,甚至被很多同学奉真理。咱啥话也不说,举个例子。假如我们有个表s1,结构如下:CREATE TABLE s1 ( id INT NOT NULL AUTO_...

    点关注,不迷路;持续更新Java相关技术及资讯!!!

    46555a52307fe03bc7257b5c7f4238ad.png

    不知道从什么时候开始,网上流传着这么一个说法:

    MySQL的WHERE子句中包含 IS NULL、IS NOT NULL、!= 这些条件时便不能使用索引查询,只能使用全表扫描。

    这种说法愈演愈烈,甚至被很多同学奉为真理。咱啥话也不说,举个例子。假如我们有个表s1,结构如下:

    CREATE TABLE s1 ( id INT NOT NULL AUTO_INCREMENT, key1 VARCHAR(100), key2 VARCHAR(100), key3 VARCHAR(100), key_part1 VARCHAR(100), key_part2 VARCHAR(100), key_part3 VARCHAR(100), common_field VARCHAR(100), PRIMARY KEY (id), KEY idx_key1 (key1), KEY idx_key2 (key2), KEY idx_key3 (key3), KEY idx_key_part(key_part1, key_part2, key_part3)) Engine=InnoDB CHARSET=utf8;

    这个表里有10000条记录:

    mysql> SELECT COUNT(*) FROM s1;+----------+| COUNT(*) |+----------+| 10000 |+----------+1 row in set (0.00 sec)

    下边我们直接贴几个图:

    0f41f72fa99fedf902ce729a529ac5e8.png
    e1af736c2c23cef4c15fe31cbc422017.png
    6b6b640eb913da901b08fea4a7b58358.png

    上边几个查询语句的WHERE子句中用了IS NULL、IS NOT NULL、!=这些条件,但是从它们的执行计划中可以看出来,这些语句都采用了相应的二级索引执行查询,而不是使用所谓的全表扫描,谣言不攻自破。当然,戳破这些谣言并不是本文的目的,本文来更细致的分析一下这些查询到底是怎么执行的。

    NULL值是怎么在记录中存储的

    在MySQL中,每一条记录都有它固定的格式,我们以InnoDB存储引擎的Compact行格式为例,来看一下NULL值是怎样存储的。在Compact行格式下,一条记录是由下边这几个部分构成的:

    69a12fa05b0e9301e9df984f9e7d5668.png

    为了故事的顺利发展,我们新建一个称之为record_format_demo的表:

    CREATE TABLE record_format_demo ( c1 VARCHAR(10), c2 VARCHAR(10) NOT NULL, c3 CHAR(10), c4 VARCHAR(10) ) CHARSET=ascii ROW_FORMAT=COMPACT;

    因为我们的重点是NULL值是如何存储在记录中的,所以重点唠叨一下行格式的NULL值列表部分,其他的部分可以到小册中查看。存储NULL值的过程如下:

    1. 首先统计表中允许存储NULL的列有哪些。
    2. 我们前边说过,主键列、被NOT NULL修饰的列都是不可以存储NULL值的,所以在统计的时候不会把这些列算进去。比方说表record_format_demo的3个列c1、c3、c4都是允许存储NULL值的,而c2列是被NOT NULL修饰,不允许存储NULL值。
    3. 如果表中没有允许存储NULL的列,则NULL值列表也不存在了,否则将每个允许存储NULL的列对应一个二进制位,二进制位按照列的顺序逆序排列,二进制位表示的意义如下:
    • 二进制位的值为1时,代表该列的值为NULL。
    • 二进制位的值为0时,代表该列的值不为NULL。
    1. 因为表record_format_demo有3个值允许为NULL的列,所以这3个列和二进制位的对应关系就是这样:
    5144b6f27b6c007f4cfebd4c5e6d718c.png

    再一次强调,二进制位按照列的顺序逆序排列,所以第一个列c1和最后一个二进制位对应。

    设计InnoDB的大叔规定NULL值列表必须用整数个字节的位表示,如果使用的二进制位个数不是整数个字节,则在字节的高位补0。

    表record_format_demo只有3个值允许为NULL的列,对应3个二进制位,不足一个字节,所以在字节的高位补0,效果就是这样:

    5446cf7a4e3d7444046101cecc4d77da.png
    1. 以此类推,如果一个表中有9个允许为NULL,那这个记录的NULL值列表部分就需要2个字节来表示了。

    假设我们现在向record_format_demo表中插入一条记录:

    INSERT INTO record_format_demo(c1, c2, c3, c4) VALUES('eeee', 'fff', NULL, NULL);复制代码

    这条记录的c1、c3、c4这3个列中c3和c4的值都为NULL,所以这3个列对应的二进制位的情况就是:

    3c67b742ed3c271100875cbbfaf1268b.png

    所以这记录的NULL值列表用十六进制表示就是:0x06。

    键值为NULL的记录是怎么在B+树中存放的

    对于InnoDB存储引擎来说,记录都是存储在页面中的(一个页面默认是16KB大小),这些页面可以作为B+树的节点而组成一个索引,类似这种样子(只是用下边的图举个B+树的例子而已,跟我们上边列举的表没关系):

    fc647e8995ea4a235d8f09a643775dd8.png

    聚簇索引和二级索引都对应着像上图一样的B+树(也就是说有多少个索引就有多少棵对应的B+树),不过:

    • 对于聚簇索引索引来说,页面中的记录是按照主键值进行排序的;而对于二级索引来说,页面中的记录是按照给定的索引列的值进行排序的。
    • 对于聚簇索引来说,B+树每一层节点(页面)都是按照页中记录的主键值大小进行排序的;而对于二级索引来说,B+树每一层节点(页面)都是按照页中记录的给定的索引列的值进行排序的。
    • 对于聚簇索引来说,B+树叶子节点对应的页面中存储的是完整的用户记录(就是一条记录中包含我们定义的所有列值,还包含一些InnoDB自己添加的一些隐藏列);而对于二级索引来说,B+树叶子节点对应的页面中存储的只是索引列的值 + 主键值。

    按规定,一条记录的主键值不允许存储NULL值,所以下边语句中的WHERE子句结果肯定为FALSE:

    SELECT * FROM tbl_name WHERE primary_key IS NULL;

    像这样的语句优化器自己就能判定出WHERE子句必定为NULL,所以压根儿不会去执行它,不信我们看(Extra信息提示WHERE子句压根儿不成立):

    7683c272f418253490a46d572c6bf968.png

    对于二级索引来说,索引列的值可能为NULL。那对于索引列值为NULL的二级索引记录来说,它们被放在B+树的哪里呢?答案是:放在B+树的最左边。比方说我们有如下查询语句:

    SELECT * FROM s1 WHERE key1 IS NULL;

    那它的查询示意图就如下所示:

    77a038f4051ca057783e5d6571e32835.png

    从图中可以看出,对于s1表的二级索引idx_key1来说,值为NULL的二级索引记录都被放在了B+树的最左边,这是因为设计InnoDB的大叔有这样的规定:

    We define the SQL null to be the smallest possible value of a field.

    也就是说他们把SQL中的NULL值认为是列中最小的值。

    在通过二级索引idx_key1对应的B+树快速定位到叶子节点中符合条件的最左边的那条记录后,也就是本例中id值为521的那条记录之后,就可以顺着每条记录都有的next_record属性沿着由记录组成的单向链表去获取记录了,直到某条记录的key1列不为NULL。

    使不使用索引的依据到底是什么?

    那既然IS NULL、IS NOT NULL、!=这些条件都可能使用到索引,那到底什么时候索引,什么时候采用全表扫描呢?

    答案很简单:成本。当然,关于如何定量的计算使用某个索引执行查询的成本比较复杂,我们在小册中花了很大的篇幅来唠叨了。不过因为篇幅有限,我们在这里只准备定性的分析一下。对于使用二级索引进行查询来说,成本组成主要有两个方面:

    • 读取二级索引记录的成本
    • 将二级索引记录执行回表操作,也就是到聚簇索引中找到完整的用户记录的操作所付出的成本。

    很显然,要扫描的二级索引记录条数越多,那么需要执行的回表操作的次数也就越多,达到了某个比例时,使用二级索引执行查询的成本也就超过了全表扫描的成本(举一个极端的例子,比方说要扫描的全部的二级索引记录,那就要对每条记录执行一遍回表操作,自然不如直接扫描聚簇索引来的快)。

    所以MySQL优化器在真正执行查询之前,对于每个可能使用到的索引来说,都会预先计算一下需要扫描的二级索引记录的数量,比方说对于下边这个查询:

    SELECT * FROM s1 WHERE key1 IS NULL;

    优化器会分析出此查询只需要查找key1值为NULL的记录,然后访问一下二级索引idx_key1,看一下值为NULL的记录有多少(如果符合条件的二级索引记录数量较少,那么统计结果是精确的,如果太多的话,会采用一定的手段计算一个模糊的值,当然算法也比较麻烦,我们就不展开说了,小册里有说),这种在查询真正执行前优化器就率先访问索引来计算需要扫描的索引记录数量的方式称之为index dive。当然,对于某些查询,比方说WHERE子句中有IN条件,并且IN条件中包含许多参数的话,比方说这样:

    SELECT * FROM s1 WHERE key1 IN ('a', 'b', 'c', ... , 'zzzzzzz');

    这样的话需要统计的key1值所在的区间就太多了,这样就不能采用index dive的方式去真正的访问二级索引idx_key1,而是需要采用之前在背地里产生的一些统计数据去估算匹配的二级索引记录有多少条(很显然根据统计数据去估算记录条数比index dive的方式精确性差了很多)。

    反正不论采用index dive还是依据统计数据估算,最终要得到一个需要扫描的二级索引记录条数,如果这个条数占整个记录条数的比例特别大,那么就趋向于使用全表扫描执行查询,否则趋向于使用这个索引执行查询。

    理解了这个也就好理解为什么在WHERE子句中出现IS NULL、IS NOT NULL、!=这些条件仍然可以使用索引,本质上都是优化器去计算一下对应的二级索引数量占所有记录数量的比值而已。

    大家可以看到,MySQL中决定使不使用某个索引执行查询的依据很简单:就是成本够不够小。而不是是否在WHERE子句中用了IS NULL、IS NOT NULL、!=这些条件。大家以后也多多辟谣吧,没那么复杂,只是一个成本而已。

    为了感谢支持我的朋友!整理了一份Java高级架构资料、Spring源码分析、Dubbo、Redis、Netty、zookeeper、Spring cloud、分布式等资料

    关注公众号:Java大型网站架构(免费获取)

    展开全文
  • 本文所述的各种数据结构(二叉树等),均考虑重复值的情况,本文简述各种数据结构的区别仅仅只是为了理解MySQL索引的需要而做的铺垫。 什么索引 提起索引,大家都知道,建立索引可以让数据库查询更快,那么索引...

    声明

    本文所述的各种数据结构(二叉树等),均不考虑重复值的情况,本文简述各种数据结构的区别仅仅只是为了理解MySQL索引的需要而做的铺垫。

    什么是索引

    提起索引,大家都知道,建立索引可以让数据库查询更快,那么索引究竟是什么?我想这就不是每个人都能说得出来了。

    索引,是数据库管理系统中一个排序的数据结构,并用以协助快速查询、 更新数据库表中数据。

    是的,索引是一种数据结构,但是那么多的数据结构中为何MySQL要选择B+树呢?接下来就让我们一起来了解下B+树相对于其他数据结构有何独特之处!

    二分查找法(Binary Search)

    首先让我们自己想一想,如果让我们去设计,我们会怎么去存储?我想大部分人想到就是用链表或者数组去存储数据,然后再按默认的顺序排好,再去查找,而一个排好顺序的链表我们就可以通过二分查找法来高效查询。

    二分查找也称折半查找,是一种效率较高的查找方法。比如有1-10十个数,我们要找到8,先从中间开始找5,然后发现8比5大,可以把5左边的数去掉,剩下6-10,再从中间开始找,依次类推,直到找到8为止。但是这种查找法有一个前提是数据必须是有序的,而且这种属于链表式的存储,我们一但要插入或者修改一个数据,可能会伴随着大量的下标移动,比如我们把1-10放在数组里面,下标分别对应0-9,然后现在要插入一个0,为了保证有序,0必须排在第一位,那么1-10所有的数据下标都要往后移动一位,这种就有点大动干戈了,所以为了解决这个问题,我们就有了二叉树。

    二叉查找树(BST)

    二叉查找树简称二叉树(BST),英文全称:Binary Search Tree,这是一种什么样的数据结构呢?请看下图

    在这里插入图片描述

    在上面这棵树中,我们要找到8,先从根节点6开始比较,发现8比6大,就往右边走,就可以找到8

    二叉树的特点

    二叉树有两个特点:

    1、左子树所有的节点都小于父节点

    2、右子树所有的节点都大于父节点

    二叉树存在的问题

    二叉树有一个严重的问题,那就是它的查找耗时是和这棵树的深度相关的,在最坏的情况下时间复杂度会退化成 O(n)。

    如下图:

    在这里插入图片描述

    上面就是一种极端情况下的二叉树,会退化成线性链表,这种如果要找到最后一个数6,就要从1开始遍历完整棵树,效率就会非常低。那么有没有一种相对平衡一点,不要出现这种极端情况的数据结构呢,所以就有了平衡二叉树。

    平衡二叉树(AVL Tree)

    平衡二叉树,英文全名叫做 Balanced binary search trees,简称AVL树,这个AVL并不是英文名的简称,而是发明者(G. M. Adelson-Velsky和E. M. Landis)两个人的人名缩写,请看下图一个平衡二叉树示例:

    在这里插入图片描述

    上图中也是从1开始插入6,如果是二叉树就会变成一种线性结构,但是平衡二叉树就会通过左旋和右旋操作,最终会生成上图所示的结构,感兴趣的可以进入网站自己操作观察旋转过程.

    平衡二叉树的特点

    平衡二叉树相比较二叉树具有一个特点就是:左右子树深度差绝对值不能超过 1,当然,平衡二叉树首先是一颗二叉树,只不过通过左旋和右旋实现左右子树深度差不超过1,避免了二叉树的极端情况的出现。

    MySQL为何不选择平衡二叉树

    既然平衡二叉树解决了普通二叉树的问题,那么mysql为何不选择平衡二叉树作为索引呢?

    索引需要存储什么

    让我们想一想,如果我们要把索引存起来,那么应该存哪些信息呢,它应该存储三块信息:

    • 索引的值:就是表里面索引列对应的值。

    • 数据的磁盘地址(通过磁盘地址找到当前数据)或者直接存储整条数据。

    • 子节点的引用:我们需要从根节点往下走,所以需要知道左右子节点的地址。

    根据这三点,可以有如下大致的一个简单的结构图:

    在这里插入图片描述

    上图中数字表示的是索引的值,0x开头的表示磁盘地址,根节点中存了左右节点的引用。

    AVL树用来存储索引存在什么问题

    我们知道,页(Page)是 Innodb 存储引擎用于管理数据的最小磁盘单位,页的默认大小为16KB。页也就是上图中的节点,每查询一次节点就需要进行一次IO操作,IO操作是一种非常耗时的操作,很多业务系统的瓶颈都是卡在IO操作上,所以如果我们需要提高查询效率的办法之一就是减少IO次数,那么问题就来了,AVL树一个节点上只存了一个关键字(索引值)+一个磁盘地址+左右节点的引用,这是远远达不到16KB的,会浪费了大量的空间。

    上图中如果我们要找到6这条数据,需要进行3次IO(获取一个节点就是一个IO操作),如果这棵树很高的话,就会进行大量的IO操作,所以说AVL树存在的最大问题就是空间利用不足,浪费了大量空间,数据量大的时候就会成为一颗瘦高的树。

    那么我们可以怎么改进呢?答案很明显了,那就是每个磁盘块多存一点东西,也就是说每个磁盘多存几个关键字,因为关键字越多,路数越多;路数越多,树也就越矮越胖,相应的操作IO次数就会越少。

    多路平衡树(Balanced Tree)

    多路平衡树简称B树,又称B-树,和AVL树一样,B树在枝节点和叶子节点存储键值、磁盘地址、左右节点引用。请看下图的一个多路平衡树的示例:

    在这里插入图片描述

    B树的特点

    相比较AVL树,B树一个磁盘上可以存多个关键字(值),而且有一个特点就是:

    • 分叉数(路数)永远比关键字数多1。

    我们可以画出如下简图(下图中只画了3路,即两个关键字,实际取决于一页能存储多少个关键字):从上图可以很明显的看出,同样高度的树,B树能存的数据远远大于平衡二叉树。

    在这里插入图片描述

    B树是如何查找数据的

    以上图为例,假如我们要找key=32这个数字,首先获取到根节点,发现18小于key,所以往右边走,获取到右边的数据,54和76,这时候遵循以下原则:

    • key<54,命中最左边分叉;

    • key=54,直接命中,返回数据;

    • 54<key<76,走中间的一个分叉;

    • key=76,直接命中,返回数据;

    • key>76,命中右边分支;

    这里因为key=32,所以走得是第1条,命中左边分支,这时候再去获取左边分支,获取到32和50,比较发现key=32,命中,返回数据。

    从上面我们可以看出B树效率相对于AVL树,在数据量大的情况效率已经提高了很多,那么为什么MySQL还是不选择B树作为索引呢?

    那么接下来让我们先看看改良版的B+树,然后再下结论吧!

    B+树

    B+树由B树改良而来,属于改良版的多路平衡查找树。

    首先让我们来看看B+树到底长什么样呢:

    在这里插入图片描述

    对比B+树,我们可以发现一个很明显的区别就是叶子节点有一个箭头指引而且从左到右是有序的。

    InnoDB中使用的B+树相比较于传统B+树,改进之后的B+树具有以下特点

    InnoDB中B+树的特点

    • 它的关键字的数量是跟路数相等的。

    • B+树的根节点和枝节点中都不会存储数据,只有叶子节点才存储数据。而搜索到关键字不会直接返回,会到最后一层的叶子节点。

    • B+树的每个叶子节点增加了一个指向相邻叶子节点的指针,它的最后一个数据会指向下一个叶子节点的第一个数据,形成了一个有序链表的结构。

    • 它是根据左闭右开的区间来检索数据的

    按照B+树的特点,我们可以画出一个存储数据的简图,如下:

    在这里插入图片描述

    B+树是如何查找数据的

    假设我们现在要找一个key=66,遵循如下步骤:

    1、获取到根节点,依据左闭右开有如下区间:[1,28),[28,66),[66,+∞),命中了最后一个区间,虽然66在根节点,但是因为根节点不存储数据,所以是会往下继续搜索右边的节点

    2、获取到右边节点,依据左闭右开有如下区间:[66,78),[78,89),[89,+∞),命中左边的范围。

    3、获取到第三排倒数第二块磁盘,找到66,返回数据。

    B+树相对于B树的改进点

    B+树是由B树改进而来的,所以B树能解决的问题,B+树都能解决,那么B+树能解决哪些B树所不能解决的问题呢?

    1、扫库、扫表能力更强:如果我们要对表进行全表扫描,只需要遍历叶子节点就可以 了,不需要遍历整棵B+Tree

    2、B+Tree 的磁盘读写能力相对于 B Tree 来说更强:根节点和枝节点不保存数据区, 所以一个节点可以保存更多的关键字,一次磁盘加载(IO操作)能获取到相对更多的关键字。

    3、天然具备排序能力:叶子节点上有下一个数据区的指针,数据形成了链表。

    4、效率稳定:B+Tree 永远是在叶子节点拿到数据,所以 IO 次数是稳定的,而B树运气好根节点就拿到数据,运气不好就要到叶子节点才能拿到数据,所花费的时间会有差异。

    总结

    本文简述了从二叉树到B+树之前的演进过程,并大致讲解了各种数据结构之间的差异以及MySQL为何最终会选择了B+树来作为索引。

    点关注,不迷路

    好了各位,以上就是这篇文章的全部内容了,能看到这里的人呀,都是人才。之前说过,PHP方面的技术点很多,也是因为太多了,实在是写不过来,写过来了大家也不会看的太多,所以我这里把它整理成了PDF和文档,如果有需要的可以

    点击进入暗号: PHP+「平台」

    在这里插入图片描述

    在这里插入图片描述


    更多学习内容可以访问【对标大厂】精品PHP架构师教程目录大全,只要你能看完保证薪资上升一个台阶(持续更新)

    以上内容希望帮助到大家,很多PHPer在进阶的时候总会遇到一些问题和瓶颈,业务代码写多了没有方向感,不知道该从那里入手去提升,对此我整理了一些资料,包括但不限于:分布式架构、高可扩展、高性能、高并发、服务器性能调优、TP6,laravel,YII2,Redis,Swoole、Swoft、Kafka、Mysql优化、shell脚本、Docker、微服务、Nginx等多个知识点高级进阶干货需要的可以免费分享给大家,需要的可以加入我的 PHP技术交流群

    展开全文
  • 为什么要用迭代器? 迭代器给你提供了一种依赖索引取值的方式 ) 它们的原型中都有一个Symbol.iterator方法。 内置对象是可迭代的这些 1)数组Arrays2)字符串Strings 3)Map4)Set5)argumen

    因为for of遍历依靠的是遍历器Iterator。
    for…of正常遍历,都需要实现一个遍历器Iterator(说到遍历器:也就是迭代器Iterator
    补充一下概念:
      什么是迭代器?
    迭代:更新换代(重复)的过程,每次的迭代都必须基于上一次的结果

    迭代器:迭代取值的工具

    为什么要用迭代器?
    迭代器给你提供了一种不依赖索引取值的方式

    它们的原型中都有一个Symbol.iterator方法。

    内置对象是可迭代的这些
    1)数组Arrays2)字符串Strings 3)Map4)Set5)arguments6)Typed Arrays7)Generators,早就内置好了Iterator(迭代器),所以它们可以被for of遍历。
    而Object对象本身没有内置的遍历器,并没有实现这个接口,使得它无法被for…of遍历。

    Array.prototype[Symbol.iterator];
     
    // ƒ values() { [native code] }
     
    String.prototype[Symbol.iterator];
     
    // ƒ [Symbol.iterator]() { [native code] }
     
    Set.prototype[Symbol.iterator];
     
    // ƒ values() { [native code] }
     
    Map.prototype[Symbol.iterator];
     
    // ƒ entries() { [native code] }
     
    Object.prototype[Symbol.iterator];
     
    // undefined
    

    在这里插入图片描述

    展开全文
  • 3. 索引的列不要太多,要选择一些selective比较低的列建B-tree索引,选择selective高的列建bitmap索引(在更新比较多的表不要建bitmap索引) 4. 将selective较低的列放在前面 5. 在更新不多的表上建索引时,可以考虑...
  • 什么索引提起索引,大家都知道,建立索引可以让数据库查询更快,那么索引究竟是什么?我想这就不是每个人都说得出来了。索引,是数据库管理系统中一个排序的数据结构,并用以协助快速查询、 更新数据库表中数据...
  • 不是一定提高查询性能,索引就是一种特殊的查询表数据库的搜索引擎可以利用它加速对数据的检索就像书的目录需要查询整本书就可以找到想要的数据优点:加快访问速度加强唯一性缺点:带索引的表在数据库中需更多的...
  • 索引、事务和锁 课程说明 本课程采用 MySQL5.7 版本,并采用 InnoDB 存储引擎 ...2) 为什么要有索引索引在 MySQL 中也叫做“键”,是存储引擎用于快速找到记录的一种数据结构。索引对于良好的性能非常关键,尤其是
  • mysql为什么使用B+ Tree索引,使用B- Tree索引? 索引顺序如何生效?什么是覆盖索引? order by 也用到索引? 何时索引失效? 如何设计索引,全方位理解mysql索引的特性.1. Sql执行顺序1. FROM 子句 组装来自不同数据...
  • 索引分为聚簇索引和非聚簇索引两种,聚簇索引是按照数据存放的物理位置顺序的,而非聚簇索引一样了;聚簇索引能提高多行检索的速度,而非聚簇索引对于单行的检索很快要注意的是,建立太多的索引将会影响更新和...
  • 索引分为聚簇索引和非聚簇索引两种,聚簇索引是按照数据存放的物理位置顺序的,而非聚簇索引一样了;聚簇索引能提高多行检索的速度,而非聚簇索引对于单行的检索很快要注意的是,建立太多的索引将会影响更新和...
  • ③ 为什么不建议使用身份证作为主键? ④ 模糊匹配like abc%,like %ass%,like %bnl都用不到索引,对吗? ⑤ 不要使用select *,写明具体查询字段为什么? 索引是什么? 数据库索引是数据库管理系统中一个排序的...
  • 数据库-索引详解

    2018-12-27 12:21:37
    数据库为什么引入索引 在数据库操作中我们最常用的就是查询,因为数据量庞大,为了提高查询速度,提高数据库性能。引入索引。 但是查询速度的提高的代价-空间换时间-就是 插入,更新,删除速度的降低。 索引分类 ...
  • 一个表可以没有主键,但最多只能有一个主键,并且主键值不能包含NULL。 在MySQL中,InnoDB数据表的主键设计我们通常遵循几个原则: 采用一个没有业务用途的自增属性列作为主键; 主键字段值总是不更新,只有新增或者...
  • 黑马视频-索引

    2015-11-14 19:34:00
    提高查询的速度 ,聚集索引 :当数据实际的存储数据与索引的顺序一致,就把该索引叫做聚集索引非聚集索引:当索引中数据的顺序与数据实际存储的顺序一致的索引叫做非聚集索引为什么索引可以提高查询速度?...
  • 在前面说过了索引能极大的提高数据的检索速度,那为什么不在每一个列上建索引呢?初学者可能会困惑这个问题,而且通常不知道哪些列该建索引,哪些不 该建, 甚至于会把like模糊查询的列也作为索引列,其实绝大...
  • 该楼层疑似违规已被系统折叠隐藏此楼查看此楼问题索引(由于百度稳定的原因,如发现下面所列举的问题找到答案,请直接到后面去找)1、蓝格怪衣在哪里?2、蓝格怪衣错过之后怎么回去拿?3、猜数字有什么诀窍?4、采药...
  • 索引什么 数据库索引,是数据库管理系统(DBMS)中一个排序的数据...另外需要注意的是,主键索引是一种特殊的唯一索引,它还多了一个限制条件,要求键值不能为空。主键索引用 primay key创建。 全文(Fulltext):
  • 前言本章将会带你深入...子线程真的不能更新UI吗?为什么在Activity的onCreate方法中无法获取View的宽和高?图解Activity启动到View显现过程上图出现了五个对象:Activity、Window、WindowManager、DecorView、Vie...
  • 高性能索引策略二

    2019-02-22 15:50:00
     在每个列创建独立索引一般是由于听到诸如“吧where条件里面的列都建立上索引”这样模糊的建议导致的,实际上这个建议的非常错误的,在多个列上简历独立的单列索引大部分情况下并不能提高mysql的查询性能。...
  • 02--MySQL的索引

    2020-09-20 17:50:22
    另外需要注意的是,主键索引是一种特殊的唯一索引,它还多了一个限制条件,要求键值不能为空。主键索引用primaykey创建。 全文(Fulltext):只有文本类型的字段才可以创建全文索引,比如char、varchar、text。 ...
  • 什么索引索引(Index)是帮助MySQL高效获取数据的数据结构。提取句子主干,就可以得到索引的本质:==索引是一种数据结构。...1、UNIQUE,唯一索引,可以null,但不能重复 2、INDEX 普通索引 3、Promary ...
  • MySQL学习笔记一索引

    2021-03-22 13:39:31
    为什么要使用数据库? 数据可以永久保存;使用SQL语句查询修改效率高;管理数据方便。 数据库三大范式: 范式1:每一列都可再分; 范式2:在范式1的基础上,非主键列完全依赖主键,依赖主键的一部分; 范式3:...
  • 在前面说过了索引能极大的提高数据的检索速度,那为什么不在每一个列上建索引呢?初学者可能会困惑这个问题,而且通常不知道哪些列该建索引,哪些不该建, 甚至于会把like模糊查询的列也作为索引列,其实like是不...
  • IndexReader用来读取一个...疑问:索引文件可能很大,不可能把所有索引放进内存,创建一个IndexReader后为什么不能直接检索到之后索引更新的内容?答:虽然不会把索引内容全部放进内存,但是Lucene索引是分段的,新...
  • 如果我们建立好索引,然后每次更新数据后重新建立索引,无疑是不合理的,为什么不能在原先索引文件的基础上再把新更新的加在上面呢?增量索引就是在建完索引的后,将数据库的最后一条记录的ID存...
  • 另外需要注意的是,主键索引是一种特使的唯一索引,它还多了一个限制条件,要求键值不能为空。 全文索引(FullText):针对比较大的数据,比如存放的是消息内容。如果要解决查询like查询效率低的问题,可以创建全文...
  • 在前面说过了索引能极大的提高数据的检索速度,那为什么不在每一个列上建索引呢?初学者可能会困惑这个问题,而且通常不知道哪些列该建索引,哪些不该建, 甚至于会把like模糊查询的列也作为索引列,其实绝大多数...
  • 在做网站SEO的过程中,站长们也非常重视网站...一、能够优先索引众所周知,网站在优化更新时,有时所提交的URL、文章、链接等都不一定会被百分百收录,往往会因为提交内容质量低不符合搜索需求、技术原因页面不能正...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 408
精华内容 163
关键字:

为什么不能更新索引