前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >MySQL 如何存储长度很长的数据字段

MySQL 如何存储长度很长的数据字段

作者头像
哲洛不闹
发布2018-09-14 11:43:27
5.4K0
发布2018-09-14 11:43:27
举报
文章被收录于专栏:java一日一条java一日一条

最近,在工作中遇到了MySQL中如何存储长度较长的字段类型问题,于是花了一周多的时间抽空学习了一下,并且记录下来。

MySQL大致的逻辑存储结构在这篇文章中有介绍,做为基本概念:InnoDB 逻辑存储结构

注:文中所指的大数据指的是长度较长的数据字段,包括varchar/varbinay/text/blob。

Compact行格式

我们首先来看一下行格式为Compact是如何存储大数据的:

我们建立一张测试表,插入数据:

我们使用py_innodb_page_info.py工具来查看表中的页分布:

可以看出,第4页的<B-tree Node>, page level <0000>格式为数据页,存放着MySQL的行数据。<Uncompressed BLOB Page>可以理解为MySQL存放大数据的地方,暂且叫作外部存储页。Compact格式没有将大数据全部放在数据页中,而是将一部分数据放在了外部存储页中。那么,是全部数据在外部存储页中,还是一部分数据。假如是一部分数据,这一部分是多少呢?

我们使用hexdump -Cv row.ibd查看一下数据页<B-tree Node>, page level <0000>,也就是第4页:

我们可以看出,数据页中存储了一部分数据,算下来一共是768字节,然后剩余部分存储在外部存储页中。那么数据页与外部存储页、外部存储页与外部存储页是如何连接在一起的呢?

我们观察这一行:

这一行是前缀768字节的结尾。注意最后的20个字节:

  • 00 00 00 02:4字节,代表外部存储页所在的space id
  • 00 00 00 04:4字节,代表第一个外部页的Page no
  • 00 00 00 26:4字节,值为38,指向blob页的header
  • 00 00 00 00 00 00 fc fc:8字节,代表该列存在外部存储页的总长度。此处的值为64764,加上前缀768正好是65532。(注意一点,虽然表示BLOB长度的是8字节,实际只有4个字节能使用,所有对于BLOB字段,存储数据的最大长度为4GB。)

验证下第一个外部存储页的头部信息:

前38个字节为File Header(关于InnoDB数据页的详细结构请参见《MySQL技术内幕 InnoDB存储引擎》4.4),这个简单提一下:

  • cd c3 b6 8e:4字节,该页的checksum。
  • 00 00 00 04:4字节,页偏移,此页为表空间中的第5个页。
  • 00 00 00 00:4字节,当前页的上一个页。此页为<Uncompressed BLOB Page>,所以没有上一页。
  • 00 00 00 00:4字节,当前页的下一个页。此页为<Uncompressed BLOB Page>,所以没有下一页。
  • 00 00 00 00 00 06 b8 a2:8字节,该页最后被修改的日志序列位置LSN。
  • 00 0a:2字节,页类型,0x000A代表BLOB页。
  • 00 00 00 00 00 00 00 00:8字节,略过。
  • 00 00 00 02:页属于哪个表空间,此处指表空间的ID为2。

之后是4字节的00 00 3f ca,这里的值为16330,代表此BLOB页的有效数据的字节数。00 00 00 05代表下一个BLOB页的page number。

我们看最后一个<Uncompressed BLOB Page>,第8个页:

最后一页的有效数据大小为0x00003d9e=15774,768+16330*3+15774 = 65532字节,符合初始插入数据的大小。 由于这是最后一个<Uncompressed BLOB Page>,所以指向下一个<Uncompressed BLOB Page>的指针为ff ff ff ff。

由此我们可以很清晰的看出数据页与BLOB页的连接关系(引用淘宝数据库月报上的一张图):

我们来再看一个比较有意思的例子。:

我们建立一张新表,有11个blob字段。然后向每个字段插入1000字节的数据,MySQL会提示ERROR 1030 (HY000): Got error 139 from storage engine,什么意思呢?

InnoDB是以B+树来组织数据的,假如每一行数据都占据一整个Page页,那么B+树将退化为单链表,所以InnoDB规定了一个Page必须包含两行数据。也就是一行数据存储在Page上的大小大概为8000字节。 而上面的例子,一行数据有11个1000字节的数据,Page层肯定放不下,所以在Page层留下768*11=8448字节,已经超过了8000字节,所以MySQL会提示ERROR 1030 (HY000): Got error 139 from storage engine。我们很轻松的定义一个字段,来存储11000个字节,但是却无法将他们分成11个字段来存储,有点意思!

