当前位置: 首页 > news >正文

让mysqldump变成并发导出导入的魔法

1. 简介

取名mypumpkin,是python封装的一个让mysqldump以多线程的方式导出库表,再以mysql命令多线程导入新库,用于成倍加快导出,特别是导入的速度。这一切只需要在 mysqldump 或 mysql 命令前面加上 mypumpkin.py 即可,所以称作魔法。

项目地址:https://github.com/seanlook/mypumpkin

该程序源于需要对现网单库几百G的数据进行转移到新库,并对中间进行一些特殊操作(如字符集转换),无法容忍mysqldump导入速度。有人可能会提到为什么不用 mydumper,其实也尝试过它但还是放弃了,原因有:

  1. 不能设置字符集
    mydumper强制使用 binary 方式来连接库以达到不关心备份恢复时的字符集问题,然而我的场景下需要特意以不同的字符集导出、再导入。写这个程序的时候正好在公众号看到网易有推送的一篇文章 (解密网易MySQL实例迁移高效完成背后的黑科技),提到他们对mydumper的改进已支持字符集设置,可是在0.9.1版本的patch里还是没找到。
  2. 没有像 mysqldump 那样灵活控制过滤选项(导哪些表、忽略哪些表)
    因为数据量之巨大,而且将近70%是不变更的历史表数据,这些表是可以提前导出转换的;又有少量单表大于50G的,最好是分库导出转换。mydumper 不具备 mysqldump 这样的灵活性
  3. 对忽略导出gtid信息、触发器等其它支持
    阿里云rds 5.6 导出必须要设置 set-gtid-purged=OFF

另外有人还可能提到 mysqlpump —— 它才是我认为mysqldump应该具有的模样,语法兼容,基于表的并发导出。但是只有 mysql服务端 5.7.9 以上才支持,这就是现实和理想的距离。。。

2. 实现方法

首先说明,mysqldump的导出速度并不慢,经测试能达到50M/s的速度,10G数据花费3分钟的样子,可以看到瓶颈在于网络和磁盘IO,再怎样的导出工具也快不了多少,但是导入却花了60分钟,磁盘和网络大概只用到了20%,瓶颈在目标库写入速度(而一般顺序写入达不到IOPS限制),所以mypumpkin就诞生了 —— 兼顾myloader的导入速度和mysqldump导出的灵活性。

用python构造1个队列,将需要导出的所有表一次放到队列中,同时启动N个python线程,各自从这个Queue里取出表名,subprocess调用操作系统的mysqldump命令,导出数据到以 dbname.tablename.sql 命名的文件中。load in 与 dump out 类似,根据指定的库名或表名,从dump_dir目录找到所有sql文件,压进队列,N个线程同时调用mysql构造新的命令,模拟 < 操作。

参数解析从原来自己解析,到改用argparse模块,几乎做了一次重构。
对于没有指定--tables的情况,程序会主动去库里查询一下所有表名,然后过滤进队列。

load in目标库,选项做到与dump out一样丰富,可以指定导入哪些db、哪些表、忽略哪些表。

其中的重点是做到与原mysqldump兼容,因为需要对与表有关的选项(-B, -A, --tables, --ignore=),进行分析并组合成新的执行命令,考虑的异常情况非常多。

3. 限制

  1. 重要:导出的数据不保证库级别的一致性

    1. 对历史不变表,是不影响的
    2. 具体到一个表能保证一致性,这是mysqldump本身采用哪些选项决定的
    3. 不同表导出动作在不同的mysqldump命令中,无法保证事务。
      在我的案例场景下,是有开发同学辅助使用一套binlog解析程序,等完成后重放所有变更,来保证最终一致性。

另,许多情况下我们导数据,并不需要完整的或者一致的数据,只是用于离线分析或临时导出,重点是快速拿数据给到开发。

  1. 不寻常选项识别
    程序已经尽力做到与mysqldump命令兼容,只需要加上 mypumpkin.py、指定dump-dir,就完成并发魔法,但有些情况的参数不方便解析,暂不支持格式:
db1 table1 table2
db2 db3

即以上无法在命令行下判断 db1、table1 是库名还是表面,用的时候只需记住“[-A|-B], [--tables], [--ignore-table]”三组,必须出现一个:db1 table1 table2改成db1 --tables table1 table2db2改成-B db2 db3

  1. 密码暂只能显式输入

4. 使用说明

