Heim >Datenbank >MySQL-Tutorial >MySQL Row格式Binlog的解析(1)
用MySQL 行格式的复制的Slave经常会遇到复制出错1062和1032 错误,一般是镜像异常宕机导致主从复制数据不一致所致,但是有些库本身很大,重建成本很大,并且这些库的数据一致性用户可能都不是太关心的,所以之前的处理办法一般是遇到主键冲突的就跳过,遇到找不到key的就用mysqlbinlog解析一下 把数据补出来,但是这种方法太人肉话,处理起来很慢,所以之前做过一个自动修数据的工具,也是解析binlog日志,然后生成SQL语句去执行。
做这个工具还有另外一个用途,既然能解析BINLOG日志,这样也可以做基于BINLOG的恢复,当然这个功能之前阿里也有人做过,但是做的方法不太一样,可以参考MySQL的 flashback.
因为是在Slave机器上运行,所以这里解析的全部都是relay log.
通过show slave status 可以清楚的知道
end-position = Relay_Log_Pos + (end_log_pos - Exec_Master_Log_Pos)
(end_log_pos - Exec_Master_Log_Pos) 这个就代表这个事物从开始到出错执行的字节数。
end_log_pos 在show slave status 的错误信息里面也有,取出来就可以了。
知道了起始位置和结束位置,剩下的就可以解析了。如果不清楚binlog event的可以看下官方文档:
http://dev.mysql.com/doc/internals/en/binlog-event.html
写代码的过程中也大量参考了文档。
首先说说event:
基本上所有的event 格式都是一样的,都是由header + data 这两部分组成。
header 里面都是固定的,不同的event可变部分在DATA里面。
DATA分为fixed part 和variable part.
header 格式
| timestamp(0:4)| type_code(4:1)| server_id(5:4)| event_length(9:4)| next_position(13:4) | flags(17:2)| extra_header(19:x-19)|
data 格式
|fix_data x:y | variable_data
header 说明 :
所以整个header一共19字节,DATA部分占用字节数:event_length - 19
下面说一下几种用到的BINLOG格式:
这里要注意一下,从MySQL5.6.2 开始 event的type 变化了,所以导5.6的需要改一下,5.6的我还没有详细的测试过,不知道还有什么坑。
因为header部分都一样,所以直接说DATA部分了。
TABLE_MAP_EVENT:
Fixed data:
Variable data
我们能从table_map_event 里面得到什么?
没错 主要能得到的就是库名和表名还有字段类型
表明和库名都好弄,直接读出来不用转,字段类型MySQL还有一个专门的转换表,每一个字节对应一个字段类型。
http://dev.mysql.com/doc/internals/en/com-query-response.html#packet-Protocol::MYSQL_TYPE_DATETIME
那么现在表表库名字段类型都有了,感觉还少了字段名称,字段名称直接去MySQL里面去查。
sql= ("select * from information_schema.columns where table_schema='
%s’"
" and table_name=
'%s’")这样我们这些元数据信息都有了,就需要解析具体的数据了,然后把解析好的数据直接填进去。
那么下面就只剩下如何解析别的event格式了。
先从WRITE_ROWS_EVENT 开始:
WRITE_ROWS_EVENT 其实就是一个insert操作,前面也说过每一份event的header都是一样的,只是DATA部分有区分而已,所以WRITE_ROWS_EVENT也一样,header部分为19字节,DATA部门为event_length-19字节。
那么DATA部分中的fixed data 和variables data各是什么呢?
Fixed data :
Variables data :
可以看到fixed data 部分有用的信息不多,可能table_id 还有些用,重要的在variables data 部分。
怎么解析Record?
Record的格式是bit_map + record 的结构。
bit_map 的每一位代表字段是否为空,所以bit_map占用的字节数为:((columns+7)/8)
比如1100 代表一共四个字段,前两个字段为非空,后两个为NULL,为NULL的数据在record里面是不记录的。
所以呢,首先根据bitmap 判断字段是否为空,如果不为空则拿程序继续往下扫描,如果为空则判断下一个字段是否为空。
至此一个insert语句基本上是解析完了。
除此之外DELETE_ROWS_EVENT 基本上和insert一样。
UPDATE_ROWS_EVENT 稍微有一点点特别,UPDATE_ROWS_EVENT 也是bitmap + record ,但是Update包含更新前像和后像,所以一条Update的格式为
bitmap + record (前像) + bitnap + record (后像)
基本上简单的一条语句就是这样:
table-map-event + insert/update/delete event
那么可能会出现这样的情况比如:
insert values (),(),() 或者delete id in (…)
一条SQL操作很多条记录的情况。
那么这种record 的格式就变成 record = (bitmap + data) + (bitmap + data) .. 直到解析完为止
所以扫描的时候一定要把这个event扫描完,不然很容易出错。
这里先简单的介绍一下如何解析BINLOG吧,下面会再详细解析如何解析各个不同类型的字段。