文章来源:http://blog.chinaunix.net/uid-26896862-id-3329896.html
目的
MySQL的binlog日志中,基于行模式的方式,有table id的信息,而该table id并不是binlog中的操作表的固定id。为了进一步了解table id的改变,研究MySQL源码(MySQL-5.5.20),查看具体的处理逻辑,供开发和DBA参考。
源码分析
table id的分配在函数assign_new_table_id()(sql\sql_base.cc:3598)中分配,从处理的逻辑来看,每次分配都是对上一次的table id自增1。核心代码如下所示:
ulong tid= ++last_table_id; /* get next id */ /* There is one reserved number that cannot be used. Remember to change this when 6-byte global table id's are introduced. */ if (unlikely(tid == ~0UL)) tid= ++last_table_id; share->table_map_id= tid;
而在TABLE_SHARE结构体(sql\table.h:541)的定义中,可以发现,table_map_id变量仅用于行模式的复制操作。具体定义如下所示:
ulong table_map_id; /* for row-based replication */
根据以上线索,跟踪调用assign_new_table_id()函数的引用。只在get_table_share()函数(sql\sql_base.cc:560)中调用,该函数首先调用my_hash_search_using_hash_value()函数(mysys\hash.c:218)查找cache中是否有操作的表定义。如果找到表定义,则对其引用数自增1
并返回该表定义;否则,创建一个TABLE_SHARE变量,并调用assign_new_table_id()函数分配一个新的table id,然后保存在cache中,调用open_table_def()函数(sql\table.cc:594)打开表定义并返回。其中,表定义在cache中是通过hash的方式组织,通过表的hash值获得。
核心代码如下所示:
/* Read table definition from cache */ if ((share= (TABLE_SHARE*) my_hash_search_using_hash_value(&table_def_cache, hash_value, (uchar*) key, key_length))) goto found; if (!(share= alloc_table_share(table_list, key, key_length))) { DBUG_RETURN(0); } /* We assign a new table id under the protection of LOCK_open. We do this instead of creating a new mutex and using it for the sole purpose of serializing accesses to a static variable, we assign the table id here. We assign it to the share before inserting it into the table_def_cache to be really sure that it cannot be read from the cache without having a table id assigned. CAVEAT. This means that the table cannot be used for binlogging/replication purposes, unless get_table_share() has been called directly or indirectly. */ assign_new_table_id(share);
写binlog的时候,在THD::binlog_write_table_map()函数(sql\log.cc:4760)中初始化Table_map_log_event对象the_event,初始化参数table
id的值,即table_map_id的值,而该值是assign_new_table_id()函数获得的table
id的值。核心代码如下:
Table_map_log_event the_event(this, table, table->s->table_map_id, is_transactional);
由以上内容可知,基于行模式的复制中,binlog中的table id是根据初始化的每个Table_map_log_event的时,传入的table_map_id的值。而get_table_share()函数可知,当cache中有该表定义时,该值的table id是不变的;而当cache中没有改表定义时,该值根据上一次操作的table id自增1获得的。
为了更清晰的了解table id与cache的关系,对table_def_cache进行分析,在get_table_share()函数中,如果在cache中找到表定义,在返回之前,会判断table_def_cache中是否太大,如果太大将清理cache中最久没用的表定义。
/* Free cache if too big */ while (table_def_cache.records > table_def_size && oldest_unused_share->next) my_hash_delete(&table_def_cache, (uchar*) oldest_unused_share);
判断大小的标准是table_def_size变量,table_def_size的定义(sql\sys_vars.cc:2148)如下所示:
static Sys_var_ulong Sys_table_def_size( "table_definition_cache", "The number of cached table definitions", GLOBAL_VAR(table_def_size), CMD_LINE(REQUIRED_ARG), VALID_RANGE(TABLE_DEF_CACHE_MIN, 512*1024), DEFAULT(TABLE_DEF_CACHE_DEFAULT), BLOCK_SIZE(1));
从以上代码中可知,table_def_size是全变变量,有效值为:400~512*1024,默认值为:400(TABLE_DEF_CACHE_MIN=400;TABLE_DEF_CACHE_DEFAULT=400)。
因此,由上面分析可知,当table_def_cache中的记录数大于400时,表定义将被置换出cache。被置换出的表如果有操作时,重新加载时,table id的值就会发生改变。
结论
基于行模式复制的binlog中,table id的变化依赖于cache中是否存储了binlog操作表的表定义。如果cache中存在,则table id不变;而当cache中不存在时,该值根据上一次操作的table id自增1。因此,table id与实际操作的数据表没有直接对应关系,而与操作的数据表是否在cache中有关。此外,table_def_cache中默认存放400个表定义,如果超出该范围,会将最久未用的表定义置换出cache。
相关table_id 的问题:
http://hickey.in/?p=146