MySQL批量导入数据时,为何表空间膨胀了N倍

本文涉及的产品
RDS MySQL DuckDB 分析主实例,基础系列 4核8GB
RDSClaw,2核4GB
RDS MySQL DuckDB 分析主实例,集群系列 4核8GB
简介: 本文目录问题缘起排查思路问题发现

问题缘起

同事在客户现场利用DTS工具,从A实例将数据迁移到B实例过程中,发现几乎稍大点的表在迁移完成后,目标端表空间大小差不多都是源端的3倍,也就是说表空间膨胀了2倍。

排查思路

对这篇文章 《叶问》第16期 有印象的话,应该还能记得,数据迁移(导入导出)过程中,也包括主从复制场景,导致表空间膨胀的原因有几种:

  1. MySQL表默认是InnoDB引擎且目前索引只支持B+树索引,在数据的增删改过程中,会因为page分裂而导致表产生碎片,主从服务器上同张表的碎片率不同也会导致表空间相差很大。
  2. 主库整理过碎片(相当于重建整表),从库则是从原先的未整理的物理备份中恢复出来的。
  3. 两端表结构不一致,如从库可能比主库多索引。
  4. 两端表的行格式不一致,如主库为dynamic,从库为compressed。
  5. 两端字符集不同,例如源端是latin1,目标端是utf8mb4。
  6. 个别云数据库在从库上可能采用特殊的并行复制技术,导致在从库上有更高的碎片率(有个极端的案例,同一个表在主库只有6G,从库上则有将近150G)。
  7. 数据表上没有自增ID作为主键,数据写入随机离散,page频繁分裂造成碎片率很高。

问题发现

顺着上面的思路,逐一排查,看能否定位问题原因。

  1. 因素1,不存在,这是全量迁移场景,不是在日常随机增删改的过程中导致膨胀的。
  2. 因素2,不存在,这是利用DTS工具迁移数据的场景。
  3. 因素3、4、5,不存在,两边表结构一致。
  4. 因素6,不存在,原因同2。
  5. 因素7,不存在,每个表都有自增ID作为主键。

排查到这里,就显得有点诡异了,似乎遇到了玄学问题。不过没关系,我们还需要先了解DTS工具的工作方式,大致如下:

  1. 计算数据表总行数。
  2. 根据batch size,分成多段并行读取数据;例如总共10000行数据,batch size是1000,则总共分为10次读取数据。
  3. 将读取出来的数据拼接成 INSERT...VALUES...ON DUPLICATE KEY UPDATE,因为DTS工具要支持增量迁移数据,所以才加上 ON DUPLICATE KEY UPDATE 子句。
  4. 将拼接后的SQL并行写入到目标端。

初看上述工作过程,似乎也没什么特别之处会导致数据写入后产生大量碎片,从而表空间文件急剧膨胀。

首先,读取数据阶段只涉及到源端,可以先排除了。所以,疑点集中在第3、4两步。

了解InnoDB引擎特点的话应该知道,当InnoDB表有自增ID作为主键时,如果写入的数据总是顺序递增的话,那么产生碎片的概率就会很低。但是,如果写入的数据是离散化的(比如插入的顺序是随机离散的,或者比如插入顺序为1、10000、2、3000、3、5000...这种完全离散无序的),则有极大可能会造成碎片率很高。

按照上述疑点,我们需要确认DTS工具构造的SQL是什么样的,这就需要修改选项 binlog_format = statement,这是为了获取其原生的SQL,row模式下可能就相对不好排查了。然后再次运行DTS工具,查看生成的SQL。

经过排查,终于发现问题所在,原来是DTS工具在拼接SQL时,虽然是分段读取数据,但没有将读取出来的结果集先行排序,造成了拼接后的SQL大概像下面这样的:

INSERT INTO t VALUES (100, ...), (99, ...), (98, ...)...(1, ...);

这种方式写入的话,而且还是并发写入,就会极大概率造成InnoDB data page频繁分裂,所以表空间文件才膨胀到原来的3倍之巨。原因不难理解,就好比排队机制,本来我们是按照身高顺序排,但现在有几位高个子的先排在前面了,那么后来的每次都要让这几个人频繁往后移动才行,这就造成了data page分裂,产生大量碎片。

我用几万条sysbench标准表做测试,采用这种方式写入的话,大概会造成约20%的表空间膨胀率。

问题已然明确,只需要在读取数据拼接插入SQL这个阶段,先行对结果集进行排序,就可以完美解决这个问题了

