从B+树的角度聊一聊为什么阿里的《JAVA开发手册》强制要求mysql表的主键应为bigint unsigned类型


1 由《java开发手册》引发的思考

有那么一段时间,我老是在为该把mysql表的主键指定成什么类型而发愁,原因是因为看到阿里巴巴的《java开发手册》中在MYSQL数据库建表规约里有下面这样一条【强制】性的规定:
在这里插入图片描述
但是我发现我们部门里数据库表的主键却全是integer,当时就在想难道是因为阿里巴巴的数据量会比较大才这样【强制】规定的么?还有没有其他的原因呢?《java开发手册》不应该是面向广大开发者的么?。。。

于是上网百度mysql主键int和bigint类型的区别,发现了下面这样一段话:
在这里插入图片描述
感觉解释好像也挺合理的。
—> 于是就有点想不明白了,为什么阿里要【强制】性这样规定呢???
—> 然后便想到了mysql的索引 —> 想到了B+树 —> 找到了一个可以说服自己的理由。


2 B树、B+树简单介绍

首先推荐一个学习数据结构与算法非常好的一个网站:https://www.cs.usfca.edu/~galles/visualization/Algorithms.html


2.1 B树

B树的数据结构如下:
在这里插入图片描述
其主要特点为:

(1)叶子节点和非叶子节点都存储数据
(2)叶子节点具有相同的深度 —> 多叉平衡树
(3)叶子节点的指针为空
(4)节点中的数据key从左到右递增排列


2.2 B+树

B+树的数据结构如下:
在这里插入图片描述
其特点主要有以下几点:

(1)非叶子节点不存储数据(数据都在叶子节点),只存储key —> 可以增大度
(2)非叶子节点仅用作索引 且 和子节点之间存在数据冗余 —> 便于快速定位
(3)叶子节点用指针连在一起 —> 便于进行范围查找,比如说想查询大于等于30的,找到30之后,后面的数据就可以很快被检索到了。


2.3 为什么mysql索引的底层数据结构使用B+树而不是B树

B树和B+树最大的区别主要在于B+树的非叶子节点不存储数据,数据全部在叶子节点, 而B树的叶子节点和非叶子节点都会存储数据。
而mysql索引的底层数据结构使用了B+树。

在说原因之前先来看一下mysql根据索引查询数据的过程

以3.2中查询20为例:
(1)先将到第一个节点(15,56,77)加载到内存(发生一次磁盘IO),然后在内存中运算发现20在15和56之间
(2)根据15的指针,将第二个节点(15,20,44)加载到内存(发生一次磁盘IO),然后在内存中运算找到了20对应的索引 —》 如果是B树,会直接返回找到的数据,如果是B+树会走第(3)步
(3)根据20的指针,将第三个节点(20,25,30)加载到内存(发生一次磁盘IO),然后找到20对应的数据进行返回 — 当然也可能是根据20的指针直接找到20对应的数据,进行返回

从上面介绍的过程来看,貌似B树比B+树还好一些,因为B树找到数据后就直接进行返回了,要比B+树少了一次磁盘IO,而众所周知的是进行磁盘IO是很耗性能的。

但是事实如此码??? — 》肯定不是。


首先我们要知道:

(1)虽然说在内存里进行操作速度很快,但我们不能一股脑把所有数据都加载都内存;
(2)mysql规定的每个节点大小是固定的16K(16384B) — 可以通过下面的语句看到

SHOW GLOBAL STATUS LIKE 'Innodb_page_size';

在这里插入图片描述
知道了上面两点,其实就可以很容易知道为什么mysql索引的底层数据结构使用了B+树而不使用B树了:

(1)由于非叶子节点不存数据,这样就可以存入更多的索引值key
(2)每一个节点存的索引值多了,树的整体高度肯定就低了
(3)树的高度一低,那发生磁盘IO的次数肯定就少了


3 阿里【强制】要求mysql表的主键应为bigint unsigned类型原因

首先应该知道

(1)mysql索引B+树的高度应为:2-4
(2)一个bigint类型的数据为8B,一个integer类型的数据为4B,在B+树上每个指针的大小为6B,如下图:

在这里插入图片描述

这样的话,对于B+树来说一个非叶子节点能存储的索引的个数为:

主键为integer类型 : 16384/(4+6) ≈ 1638个
主键为bigint类型:16384/(8+6)≈ 1170个

假如每条数据大小为1K(1024B)的话,一个叶子节点就可以存大概16条数据

那假如B+树的高度为4层的话,那mysql存储的所有数据的条数应为:

主键为integer类型:1638 * 1638 * 1638 * 16 = 70317217152 大于int的上限42亿
主键为bigint类型:1170 * 1170 * 1170 * 16 = 25625808000 肯定远小于long的上限,也大于42亿

也就是说虽然42亿的预留量可能已经非常充足了,但万一不够了呢?

既然mysql即使是256亿的数据量也仅需4次IO就可以查到,而这个时候int类型的预留量肯定不够了,bigint却十分充足,为什么不用bigint呢???

我想得可能正是基于这个原因,阿里才会强制要求mysql表的主键应为bigint unsigned类型。

发布了189 篇原创文章 · 获赞 187 · 访问量 39万+

猜你喜欢

转载自blog.csdn.net/nrsc272420199/article/details/102877399