那么如何解决上面的问题呢?

  • 将行格式转为接下来要说的Dynamic格式。此种格式只用20字节指向外部存储空间。
  • 将多个blob字段转为一个blob字段。多个字段可以用数组存储,然后json_encode打包进blob。

我们向表中插入一条有效记录:

我们可以看出这一行数据有9个外部存储页,而我们一共就插入了9列数据,是不是当每一列的数据在page页放不下,都单独申请一个外部存储页,而互相之前不共享外部存储页。我们看一下page页的结构就知道了:

根据前面的分析,我们现在可以看出,外部存储页是不共享的,即使一个列的数据多出一个字节,这一个字节也是独占一个16KB空间的大小,这很浪费存储空间。(当然,这对现代计算机可能不是问题,呵呵)。

说了这么多,总结下Compact格式存储大数据的缺点:

  • 由于存在768字节的前缀在Page页,所以会存在能定义一个字段,存储11000字节,但是不能定义11个字段,每个字段存储1000字节的”bug”。
  • 外部存储页不共享,即使多余一个字节也是独享16KB的页面。

Dynamic行格式

接着我们首先看一下行格式为Dynamic是如何存储大数据的:

创建和compact格式一样的表:

看下页分布:

第4页是数据页,第5-9页是二进制页。我们直接看磁盘中第4页的数据:

和Compact格式有着明显的不同,当大数据在Page页存放不下时,Dynamic行格式不会留768字节在Page页,并且将全部大数据都放在外部存储页。具体的数据页和外部存储页的连接关系同Compact格式一样。

我们再看看Dynamic格式的外部存储页是不是每一个列独享外部存储空间,还是同Compact格式实验过程一样:

看一下外部存储页数据:

好的,可以不用向下看其他列的了,Dynamic的外部存储页也不是共享的。

但是MySQL为什么要这么设计呢?可能是为了实现简单吧,沿着链表通过有效数据大小就能读取blob的全部数据。假如多个字段的blob混在一起,可能设计更复杂,要更新每个字段的偏移量之类的,更新的话页数据管理也比较麻烦。我的个人猜测,呵呵。

总结下Dynamic格式存储大数据的特点:

  • 当数据页放不下时,MySQL会将大数据全部放在外部存储页,数据页只留指向外部存储页的指针。
  • 外部存储页不共享,即使多余一个字节也是独享16KB的页面。

将列放入外部存储页的标准

当一行中的数据不能在数据页中放下,需要申请外部存储页时,MySQL需要决定将哪一列的数据放到外部存储页,遵循的规则如下:

  • 长度固定的字段不会被放到外部存储页(int、char(N)等)
  • 长度小于20字节的字段不会被放到外部存储页。(假如放到外部存储页,不仅会单独占据16KB,还要额外的20字节指针,没有必要)
  • 对于Compact和REDUNDANT格式的行数据,长度小于768字节的字段不会被放到外部存储页。(这个原因很显然,本来就不够768字节的前缀,总不能生搬硬凑吧)。

当有多个大数据字段满足上面条件,需要被放到外部存储页时,MySQL会优先选择大的字段放到外部存储页,因为这样可以最大限度的省下数据页的空间,使得更多的字段能够被放到数据页。

由于有较多的实验过程,所以显得比较乱,建议看到这篇文章人自己实践一遍,毕竟自己动手会思考更多的问题与细节,理解的也比较深刻,哈哈哈。

参考资料:

http://dev.mysql.com/doc/refman/5.7/en/column-count-limit.html http://mysqlserverteam.com/externally-stored-fields-in-innodb/ https://www.percona.com/blog/2010/02/09/blob-storage-in-innodb/ http://mysql.taobao.org/monthly/2016/02/01/

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2017-02-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 java一日一条 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Compact行格式
  • Dynamic行格式
  • 将列放入外部存储页的标准
相关产品与服务
云数据库 MySQL
腾讯云数据库 MySQL(TencentDB for MySQL)为用户提供安全可靠,性能卓越、易于维护的企业级云数据库服务。其具备6大企业级特性,包括企业级定制内核、企业级高可用、企业级高可靠、企业级安全、企业级扩展以及企业级智能运维。通过使用腾讯云数据库 MySQL,可实现分钟级别的数据库部署、弹性扩展以及全自动化的运维管理,不仅经济实惠,而且稳定可靠,易于运维。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档