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

你的mysql到底能存多少数据呢? mysql存储多少数据 阅读详情

故事从好多年前说起。

想必大家也听说过数据库单表建议最大两千万条数据这个说法。如果超过了,性能就会下降得比较厉害。

巧了。我也听说过。

但我不接受它的建议,硬是单表装了 1 亿条数据。

这时候,我们组里新来的实习生看到了之后,天真无邪地问我:"单表不是建议最大两千万吗?为什么这个表都放了 1 个亿还不分库分表"?

我能说我是因为懒吗?我当初设计时哪里想到这表竟然能涨这么快……

我不能。

说了等于承认自己是开发组里的毒瘤,虽然我确实是,但我不能承认。

我如坐针毡,如芒刺背,如鲠在喉。

开始了一波骚操作。

"我这么做是有道理的。"

"虽然这个表很大,但你有没有发现它查询其实还是很快。"

"这个两千万是个建议值,我们要来看下这个两千万是怎么来的。"

数据库单表行数最大多大?

我们先看下单表行数理论最大值是多少。

建表的 SQL 是这么写的,

CREATE TABLE `user` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
  `name` varchar(100) NOT NULL DEFAULT '' COMMENT '名字',
  `age` int(11) NOT NULL DEFAULT '0' COMMENT '年龄',
  PRIMARY KEY (`id`),
  KEY `idx_age` (`age`)
) ENGINE=InnoDB AUTO_INCREMENT=100037 DEFAULT CHARSET=utf8;

其中 id 就是主键。主键本身唯一,也就是说主键的大小可以限制表的上限。

如果主键声明为 int 大小,也就是 32 位。那么能支持 2^32-1,也就是 21 个亿左右。

如果是 bigint,那就是 2^64-1。但这个数字太大,一般还没到这个限制之前,磁盘先受不了。

搞离谱点。

如果我把主键声明为 tinyint 一个字节, 8位。最大 2^8-1,也就是 255。

