关于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