并顺手给负责SQL优化器的同学提了个feature request(MySQL bug#109087),希望能在遇到上述倒序INSERT的情况下,自动完成SQL改写,改倒序为正序(或者说,INSERT的顺序和表主键定义的顺序一致,通常都是正序的INT),也就可以完美避开这类风险了。

全文完。

Enjoy MySQL :




文章推荐:


相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。   相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情: https://www.aliyun.com/product/rds/mysql 
相关文章
|
8月前
|
存储 关系型数据库 MySQL
在CentOS 8.x上安装Percona Xtrabackup工具备份MySQL数据步骤。
以上就是在CentOS8.x上通过Perconaxtabbackup工具对Mysql进行高效率、高可靠性、无锁定影响地实现在线快速全量及增加式数据库资料保存与恢复流程。通过以上流程可以有效地将Mysql相关资料按需求完成定期或不定期地保存与灾难恢复需求。
606 10
|
9月前
|
SQL 存储 缓存
MySQL 如何高效可靠处理持久化数据
本文详细解析了 MySQL 的 SQL 执行流程、crash-safe 机制及性能优化策略。内容涵盖连接器、分析器、优化器、执行器与存储引擎的工作原理,深入探讨 redolog 与 binlog 的两阶段提交机制,并分析日志策略、组提交、脏页刷盘等关键性能优化手段,帮助提升数据库稳定性与执行效率。
231 0
|
9月前
|
SQL 人工智能 关系型数据库
如何实现MySQL百万级数据的查询?
本文探讨了在MySQL中对百万级数据进行排序分页查询的优化策略。面对五百万条数据,传统的浅分页和深分页查询效率较低,尤其深分页因偏移量大导致性能显著下降。通过为排序字段添加索引、使用联合索引、手动回表等方法,有效提升了查询速度。最终建议根据业务需求选择合适方案:浅分页可加单列索引,深分页推荐联合索引或子查询优化,同时结合前端传递最后一条数据ID的方式实现高效翻页。
459 0
|
10月前
|
关系型数据库 MySQL
MySQL数据表添加字段(三种方式)
本文解析了数据表的基本概念及字段添加方法。在数据表中,字段是纵向列结构,记录为横向行数据。MySQL通过`ALTER TABLE`指令支持三种字段添加方式:1) 末尾追加字段,直接使用`ADD`语句;2) 首列插入字段,通过`FIRST`关键字实现;3) 指定位置插入字段,利用`AFTER`指定目标字段。文内结合`student`表实例详细演示了每种方法的操作步骤与结构验证,便于理解与实践。
|
11月前
|
缓存 NoSQL 关系型数据库
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
|
11月前
|
存储 SQL 缓存
mysql数据引擎有哪些
MySQL 提供了多种存储引擎,每种引擎都有其独特的特点和适用场景。以下是一些常见的 MySQL 存储引擎及其特点:
285 0
|
12月前
|
关系型数据库 MySQL Linux
在Linux环境下备份Docker中的MySQL数据并传输到其他服务器以实现数据级别的容灾
以上就是在Linux环境下备份Docker中的MySQL数据并传输到其他服务器以实现数据级别的容灾的步骤。这个过程就像是一场接力赛,数据从MySQL数据库中接力棒一样传递到备份文件,再从备份文件传递到其他服务器,最后再传递回MySQL数据库。这样,即使在灾难发生时,我们也可以快速恢复数据,保证业务的正常运行。
518 28
|
SQL 关系型数据库 MySQL
【YashanDB知识库】字符集latin1的MySQL中文数据如何迁移到YashanDB
本文探讨了在使用YMP 23.2.1.3迁移MySQL Server字符集为latin1的中文数据至YashanDB时出现乱码的问题。问题根源在于MySQL latin1字符集存放的是实际utf8编码的数据,而YMP尚未支持此类场景。文章提供了两种解决方法:一是通过DBeaver直接迁移表数据;二是将MySQL表数据转换为Insert语句后手动插入YashanDB。同时指出,这两种方法适合单张表迁移,多表迁移可能存在兼容性问题,建议对问题表单独处理。
【YashanDB知识库】字符集latin1的MySQL中文数据如何迁移到YashanDB
|
缓存 NoSQL 关系型数据库
Redis和Mysql如何保证数据⼀致?
1. 先更新Mysql,再更新Redis,如果更新Redis失败,可能仍然不⼀致 2. 先删除Redis缓存数据,再更新Mysql,再次查询的时候在将数据添加到缓存中 这种⽅案能解决1 ⽅案的问题,但是在⾼并发下性能较低,⽽且仍然会出现数据不⼀致的问题,⽐如线程1删除了 Redis缓存数据,正在更新Mysql,此时另外⼀个查询再查询,那么就会把Mysql中⽼数据⼜查到 Redis中 1. 使用MQ异步同步, 保证数据的最终一致性 我们项目中会根据业务情况 , 使用不同的方案来解决Redis和Mysql的一致性问题 : 1. 对于一些一致性要求不高的场景 , 不做处理例如 : 用户行为数据 ,
|
存储 SQL 关系型数据库
【YashanDB知识库】MySQL迁移至崖山char类型数据自动补空格问题
**简介**:在MySQL迁移到崖山环境时,若字段类型为char(2),而应用存储的数据仅为'0'或'1',查询时崖山会自动补空格。原因是mysql的sql_mode可能启用了PAD_CHAR_TO_FULL_LENGTH模式,导致保留CHAR类型尾随空格。解决方法是与应用确认数据需求,可将崖山环境中的char类型改为varchar类型以规避补空格问题,适用于所有版本。

推荐镜像

更多
下一篇
开通oss服务