现在的位置: 首页 > 综合 > 正文

mysql 多线程备份工具mydumper 备份测试(含与mysqldump的对比)

2018年01月21日 ⁄ 综合 ⁄ 共 10817字 ⁄ 字号 评论关闭

关于Mydumper 的详细信息可参见:mydumper 源码分析 ,mydumper 官方网站 

测试环境说明:

OS Centos 6.4 X86_64  2U 8 Core 24 Threads
MYSQL 5.5.34
备份盘:普通SAS盘 10k 2块  Raid 1
MYSQL 数据文件盘: Intel® SSD DC S3500 Series Raid 1

下载mydumper 源文件并解压
wget https://launchpadlibrarian.net/123439581/mydumper-0.5.2.tar.gz

安装依赖包
yum install cmake glib2-devel  pcre-devel  zlib-devel [mysql-devel 这里已经安装过]
 
[root@host105 mydumper-0.5.2]# cmake .    ###这里cmake 后面有一个点(.)表示当前目录   

[root@host105 mydumper-0.5.2]# cmake .
省略部分输出 .. ... 
-- Configuring done
-- Generating done
-- Build files have been written to: /home/lidan/mydumper-0.5.2

看到这cmake基本上就可以说成功了
然后
[root@host105 mydumper-0.5.2]# make&&make install 
 省略部分输出 .. ... 
[ 80%] Built target mydumper
[100%] Built target myloader
Install the project...
-- Install configuration: ""
-- Installing: /usr/local/bin/mydumper

-- Installing: /usr/local/bin/myloader

之后使用/usr/local/bin/mydumper --help 检查是否可用

测试数据库大小220M
 
注意:注意账号密码和密码与选项间的空格是必须的

 单 线程无压缩
[root@host105 mysql_bak]# time mydumper -B yiqifa_bs_push -u root -p xxx --binlogs -t 1 -o /data/mysql_bak/

real    0m9.221s
user    0m2.122s

sys     0m0.299s

备份出的文件大小:190M
drwx------ 2 root root      4096 Mar  8 13:25 binlog_snapshot  --拷贝的binlog文件(该binlog表示的是备份镜像之前的所有binlog)
-rw-r--r-- 1 root root       210 Mar  8 13:25 metadata         --binlog的信息以及复制到master的位置信息
rw-r--r-- 1 root root       417 Mar  8 21:00 yiqifa_bs_push.yiqifa_commission_qq-schema.sql  --dump表结构文件
-rw-r--r-- 1 root root       206 Mar  8 21:00 yiqifa_bs_push.yiqifa_commission_qq.sql    --dump数据文件
... ...

其中metadata记录的信息如下:
SHOW MASTER STATUS:
        Log: mysql-bin.000003
        Pos: 2414
上述表示备份到备份数据库的binlog文件和position点

SHOW SLAVE STATUS:
        Host: 192.168.5.112
        Log: mysql-bin.000294
        Pos: 604030830

上述表示备份时间点master的信息

注意:这里Pos表示的是备库sql_thread 执行到主库binlog的位置即show slave status 里的 Exec_Master_Log_Pos

Finished dump at: 2014-03-08 13:25:42

备份期间登陆mysql查看进程:
有一个查询线程(dump数据),另外还有这两个模拟slave 导出binlog的线程
 93616 | root        | localhost | NULL           | Binlog Dump |      5 | Master has sent all binlog to slave; waiting for binlog to be updated       | NULL                                                               |
| 93617 | root        | localhost | NULL           | Binlog Dump |      5 | Master has sent all binlog to slave; waiting for binlog to be updated       | NULL                                                               |

2线程无压缩备份
[root@host105 mysql_bak]# time mydumper -B yiqifa_bs_push -u root -p xxx --binlogs -t 2 -o /data/mysql_bak/
real    0m8.947s
user    0m2.262s

sys     0m0.284s

 4线程无压缩
