0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

MySQL单表数据最大不要超过多少行

jf_ro2CN3Fa 来源:芋道源码 2023-06-02 15:30 次阅读

1、背景

2、实验

3、单表数量限制

4、表空间

5、页的数据结构

6、索引的数据结构

7、单表建议值

8、总结

9、参考

1、背景

作为在后端圈开车的多年老司机,是不是经常听到过,“mysql 单表最好不要超过 2000w”,“单表超过 2000w 就要考虑数据迁移了”,“你这个表数据都马上要到 2000w 了,难怪查询速度慢”

这些名言民语就和 “群里只讨论技术,不开车,开车速度不要超过 120 码,否则自动踢群”,只听过,没试过,哈哈。

下面我们就把车速踩到底,干到 180 码试试…….

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

项目地址:https://github.com/YunaiV/ruoyi-vue-pro

视频教程:https://doc.iocoder.cn/video/

2、实验

实验一把看看…建一张表

CREATETABLEperson(
idintNOTNULLAUTO_INCREMENTPRIMARYKEYcomment'主键',
person_idtinyintnotnullcomment'用户id',
person_nameVARCHAR(200)comment'用户名称',
gmt_createdatetimecomment'创建时间',
gmt_modifieddatetimecomment'修改时间'
)comment'人员信息表';

插入一条数据

insertintopersonvalues(1,1,'user_1',NOW(),now());

利用 mysql 伪列 rownum 设置伪列起始点为 1

select(@i:=@i+1)asrownum,person_namefromperson,(select@i:=100)asinit;
set@i=1;

运行下面的 sql,连续执行 20 次,就是 2 的 20 次方约等于 100w 的数据;执行 23 次就是 2 的 23 次方约等于 800w , 如此下去即可实现千万测试数据的插入,如果不想翻倍翻倍的增加数据,而是想少量,少量的增加,有个技巧,就是在 SQL 的后面增加 where 条件,如 id > 某一个值去控制增加的数据量即可。

insertintoperson(id,person_id,person_name,gmt_create,gmt_modified)
select@i:=@i+1,
left(rand()*10,10)asperson_id,
concat('user_',@i%2048),
date_add(gmt_create,interval+@i*cast(rand()*100assigned)SECOND),
date_add(date_add(gmt_modified,interval+@i*cast(rand()*100assigned)SECOND),interval+cast(rand()*1000000assigned)SECOND)
fromperson;

此处需要注意的是,也许你在执行到近 800w 或者 1000w 数据的时候,会报错:The total number of locks exceeds the lock table size,这是由于你的临时表内存设置的不够大,只需要扩大一下设置参数即可。

SETGLOBALtmp_table_size=512*1024*1024;(512M)
SETglobalinnodb_buffer_pool_size=1*1024*1024*1024(1G);

先来看一组测试数据,这组数据是在 mysql8.0 的版本,并且是在我本机上,由于本机还跑着 idea , 浏览器等各种工具,所以并不是机器配置就是用于数据库配置,所以测试数据只限于参考。

6329ecac-fbcb-11ed-90ce-dac502259ad0.png6355e0d2-fbcb-11ed-90ce-dac502259ad0.png

看到这组数据似乎好像真的和标题对应,当数据达到 2000w 以后,查询时长急剧上升;难道这就是铁律吗?

那下面我们就来看看这个建议值 2kw 是怎么来的?

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

项目地址:https://github.com/YunaiV/yudao-cloud

视频教程:https://doc.iocoder.cn/video/

3、单表数量限制

首先我们先想想数据库单表行数最大多大?

CREATETABLEperson(
idint(10)NOTNULLAUTO_INCREMENTPRIMARYKEYcomment'主键',
person_idtinyintnotnullcomment'用户id',
person_nameVARCHAR(200)comment'用户名称',
gmt_createdatetimecomment'创建时间',
gmt_modifieddatetimecomment'修改时间'
)comment'人员信息表';

看看上面的建表 sql,id 是主键,本身就是唯一的,也就是说主键的大小可以限制表的上限,如果主键声明 int 大小,也就是 32 位,那么支持 2^32-1 ~~21 亿;如果是 bigint,那就是 2^62-1 ?(36893488147419103232),难以想象这个的多大了,一般还没有到这个限制之前,可能数据库已经爆满了!!有人统计过,如果建表的时候,自增字段选择无符号的 bigint , 那么自增长最大值是 18446744073709551615,按照一秒新增一条记录的速度,大约什么时候能用完?

6360c6dc-fbcb-11ed-90ce-dac502259ad0.png

4、表空间

