[pymysqlbinlog] TABLE_MAP_EVENT

2024年 4月 24日 86.2k 0

导读

本来打算table_map_event和row_event一起写的. 但table_map_event的信息还是太多了, 就先写一部分. 其实之前有提过的 https://www.modb.pro/db/1763358489816174592
但现在要系统的更新下, 尽量更完善.

table_map_event 是在row event前面的, 主要是记录元数据信息的, 比如库名,表名,字段类型等

TABLE_MAP_EVENT

5.7 版本和8.0 版本是存在区别的, 8.0 新增了opt optional metadata fields 来描述更多信息, 比如符号之类的

先看下结构吧

对象 大小(字节) 描述
table_id 6 表id
flags 2
dbname_length 1 库名字长度
dbname dbname_length 库名(x00结尾, 不计入length)
tablename_length 1 表名字长度(表名长度限制64字节)
table_name tablename_length 表名字(x00结尾, 不计入length)
column_count pack_int 字段数量
column_type column_count 字段类型, 每个字段使用1字节表示
metadata_length pack_int 元数据信息长度
metadata metadata_length 元数据信息
null_bits int((self.column_count+7)/8) 是否允许为空
opt tlv 8.0新增的

pack_int

先来看看 pack_int 其实之前讲过, 变长类型
代码参考:

# @mysys/pack.cc net_field_length_size
def read_net_int(self,):
"""
1 3 4 9 (不含第一字节)
"""
data = self.read_uint(1)
if data < 251:
return data
elif data == 251:
return self.read_uint(1)
elif data == 252:
return self.read_uint(2)
elif data == 253:
return self.read_uint(3)
else:
return self.read_uint(8)

column_type

字段类型. 先看看吧

# @include/field_types.h
MYSQL_TYPE_DECIMAL = 0
MYSQL_TYPE_TINY = 1
MYSQL_TYPE_SHORT = 2
MYSQL_TYPE_LONG = 3
MYSQL_TYPE_FLOAT = 4
MYSQL_TYPE_DOUBLE = 5
MYSQL_TYPE_NULL = 6
MYSQL_TYPE_TIMESTAMP = 7
MYSQL_TYPE_LONGLONG = 8
MYSQL_TYPE_INT24 = 9
MYSQL_TYPE_DATE = 10
MYSQL_TYPE_TIME = 11
MYSQL_TYPE_DATETIME = 12
MYSQL_TYPE_YEAR = 13
MYSQL_TYPE_NEWDATE = 14 #/**< Internal to MySQL. Not used in protocol */
MYSQL_TYPE_VARCHAR = 15
MYSQL_TYPE_BIT = 16
MYSQL_TYPE_TIMESTAMP2 = 17
MYSQL_TYPE_DATETIME2 = 18 #/**< Internal to MySQL. Not used in protocol */
MYSQL_TYPE_TIME2 = 19 #/**< Internal to MySQL. Not used in protocol */
MYSQL_TYPE_TYPED_ARRAY = 20 #/**< Used for replication only */
MYSQL_TYPE_INVALID = 243
MYSQL_TYPE_BOOL = 244 #/**< Currently just a placeholder */
MYSQL_TYPE_JSON = 245
MYSQL_TYPE_NEWDECIMAL = 246
MYSQL_TYPE_ENUM = 247
MYSQL_TYPE_SET = 248
MYSQL_TYPE_TINY_BLOB = 249
MYSQL_TYPE_MEDIUM_BLOB = 250
MYSQL_TYPE_LONG_BLOB = 251
MYSQL_TYPE_BLOB = 252
MYSQL_TYPE_VAR_STRING = 253
MYSQL_TYPE_STRING = 254
MYSQL_TYPE_GEOMETRY = 255

metadata

再来看看元数据信息. 部分字段是存在元数据信息的, 比如varchar(N), 这个N就是它的元数据信息, 记录最大值. 具体信息等到 row event 再说. 基本上就是不固定长度的类型才有的.

null_bits

记录字段是否为空的. 和row_event里面的bitmask有关联的(到了再看). 每个字段使用1bit, 所以要使用 int((column_count+7)/8) 字节

opt

8.0 才有的opt optional metadata fields, 但8.0的mysqlbinlog不会解析这个信息(我怀疑是官方偷懒). 作为binlog解析工具, 我们还是要来解析的.
该opt是存在event结尾的, 所以null_bits后面全是opt, 不用记录大小, 直接读就行. 格式是tls的
1字节记录类型, L字节(pack_int)记录元数据长度. V就是记录长度的结果
先来看看1字节的字段类型: 1 表示第一个, 2表示第二个, 依次类推
不是所有类型都有, 有些要求 binlog_row_metadata=FULL 比如字段名字. 这里只看部分信息, 其它信息等row event的时候再看