[root@host105 mysql_bak]# time mydumper -B yiqifa_bs_push -u root -p xxx --binlogs -t 4 -o /data/mysql_bak/
real    0m8.564s
user    0m2.076s
sys     0m0.297s

 6线程无压缩

[root@host105 mysql_bak]# time mydumper -B yiqifa_bs_push -u root -p xxx --binlogs -t 6 -o /data/mysql_bak/ 
real    0m9.042s
user    0m2.308s
sys     0m0.322s

 8线程无压缩

[root@host105 mysql_bak]# time mydumper -B yiqifa_bs_push -u root -p xxx --binlogs -t 8 -o /data/mysql_bak/ 

real    0m8.432s
user    0m1.964s
sys     0m0.260s
 12线程无压缩
[root@host105 mysql_bak]# time mydumper -B yiqifa_bs_push -u root -p xxx --binlogs -t 12 -o /data/mysql_bak/
real    0m9.583s
user    0m2.545s
sys     0m0.359s

总结:

这里所有的数据都是测试过3-5次然后取平均值(样本太少,可能还是不准)多线程备份随着线程的增加备份速度会相应加快,但是当线程增大到一定程度后备份速度反而下降,这个时候主要是磁盘瓶颈,这里测试在8线程下备份速度最优。

需要注意的是:在测试6线程的时候几乎和开启1个线程的备份消耗时间差不多,后来通过show processlist发现当开启6线程的时候实际上只有一个会话在dump数据,但是-v 3 看到的又是6线程dump数据不知道这算不算一个bug。

多线程备份带压缩 后的备份文件大小 :36M 压缩大概为1:6到1:7左右 猜测就是使用gzip 进行的压缩不过备份效率下降了近50%

8线程带压缩

[root@host105 mysql_bak]# time mydumper -B yiqifa_bs_push -u root -p xxx  --binlogs -c -t 8 -o /data/mysql_bak/

real    0m16.587s
user    0m11.127s
sys     0m0.179s
real    0m16.757s
user    0m11.320s
sys     0m0.149s

查看备份后的文件,可见压缩其实使用的就是类似gzip的方式,包括binlog在内的文件都进行压缩

-rw-r--r-- 1 root root   4671 Mar  8 19:54 mysql-bin.000001.gz
-rw-r--r-- 1 root root 182394 Mar  8 19:54 mysql-bin.000002.gz
-rw-r--r-- 1 root root    673 Mar  8 19:54 mysql-bin.000003.gz
drwx------ 2 root root     4096 Mar  8 19:54 binlog_snapshot
-rw-r--r-- 1 root root      210 Mar  8 19:54 metadata
-rw-r--r-- 1 root root      281 Mar  8 19:54 yiqifa_bs_push.xxxxxxschema.sql.gz   --使用gzip压缩,可手动gunzip解压
-rw-r--r-- 1 root root      615 Mar  8 19:54 yiqifa_bs_push.xxxxwebsite.sql.gz

查看mydumper执行时候的详细信息 -v 3

[root@host105 mysql_bak]# time mydumper -B yiqifa_bs_push -u root -p xxx --binlogs -e -c -t 2 -v 3 -o /data/mysql_bak/  
** Message: Connected to a MySQL server
** Message: Started dump at: 2014-03-08 21:14:20
** Message: Written master status
** Message: Written slave status
** Message: Thread 1 connected using MySQL connection ID 93732
** Message: Thread 2 connected using MySQL connection ID 93733
** Message: Thread 1 dumping data for `yiqifa_bs_push`.`apply_xxx_website`
** Message: Non-InnoDB dump complete, unlocking tables
** Message: Thread 2 dumping data for `yiqifa_bs_push`.`cpa_xxx_log`
** Messa
** Message: Thread 2 shutting down
** Message: Finished dump at: 2014-03-08 21:14:32

