目录
索引有什么优缺点?(优:提高数据检索速度、便于进行排序和分组 缺:占用存储空间、维护有序性成本高、频繁写操作的表,降低写入性能)
B+树的页是单向链表还是双向链表?如果从大值向小值检索,如何操作?
为什么 MongoDB 索引用 B树,而 MySQL 用 B+ 树?
insert操作对B+树结构的改变是怎样的?(不是主键递增会发生页分裂)
innodb 使用数据页存储数据,默认数据页大小 16K,我现在有一张表,有 2kw 数据,我这个 b+树的高度有几层?
索引
为什么要建索引?
建索引的三个优点:
-
索引大大减少了 MySQL需要扫描的数据量;
-
索引可以帮助MySQL避免外部排序和使用临时表;
-
索引可以将随机 I/O变为顺序I/O;
如果没有建立索引,数据库会进行全表扫描(Sequential Scan),搜索时间复杂度是O(n),这样的查询效率还是比较低的,为了提高查询率,我们可以建立索引。
建立了索引后数据都会按照顺序存储,这时候我们可以利用类似二分查找的方式快速查找数据,B+树索引是多叉树,搜索时间复杂度是 O(logdN),这样就提高了查询速度,
除此之外还可以避免外部排序和使用临时表等问题,以及将随机 I/O变为顺序I/O。
索引有什么优缺点?(优:提高数据检索速度、便于进行排序和分组 缺:占用存储空间、维护有序性成本高、频繁写操作的表,降低写入性能)
索引的优点包括:
-
提高数据检索效率:索引可以加快数据的检索速度,减少数据查找的时间。
-
支持快速排序和分组:索引可以帮助数据库快速排序和分组数据。
-
索引可以帮助MySQL避免外部排序和使用临时表;
-
索引可以将随机 I/O变为顺序I/O;
索引的缺点包括:
-
占用存储空间:索引会占用额外的存储空间,增加数据库的存储成本。
-
维护成本高:随着数据的增删改查,索引需要不断更新和维护,增加数据库的维护成本。
-
降低写入性能:对于频繁进行写操作的表,索引可能会降低写入性能,因为每次写入都需要更新索引。
简单说一下索引的分类
可以按照四个角度来分类索引。
-
按「数据结构」分类:B+tree索引、Hash索引、Full-text索引。
-
按「存储位置」分类:聚簇索引(主键索引)、二级索引(非聚簇索引)。
-
按「字段特性」分类:主键索引、唯一索引、普通索引、前缀索引
-
按「字段个数」分类:单列索引、联合索引。
MySQL有哪些索引类型(结构)
我了解到 MySQL支持 B+ 树索引、哈希索引、全文索引这三种索引类型。我比较常用的是 B+ 树索引,因为它是InnodB引擎默认使用的索引类型,支持排序、分组、范围查询、模糊查询等功能
自适应哈希索引并不是由用户显式创建的,而是 InnoDB 根据数据访问的模式自动建立和管理的。当 InnoDB 发现某个索引被频繁访问时,会在内存中创建一个哈希索引,以加速对这个索引的访问。
B+ 树索引、哈希索引、全文索引的区别
B+ 树索引: InnodB引擎默认的索引,支持排序、分组范围查询、模糊查询等,并且性能稳定
哈希索引: 哈希索引多用于等值查询,in 或者 = ,时间复杂度为O(1),效率非常高,但不支持排序、范围查询及模糊查询等
全文索引:一般用于查找文本中的关键字,而不是直接比较是否相等,主要是用来解决 WHERE name LIKE"%aaaa%”等针对文本的模糊查询效率低的问题
B+树与其他结构比较
-
B+树是多叉树,平衡二叉树、红黑树是二叉树,在同等数据量下,平衡二叉树、红黑树高度更高,磁盘IO次数更多,性能更差,而且它们会频繁执行再平衡过程,来保证树形结构平衡。
-
和B+树相比,跳表在极端情况下会退化为链表,平衡性差,而数据库查询需要一个可预期的查询时间,并且跳表需要更多的内存。
-
和B+树相比,B树的数据存储在全部节点中,对范围查询不友好。非叶子节点存储了数据,导致内存中难以放下全部非叶子节点。如果内存放不下非叶子节点,那么就意味着查询非叶子节点的时候都需要磁盘IO。
为什么索引用B+树,而不用B树?
我觉得主要有三个原因:
-
B+树的磁盘读写代价更低 : B+ 树只有叶子节点才会存放索引和数据,非节点只存放索引,而B树所有节点都会存放索引和数据,因此存储相同数据量的情况下, B+ 树可以比 B 树更矮胖,查询叶子节点的磁盘 I/O次数会更少;
-
B+树便于范围查询 : MySQL是需要经常使用范围查询的关系型数据,B+ 树所有叶子节点间会用链表进行连接,这种设计对范围查找非常有帮助,而B树没有将所有叶子节点用链表串联起来的结构,只能通过中序遍历来完成范围查询,这会比B+树范围查询涉及更多个节点的磁盘I/O操作,因此范围查询效率不如B+树;
-
B+树增删查改效率更加稳定: B+ 树有大量的冗余节点,这些冗余数据可以让 B+ 在插入、删除的效率都更高,比如删除根节点的时候,不会像B树那样会发生复杂的树的变化。另外,B+树把所有数据都放到了叶子节点,因此查询、插入、删除数据都需要走到最后一层不同于B树可能在任意一层找到数据,所以B+树更为稳定。
所以,InnodB引擎的索引选择了 B+ 树
为什么索引用B+树?而不用哈希表?
MySQL会有很多范围查询和排序的场景,虽然哈希表的搜索时间复杂度是 O(1),但是由于哈希表的数据都是通过哈希函数计算后散列分布的,所以哈希表索引不支持范围查询和排序操作,不支持联合索引最左匹配原则,如果重复键值比较多,还容易造成哈希碰撞导致效率进一步降低。而 B+ 树可以满足这些应用场景,因此选择了用 B+ 树索引
B+树的页是单向链表还是双向链表?如果从大值向小值检索,如何操作?
B+树的叶子节点是通过双向链表连接的,这样可以方便范围查询和反向遍历。
-
当执行范围查询时,可以从范围的开始点或结束点开始,向前或向后遍历,这使得查询更为灵活。
-
在需要对数据进行逆序处理时,双向链表非常有用。
如果需要在 B+树中从大值向小值进行检索,可以按以下步骤操作:
-
定位到最右侧节点:首先,找到包含最大值的叶子节点。这通常通过从根节点开始向右遍历树的方式实现。
-
反向遍历:一旦定位到了最右侧的叶子节点,可以利用叶节点间的双向链表向左遍历。
为什么 MongoDB 索引用 B树,而 MySQL 用 B+ 树?
B树的特点是每个节点都存储数据,相邻的叶子节点之间没有指针链接。
B+树的特点是非叶子节点只存储索引,叶子节点存储数据,并且相邻的叶子节点之间有指针链接。
那么在查找单条数据时,B 树的查询效率可能会更高,因为每个节点都存储数据,所以最好情况就是 O(1)。
但由于 B 树的节点之间没有指针链接,所以并不适合做范围查询,因为范围查询需要遍历多个节点。
而 B+ 树的叶子节点之间有指针链接,所以适合做范围查询,因为可以直接通过叶子节点间的指针顺序访问整个查询范围内的所有记录,而无需对树进行多次遍历。
MongoDB 属于非关系型数据库,在大多数情况下,只需要查询单条数据,所以 MongoDB 选择了 B 树。
那一棵 B+树能存储多少条数据呢?
理论上,在 InnoDB 存储引擎中,B+树的高度一般为 2-4 层,就可以满足千万级数据的存储。查找数据的时候,一次页的查找代表一次 IO,当我们通过主键索引查询的时候,最多只需要 2-4 次 IO 就可以了。
删除表中大量数据 ,B+树索引结构会发生变化吗
对于千万级的表数据存储,删除大量记录后,表文件大小并没有随之变小。
数据表操作有新增、删除、修改、查询
,其中查询
属于读操作,并不会修改文件内容。修改文件内容的是写操作
,具体分为有删除、新增、修改
三种类型。
InnoDB 中的数据采用B+
树来组织结构。
delete会将删除位置标记为空,可以进行空间的复用,比如说删除id=7
这条记录,InnoDB引擎只是把id=7
这条记录标记为删除,但是空间保留。如果后面有id位于(6,19)区间内的数据插入时,可以重复使用这个空间。
除了记录可以复用外,数据页也可以复用。当整个页
从B+树摘掉后,可以复用到任何位置。
比如,将page number=5
页上的所有记录删除以后,该page标记为可复用。此时如果插入一条id=100
的记录需要使用新页,此时page number=5
便可以被复用了。
如果相邻两个page的利用率都很低,数据库会将两个页的数据合并到其中一个page上,另一个page被标记为可复用。
当然,如果是像上面我们做的实验那样,将整个表的数据全部delete掉呢?所有的数据页都会被标记为可复用,但空间并没有释放,所以表文件大小依然没有改变。
聚簇索引和非聚簇索引有什么区别?
在 MySQL 的 InnoDB 引擎中,每个索引都会对应一颗 B+ 树,而聚簇索引和非聚簇索引最大的区别在于叶子节点存储的数据不同:
-
聚簇索引:聚簇索引的叶子节点保存了一行记录的所有列信息,查询效率高。
-
非聚簇索引:它的叶子节点只包含一个主键值,通过非聚簇索引查找记录要先找到主键,然后通过主键再到聚簇索引中找到对应的记录行,这个过程被称为回表。
insert操作对B+树结构的改变是怎样的?(不是主键递增会发生页分裂)
-
B+树的数据都是有序的,所以: 如果我们使用主键是顺序递增,那么每次插入的新数据就会顺序插入到叶子节点最右边的节点里,如果该页面满了,就会自动开辟一个新页面,将新数据插入到新页面。因为每次插入一条新记录,都是追加操作,不需要重新移动数据,因此这种插入数据的方法效率非常高。
-
如果我们使用主键不是顺序递增,由于每次插入主键的索引值都是随机的,因此每次插入新的数据时,就可能会插入到现有数据页中间的某个位置,这时候为了保证B+ 树的有序性,要移动其它数据来满足新数据的插入。如果该页面满了,就发生页分裂,这时候要从一个页面复制数据到另外一个页面,目的是保证后一个数据页中的所有行主键值比前个数据页中主键值大,页分裂可能会造成大量的内存碎片导致索引结构不紧凑,从而影响查询效率
所以,我们在设计主键的时候,最好采用自增的方式,或者顺序递增主键值
innodb 使用数据页存储数据,默认数据页大小 16K,我现在有一张表,有 2kw 数据,我这个 b+树的高度有几层?
在 MySQL 中,InnoDB 存储引擎的最小存储单元是页,默认大小是 16k,可通过参数设置。页可以用来存储 B+树叶子节点上的数据,也可以存放非叶子节点上的键值对。
在查找数据时,一次页的查找代表一次 IO,一般 B+树的高度为 2-4 层,所以通过主键索引查询时,最多只需要 2-4 次 IO 就可以了。
已知非叶子节点可以存储 1170 个键值对。
主键 ID 是 bigint 类型,长度为 8 个字节。指针大小在 InnoDB 源码中设置为 6 字节,这样一共是 14 字节。所以非叶子节点(一页)可以存储 16384/14=1170 个这样的单元(键值+指针)。
假设一行数据的大小为 1KB,那么一页的叶子节点就可以存储 16 条数据。对于 3 层的 B+树,第一层叶子节点数*第二层叶子节点数*一页能够存储的数据量 = 1170*1170*16 = 21902400 条数据。
如果有 2KW 条数据,那么这颗 B+树的高度为 3 层。
自己整理,借鉴很多博主,感谢他们