MySQL里的Table_map_log_event内容详解

发布时间:2020-05-12 浏览次数:197

00 - Table_map_log_event

Table_map_log_event是Row Format Binlog中的一种Event。它记录了一个表的元数据信息。例如数据库名,表名和字段的类型等信息。当slave执行row events时,Table_map_log_event的作用有:

- 根据其中的数据库名和表名打开slave上对应的表

- 检测slave上的表的字段类型是否和master上的表的字段类型兼容。如果不兼容,slave会报告错误。

- 将数据转换成slave上表的字段类型。但转换仅限于slave和master的字段类型是兼容的情况。


01 - Table_map_log_event的产生

Table_map_log_event以语句为单位。在语句记录第一个rows event之前,会为每一个要更新的表产生一个Table_map_log_event. 因此binlog中的Table_map_log_event有以下特点:

- Table_map_log_event在所有rows events之前

一个语句的row format events,看起来如下所示:

Query_log_event("BEGIN")

Table_map_log_event // for db1.t1

Table_map_log_event // for db1.t2

Update_rows_log_event // for db1.t1

Update_rows_log_event // for db1.t2

Xid_log_event

- 语句不产生rows event时,也不产生Table_map_log_event

这是因为在产生第一个rows event时,才会产生Table_map_log_event。这样可以保证在语句不产生任何更新时,不会记录任何event到binlog中。

- 不产生rows event的表,也可能会记录Table_map_log_event

在记录Table_map_log_event时,还不能确定一个表是否会更新数据。因此会为所有加了写锁的表产生Table_map_log_event。当看到下面的binlog内容时,不要感到奇怪:

Query_log_event("BEGIN")

Table_map_log_event // for db1.t1

Table_map_log_event // for db1.t2

Update_rows_log_event // for db1.t1

Xid_log_event


- 参考代码

sql/handler.cc中write_locked_table_maps()和binlog_log_row()。


02 - table_map_log_event的格式

table_map_log_event的格式

- pack_length

首先介绍一个常用的术语pack_length。它是一种数值的存储方式,在Binary Log和MySQL的通讯包中经常用到。为了减少存储和传输时占用的空间,长度字段和整形的数值采用变长的方式存储。具体算法是:

- 小于251的数值

用1个字节直接存储该数值

- 大于等于251,小于等于65535(0xFF)的数值

先用1个字节存储数值252,再用2个字节存储该数值。

- 大于65535,小于等于16777215(0xFFF)的数值

先用1个字节存储数值253,再用3个字节存储该数值。

- 大于16777215的数值

先用1个字节存储数值254,再用8个字节存储该数值。

如果大多数情况下,数值小于251,这种存储方法比较节约空间。同时,又能够支持数值很大的情况。

- 参考代码

sql-common/pack.c中的net_store_length().

- Event Header

和其他 event一样。

- table id

table id就像一个表的主键,唯一识别一个table_map_log_event。为了节省空间,rows event中不会记录数据库名和表名,只会记录一个table id。通过table id来关联到一个table_map_log_event,就可以知道是哪个表产生的。

- flags

目前未使用,内容始终为0,占2字节。

- db name length

数据库名的长度,不包含'\0'。采用pack_lenght存储。

- database name

数据库名,以'\0'结尾,因此占用的空间是db name length+1字节。

- table name length

表名长度,不包含'\0'。采用pack_lenght存储。

- table name

表名名,以'\0'结尾,因此占用的空间是table name length+1字节。

- column count


字段的个数。采用pack_lenght存储。


- column types

这里的字段类型不是SQL语句中的类型而是MySQL内部的数据类型。后边会详细介绍。每个字段的类型占一个字节,总长度等于字段的个数。类型存储的顺序是按照master上该表中字段的顺序产生的。所以如果slave上表的字段顺序变了,就会导致错误。

- metadata length

字段的元数据长度。采用pack_lenght存储。

- metadata

除了类型,字段还有长度等属性。这些属性信息存储在metadata中,后边会详细介绍。

- null flags