下面我们再来看看索引的结构,对了,我们下面讲内容都是基于 Innodb 引擎的,大家都知道 Innodb 的索引内部用的是 B+ 树

636ff44a-fbcb-11ed-90ce-dac502259ad0.png

这张表数据,在硬盘上存储也是类似如此的,它实际是放在一个叫 person.ibd (innodb data)的文件中,也叫做表空间;虽然数据表中,他们看起来是一条连着一条,但是实际上在文件中它被分成很多小份的数据页,而且每一份都是 16K。大概就像下面这样,当然这只是我们抽象出来的,在表空间中还有段、区、组等很多概念,但是我们需要跳出来看。

638ba6f4-fbcb-11ed-90ce-dac502259ad0.png

5、页的数据结构

因为每个页只有 16K 的大小,但是如果数据很多,那一页肯定就放不下这些数据,那数据肯定就会被分到其他的页中,所以为了把这些页关联起来,肯定就会有记录前后页地址,方便找到对应页;同时每页都是唯一的,那就会需要有一个唯一标志来标记页,就是页号;页中会记录数据所以会存在读写操作,读写操作会存在中断或者其他异常导致数据不全等,那就会需要有校验机制,所以里面还有会校验码,而读操作最重要的就是效率问题,如果按照记录一个个进行遍历,那肯定是很费劲的,所以这里面还会为数据生成对应的页目录(Page Directory); 所以实际页的内部结构像是下面这样的。

63ac64a2-fbcb-11ed-90ce-dac502259ad0.png

从图中可以看出,一个 InnoDB 数据页的存储空间大致被划分成了 7 个部分,有的部分占用的字节数是确定的,有的部分占用的字节数是不确定的。

在页的 7 个组成部分中,我们自己存储的记录会按照我们指定的行格式存储到 User Records 部分。

但是在一开始生成页的时候,其实并没有 User Records 这个部分,每当我们插入一条记录,都会从 Free Space 部分,也就是尚未使用的存储空间中申请一个记录大小的空间划分到 User Records 部分,当 Free Space 部分的空间全部被 User Records 部分替代掉之后,也就意味着这个页使用完了,如果还有新的记录插入的话,就需要去申请新的页了。这个过程的图示如下。

63b95e50-fbcb-11ed-90ce-dac502259ad0.png

刚刚上面说到了数据的新增的过程。

那下面就来说说,数据的查找过程,假如我们需要查找一条记录,我们可以把表空间中的每一页都加载到内存中,然后对记录挨个判断是不是我们想要的,在数据量小的时候,没啥问题,内存也可以撑;但是现实就是这么残酷,不会给你这个局面;为了解决这问题,mysql 中就有了索引的概念;大家都知道索引能够加快数据的查询,那到底是怎么个回事呢?下面我就来看看。

6、索引的数据结构

在 mysql 中索引的数据结构和刚刚描述的页几乎是一模一样的,而且大小也是 16K, 但是在索引页中记录的是页 (数据页,索引页) 的最小主键 id 和页号,以及在索引页中增加了层级的信息,从 0 开始往上算,所以页与页之间就有了上下层级的概念。

63d43964-fbcb-11ed-90ce-dac502259ad0.png

看到这个图之后,是不是有点似曾相似的感觉,是不是像一棵二叉树啊,对,没错!它就是一棵树,只不过我们在这里只是简单画了三个节点,2 层结构的而已,如果数据多了,可能就会扩展到 3 层的树,这个就是我们常说的 B+ 树,最下面那一层的 page level =0, 也就是叶子节点,其余都是非叶子节点。

63e3bd6c-fbcb-11ed-90ce-dac502259ad0.png

看上图中,我们是单拿一个节点来看,首先它是一个非叶子节点(索引页),在它的内容区中有 id 和 页号地址两部分,这个 id 是对应页中记录的最小记录 id 值,页号地址是指向对应页的指针;而数据页与此几乎大同小异,区别在于数据页记录的是真实的行数据而不是页地址,而且 id 的也是顺序的。

7、单表建议值

下面我们就以 3 层,2 分叉(实际中是 M 分叉)的图例来说明一下查找一个行数据的过程。

比如说我们需要查找一个 id=6 的行数据,因为在非叶子节点中存放的是页号和该页最小的 id,所以我们从顶层开始对比,首先看页号 10 中的目录,有 [id=1, 页号 = 20],[id=5, 页号 = 30], 说明左侧节点最小 id 为 1,右侧节点最小 id 是 5;6>5, 那按照二分法查找的规则,肯定就往右侧节点继续查找,找到页号 30 的节点后,发现这个节点还有子节点(非叶子节点),那就继续比对,同理,6>5&&6<7, 所以找到了页号 60,找到页号 60 之后,发现此节点为叶子节点(数据节点),于是将此页数据加载至内存进行一一对比,结果找到了 id=6 的数据行。

