MySQL binlog字符编码方式

By | 2013 年 7 月 24 日

问题背景

希望根据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语句:

chaset_test_1

e) 发现@2(列`gbk`)没有显示全(乱码?),遂以16进制方式查看这部分完整信息

$hexdump -C /tmp/binlog

chaset_test_2

UTF8字符串‘中’字符串‘三个字在右边显示为’………’,其对应的16进制为e5ad97 e7aca6 e4b8b2,为啥每隔三个字符隔开?查看’字符串’的UTF编码,发现正好是:

字->E5AD97
符->E7ACA6
串->E4B8B2

证实了其的确是UTT8编码,而’GBK字符串‘中’字符串‘三个中文通过GBK编码查看(GBK向下兼容GB2312,一个中文的GB2312编码一定与其GBK编码相同),也正好是一一对应:

字->D7D6
符->B7FB
串->B4AE

至此,分析结束,后续问题解决方案,待续…

总结:

ROW模式binlog中文件,’###’开头的INSERT/DELETE/UPDATE语句中的各个列的字符串编码方式与其在表中定义(存储)的编码方式一致。

2 thoughts on “MySQL binlog字符编码方式

  1. hades

    淘宝智胜在这方面做了很多工作,详情参考利用MySQL日志模拟恢复数据变化轨迹, 利用MySQL日志模拟恢复数据变化轨迹II
    现在无法打开链接了,网上查找只能找到它的前两篇文章,请问作者有他文章里面的代码吗???

    Reply
    1. gpfeng Post author

      链接已更新,文章主要学习一下思路,代码量较大,而且依赖很多淘宝其他的lib

      Reply

发表评论

电子邮件地址不会被公开。 必填项已用*标注