阿里一面:MySQL单表数据最大不要超过多少行?为什么?

在后端开发中,我们经常听到关于MySQL单表数据量的建议,比如“通常不超过2000万行”。
然而,这个建议并不是硬性规定,实际性能受到各种因素的限制。
让我们通过实验探索这种限制的根源。
首先,创建一个表并插入数据。
我们使用rownum来插入大数据。
随着数据量的增加,当达到千万级时,查询速度明显变慢。
这提醒我们,单表数据量的限制可以与查询效率密切相关。
单个数据库表的行数实际上受到主键类型的限制。
例如int的32位限制约为21亿,h3int的限制更大。
然而,最大的限制来自硬盘存储。
InnoDB引擎采用B+树索引,数据存储在表空间文件中,每页16KB,随着数据的增长,页间关联和页目录会消耗空间,导致存储效率降低。
当行数据增大,单页无法容纳更多记录时,采用索引页来存储页地址,非叶子节点的B+树层次结构增加了研究的复杂度。
例如,在3层2分支的B+树中,每个非叶子节点最多可以存储1280行数据,而叶子节点中存储的数据会根据字段的类型和数量减少。
假设B+树有三层,则每个页面最多可以存储24,576,000行(约245亿),接近推荐值2000万。
但是,该值可能会受到实际数据大小、数据库版本、服务器配置和内存策略的影响。
在内存充足的环境中,查询性能可能不受此限制。
当单表数据量接近硬件极限时,例如内存不足,磁盘I/O就成为性能瓶颈。
综上所述,2000万行虽然是一个经验建议,但单表数据量的实际上限会受到多种因素的影响,应根据具体情况进行评估和优化。

为什么大家说mysql数据库单表最大两千万?依据是啥?

故事要从很多年前开始。
您一定听说过,建议单库表的数据量最大为2kW。
如果超过这个值,性能将会显着下降。
当我设计它的时候,我根本没有想到这款手表能够崛起得如此之快。


单个数据库表的理论最大行数是多少?创建表的SQL是这样写的,其中id为主键。
主键本身是唯一的,这意味着它的大小可以限制表的上限。
如果主键声明的大小为int,即32位,那么它可以支持2^32-1,大约是21亿。
如果是h3int的话就是2^64-1,但是这个数字太大了。
通常磁盘在达到此限制之前无法支持它。
除了主键之外,还有哪些因素影响行数?索引的内部结构采用的是B+树,这也是八篇文中的老篇了,想必大家都比较熟悉。
为了避免大家厌倦评判丑陋,今天我尝试从另一个角度和大家谈谈这件事。
假设我们有这样一个用户数据表。
在user表中,id是唯一的主键。
它看起来像数据行。
为了方便起见,我们稍后将它们称为记录。
该表看起来像Excel电子表格。
Excel数据是硬盘上的xx.excel文件。
上面的用户表数据在硬盘上其实也是类似的,放在user.ibd文件下。
这意味着用户表的innodbdata文件。
专业上也称为表空间。
尽管在数据表中它们似乎是并排的。
但实际上,它们在user.ibd中被分成了几个小数据页,每个数据页有16KB整个页有16KB,不算大,但这么多记录肯定放不下一个页,因此,它将分为几页。
而且您不可能使用全部16k来存储磁盘,对吗?由于记录被分成几部分,放在好几页上,为了唯一标识是哪一页,需要引入页号(实际上是‘一个表空间’的地址偏移量)。
同时,为了关联这些数据页,引入了前向和后向指针来指向前向和后向页。
这些被添加到页眉中。
必须读取和写入该页。
16KB已经不小了。
写入时可能会拔掉电源线。
因此,为了保证数据页的正确性,还引入了校验码。
这已添加到页面末尾。
剩余空间用于存储我们的录音。
如果记录的行数特别多,进入页面时逐一浏览效率会很低,所以会为这些数据生成一个页目录,具体实现细节也不是不重要。
你需要知道的是,它可以通过二分搜索将搜索效率从O(n)更改为O(lgn)。
从页到索引的页结构如果我们想要搜索一条记录,我们可以遍历表空间中的每个页,然后搜索其中的记录以确定它们是否与我们要查找的内容匹配。
当行数较少时,这不会造成问题。
行数越多,性能越慢。
所以,为了加快查找速度,我们可以选择每个数据页中主键ID最小的记录,只需要它的主键ID和页码即可。
页。
创建一条新记录并将其放置在新生成的数据页中。
这个新的数据页与之前的数据页的结构没有什么不同,其大小仍然是16KB,但是为了与之前的数据页进行区分。
页级信息添加到数据页中,从0开始向上计数。
所以页面之间就有了上下级的概念,如下图。
两层B+树结构突然看起来就像页面之间的倒置树。
这就是我们常说的B+树索引。
最底层的页面级别为0,即所谓的叶子节点,其余的称为非叶子节点。
上图是一个两层树。
如果数据变多了,我们可以用类似的方法再向上建一层。
它长成了一棵三层楼的树。
三层B+树结构我们现在可以通过这样的B+树来加速查询。
举个例子。
假设我们要查找第5行中的数据。
我们将从第一页上的记录开始。
该记录包含主键标识符和页码(页地址)。
请看下图中的黄色箭头。
左边最小的标识符是1,右边最小的标识符是7。
如果id=5的数据存在,那么它一定在左边的箭头上。
于是顺着记录页地址到数据页#6,然后判断id=5>4,所以定义在右边的数据页中,所以加载数据页#105。
在数据页中找到id=5的数据行,完成查询。
B+树查询过程中另外需要注意的是,上面页面的页码不是连续的,在磁盘上不一定是相邻的。
在此过程中查询了三个页面。
如果这三个页面都在磁盘上(没有提前加载到内存中),则最多需要三个磁盘I/O请求才能将它们加载到内存中。
B+树携带的记录数。
从上面的结构我们可以看出,注册数据被放置在B+树的最后一个叶子节点中。
非叶节点包含用于加速查询的索引数据。
也就是说,对于同一个16KB的页面,非叶子节点中的每个数据项都指向一个新页面,而新页面有两种可能。
假设计算出总行数,则这棵B+树的行数据总量等于(x^(z-1))*y。
如何计算x?我们回过头来看看数据页的结构。
页结构的非叶子节点主要存储与索引查询相关的数据,包括主键和指向页号。
假设主键为h3int(8字节),页号在源码中称为FIL_PAGE_OFFSET(4字节),那么非叶子节点中的一条数据大约为12字节。
整个数据页为16KB,页首和页尾的数据总计约128字节。
原始剧目页加上页目录估计为1KB,剩下的15KB除以12字节等于1280,这意味着它可以指向页x=1280。
我们常说的二叉树是指可以分裂成两个新的节点。
节点。
m叉树的一个节点可以指向m个新节点。
这种指向新节点的操作称为扇出。
上面的B+树可以指向1280个新节点,这是很恐怖的。
可以说扩散度非常高。
计算y时,叶子节点和非叶子节点的数据结构是一样的,所以也假设还剩下15KB可以玩。
实际的行数据放置在叶节点中。
假设一行数据为1KB,那么一页上可以放置y=15行。
总行数使用公式(x^(z-1))*y计算。
我们知道x=1280,y=15。
假设B+树由两层组成,则z=2。
那么就是(1280^(2-1))*15≈2w假设B+树是由三层组成,那么z=3。
则为(1280^(3-1))*15≈2.5kw。
这些2.5kw是单个2kw表建议的最大线路数的来源。
毕竟,如果再添​​加一层,数据就会大得离谱。
三层数据页最多对应三个磁盘I/O,这也是合理的。