安装基于python 2.7 开发,其它版本没测。需要按 MySQLdb 库。

4.1 help

./mypumpkin.py --help
Only mysqldump or mysql allowed after mypumpkin.py

usage: mypumpkin.py {mysqldump|mysqls} [--help]

This's a program that wrap mysqldump/mysql to make them dump-out/load-in
concurrently. Attention: it can not keep consistent for whole database(s).

optional arguments:
  --help                show this help message and exit
  -B db1 [db1 ...], --databases db1 [db1 ...]
                        Dump one or more databases
  -A, --all-databases   Dump all databases
  --tables t1 [t1 ...]  Specifiy tables to dump. Override --databases (-B)
  --ignore-table db1.table1 [db1.table1 ...]
                        Do not dump the specified table. (format like
                        --ignore-table=dbname.tablename). Use the directive
                        multiple times for more than one table to ignore.
  --threads =N          Threads to dump out [2], or load in [CPUs*2].
  --dump-dir DUMP_DIR   Required. Directory to dump out (create if not exist),
                        Or Where to load in sqlfile

At least one of these 3 group options given: [-A,-B] [--tables] [--ignore-table]
  • --dump-dir,必选项,原来用的shell标准输入输出 > or < 不允许使用。dump-dir指定目录不存在时会尝试自动创建。
  • --threads=N,N指定并发导出或导入线程数。dump out 默认线程数2, mypumpkin load in 默认线程数是 cpu个数 * 2。
    注:线程数不是越大越好,这里主要的衡量指标是网络带宽、磁盘IO、目标库IOPS,最好用 dstat 观察一下。
  • -B, --tables--ignore-table,使用与mysqldump相同,如:

    1. 在mysqldump里面,--tables会覆盖--databases/-B选项
    2. 在mysqldump里面,--tables--ignore-table不能同时出现
    3. 在mysqldump里面,如果没有指定-B,则--tables--ignore-table必须紧跟db名之后
  • 其它选项,mypumpkin会原封不动的保留下来,放到shell去执行。所以如果其它选项有错误,检查是交给原生mysqldump去做的,执行过程遇到一个失败则会退出线程。

4.2 example

导出:

## 导出源库所有db到visit_dumpdir2目录 (不包括information_schema和performance_schema)
$ ./mypumpkin.py mysqldump -h dbhost_name -utest_user -pyourpassword -P3306 \
 --single-transaction --opt -A --dump-dir visit_dumpdir2

## 导出源库db1,db2,会从原库查询所有表名来过滤
$ ./mypumpkin.py mysqldump -h dbhost_name -utest_user -pyourpassword -P3306 \
 --single-transaction --opt -B db1 db2 --dump-dir visit_dumpdir2

## 只导出db1库的t1,t2表,如果指定表不存在则有提示
$ ./mypumpkin.py mysqldump -h dbhost_name -utest_user -pyourpassword -P3306 \
 --single-transaction --opt -B db1 --tables t1 t2 --dump-dir visit_dumpdir2

## 导出db1,db2库,但忽略 db1.t1, db2.t2, db2.t3表
## mysqldump只支持--ignore-table=db1.t1这种,使用多个重复指令来指定多表。这里做了兼容扩展
$ ./mypumpkin.py mysqldump -h dbhost_name -utest_user -pyourpassword --single-transaction \
 --opt -B db1 db2 --ignore-table=db1.t1 --ignore-table db2.t2 db2.t3 --dump-dir visit_dumpdir2 (如果-A表示全部db)

## 不带 -A/-B
$ ./mypumpkin.py mysqldump -h dbhost_name -utest_user -pyourpassword -P3306 \
 --single-transaction --opt db1 --ignore-table=db1.t1 --dump-dir=visit_dumpdir2

## 其它选项不做处理
$ ./mypumpkin.py mysqldump -h dbhost_name -utest_user -pyourpassword -P3306 \
 --single-transaction --set-gtid-purged=OFF --no-set-names --skip-add-locks -e -q -t -n --skip-triggers \
 --max-allowed-packet=134217728 --net-buffer-length=1638400 --default-character-set=latin1 \
 --insert-ignore --hex-blob --no-autocommit \
 db1 --tables t1 --dump-dir visit_dumpdir2

导入:
-A, -B, --tables, --ignore-table, --threads, --dump-dir用法与作用与上面完全相同,举部分例子:

