MySQL多线程备份工具mydumper 之 RDS外部实例迁移平台
此文已由作者温正湖授权网易云社区发布。
欢迎访问网易云社区,了解更多网易技术产品运营经验。
1、Format_description_event问题:
BINLOG '
kTXkUxMKAAAALQAAAPoDAAAAAEgAAAAAAAEABHRlc3QAAnQzAAEDAAFS1mTW
kTXkUyAKAAAALQAAAOgEAAAAAEgAAAAAAAEAAgAB//4BAAAA/gIAAAC52uck'/*!*/;
假设以上是使用 ../mysql/bin/mysqlbinlog -v --start-position=1094 mysql-bin.000036生成的一个BINLOG,类型为 DELETE_ROW_LOG_EVENT。内容如下:
### DELETE FROM `test`.`t3`### WHERE###
@1=1### DELETE FROM `test`.`t3`### WHERE###
@1=2
mysql客户端,单独执行这个binlog无法达到删除t3中 (1), (2)这两行的目的。报错如下:
mysql> BINLOG '
kTXkUxMKAAAALQAAAPoDAAAAAEgAAAAAAAEABHRlc3QAAnQzAAEDAAFS1mTW
kTXkUyAKAAAALQAAAOgEAAAAAEgAAAAAAAEAAgAB//4BAAAA/gIAAAC52uck'/*!*/;
ERROR 1609 (HY000): The BINLOG statement of type `Table_map` was not preceded by a format description BINLOG statement.
mysql>
DBA刚开始误以为是个mysql bug。其实不然。
错误提示需要先执行format description BINLOG,即需要先执行Format_description_event,该binlog用于描述接下来要执行的BINLOG的版本等信息,如下:
# at 4#140808 10:26:53 server id 10 end_log_pos 121 CRC32 0x5731b21e Start: binlog v 4, server v 5.6.19-log created 140808 10:26:53# Warning: this binlog is either in use or was not closed properly.BINLOG '
bTXkUw8KAAAAdQAAAHkAAAABAAQANS42LjE5LWxvZwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAEzgNAAgAEgAEBAQEEgAAXQAEGggAAAAICAgCAAAACgoKGRkADQEe
sjFX'/*!*/;
该BINLOG位于每个binlog文件偏移位置4开始。
似乎是合理的,在mysql-5.5.30版本中,DBA第二次执行原来的DELETE_ROW_LOG_EVENT时,却能够执行成功。这似乎不合理。其实第二次执行也不应该成功,这是mysql-5.5.30的一个bug
链接为:https://bugs.launchpad.net/percona-server/+bug/966148
在mysql 5.6.19验证了下,该bug已经修复:
mysql> BINLOG 'kTXkUxMKAAAALQAAAPoDAAAAAEgAAAAAAAEABHRlc3QAAnQzAAEDAAFS1mTW
kTXkUx4KAAAALQAAACcEAAAAAEgAAAAAAAEAAgAB//4BAAAA/gIAAAC52uck
'/*!*/;
ERROR 1609 (HY000): The BINLOG statement of type `Table_map` was not preceded by a format description BINLOG statement.
mysql> BINLOG 'kTXkUxMKAAAALQAAAPoDAAAAAEgAAAAAAAEABHRlc3QAAnQzAAEDAAFS1mTW
kTXkUx4KAAAALQAAACcEAAAAAEgAAAAAAAEAAgAB//4BAAAA/gIAAAC52uck
'/*!*/;
ERROR 1609 (HY000): The BINLOG statement of type `Table_map` was not preceded by a format description BINLOG statement.
mysql>
mysql> BINLOG 'kTXkUxMKAAAALQAAAPoDAAAAAEgAAAAAAAEABHRlc3QAAnQzAAEDAAFS1mTW
kTXkUx4KAAAALQAAACcEAAAAAEgAAAAAAAEAAgAB//4BAAAA/gIAAAC52uck
'/*!*/;
ERROR 1609 (HY000): The BINLOG statement of type `Table_map` was not preceded by a format description BINLOG statement.
mysql>
即不管执行多少次同一个BINLOG,都无法成功。除非先执行了Format_description_event:
mysql> BINLOG '
'> bTXkUw8KAAAAdQAAAHkAAAABAAQANS42LjE5LWxvZwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA '> AAAAAAAAAAAAAAAAAAAAAAAAEzgNAAgAEgAEBAQEEgAAXQAEGggAAAAICAgCAAAACgoKGRkADQEe
'> sjFX '> '/*!*/; //这是一个Format_description_eventQuery OK, 0 rows affected (0.00 sec) mysql> BINLOG ' '> kTXkUxMKAAAALQAAAPoDAAAAAEgAAAAAAAEABHRlc3QAAnQzAAEDAAFS1mTW
'> kTXkUx4KAAAALQAAACcEAAAAAEgAAAAAAAEAAgAB//4BAAAA/gIAAAC52uck
'> '/*!*/;
Query OK, 0 rows affected (0.00 sec)
2、执行binlog时“0 rows affected”问题
这样就成功得将对应的t3中对应的(1),(2)删掉。但问题也来了,为什么执行完显示 “Query OK, 0 rows affected (0.00 sec)”。这个让人很费解。
“0 rows affected” 这个问题,也是有DBA提出来了,他们在使用mysqlbinlog -B 进行flashback操作时,执行过程一直是“Query OK, 0 rows affected (0.00 sec)”,怀疑
flashback功能出了问题。其实是因为执行BINLOG直接调用存储引擎的相关接口,跟常规的sql语句执行走的代码路径不完全相同,所以rows affected没有得到正确统计。
继续回到Format_description_event问题上,需要注意的是,在一次客户端连接期间,只需执行一次Format_description_event即可。这个也是好理解的。
3、flashback功能需要与-v配合使用问题
不过mysql的flashback工具确实存在一个问题,就是在flashback update 操作时,需要-v和-B一起作用才能够成功。这是因为:
Rows_log_event::exchange_update_rows在进行set和where内容调换时,需要计算这两段内容的长度,该功能由Rows_log_event::print_verbose_one_row完成,
该函数会根据表定义table_def (td)来获取表中每个field的length,并通过binlog中的记录获取set和where中那几个field是NULL的。
而td是从Table_map_log_event中获取的,且只有在-v时才会生成td:
if (print_event_info->verbose)
{ Rows_log_event *ev= NULL;
Log_event_type et= (Log_event_type) ptr[EVENT_TYPE_OFFSET]; if (checksum_alg != BINLOG_CHECKSUM_ALG_UNDEF &&
checksum_alg != BINLOG_CHECKSUM_ALG_OFF)
size-= BINLOG_CHECKSUM_LEN; // checksum is displayed through the header
switch(et)
{ case TABLE_MAP_EVENT:
{
Table_map_log_event *map;
map= new Table_map_log_event((const char*) ptr, size,
glob_description_event);
print_event_info->m_table_map.set_table(map->get_table_id(), map);break;
}
通过set_table(map->get_table_id(), map)来将对应的map( td从这里来map->create_table_def() )放入table的哈希表中;
所以,不加-v进行update操作的flashback解析时,由于无法通过map= print_event_info->m_table_map.get_table(m_table_id)来获取map进而获取td。
导致无法知道表定义,从而无法进行set和where长度的计算并进行内容调换。
为了修复这个bug,修改如下:
if (is_flashback) //Flashback
{
uint32 fb_size = size;if (checksum_alg != BINLOG_CHECKSUM_ALG_UNDEF &&
checksum_alg != BINLOG_CHECKSUM_ALG_OFF)
fb_size-= BINLOG_CHECKSUM_LEN; // checksum is displayed through the header
switch (ptr[EVENT_TYPE_OFFSET]) { //add TABLE_MAP_EVENT, as UPDATE_ROWS_EVENT need to get_table from map
case TABLE_MAP_EVENT:
{
Table_map_log_event *map;
map= new Table_map_log_event((const char*) ptr, fb_size,
glob_description_event);
print_event_info->m_table_map.set_table(map->get_table_id(), map);break;
} case WRITE_ROWS_EVENT:
ptr[EVENT_TYPE_OFFSET]= DELETE_ROWS_EVENT; break;
.... if (print_event_info->verbose)
{
Rows_log_event *ev= NULL;
Log_event_type et= (Log_event_type) ptr[EVENT_TYPE_OFFSET]; if (checksum_alg != BINLOG_CHECKSUM_ALG_UNDEF &&
checksum_alg != BINLOG_CHECKSUM_ALG_OFF)
size-= BINLOG_CHECKSUM_LEN; // checksum is displayed through the header
switch(et)
{ case TABLE_MAP_EVENT:
{ //if flashback, map has already set into hash above
if (!is_flashback)
{
Table_map_log_event *map;
map= new Table_map_log_event((const char*) ptr, size,
glob_description_event);
print_event_info->m_table_map.set_table(map->get_table_id(), map);
} break;
}
4、mysqlbinlog解析relay log报“unknown table”问题
组内的小伙伴在做基于relay-log进行的数据库实例故障恢复时,发现mysqlbinlog的又一个bug,社区之前也已提及,如下:http://bugs.mysql.com/bug.php?id=60964。大意是在使用mysqlbinlog解析高可用数据库实例从库的relay log时,提示“### Row event for unknown table #16#”,简单分析可知该提示是说DML操作(UPDATE/INSERT/DELETE)所需的table map找不到了,而table map可以将table id(上述的16)跟表定义建立关联。没有table map当然就无法解析DML操作。
那么table map真的不见了吗?其实不然,只是table map位于上一个relay log文件而已。那么,既然mysqlbinlog能够同时解析多个文件,能不能将上一个文件跟当前文件一起解析,这样应该就不会报这个错误了吧。我刚开始也是这么认为的,还跟小伙伴争了,结果应了那句:“没有调查就没有发言权”。小伙伴可是试验过了,结果还是无法解析。个人觉得这不合理啊。
为此,看了mysqlbinlog的代码,很快就找到了问题所在,下面进行简单分析
1、mysqlbinlog用一个for循环来处理一个或多个binlog文件,如下:
// main loop:
for (save_stop_position= stop_position, stop_position= ~(my_off_t)0 ; (--argc >= 0) ; )
{
if (argc == 0) // last log, --stop-position applies stop_position= save_stop_position;
if ((retval= dump_log_entries(*argv++)) != OK_CONTINUE)
break;
// For next log, --start-position does not apply
start_position= BIN_LOG_HEADER_SIZE;
}
这个循环代码很是简练,以至于刚开始看代码的还是,根本不知道是什么意思。其实,循环中argc表示有几个binlog文件需要解析,*argv表示具体的文件名称。start-position和stop_position处理得也很奇妙,在此不细说。
2、有以上的铺垫可以知道,dump_log_entries函数是用来处理每个binlog文件的。
/**
High-level function for dumping a named binlog.
This function calls dump_remote_log_entries() or
dump_local_log_entries() to do the job.
@param[in] logname Name of input binlog.
@retval ERROR_STOP An error occurred - the program should terminate.
@retval OK_CONTINUE No error, the program should continue.
@retval OK_STOP No error, but the end of the specified range of
events to process has been reached and the program should terminate.
*/static Exit_status dump_log_entries(const char* logname)
{
Exit_status rc;
PRINT_EVENT_INFO print_event_info; if (!print_event_info.init_ok()) return ERROR_STOP; if (!only_event_info)
{ /*
Set safe delimiter, to dump things
like CREATE PROCEDURE safely
*/
fprintf(result_file, "DELIMITER /*!*/;\n");
strmov(print_event_info.delimiter, "/*!*/;");
}
print_event_info.verbose= short_form ? 0 : verbose;
rc= (remote_opt ? dump_remote_log_entries(&print_event_info, logname) :
dump_local_log_entries(&print_event_info, logname)); if (!only_event_info)
{ if (!flashback_opt)
{ /* Set delimiter back to semicolon */
fprintf(result_file, "DELIMITER ;\n");
strmov(print_event_info.delimiter, ";");
}
} return rc;
}
根据解析的binlog是否为远程的,又分为dump_remote_log_entries和dump_local_log_entries,我们主要用了dump_local_log_entries,进入函数后我们会发现,mysqlbinlog使用Log_event::read_log_event(file, glob_description_event)和process_event(print_event_info, ev, old_off, logname)两个函数来读取、构建和解析每个binlog。我们应该关注的是print_event_info,这个是控制整个解析过程的关键对象。比如说,如果binlog的类型是TABLE_MAP_EVENT (这个就是问题所在,保存table map的地方),那么在解析时,会将table id和具体的表定义联系起来。并记录到print_event_info中。process_event在处理WRITE_ROWS_EVENT/UPDATE_ROWS_EVENT/DELETE_ROWS_EVENT(对应前述的DML操作)时,由于event中仅记录所操作的表的table id,所以就需要借助print_event_info来获取对应的映射关系。跨relay log的事务在使用mysqlbinlog进行解析时出错的原因就在于,print_event_info是在dump_log_entries中定义的。在完成一个binlog文件的解析后会自动销毁掉,无法继承给下一个待解析的binlog文件。
所以,解决问题的办法很简单,将PRINT_EVENT_INFO print_event_info定义提取出来,设为mysqlbinlog的全局变量即可解决问题。
之前有小伙伴提问,为什么不把表定义直接记录到DML操作中,答案是显而易见的,如果每个DML的binlog都记录表定义,一是没有必要,二是太浪费存储空间了。
网易云免费体验馆,0成本体验20+款云产品!
更多网易技术、产品、运营经验分享请点击。
相关文章:
【推荐】 一次活动引发的血案
MySQL多线程备份工具mydumper 之 RDS外部实例迁移平台的更多相关文章
- MySQL多线程备份工具:mydumper
MySQL多线程备份工具:mydumper http://www.orczhou.com/index.php/2011/12/how-to-split-mysqldump-file/ Mydumper ...
- MySQL多线程备份工具mydumper
mydumper是一个针对MySQL和Drizzle的高性能多线程的备份和恢复工具.此工具的开发人员分别来自MySQL.Fackbook.SkySQL公司,目前已经有一些大型产品业务测试并使用了该工具 ...
- MySQL数据库之多线程备份工具mydumper
Mydumper介绍: 1)Mydumper是一个针对MySQL和Drizzle的高性能多线程备份和恢复工具 2)特性: 轻量级C语言编写 执行速度比mysqldump快10倍 快速的文件压缩 支持导 ...
- MySQL 逻辑备份工具
简介: Mydumper.Myloader 是一个第三方的.开源的 MySQL 逻辑备份工具. 支持多线程,比起 mysqldump 要快很多,也能解决 innobackupex 备份工具对 MyIS ...
- Mysql自动备份工具1.0(2013年11月15日更新)
Mysql自动备份工具1.0 下载地址 2013-11-15 1.解决日历控件在Windows7/8/8.1环境下遮挡按钮问题:2.解决按月备份当月没有该日期问题: 2013-11-13 1.Mysq ...
- mysql多线程备份与还原工具mydumper
(一)mydumper介绍 之前我们已经学过如何使用mysqldump备份恢复数据库:<mysql逻辑备份与还原工具mysqldump>,就目前来说,mysqldump是使用最广泛的MyS ...
- MySQL逻辑备份利器-mydumper
关于mydumper的简介和下载请访问:https://launchpad.net/mydumper 简言之,mydumper是多线程逻辑备份,对于表和数据量很大的情况下,建议使用mydumper提高 ...
- mysql innobackupex备份工具
先简单介绍一下这个工具:innobackupexinnobackupex比xtarbackup有更强的功能,它整合了xtrabackup和其他的一些功能,他不但可以全量备份/恢复,还可以基于时间的增量 ...
- Mysql多线程性能测试工具sysbench 安装、使用和测试
From:http://www.cnblogs.com/zhoujinyi/archive/2013/04/19/3029134.html 摘要: sysbench是一个开源的.模块化的.跨 ...
随机推荐
- django内置html模板的extends和include,模板标签{{ ex }}
base.html内容 <!DOCTYPE html> <html lang="en"> <head> <meta charset=&qu ...
- servlet笔记1
Myeclipse关于Servlet项目文件的组织方面,如下: WEB-INF:这个目录名称和位置是固定的,放置在该目录下的文件或目录,对外界来说的封闭的,也就是客户端无法用http的任何方式访问到其 ...
- 串口通信,帧与帧之间的时间间隔问题?9600波特率,帧将各在20ms以上
- 0 1 1 2 3 5 8 13 21 34 求第N个, 用js实现
function fibo(n) { var f = []; for (var c = 0; c < n; ++c) { console.log(f.join("")) f. ...
- MS SQL 2000 分配权限
/** 分配权限 **/ use [master]create login [ln-tf\liaobin] from windows;gogrant control server to [ln-tf\ ...
- Apache Flume 安装文档、日志收集
简介: 官网 http://flume.apache.org 文档 https://flume.apache.org/FlumeUserGuide.html hadoop 生态系统中,flume 的职 ...
- Kafka学习之broker配置(0.8.1版)(转)
broker.id 默认值:无 每一个broker都有一个唯一的id,这是一个非负整数,这个id就是broker的"名字",这样就允许broker迁移到别的机器而不会影响消费者. ...
- aws代理
ssh -i ~/.ssh/seoul-notification-dev.pem ec2-user@52.79.58.125ssh -CqTnN -D localhost:7080 -i ~/. ...
- Excel VBA入门(八)单元格边框
本文基于以下文件 http://pan.baidu.com/s/1nvJtsu9 (部分)内容预览: 1. 边框样式 Sub cell_format() Dim sht As Worksheet Di ...
- unity平行光太亮?物体发白?可能你使用了2个或多个平行光
unity平行光太亮?物体发白?可能你使用了2个或多个平行光 今天做项目时就遇到了这个问题,光亮得让物体发白 发现加载的场景 里面有个 平行光,删了就好了 要是感觉还是太亮,就把主平行光的Intens ...