备份过程如果有慢查询是否Kill 慢查询:(默认不kill,而是等待60s后如果发现慢查询还在那么备份就自己退出)
[root@host105 mysql_bak]# time mydumper -B yiqifa_bs_push   -u root -p emarmysqldba --binlogs -e -c -t 2 -v 3--long-query-guard 2 --kill-long-queries -o /data/mysql_bak/ 
** Message: Connected to a MySQL server

** (mydumper:25127): WARNING **: Killed a query that was running for 3s  --kill 了一个慢查询
** Message: Started dump at: 2014-03-08 21:21:54
** Message: Thread 2 shutting down
** Message: Thread 1 shutting down
** Message: Finished dump at: 2014-03-08 21:22:06

被杀掉的查询:
(user:root  time: 21:21)[db: test]select count(1) ,sleep(30) from test; 
ERROR 2013 (HY000): Lost connection to MySQL server during query

如果只指定了了慢查询时间(默认60s)但是没有指定--kill-long-queries 那么mydumper直接崩溃退出
[root@host105 mysql_bak]# time mydumper -B yiqifa_bs_push   -u root -p emarmysqldba --binlogs -e -c -t 2 -v 3--long-query-guard 2  -o /data/mysql_bak/

** (mydumper:26620): CRITICAL **: There are queries in PROCESSLIST running longer than 2s, aborting dump,
        use --long-query-guard to change the guard value, kill queries (--kill-long-queries) or use 
        different server for dump

测试备份是否真的需要flush table with read lock:
[root@host105 mysql_bak]# time mydumper -B mysql   -u root -p xxx  --binlogs -m -c  -v 3   -o /data/mysql_bak/ 

部分输出信息:
** Message: Thread 2 dumping data for `mysql`.`time_zone_transition_type`
** Message: Thread 2 dumping data for `mysql`.`user`
** Message: Thread 2 connected using MySQL connection ID 97512 (in binlog mode)
** Message: Thread 2 dumping binary log file mysql-bin.000001
** Message: Non-InnoDB dump complete, unlocking tables  --这里有一个释放锁的操作
** Message: Finished dump at: 2014-03-08 21:57:54

另一个会话同时执行的
(user:root  time: 21:57)[db: test]create table a(id int) ;drop table a;
Query OK, 0 rows affected (5.38 sec) --阻塞了5秒多,说明确实加了一把全局的读锁

另外,如果备份的数据库都是innodb引擎的表,因为没有myisam表需要备份,那么flush table with read lock 将会很快释放,所以如果没有myisam表的备份,那么你几乎感觉不到读锁的存在。
再者,因为对myisam表备份需要表锁,所有mydumper会优先处理myisam表,记录myisam表个数,每处理一个myisam都原子操作数量减一,在myisam表都处理完毕后立即解锁,尽量减少锁定的时间,而不是在导出innodb表数据的时候还在lock myisam表。

测试 -e (默认情况不加-e 如果表没有记录将不会导出,加上-e 没有数据也会在导出的时候创建空文件)
root@host105 mysql_bak]# time mydumper -T user   -u root -p xxx --binlogs  -c
-e
 -v 3   -o /data/mysql_bak/

-rw-r--r-- 1 root root  167 Mar  9 01:26 test.user-schema.sql.gz
-rw-r--r-- 1 root root   78 Mar  9 01:26 test.user.sql.gz   -- 没有数据依然导出一个空数据的文件
 
root@host105 mysql_bak]# time mydumper -T user   -u root -p xxx --binlogs  -c  -v 3   -o /data/mysql_bak/

-rw-r--r-- 1 root root  509 Mar  9 01:30 mysql.user.sql.gz
-rw-r--r-- 1 root root  167 Mar  9 01:30 test.user-schema.sql.gz  -- 不加-e 的话test.user只有表结构文件

测试 -T (不需要加dbname 前缀,表之间用,分隔 ;逗号和表之间不能有空格)和 --regex  (需要dbname前缀)
time mydumper -T user,test -u root -p xxx --binlogs  -c  -v 3   -o /data/mysql_bak/  