类型 格式 描述
IGNEDNESS 对于有符号的字段, 每个字段使用1bit来表示. 符号
DEFAULT_CHARSET 字符集
COLUMN_CHARSET 字段字符集
COLUMN_NAME 1字节大小,后面就是字段名字 字段名字
SET_STR_VALUE set的值
ENUM_STR_VALUE enum的值
GEOMETRY_TYPE 空间坐标
SIMPLE_PRIMARY_KEY 主键
PRIMARY_KEY_WITH_PREFIX 主键前缀
ENUM_AND_SET_DEFAULT_CHARSET
ENUM_AND_SET_COLUMN_CHARSET
COLUMN_VISIBILITY 空间坐标

验证

现在我们来验证下
使用官方的mysqlbinlog解析信息如下:
就一丢丢, 只有名字…

再使用我们的工具来解析下

我们解析出来的信息就要多很多了. 比如字段名字啊, 符号啊之类的.再和数据库验证下吧

(root@127.0.0.1) [ibd2sql]> desc ddcw_alltype_table;
+---------------+-------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+-------------------+------+-----+---------+----------------+
| id | int | NO | PRI | NULL | auto_increment |
| int_col | int | YES | | NULL | |
| tinyint_col | tinyint | YES | | 1 | |
| smallint_col | smallint | YES | | NULL | |
| mediumint_col | mediumint | YES | | NULL | |
| bigint_col | bigint | YES | | NULL | |
| float_col | float | YES | | NULL | |
| double_col | double | YES | | NULL | |
| decimal_col | decimal(10,2) | YES | | NULL | |
| date_col | date | YES | | NULL | |
| datetime_col | datetime | YES | | NULL | |
| timestamp_col | timestamp | YES | | NULL | |
| time_col | time | YES | | NULL | |
| year_col | year | YES | | NULL | |
| char_col | char(100) | YES | | NULL | |
| varchar_col | varchar(200) | YES | | aa | |
| binary_col | binary(10) | YES | | NULL | |
| varbinary_col | varbinary(20) | YES | | NULL | |
| bit_col | bit(4) | YES | | NULL | |
| enum_col | enum('A','B','C') | YES | | NULL | |
| set_col | set('X','Y','Z') | YES | | NULL | |
| josn_type | json | YES | | NULL | |
| newcol | varchar(200) | YES | | aa | |
| newcol2 | varchar(200) | YES | | aa | |
| newcoldasdas2 | varchar(300) | YES | | bbaa | |
+---------------+-------------------+------+-----+---------+----------------+

是对应上了的. 说明我们解析正确.
注: 由于数据存储只能按照字节存, 所以null_bits里面实际上可能结尾多几个bit. 可以看null_bit_bool 是按照字段数量来做的.

其它

有了元数据信息后, 我们就可以解析row event了. 然后就能拼接为SQL了. 但5.7版本没得opt信息, 无法区分符号, 也没得字段信息, 所以还是建议转为base64 方便点. (毕竟官方也是使用的base64来做的.)
话说Pymysqlbinlog是用来干嘛的呢. 目前计划是实现如下功能. 数据过滤那一段已经实现了, 主要是比较简单…

注意: binlog的元数据信息可能不包含 字段名字, 字段符号(mysqlbinlog解析的时候也有这个问题, 所以要使用base64,或者知道元数据信息)

除了数据走stdout, 其它均走stderr
rollback 生成回滚SQL. 按照事务顺序 (读两次IO)
metadata 元数据信息的xml文件. 如果有的话, 则自动替换sql/sql2里面的字段名字
sql2 生成sql格式 (非注释), 由于数据类型转换可能存在问题, 建议使用base64格式
sql 生成sql格式 (注释的sql)
sql-complete 对于insert使用完整sql, 含字段名
sql-replace 使用replace替换insert和update
base64 生成base64格式(默认)
base64-disable 不要base64格式的数据, 如果没有sql/sql2, 则自动启动sql选项
debug 展示完整过程, 主要用于调试(stderr)
verbose 显示格外DEUG信息.

# 数据过滤, 优先匹配include, 匹配失败再匹配exclude, 匹配成功(返回False)则跳过
schema-include 同schema
schema-exclude
schema-replace 库名字替换, 所有符合要求的schema换为这个名字
table-include 同table
table-exclude
gtid-skip
gtid-include 同gtid
gtid-exclude
serverid-include
serverid-exclude
start-datetime
stop-datetime
start-position
stop-position

# 审计相关(不统计过滤掉的event), 走stderr
analyze-event 基于event做统计, 各event类型的数量, 大小
analyze-table 基于表做统计 各表的大小, 各表的dml操作数量/行数/大小
analyze-trx 基于事务做统计 大事务(不含gtid event, 但起止pos含gtid和xid).

相关文章

Oracle如何使用授予和撤销权限的语法和示例
Awesome Project: 探索 MatrixOrigin 云原生分布式数据库
下载丨66页PDF,云和恩墨技术通讯(2024年7月刊)
社区版oceanbase安装
Oracle 导出CSV工具-sqluldr2
ETL数据集成丨快速将MySQL数据迁移至Doris数据库

发布评论