字段是否可以为空的属性,每个字段占一个比特。由于记录日志时是以字节为最小单位的,尾部不足1字节时,按1个字节存储。例如:有9个字段,前8个字段占一个字节,后一个字段也需要占1个字节,总共2个字节。存储顺序是:第一个字节存储master上第1-8个字段的null属性,第二个字节存储第9-16个字段的null属性,后面依次轮推。在一个字节中8个字段的null属性按字段的顺序从低位到高位排列。例如:01010101,左边第一位是第一个字段的null属性,依次轮推。代码在log_event.cc中的Table_map_log_event()中。


03 - table_map_log_event中的字段类型

table_map_log_event中存储的字段类型是MySQL内部类型,了解内部类型和SQL数据类型的关系,可以帮助我们更好的理解MySQL的实现。SQL数据类型和MySQL内部类型的对应关系

- TINYINT/BOOL

BOOL只是TINYINT的别名。

- CHAR/BINARY

CHAR和BINARY除了字符集不同,其他特征都是相同的。因此它们使用同一个内部类型,通过字符集属性来区分。BINARY(N)等价于 CHAR(N) CHARSET binary.

- VARCHAR/VARBINARY

类似于CHAR/BINARY

- ENUM/SET

这个要特别注意,ENUM/SET在Table_map_log_event中的类型和CHAR类型相同都是MYSQL_TYPE_STRING。实际的类型信息存储在metadata中。通过metadata中的类型信息可以区别这三种类型。

- TINYBLOB/BLOB/

- MEDIUMBLOB/LONGBLOB/

- TINYTEXT/TEXT/

- MEDIUMTEXT/LONGTEXT

TEXT和BLOB的关系类似于CHAR和BINARY。不同的BLOB/TEXT类型的区别只是最大长度上的区别。因此内部对所有的BLOB/TEXT字段使用同一类型表示。通过该类型的字符集属性可以区别是BLOB还是TEXT。该类型还有一个最大长度的属性,通过此属性可以区分出是TINYBLOB还是其他的BLOB。最大长度的信息存储在metadata中。


04 - 不同字段的元数据

metadata中按master上字段的顺序记录了字段的元数据(属性)信息。元数据信息有以下特点:

- 不同类型的字段可能会有不同的属性信息,长度也可能不相同。

- 有一些类型则没有属性信息。

- 任何类型的元数据信息长度总是固定的。

根据column type中的类型信息,我们就可以知道每一个类型的metadata信息。metadata中的元数据格式

C1的元数据占2字节,C2的类型没有元数据,因此不占空间。C3的元数据只有一字节,C4占2字节。


除了以下的类型,其他类型均没有metadata。

- MYSQL_TYPE_FLOAT

1字节,内容为:sizeof(float).

- MYSQL_TYPE_DOUBLE

1字节,内容为:sizeof(double).

- MYSQL_TYPE_NEWDECIMAL

2字节,第一个字节是精度,第二个字节存储小数位数。


- MYSQL_TYPE_BIT

2字节,第一个字节是(bit length)/8,第二个字节是:(bit length)%8。

- MYSQL_TYPE_VARCHAR

2字节,存储长度。- MYSQL_TYPE_BLOB

1字节,存储blob的最大长度,单位字节。内容为:1,2,3或4。

- MYSQL_TYPE_JSON

- MYSQL_TYPE_GOMETRY

这两个类型都是从BLOB继承来的,因此metadata内容和MYSQL_TYPE_BLOB相同。

MYSQL_TYPE_STRING

2个字节。

- 当实际的类型是ENUM或SET时,第一个字节存储真实类型MYSQL_TYPE_ENUM或MYSQL_TYPE_SET,第二个字节存储长度。

- 当实际类型是CHAR时,用2个字节来存储长度。为了避免和ENUM/SET的第一个字节冲突,采用了特定的编码方式。


- 参考代码

sql/field.cc中的do_save_field_metadata()函数。

上一篇: MySQL里的Table_map_log_event内容详解

下一篇: MySQL之DDL高成本的有解之谜

咨询客服 在线咨询
400-820-6580 免费电话