问题背景
希望根据MySQL binlog回滚已经提交的事务,一个重要的应用场景:回滚误操作,淘宝智胜在这方面做了很多工作,详情参考利用MySQL日志模拟恢复数据变化轨迹, 利用MySQL日志模拟恢复数据变化轨迹II,最近遇到了一些字符集相关问题,回滚失败。这篇文章旨在分析MySQl binlog中与表操作相关的字符集。
实验测试:
a) 系统字符集,保证能够支持中文
$echo $LANG en_US.UTF-8 |
b) 定义一个包含两个字符集不同(UTF8与GBK)的列的表
mysql> show create table charset_test\G *************************** 1. row *************************** Table: charset_test Create Table: CREATE TABLE `charset_test` ( `utf8` varchar(32) CHARACTER SET utf8 DEFAULT NULL, `gbk` varchar(32) DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=gbk 1 row in set (0.00 sec) mysql> flush logs; Query OK, 0 rows affected (0.00 sec) mysql> insert into charset_test values('UTF8字符串', 'GKB字符串'); Query OK, 1 row affected (0.00 sec) mysql> show master status\G *************************** 1. row *************************** File: mysql-bin.000008 Position: 312 Binlog_Do_DB: Binlog_Ignore_DB: 1 row in set (0.00 sec) |
c) 用mysqlbinlog工具查看上述INSERT语句产生的binlog:
$bin/mysqlbinlog -vvv log/mysql-bin.000008>/tmp/binlog $cat /tmp/binlog |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # at 107 #130724 17:39:59 server id 1 end_log_pos 175 Query thread_id=2 exec_time=0 error_code=0 SET TIMESTAMP=1374658799/*!*/; SET @@session.pseudo_thread_id=2/*!*/; SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=0, @@session.unique_checks=1, @@session.autocommit=1/*!*/; SET @@session.sql_mode=0/*!*/; SET @@session.auto_increment_increment=1, @@session.auto_increment_offset=1/*!*/; /*!\C utf8 *//*!*/; SET @@session.character_set_client=33,@@session.collation_connection=33,@@session.collation_server=28/*!*/; SET @@session.lc_time_names=0/*!*/; SET @@session.collation_database=DEFAULT/*!*/; BEGIN /*!*/; # at 175 # at 231 #130724 17:39:59 server id 1 end_log_pos 231 Table_map: `test`.`charset_test` mapped to number 35 #130724 17:39:59 server id 1 end_log_pos 285 Write_rows: table id 35 flags: STMT_END_F BINLOG ' 76DvURMBAAAAOAAAAOcAAAAAACMAAAAAAAEABHRlc3QADGNoYXJzZXRfdGVzdAACDw8EYABAAAM= 76DvURcBAAAANgAAAB0BAAAAACMAAAAAAAEAAv/8DVVURjjlrZfnrKbkuLIJR0tC19a3+7Su '/*!*/; ### INSERT INTO test.charset_test ### SET ### @1='UTF8字符串' /* VARSTRING(96) meta=96 nullable=1 is_null=0 */ ### @2='GKB' /* VARSTRING(64) meta=64 nullable=1 is_null=0 */ # at 285 #130724 17:39:59 server id 1 end_log_pos 312 Xid = 31 COMMIT/*!*/; DELIMITER ; # End of log file ROLLBACK /* added by mysqlbinlog */; /*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
d) 这里只关心row模式binlog对应的insert语句:
e) 发现@2(列`gbk`)没有显示全(乱码?),遂以16进制方式查看这部分完整信息
$hexdump -C /tmp/binlog |
‘UTF8字符串‘中’字符串‘三个字在右边显示为’………’,其对应的16进制为e5ad97 e7aca6 e4b8b2,为啥每隔三个字符隔开?查看’字符串’的UTF编码,发现正好是:
字->E5AD97
符->E7ACA6
串->E4B8B2
证实了其的确是UTT8编码,而’GBK字符串‘中’字符串‘三个中文通过GBK编码查看(GBK向下兼容GB2312,一个中文的GB2312编码一定与其GBK编码相同),也正好是一一对应:
字->D7D6
符->B7FB
串->B4AE
至此,分析结束,后续问题解决方案,待续…
总结:
ROW模式binlog中文件,’###’开头的INSERT/DELETE/UPDATE语句中的各个列的字符串编码方式与其在表中定义(存储)的编码方式一致。
淘宝智胜在这方面做了很多工作,详情参考利用MySQL日志模拟恢复数据变化轨迹, 利用MySQL日志模拟恢复数据变化轨迹II
现在无法打开链接了,网上查找只能找到它的前两篇文章,请问作者有他文章里面的代码吗???
链接已更新,文章主要学习一下思路,代码量较大,而且依赖很多淘宝其他的lib