## 导入dump-dir目录下所有表
$ ./mypumpkin.py mysql -h dbhost_name -utest_user -pyourpassword --port 3307 -A \
 --dump-dir=visit_dumpdir2

## 导入db1库(所有表)
$ ./mypumpkin.py mysql -h dbhost_name -utest_user -pyourpassword --port 3307 -B db1 \
 --dump-dir=visit_dumpdir2

## 只导入db.t1表
$ ./mypumpkin.py mysql -h dbhost_name -utest_user -pyourpassword --port 3307 \
 --default-character-set=utf8mb4 --max-allowed-packet=134217728 --net-buffer-length=1638400 \
 -B db1 --tables t1 --dump-dir=visit_dumpdir2

## 导入db1,db2库,但忽略db1.t1表(会到dump-dir目录检查db1,db2有无对应的表存在,不在目标库检查)
$ ./mypumpkin.py mysql -h dbhost_name -utest_user -pyourpassword --port 3307 \
 -B db1 db2 --ignore-table=db1.t1 --dump-dir=visit_dumpdir2

5.速度对比

相关文章:

  • kafka术语
  • 《深入 React 技术栈》
  • 聊聊 Redis 使用场景
  • 四: 基本标签
  • 离线安装 Python 2.7, paramiko 和 tornado
  • 线程组
  • Android图表库MPAndroidChart(十一)——多层级的堆叠条形图
  • Odoo 二次开发教程(五)-新API的介绍与应用
  • 代码托管工具 git
  • How can I determine the URL that a local Git repository was originally cloned from?
  • Idea14解决JSP/JS文件需要重启问题解决
  • 解决Ubuntu Kylin 1610安装ANSYS17.2的NVIDIA显卡驱动问题
  • java设置代理的几种方式
  • 二叉查找树转双向链表JAVA实现
  • VC++ 一个简单的Log类
  • [NodeJS] 关于Buffer
  • 《Javascript数据结构和算法》笔记-「字典和散列表」
  • 【159天】尚学堂高琪Java300集视频精华笔记(128)
  • 77. Combinations
  • echarts花样作死的坑
  • E-HPC支持多队列管理和自动伸缩
  • iOS高仿微信项目、阴影圆角渐变色效果、卡片动画、波浪动画、路由框架等源码...
  • Java超时控制的实现
  • Java读取Properties文件的六种方法
  • Node项目之评分系统(二)- 数据库设计
  • passportjs 源码分析
  • Spring声明式事务管理之一:五大属性分析
  • STAR法则
  • 如何优雅地使用 Sublime Text
  • 三分钟教你同步 Visual Studio Code 设置
  • 双管齐下,VMware的容器新战略
  • 源码之下无秘密 ── 做最好的 Netty 源码分析教程
  • 怎么将电脑中的声音录制成WAV格式
  • 正则学习笔记
  • 字符串匹配基础上
  • nb
  • 摩拜创始人胡玮炜也彻底离开了,共享单车行业还有未来吗? ...
  • ​云纳万物 · 数皆有言|2021 七牛云战略发布会启幕,邀您赴约
  • ​直流电和交流电有什么区别为什么这个时候又要变成直流电呢?交流转换到直流(整流器)直流变交流(逆变器)​
  • $GOPATH/go.mod exists but should not goland
  • (17)Hive ——MR任务的map与reduce个数由什么决定?
  • (done) NLP “bag-of-words“ 方法 (带有二元分类和多元分类两个例子)词袋模型、BoW
  • (NSDate) 时间 (time )比较
  • (PyTorch)TCN和RNN/LSTM/GRU结合实现时间序列预测
  • (Redis使用系列) Springboot 使用Redis+Session实现Session共享 ,简单的单点登录 五
  • (附源码)springboot炼糖厂地磅全自动控制系统 毕业设计 341357
  • (经验分享)作为一名普通本科计算机专业学生,我大学四年到底走了多少弯路
  • (十八)用JAVA编写MP3解码器——迷你播放器
  • .net Application的目录
  • .NET Micro Framework 4.2 beta 源码探析
  • .net 托管代码与非托管代码
  • .NET 线程 Thread 进程 Process、线程池 pool、Invoke、begininvoke、异步回调
  • .NET/C# 编译期能确定的字符串会在字符串暂存池中不会被 GC 垃圾回收掉
  • /proc/interrupts 和 /proc/stat 查看中断的情况
  • @data注解_一枚 架构师 也不会用的Lombok注解,相见恨晚