如何确定MySQL行级事件中列所使用的字符集?
这个问题问得特别实在,我之前做binlog解析工具的时候也被这个点卡过一阵!咱们一步步理清楚:
首先要明确核心结论:行级事件(Insert/Update/Delete Row)里的列数据,是完全按照该列在数据库表结构中定义的字符集来存储的,和客户端连接时用的字符集没有直接关系。
为什么会这样?其实MySQL在处理写入请求时已经帮我们做了转换:当客户端用某个字符集发送SQL语句时,MySQL会先把语句里的字符串从「客户端连接字符集」转换为「目标列定义的字符集」,然后才会把转换后的数据写入磁盘,同时也会以这个转换后的格式写入binlog的行事件中。也就是说,行事件里存的是最终落地的、符合列定义的数据,自然不需要再额外记录客户端的字符集信息。
对比一下Query Event的情况就更清楚了:Query Event记录的是原始的SQL语句文本,所以必须用Q_CHARSET_CODE标记这条SQL是用什么字符集编写的,这样在重放日志的时候才能正确解析SQL里的字符串。而行级事件直接存的是已经处理好的实际数据,就不需要多此一举了。
举个实际例子你就懂了:假设你有个表的列定义为utf8mb4,但客户端用latin1连接插入了带特殊字符的内容。这时候Query Event里的Q_CHARSET_CODE会是latin1对应的编码,但行级事件里存储的是转换后的utf8mb4格式数据。你解析行数据的时候,直接按照列定义的utf8mb4去解码,就能得到正确的内容。
最后补充个实用提示:如果你要解析行级事件的列字符集,得从information_schema.COLUMNS表中获取对应表和列的CHARACTER_SET_NAME字段——binlog的行事件本身不会携带这个信息,它默认你已经知晓目标表的结构定义。
内容来源于stack exchange

