MySQL Binlog中TABLE ID源码分析

文章来源: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

你可能感兴趣的:(MySQL Binlog中TABLE ID源码分析 )