从上述的过程中发现,我们为了查找 id=6 的数据,总共查询了三个页,如果三个页都在磁盘中(未提前加载至内存),那么最多需要经历三次的磁盘 IO。需要注意的是,图中的页号只是个示例,实际情况下并不是连续的,在磁盘中存储也不一定是顺序的。

63f6aa44-fbcb-11ed-90ce-dac502259ad0.png

至此,我们大概已经了解了表的数据是怎么个结构了,也大概知道查询数据是个怎么的过程了,这样我们也就能大概估算这样的结构能存放多少数据了。

从上面的图解我们知道 B+ 数的叶子节点才是存在数据的,而非叶子节点是用来存放索引数据的。

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

如果是叶子节点,那么里面就是一行行的数据

如果是非叶子节点的话,那么就会继续指向新的页

假设

非叶子节点内指向其他页的数量为 x

叶子节点内能容纳的数据行数为 y

B+ 数的层数为 z

如下图中所示Total =x^(z-1) *y 也就是说总数会等于 x 的 z-1 次方 与 Y 的乘积。

64105a3e-fbcb-11ed-90ce-dac502259ad0.png

X =?

在文章的开头已经介绍了页的结构,索引也也不例外,都会有 File Header (38 byte)、Page Header (56 Byte)、Infimum + Supermum(26 byte)、File Trailer(8byte), 再加上页目录,大概 1k 左右,我们就当做它就是 1K, 那整个页的大小是 16K, 剩下 15k 用于存数据,在索引页中主要记录的是主键与页号,主键我们假设是 Bigint (8 byte), 而页号也是固定的(4Byte), 那么索引页中的一条数据也就是 12byte; 所以 x=15*1024/12≈1280 行。

Y=?

叶子节点和非叶子节点的结构是一样的,同理,能放数据的空间也是 15k;但是叶子节点中存放的是真正的行数据,这个影响的因素就会多很多,比如,字段的类型,字段的数量;每行数据占用空间越大,页中所放的行数量就会越少;这边我们暂时按一条行数据 1k 来算,那一页就能存下 15 条,Y≈15。

算到这边了,是不是心里已经有谱了啊根据上述的公式,Total =x^(z-1) y,已知 x=1280,y=15假设 B+ 树是两层,那就是 Z =2, Total = (1280 ^1 )15 = 19200假设 B+ 树是三层,那就是 Z =3, Total = (1280 ^2) *15 = 24576000 (约 2.45kw)

哎呀,妈呀!这不是正好就是文章开头说的最大行数建议值 2000w 嘛!对的,一般 B+ 数的层级最多也就是 3 层,你试想一下,如果是 4 层,除了查询的时候磁盘 IO 次数会增加,而且这个 Total 值会是多少,大概应该是 3 百多亿吧,也不太合理,所以,3 层应该是比较合理的一个值。

到这里难道就完了?

不我们刚刚在说 Y 的值时候假设的是 1K ,那比如我实际当行的数据占用空间不是 1K , 而是 5K, 那么单个数据页最多只能放下 3 条数据同样,还是按照 Z=3 的值来计算,那 Total = (1280 ^2) *3 = 4915200 (近 500w)

所以,在保持相同的层级(相似查询性能)的情况下,在行数据大小不同的情况下,其实这个最大建议值也是不同的,而且影响查询性能的还有很多其他因素,比如,数据库版本,服务器配置,sql 的编写等等,MySQL 为了提高性能,会将表的索引装载到内存中。在 InnoDB buffer size 足够的情况下,其能完成全加载进内存,查询不会有问题。但是,当单表数据库到达某个量级的上限时,导致内存无法存储其索引,使得之后的 SQL 查询会产生磁盘 IO,从而导致性能下降,所以增加硬件配置(比如把内存当磁盘使),可能会带来立竿见影的性能提升哈。

8、总结

Mysql 的表数据是以页的形式存放的,页在磁盘中不一定是连续的。

页的空间是 16K, 并不是所有的空间都是用来存放数据的,会有一些固定的信息,如,页头,页尾,页码,校验码等等。

在 B+ 树中,叶子节点和非叶子节点的数据结构是一样的,区别在于,叶子节点存放的是实际的行数据,而非叶子节点存放的是主键和页号。

