查看mysql数据大小
来源头条作者:Java架构嘻嘻嘻事情是这样的下面是我朋友的面试记录:面试官:讲一下你实习做了什么。朋友:我在实习期间做了一个存储用户操作记录的功能,主要是从MQ获取上游服务发送过来的用户操作信息,然后把这些信息存到MySQL里面,提供给数仓的同事使用。由于数据量比较大,每天大概有四五千多万条,所以我还给它做了分表的操作。每天定时生成3张表,然后将数据取模分别存到这三张表里,防止表内数据过多导致查询速度降低。这表述,好像没什么问题是吧,别急,接着看:面试官:那你为什么要分三张表呢,两张表不行吗?四张表不行吗?朋友:因为MySQL每张表最好不超过2000万条数据,否则会导致查询速度降低,影响性能。我们每天的数据大概是在五千万条左右,所以分成三张表比较稳妥。面试官:还有吗?朋友: 没有了…… 你干嘛,哎呦面试官:那你先回去等通知吧。讲完了,看出什么了吗,你们觉得我这位朋友回答的有什么问题吗?前言一般来说,MySQL每张表最好不要超过2000万条数据,否则就会导致性能下降。阿里的Java开发手册上也提出:单表行数超过 500 万行或者单表容量超过 2GB,才推荐进行分库分表。但实际上,这个2000万或者500万都只是一个大概的数字,并不适用于所有场景,如果盲目的以为表数据只要不超过2000万条就没问题了,很可能会导致系统的性能大幅下降。实际情况下,每张表由于自身的字段不同、字段所占用的空间不同等原因,它们在最佳性能下可以存放的数据量也就不同。那么,该如何计算出每张表适合的数据量呢?别急,慢慢往下看。本文适合的读者阅读本文你需要有一定的MySQL基础,最好对InnoDB和B+树都有一定的了解,可能需要有一年以上的MySQL学习经验(大概一年?),知道 “InnoDB中B+树的高度一般保持在三层以内会比较好”。本文主要是针对 “InnoDB中高度为3的B+树最多可以存多少数据” 这一话题进行讲解的。且本文对数据的计算比较严格(至少比网上95%以上的相关博文都要严格),如果你比较在意这些细节并且目前不太清楚的话,请继续往下阅读。阅读本文你大概需要花费10-20分钟的时间,如果你在阅读的过程中对数据进行验算的话,可能要花费30分钟左右。基础知识快速回顾众所周知,MySQL中InnoDB的存储结构是B+树,B+树大家都熟悉吧?特性大概有以下几点,一起快速回顾一下吧!注:下面这这些内容都是精华,看不懂或者不理解的同学建议先收藏本文,之后有知识基础了再回来看 。一张数据表一般对应一颗或多颗树的存储,树的数量与建索引的数量有关,每个索引都会有一颗单独的树。聚簇索引和非聚簇索引:主键索引也是聚簇索引,非主键索引都是非聚簇索引,两种索引的非叶子节点都是只存索引数据的,比如索引为id,那非叶子节点就只存id的数据。叶子节点的区别如下:聚簇索引的叶子节点存的是这条数据的所有字段信息。所以我们 select * from table where id = 1 的时候,都是要去叶子节点拿数据的。非聚簇索引的叶子节点存的是这条数据所对应的主键信息。比如这条非聚簇索引是username,然后表的主键是id,那该非聚簇索引的叶子节点存的就是id,而不存其他字段。 相当于是先从非聚簇索引查到主键的值,再根据主键索引去查数据内容,一般情况下要查两次(除非索引覆盖),这也称之为 回表 ,就有点类似于存了个指针,指向了数据存放的真实地址。B+树的查询是从上往下一层层查询的,一般情况下我们认为B+树的高度保持在3层是比较好的,也就是上两层是索引,最后一层存数据,这样查表的时候只需要进行3次磁盘IO就可以了(实际上会少一次,因为根节点会常驻内存)。如果数据量过大,导致B+数变成4层了,则每次查询就需要进行4次磁盘IO了,从而使性能下降。所以我们才会去计算InnoDB的3层B+树最多可以存多少条数据。MySQL每个节点大小默认为16KB,也就是每个节点最多存16KB的数据,可以修改,最大64KB,最小4KB。扩展:那如果某一行的数据特别大,超过了节点的大小怎么办?MySQL5.7文档的解释是:对于 4KB、8KB、16KB 和 32KB设置 ,最大行长度略小于数据库页面的一半 ,例如:对于默认的 16KB页大小,最大行长度略小于 8KB 。而对于 64KB 页面,最大行则长度略小于 16KB。如果行超过最大行长度, 则将可变长度列用外部页存储,直到该行符合最大行长度限制。 就是说把varchar、text这种长度可变的存到外部页中,来减小这一行的数据长度。文档地址:MySQL :: MySQL 5.7 Reference Manual :: 14.12.2 File Space ManagementMySQL查询速度主要取决于磁盘的读写速度,因为MySQL查询的时候每次只读取一个节点到内存中,通过这个节点的数据找到下一个要读取的节点位置,再读取下一个节点的数据,直到查询到需要的数据或者发现数据不存在。肯定有人要问了,每个节点内的数据难道不用查询吗?这里的耗时怎么不计算?这是因为读取完整个节点的数据后,会存到内存当中,在内存中查询节点数据的耗时其实是很短的,再配合MySQL的查询方式,时间复杂度差不多为 O(log2N)O(log_2N)O(log2N) ,相比磁盘IO来说,可以忽略不计。MySQL B+树每个节点都存里些什么?在Innodb的B+树中,我们常说的节点被称之为 页(page),每个页当中存储了用户数据,所有的页合在一起组成了一颗B+树(当然实际会复杂很多,但我们只是要计算可以存多少条数据,所以姑且可以这么理解)。页 是InnoDB存储引擎管理数据库的最小磁盘单位,我们常说每个节点16KB,其实就是指每页的大小为16KB。这16KB的空间,里面需要存储 页格式 信息和 行格式 信息,其中行格式信息当中又包含一些元数据和用户数据。所以我们在计算的时候,要把这些数据的都计算在内。页格式每一页的基本格式,也就是每一页都会包含的一些信息,总结表格如下:名称空间含义和作用等File Header38字节文件头,用来记录页的一些头信息。包括校验和、页号、前后节点的两个指针、页的类型、表空间等。Page Header56字节页头,用来记录页的状态信息。包括页目录的槽数、空闲空间的地址、本页的记录数、已删除的记录所占用的字节数等。Infimum 复制代码先来分析一下这张表的行数据:无null值列表,无可变长字段列表,需要算上事务ID和指针字段,需要算上行记录头,那么每行数据所占用的空间就是 4+4+4+6+7+5=304 + 4 + 4 + 6 + 7 + 5 = 304+4+4+6+7+5=30 字节,每个叶子节点可以存放 15232÷30≈50715232 \p 30 \approx 50715232÷30≈507 条数据。算上页目录的槽位所占空间,每个叶子节点可以存放 502 条数据,那么三层B+树可以存放的最大数据量就是 502×986049=494,996,598502 \times 986049 = 494,996,598502×986049=494,996,598,将近5亿条数据!没想到吧。常规表的存放记录数大部分情况下我们的表字段都不是上面那样的,所以我选择了一场比较常规的表来进行分析,看看能存放多少数据。表情况如下:CREATE TABLE `blog` ( `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '博客id', `author_id` bigint unsigned NOT NULL COMMENT '作者id', `title` varchar(50) CHARACTER SET utf8mb4 NOT NULL COMMENT '标题', `description` varchar(250) CHARACTER SET utf8mb4 NOT NULL COMMENT '描述', `school_code` bigint unsigned DEFAULT NULL COMMENT '院校代码', `cover_image` char(32) DEFAULT NULL COMMENT '封面图', `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `release_time` datetime DEFAULT NULL COMMENT '首次发表时间', `modified_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', `status` tinyint unsigned NOT NULL COMMENT '发表状态', `is_delete` tinyint unsigned NOT NULL DEFAULT 0, PRIMARY KEY (`id`), KEY `author_id` (`author_id`), KEY `school_code` (`school_code`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_general_mysql500_ci ROW_FORMAT=DYNAMIC; 复制代码这是我的开源项目“校园博客”(GitHub地址:github.com/stick-i/scb…) 中的博客表,用于存放博客的基本数据。分析一下这张表的行记录:行记录头信息:肯定得有,占用5字节。可变长度字段列表:表中 title占用1字节,description占用2字节,共3字节。null值列表:表中仅school_code、cover_image、release_time3个字段可为null,故仅占用1字节。事务ID和指针字段:两个都得有,占用13字节。字段内容信息: id、author_id、school_code 均为bigint型,各占用8字节,共24字节。 create_time、release_time、modified_time 均为datetime类型,各占8字节,共24字节。 status、is_delete 为tinyint类型,各占用1字节,共2字节。 cover_image 为char(32),字符编码为表默认值utf8,由于该字段实际存的内容仅为英文字母(存url的),结合前面讲的字符编码不同情况下的存储 ,故仅占用32字节。 title、description 分别为varchar(50)、varchar(250),这两个应该都不会产生溢出页(不太确定),字符编码均为utf8mb4,实际生产中70%以上都是存的中文(3字节),25%为英文(1字节),还有5%为4字节的表情,则存满的情况下将占用 (50+250)×(0.7×3+0.25×1+0.05×4)=765(50 + 250) \times (0.7 \times 3 + 0.25 \times 1 + 0.05 \times 4 ) = 765(50+250)×(0.7×3+0.25×1+0.05×4)=765 字节。统计上面的所有分析,共占用 869 字节,则每个叶子节点可以存放 15232÷869≈1715232 \p 869 \approx 1715232÷869≈17 条,算上页目录,仍然能放 17 条。则三层B+树可以存放的最大数据量就是 17×619369=10,529,27317 \times 619369 = 10,529,27317×619369=10,529,273,约一千万条数据,再次没想到吧。数据计算总结根据上面三种不同情况下的计算,可以看出,InnoDB三层B+树情况下的数据存储量范围为 一百二十多万条 到 将近5亿条,这个跨度还是非常大的,同时我们也计算了一张博客信息表,可以存储 约一千万条 数据。所以啊,我们在做项目考虑分表的时候还是得多关注一下表的实际情况,而不是盲目的认为两千万数据都是那个临界点。面试时如果谈到这块的问题,我想面试官也并不是想知道这个数字到底是多少,而是想看你如何分析这个问题,如何得出这个数字的过程。如果本文中有任何写的不对的地方,欢迎各位朋友在评论区指正。写在后面的一些话这篇文章写了整整两周(虽然第一周在划水),真的超级干货了,前前后后查了好多资料,也看了好多博文,官方文档有些地方写的确实含糊,我看了好久都没看懂。学到知识的小伙伴请一定要给我点个赞啊。
作者:阿杆链接:https://juejin.cn/post/7165689453124517896
网友留言