CREATE TABLE `user` (
  `id` tinyint(2) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
  `name` varchar(100) NOT NULL DEFAULT '' COMMENT '名字',
  `age` int(11) NOT NULL DEFAULT '0' COMMENT '年龄',
  PRIMARY KEY (`id`),
  KEY `idx_age` (`age`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8;

如果我想插入一个 id=256 的数据,那就会报错。

mysql> INSERT INTO `tmp` (`id`, `name`, `age`) VALUES (256, '', 60);
ERROR 1264 (22003): Out of range value for column 'id' at row 1

也就是说,tinyint 主键限制表内最多 255 条数据。

除了主键,还有哪些因素会影响行数?

索引的结构

索引内部是用的 B+ 树,这个也是八股文老股了,大家估计也背得很熟了。

为了不让大家有过于强烈的审丑疲劳,今天我尝试从另外一个角度给大家讲讲这玩意。

页的结构

假设我们有这么一张 user 数据表。

a3f4318217db7be44c32423da5b932f1.png
user 表

其中 id 是唯一主键。

这看起来的一行行数据,为了方便,我们后面就叫它们 record 吧。

这张表看起来就跟个 Excel 表格一样。Excel 的数据在硬盘上是一个 xx.xlsx 文件。

而上面 user 表数据,在硬盘上其实也是类似,放在了 user.ibd 文件下。含义是 user 表的 innodb data 文件,专业说法又叫表空间。

虽然在数据表里,它们看起来是挨在一起的。但实际上在 user.ibd 里他们被分成很多小数据页,每份大小16K。

类似于下面这样:

d981a8a03c5d12ab17dd3ae9bc659a8a.png
ibd 文件内部有大量的页

我们把视角聚焦到页上面。

整个页大小为 16K,不大。但 record 这么多,一页肯定放不下,所以会分开放到很多页里。并且这 16K 也不可能全用来放 record,对吧。

因为,这些 record 被分成好多份,放到各个页里了。为了唯一标识具体是哪一页,那就需要引入页号(其实是一个表空间的地址偏移量)。同时为了把这些数据页给关联起来,于是引入了前后指针,用于指向前后的页。这些都被加到了页头里。

页需要支持读写,16K 说小也不小,写一半电源线被拔了也是有可能发生的。所以,为了保证数据页的正确性,还引入了校验码。这个被加到了页尾。

那剩下的空间才被用来放 record。如果 record 行数特别多,进入到页内时会挨个遍历效率也不太行。所以,为这些数据生成了一个页目录。具体实现细节不重要,只需要知道,它可以通过二分查找的方式将查找效率从 O(n) 变成 O(logn)。

d383c59e916f006e5d5e40a269a63aeb.png
页结构
从页到索引

如果想查一条 record,可以把表空间里每一页查出来,再把里面的 record 挨个判断是不是我们要找的。

行数小的时候,这么操作也没啥问题。行数多了,性能就慢了。

于是为了加快搜索,可以在每个数据页里选出主键 id 最小的 record,而且只需要它们的主键 id 和所在页的页号。将它们组成新的 record,放入到一个新生成的一个数据页中。这个新数据页跟之前的页结构没啥区别,大小还是 16K。

但为了跟之前的数据页进行区分,数据页里加入了页层级(Page Level)信息,从 0 开始往上算。于是页与页之间就有了上下层级的概念,就像下面这样。

f4d5788c7e3ed8ca7f3e903d82a28283.png
两层 B+ 树结构

页跟页之间看起来就像是一棵倒过来的树,也就是我们常说的 B+ 树索引。

最下面一层 Page Level 为 0,也就是所谓的叶子结点。其余都叫非叶子结点。

上面展示的是两层的树。如果数据变多了,还可以再通过类似的方法,往上构建一层,成了三层树。

6b759177fac805f7b75a7ad8f604a0d7.png

三层 B+ 树结构

现在,可以通过这样一棵 B+ 树加速查询。

举个例子,比方说我们想要查找数据行 5。

先从顶层页的 record 入手。record 里包含了主键 id 和页号(页地址)。

下图中黄色的箭头:向左最小 id 是 1,向右最小 id 是 7。

  1. 如果 id=5 的数据存在,那必定在左边箭头;

  2. 于是顺着的 record 的页地址就到了 6 号数据页里;

  3. 再判断 id=5>4,所以肯定在右边的数据页里;

  4. 于是加载 105 号数据页;

  5. 在数据页里找到 id=5 的数据行,完成查询。

22654c0b56d64c1b543a1fba4bcc772c.png
B+ 树的查询过程

另外需要注意,上面的页的页号并不是连续的,它们在磁盘里也不一定是挨在一起的。

这个过程中查询了三个页,如果这三个页都在磁盘中(没有被提前加载到内存中),那么最多需要经历三次磁盘 IO 查询,它们才能被加载到内存中。

B+ 树承载的记录数量

从上面的结构里可以看出,B+ 树的最末级叶子结点里放了 record 数据。而非叶子结点里则放了用来加速查询的索引数据。

也就是说,同样一个 16K 的页,非叶子节点里每一条数据都指向一个新的页。而新的页有两种可能。

  • 如果是末级叶子节点的话,那么里面放的就是 record 数据;

  • 如果是非叶子节点,那么就会循环继续指向新的数据页。

假设:

  • 非叶子结点内指向其他内存页的指针数量为 X;

  • 叶子节点内能容纳的 record 数量为 Y;

  • B+ 树的层数为 Z。

17c1b6440bf7e7f025d8cfeae795ba9e.png
总行数的计算方法

那这棵 B+ 树放的行数据总量等于 (X ^ (Z-1)) * Y。

怎么计算 X

我们回去看数据页的结构。

d5659212d37ce0df71aa85d956916567.png
页结构

非叶子节点里主要放索引查询相关的数据,放的是主键和指向页号。

主键假设是 bigint(8Byte),而页号在源码里叫 FIL_PAGE_OFFSET(4 Byte),那么非叶子节点里的一条数据是 12 Byte 左右。

整个数据页 16K, 页头页尾那部分数据全加起来大概 128 Byte,加上页目录毛估占 1K 吧。那剩下的 15K 除以 12 Byte 等于 1280,也就是可以指向 X=1280 页。

我们常说的二叉树指的是一个结点可以发散出两个新的结点。m 叉树一个节点能指向 m 个新的节点。这个指向新节点的操作就叫扇出(Fanout)。

而上面的 B+ 树能指向 1280 个新的节点。恐怖如斯,可以说扇出非常高了。

如何计算 Y

叶子节点和非叶子节点的数据结构是一样的,所以也假设剩下 15KB 可以利用。

叶子节点里放的是真正的行数据。假设一条行数据 1KB,所以一页里能放 Y=15 行。

行总数计算

回到  (X ^ (Z-1)) * Y 这个公式,已知 X=1280,Y=15。

  • 假设 B+ 树是两层,那 Z=2。总行数 (1280 ^ (2-1)) * 15 ≈ 2万

  • 假设 B+ 树是三层,那 Z=3。总行数 (1280 ^ (3-1)) * 15 ≈ 2.5千万

这个 2.5千万,就是我们常说的单表建议最大行数两千万的由来。毕竟再加一层,数据就大得有点离谱了。三层数据页对应最多三次磁盘 IO,也比较合理。

行数超 1 亿就慢了吗?

上面假设单行数据用了 1KB,所以一个数据页能放个 15 行数据。

如果我单行数据用不了这么多,比如只用了 250 Byte。那么单个数据页能放 60 行数据。

那同样是三层 B+ 树,单表支持的行数就是 (1280 ^ (3-1)) * 60 ≈ 1亿。

你看我 1 亿数据,其实也就三层 B+ 树。在这个 B+ 树里要查到某行数据,最多也是三次磁盘 IO,所以并不慢。

这就很好的解释了文章开头,为什么我单表 1 亿条数据,但查询性能没啥大毛病。

B 树承载的记录数量

既然都聊到这里了,我们就顺着这个话题多聊一些吧。

我们都知道,现在 MySQL 的索引都是 B+ 树。而有一种树,跟 B+ 树很像叫 B 树,也叫 B- 树。它跟 B+ 树最大的区别在于,B+ 树只在末级叶子结点处放数据表行数据,而 B 树则会在叶子和非叶子结点上都放。

B 树的结构类似这样:

f5fdb09f14d99452c12dea0db5c726f6.png
B 树结构

B 树将行数据都存在非叶子节点上。假设每个数据页还是 16KB,掐头去尾每页剩15KB,并且一条数据表行数据还是占 1KB。就算不考虑各种页指针的情况下,也只能放个 15 条数据,数据页的扇出明显变小了。

计算可承载的总行数的公式也变成了一个等比数列。

15 + 15^2 +15^3 + ... + 15^Z

其中 Z 还是层数的意思。

为了能放两千万左右的数据需要 Z>=6,也就是树需要有 6 层。查一次要访问 6 个页。假设这 6 个页并不连续,为了查询其中一条数据,最坏情况需要进行 6 次磁盘 IO。

而 B+ 树同样情况下放两千万数据左右,查一次最多是 3 次磁盘 IO。

磁盘 IO 越多则越慢,这两者在性能上差距略大。因此,B+ 树比 B 树更适合成为 MySQL 索引。

总结

  • B+ 树叶子和非叶子结点的数据页都是 16KB,并且数据结构一致。区别在于叶子节点放的是真实的行数据,而非叶子节点放的是主键和下一个页的地址;

  • B+ 树一般有两到三层。由于其高扇出,三层就能支持两千万以上的数据。并且一次查询最多 1~3 次磁盘 IO,性能也还行;

  • 存储同样量级的数据,B 树比 B+ 树层级更高,因此磁盘 IO 也更多。所以,B+ 树更适合成为 MySQL 索引。

  • 索引结构不会影响单表最大行数,两千万也只是推荐值。超过了这个值可能会导致 B+ 树层级更高,影响查询性能;

  • 单表最大值还受主键大小和磁盘大小限制。

参考资料

《MYSQL内核:INNODB存储引擎 卷1》

- EOF -

MySQL数据库 - 单表查询 MySQL数据库 - 单表查询(一) MySQL数据库 - 单表查询(二) MySQL数据库 - 单表查询(三) 阅读详情

相关推荐

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

B+树叶子和非叶子结点的数据页都是16k,且数据结构一致,区别在于叶子节点放的是真实的行数据,而非叶子结点放的是主键和下一个页的地址。B+树一般有两到三层,由于其高扇出,三层就能支持2kw以上的数据,且一次查询最多1~3次磁盘IO,性能也还行。存储同样量级的数据,B树比B+树层级更高,因此磁盘IO也更多,所以B+树更适合成为mysql索引。索引结构不会影响单表最大行数,2kw也只是推荐值,超过了这个值可能会导致B+树层级更高,影响查询性能。单表最大值还受主键大小和磁盘大小限制。

九师兄 3632

mysql面试题:为什么MySQL单表不能超过2000W条数据?

在MySQL数据库中,单表不能超过2000W(即2亿)条数据的限制是一项常见的规定,它引起了许多开发者和数据库管理员的关注。这个限制涉及到多个方面,包括存储引擎的限制、性能考虑以及数据库设计的最佳实践等。在本文中,我们将深入探讨这个问题,并解释为什么会有这样的限制。

u013938578的博客 1284

攻克TypeError: Cannot read properties of undefined (reading ‘NormalModule‘)的四种实战策略

本文详细解析了`TypeError: Cannot read properties of undefined (reading 'NormalModule')`错误的四种实战解决策略。从版本一致性检查、包管理器优化到依赖替换和构建环境配置,帮助开发者快速定位和解决Webpack构建过程中的常见问题,提升开发效率。

weixin_42523670的博客 243

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

Mysql 的表数据是以页的形式存放的,页在磁盘中不一定是连续的。页的空间是 16K, 并不是所有的空间都是用来存放数据的,会有一些固定的信息,如,页头,页尾,页码,校验码等等。在 B+ 树中,叶子节点和非叶子节点的数据结构是一样的,区别在于,叶子节点存放的是实际的行数据,而非叶子节点存放的是主键和页号。索引结构不会影响单表最大行数,2kw 也只是推荐值,超过了这个值可能会导致 B + 树层级更高,影响查询性能。

2401_85910670的博客 1604

Proteus仿真——电压比较器

电压比较器是一种对输入信号进行鉴别与比较的电路,是组成非正弦波发生电路的基本单元电路。常用电压比较器有如下几种:单限比较器、滞回比较器、窗口比较器、集成电压比较器等。

weixin_58745307的博客 3377

英飞凌TC3xx DSADC旋变软解码实战:从示波器波形到VX1000数据,手把手教你避坑

本文详细介绍了英飞凌TC3xx系列微控制器的DSADC模块在电机旋变软解码中的实战应用。从示波器波形分析到VX1000数据采集,提供了一套完整的调试方法论,帮助工程师快速定位和解决常见问题,提升解码精度和系统性能。

weixin_30598225的博客 517

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

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

浩浩的博客 4065

思科模拟器CIsco Packet Tracer利用三层交换机实现VLAN间路由

版权声明:如果对大家有帮助,大家可以自行转载的。 1.技术原理: 三层交换机具备网络层的功能,实现 VLAN 相互访问的原理是:利用三层交换机的路由功能,通过识别数据包的 IP 地址,查找路由表进行选路转发,三层交换机利用直连路由可以实现不同 VLAN 之间的相互访问。三层交换机给接口配置 IP 地址。采用 SVI(交换虚拟接口)的方式实现 VLAN 间互连。SVI 是指为交换机中的VLAN 创建...

qq_37992321的博客 1万+

MySQL学习笔记-2

悲观锁(数据库):认为数据随时会被修改,因此每次读取数据之前都会上锁,防止其它事务读取或修改数据;应用于数据更新比较频繁的场景;乐观锁:操作数据时不会上锁,但是更新时会判断在此期间有没有别的事务更新这个数据,若被更新过,则失败重试;适用于读多写少的场景。乐观锁的实现方式有:不采用数据库自身的锁机制,而是通过程序来实现。加一个版本号或者时间戳字段,每次数据更新时同时更新这个字段;先读取想要更新的字段或者所有字段,更新的时候比较一下,只有字段没有变化才进行更新。

yueliangzy的博客 4761

AUTOSAR-SOMEIP配置总结

导入arxml之后someip信号一般以“pduSomeIp”开头或者“SomeIp”开头。以某个pdu为例,设置项“Pdu Default Partition Ref”主要是根据模块设计,配置相应的Ecuc核分区。在导入arxml之后someip信号中有三个特殊的信号,形如“SdInstance”开头的信号,设置项“Pdu Default Partition Ref”主要是根据模块设计,配置相应的Ecuc核分区。“EcuC”模块主要关注“EcucPduCollection”中的“Pdus”下面的配置。

xiandang8023的博客 4654

乐企增值税及附加税费(一般纳税人适用)申报表填报能力说明文档V3.009

申报表填报,是指企业在自有业务系统中将算税结果数据按预填规则要求加工汇总后,进行申报表填报的过程。申报表填报前对纳税申报所需的前提条件进行校验。校验通过后,应用前序算税结果数据及预填规则进行纳税申报表预填,生成申报表数据。申报表数据通过申报业务规则校验后,经纳税人确认即完成申报表填报业务。

记录点滴 345

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

故事从好多年前说起。想必大家也听说过数据库单表建议最大2kw条数据这个说法。如果超过了,性能就会下降得比较厉害。巧了。我也听说过。但我不接受它的建议,硬是单表装了1亿条数据。这时候,我们组里新来的实习生看到了之后,天真无邪的问我:"单表不是建议最大两千万吗?为什么这个表都放了1个亿还不分库分表"?我能说我是因为懒吗?我当初设计时哪里想到这表竟然能涨这么快。。。我不能。说了等于承认自己是开发组里的毒瘤,虽然我确实是,但我不能承认。我如坐针毡,如芒刺背,如鲠在喉。开始了一波骚操作。"我这么做是有道理的""虽然这

ilini的博客 6479

安装西门子TIA Portal V15.1提示先决条件不足如何处理?

安装TIA Portal STEP 7 Professional WinCC Professional V15.1提示先决条件不足,需要如何才能够满足安装条件呢? 1.硬件要求: 安装 STEP 7 Basic / Professional V15.1 的计算机必须至少满足以下需求: 处理器: Core i5-6440EQ 3.4 GHz 或者相当 内存: 16GB或者更多(对于大型项目,为32GB) 硬盘:SSD,配备至少50GB的存储空间 图形分辨率:最小 1920 x 1080 显示器:15.6"

Robot_PLC_HMI_Automation 2万+

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

故事从好多年前说起。想必大家也听说过数据库单表建议最大2kw条数据这个说法。如果超过了,性能就会下降得比较厉害。巧了。我也听说过。但我不接受它的建议,硬是单表装了1亿条数据。这时候,我们组里新来的实习生看到了之后,天真无邪的问我:“单表不是建议最大两千万吗?为什么这个表都放了1个亿还不分库分表”?我能说我是因为懒吗?我当初设计时哪里想到这表竟然能涨这么快。。。我不能。说了等于承认自己是开发组里的毒瘤,虽然我确实是,但我不能承认。我如坐针毡,如芒刺背,如鲠在喉。开始了一波骚操作。“我这么做是有道理的”“虽然这

Salted fish in hand, I have the world. 921

37.华为路由器:loopback(本地回环)接口的作用

本地环回接口(或地址),亦称回送地址(loopback address)。 此类接口是应用最为广泛的一种虚接口,几乎在每台路由器上都会使用。常见于如下用途: 1 作为一台路由器的管理地址 系统管理员完成网络规划之后,为了方便管理,会为每一台路由器创建一个loopback 接口,并在该接口上单独指定一个IP 地址作为管理地址 ,管理员会使用该地址对路由器远程登录(telnet ),该地址实际上起到了类似设备名称一类的功能。 但是通常每台路由器上存在众多接口和地址,为何不从当中随便挑选一个呢 ? 原因如下:由于

迷逝 3万+

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

为什么都动不动就说mysql数据库单表最大两千万?依据是啥???

假设掐头去尾,大概剩下15k用来存放索引数据,每条索引数据主要由主键和指向页号组成,主键假设是bigint大小 8byte,页号叫PAGE_NUMBER 大小4byte,那非叶子节点里的每一条数据就是12byte左右,用15k除以12byte等于1280,也就是说x为1280,叶子节点和非叶子节点数据结构是一样的,假设一条行数据占1k,那么一个数据页能放15行,y等于15,假设树是2层 z等于2,那么数据表的总行数就是2w,如果查询过程中两个页都在磁盘中,那么最多需要两次磁盘IO,它们才能被拿到内存。

2301_77179892的博客 1121

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

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

故事从好多年前说起。想必大家也听说过数据库单表建议最大2kw条数据这个说法。如果超过了,性能就会下降得比较厉害。巧了。我也听说过。但我不接受它的建议,硬是单表装了1亿条数据。这时候,我们组里新来的实习生看到了之后,天真无邪地问我:"单表不是建议最大两千万吗?为什么这个表都放了1个亿还不分库分表"?我能说我是因为懒吗?我当初设计时哪里想到这表竟然能涨这么快。。。我不能。说了等于承认自己是开发组里的毒瘤,虽然我确实是,但我不能承认。我如坐针毡,如芒刺背,如鲠在喉。开始了一波骚操作。"我这么做是有道理的""虽然这

l688899886的博客 1700

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

读【mysql单表最大两千万】文章有感

前言 mysql我们都很熟悉,现在常用的存储引擎是innodb,mysql数据是存储在物理磁盘上的,而真正的数据处理又是在内存中执行的。由于磁盘的读写速度非常慢,如果每次操作都对磁盘进行频繁读写的话,那么性能一定非常差。为了上述问题,InnoDB将数据划分为若干页,以页作为磁盘与内存交互的基本单位,一般页的大小为16KB。这样的话,一次性至少读取1页数据到内存中或者将1页数据写入磁盘。通过减少内存与磁盘的交互次数,从而提升性能。 那么mysql单表的瓶颈是多大呢?阿里巴巴《Java 开发手册》提出单表行

安迪爸爸 2301
上一篇: Java8中一个极其强悍的新接口,炸裂!很多人没用过(非常实用)
下一篇: 比 MyBatis 快 100 倍,天生支持联表!
程序员乔戈里
博客等级 码龄8年 1057粉丝 220原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值