索引结构不会影响单表最大行数,2kw 也只是推荐值,超过了这个值可能会导致 B + 树层级更高,影响查询性能。
责任编辑:彭菁

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 数据
    +关注

    关注

    8

    文章

    6513

    浏览量

    87609
  • 管理系统
    +关注

    关注

    1

    文章

    1936

    浏览量

    34419
  • MySQL
    +关注

    关注

    1

    文章

    775

    浏览量

    26006

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

文章出处:【微信号:芋道源码,微信公众号:芋道源码】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    谁说MySQL单表行数不要超过2000W?

    网上看了一篇文章《为什么说MySQL单表行数不要超过2000w》,亲自实践了一下,跟原作者有不同的结论。原文的结论是2000W左右性能会成指数级的下降,而我的结论是:随着数据量成倍地增
    的头像 发表于 12-15 10:02 338次阅读
    谁说<b class='flag-5'>MySQL</b>单表行数<b class='flag-5'>不要</b><b class='flag-5'>超过</b>2000W?

    mysql中文参考手册chm

    数据类型 10 从 MySQL 得到最大的性能 10.1 优化概述 10.2 系统/编译时和启动参数的调节 10.2.1 编译和链接如何影响 M
    发表于 12-26 13:32

    变压器的大小有效电流最大不超过1A,这样的话功率不是达不到吗?

    输入220v交流经整压滤波稳流后输出为24v5A,现在比较奇怪的是。在变压器部分降压之后的电压最大不超过40v,看变压器的大小有效电流最大不超过1A,这样的话功率不是达不到吗???
    发表于 11-10 20:38

    MySQL笔记和小练习

    数据库对象中所包含的数据,能够进行查询,连接查询,嵌套查询,以及集合查询等各种复杂程度不同的数据库查询,并将
    发表于 06-27 08:45

    MySQL root密码忘记怎么办?

    MySQL实例1. 跳过授权登录mysqld_safe --skip-grant-table --user=mysql &2. 更改密码mysq
    发表于 06-22 17:54

    MySQL分区类型及介绍

    分区是将一个数据按照一定规则水平划分成不同的逻辑块,并分别进行物理存储,这个规则就叫做分区函数,可以有不同的分区规则。通过show plugins语句查看当前MySQL是否支持
    发表于 06-29 16:31

    请问TAS5717的MCLK是12.288MHZ那频率的上下误差最大不能超过多少?

    TAS5717的MCLK如果是12.288MHZ,这个频率的上下误差最大不能超过多少?
    发表于 08-06 10:49

    如何利用labview获取MySQL数据中某一列的最大

    如题,想获取MySQL数据中的data7那一列的最大值,下面是程序框图一直报语法错误,但是该语句在mysql command line
    发表于 12-06 21:37

    labview插入数据MySQL数据

    最近在用labview写入数据MySQL数据库,遇到一个问题:(如图片所示)利用insert指令插入数据,为什么每次插入单个值都会新起一
    发表于 12-26 16:52

    mysql转列如何操作

    mysql 转列操作
    发表于 04-28 11:27

    MySQL存储引擎简析

    MySQL存储引擎InnoDB  InnoDB 的存储文件有两个,后缀名分别是.frm和.idb,其中.frm是的定义文件,而.idb是数据文件。InnoDB 中存在锁和
    发表于 09-06 06:07

    mysql数据导出golang实现

    mysql数据导出为excel文件,golang实现:首先下载依赖到的三方库:Simple install the package to your $GOPATH
    发表于 10-21 15:14

    关于MySQL的基础知识简析

    随机分配一个root密码,记住密码,安装完毕用root登录,但密码是过期状态,mysql默认情况下密码有效期是360天,需要重新改下,基础的概念数据库(database): 保存有组织的数据库。
    发表于 11-03 11:50

    为什么 MySQL 单表不能超过 2000 万行?

    最近看到一篇《我说 MySQL 每张表最好不要超过 2000 万数据,面试官让我回去等通知》的文章,非常有趣。 文中提到,他朋友在面试的过程中说,自己的工作就是把用户操作信息存到
    的头像 发表于 06-29 16:48 353次阅读
    为什么 <b class='flag-5'>MySQL</b> 单表不能<b class='flag-5'>超过</b> 2000 万行?

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

    想必大家也听说过数据库单表建议最大2kw 条数据这个说法。如果超过了,性能就会下降得比较厉害。
    的头像 发表于 07-06 09:46 618次阅读
    <b class='flag-5'>MySQL</b>单表<b class='flag-5'>数据</b><b class='flag-5'>最大不要</b><b class='flag-5'>超过多</b>少行?为什么?