-rw-r--r-- 1 root root  624 Mar  9 01:38 mysql.user-schema.sql.gz
-rw-r--r-- 1 root root  121 Mar  9 01:38 test.test.sql.gz
-rw-r--r-- 1 root root  167 Mar  9 01:38 test.user-schema.sql.gz
-rw-r--r-- 1 root root  109 Mar  9 01:38 test.user.sql.gz

 time mydumper  --regex='mysql.user|test.test' -u root -p xxx --binlogs  -c  -v 3   -o /data/mysql_bak/  

-rw-r--r-- 1 root root  624 Mar  9 01:42 mysql.user-schema.sql.gz
-rw-r--r-- 1 root root  509 Mar  9 01:42 mysql.user.sql.gz
-rw-r--r-- 1 root root  235 Mar  9 01:42 test.test2-schema.sql.gz
-rw-r--r-- 1 root root  229 Mar  9 01:42 test.test-schema.sql.gz
-rw-r--r-- 1 root root  121 Mar  9 01:42 test.test.sql.gz

排除yiqifa_bs_push、yiqifa_bs和yiqifa_an及mysql库,相当于只备份test库
time mydumper  --regex='^(?!(yiqifa_bs_push|yiqifa_bs|yiqifa_an|mysql))'  -u root -p xxx --binlogs  -c  -v 3   -o /data/mysql_bak/ 
 
-rw-r--r-- 1 root root  235 Mar  9 01:47 test.test2-schema.sql.gz
-rw-r--r-- 1 root root  229 Mar  9 01:47 test.test-schema.sql.gz

测试 --regex 和 -T 的优先级
time mydumper  --regex='mysql.user'   -T test,user -u root -p xxx --binlogs  -c  -v 3   -o /data/mysql_bak/
-rw-r--r-- 1 root root  624 Mar  9 02:01 mysql.user-schema.sql.gz
-rw-r--r-- 1 root root  509 Mar  9 02:01 mysql.user.sql.gz

可见,--regex的处理在--tables-list(-T)后, 先满--tables-list再满足--regex(两者必须是and的关系否则什么都不导出),如下只会dump表user

测试 --snapshot-interval (-I) (必须和 --daemon 一起用)
mydumper    --regex='test.*' -u root -p xxx -I 1 -D-c  -v 3  -L /tmp/dump.log  -o /data/mysql_bak/
drwx------ 2 root root 4096 Mar  9 02:35 0
drwx------ 2 root root 4096 Mar  9 02:33 1
drwx------ 2 root root 4096 Mar  9 02:38 binlogs
lrwxrwxrwx 1 root root    1 Mar  9 02:33 last_dump -> 1
-rw-r--r-- 1 root root  210 Mar  9 02:17 metadata
-rw-r--r-- 1 root root  235 Mar  9 02:17 test.test2-schema.sql.gz
-rw-r--r-- 1 root root  229 Mar  9 02:17 test.test-schema.sql.gz
-rw-r--r-- 1 root root  121 Mar  9 02:17 test.test.sql.gz
-rw-r--r-- 1 root root  167 Mar  9 02:17 test.user-schema.sql.gz
-rw-r--r-- 1 root root  109 Mar  9 02:17 test.user.sql.gz

设置这两个参数那么mydumper会定时 在后台(这里每隔 1分钟)进行一次备份,并且备份文件目录会在目录0,1(last_dump交叉指向0或1目录)之间进行切换。

 -rows的使用 

设置-rows可以把一个表分成多个文件。分块的原则并不是根据-rows设定的行数来决定生成文件里包含的函数,而是通过rows和表的总行数计算出要生成的文件个数,尽量保证每个文件的大小一致。表的总行数是如何获得的?首先mydumper会选择一个索引,顺序是pk、uk或者show index from table里Cardinality最高的一个索引,再通过explain select index from table的rows字段获得总行数total_nums(可能不准确),于是第一个文件就是从select
* from table where index >=1 and index < total_nums/ (int(total_nums/ rows) – 1) + 1。每个分块可以分到不同的线程,所以即便同一个表dump都可以很快加速。

PS:

如果想知道dump镜像所在的binlog日志位置可以:

 cd binlog_snapshot/ root@host105 binlog_snapshot]# ll

total 1076
-rw-r--r-- 1 root root   27736 Mar  9 03:48 mysql-bin.000001
-rw-r--r-- 1 root root 1062832 Mar  9 03:48 mysql-bin.000002
-rw-r--r-- 1 root root    8034 Mar  9 03:48 mysql-bin.000003
解析最后一个日志就可以知道
mysqlbinlog mysql-bin.000003 
#140309  3:36:27 server id 127105  end_log_pos 8034     Xid = 252012609
COMMIT/*!*/;

而binlog当前的position点为
#140309  3:39:27 server id 127105  end_log_pos 8129     Query   thread_id=99206 exec_time=0     error_code=0
SET TIMESTAMP=1394307567/*!*/;

create table b11111(id int)

故备份文件里的binlog镜像并不是最新的binlog日志。

 
mydumper 和mysqldump 性能测试对比 (以及metadata的问题)

需要备份数据库大小:129G

最后备份文件(无压缩)大小:100G

使用mydumper 分别 在4、8、12线程下进行备份

[root@host105 mysql_bak]# time mydumper -B yiqifa_bs -u root -pxxx  -t
4
-o /data/mysql_bak/     
real    11m37.456s
user    17m48.952s
sys     2m52.717s
[root@host105 mysql_bak]# time mydumper -B yiqifa_bs -u root -p xxx  -t
8
-o /data/mysql_bak/   
real    11m19.416s
user    21m23.441s

sys     3m38.803s

第二次 在凌晨测试

real    7m4.005s
user    17m43.677s
sys     4m40.592s

real    8m25.918s
user    18m53.535s
sys     5m9.220s
[root@host105 mysql_bak]# time mydumper -B yiqifa_bs -u root -p emarmysqldba  -t
12
-o /data/mysql_bak/   

real    12m14.479s
user    27m20.982s
sys     5m16.919s

使用mysql原生mysqldump进行备份 (备份文件大小也是100G)
 time mysqldump -uroot -p xxx  yiqifa_bs --opt  --single-transaction >yiqifa_bs.dmp
 
real    50m49.690s
user    33m49.342s

sys     4m27.011s

对比4、8线程备份mydumper的备份速度要比原生mysqldump快近5-7倍。需要特别注意mydumper备份不会对表加metadata lock,故使用mydumper进行备份期间尽量不要对表进行ddl操作,否则可能在基于时间点恢复的时候出现数据不一致的问题

什么情况下会导致数据不一致?

比如,在备份期间先对表t 执行了insert 操作,然后执行alter table t drop name 而此时备份记录的binlog pos点是在insert 之前的点

当基于时间点恢复的时候因为备份文件里T的字段name 已经被drop 那么在解析binlog 执行insert的时候就会报列匹配不上的错误

这也是为何mysql5.5加入metadata lock的主要原因----防止事物被ddl语句破坏。(读者可自行测试,我已验证)

最后,在测试过程中mydumper出现丢失数据的问题,其中有一个表test 里面含有5条记录dump出来后数据文件为空,这个问题有空再找找原因。(后来没有复现)

另外需要注意的是mydumper 无法备份view和trigger (无论是整库还是单独指定库),不过整库备份模式下可以备份procedure和function(因为存储在mysql.proc里),但是单库备份依然无法备份procedure和function,希望想使用mydumper进行备份的小伙伴们引起重视。

后续有时间会补上一篇myloader 恢复的文章

新浪微博:freedom3959

qq:554370286

抱歉!评论已关闭.