MySQL Binlog ROW模式分析

这几天研究了下binlog中row模式如何工作的,目的是寻求一种方法来模拟mysql的处理方式,解析出row模式时存储在binlog的行数据

当在master上执行一条SQL语句,在ROW模式下,总共做四次解析

1.Begin:Query_log_event::Query_log_event
2.Table Map:Table_map_log_event::Table_map_log_event 与表模式相关的信息,用于配合对行数据的解析
3.

Write_rows_log_event::Write_rows_log_event
Update_rows_log_event::Update_rows_log_event
Delete_rows_log_event::Delete_rows_log_event

4.commit:XID_EVENT


1.入口函数:我们可以把断点设在

0x00000000006d6dc4 in exec_relay_log_event (thd=0xe94d910, rli=0xe93a960) at slave.cc:2242

在handle_slave_sql函数中,有一个while循环会不断的调用exec_relay_log_event函数


在exec_relay_log_event中,主要会调用以下几个函数:

Log_event * ev = next_event(rli);   从cache或relay log中读取一条记录
exec_res= apply_event_and_update_pos(ev, thd, rli); //执行binlog事件并修改当前读的位置

2. next_event

next_event首先会调用:

Log_event* Log_event::read_log_event(IO_CACHE* file,
				     pthread_mutex_t* log_lock,
                                     const Format_description_log_event
                                     *description_event)

来读取记录,首先会读取头部信息,并检查头部信息是否合法,然后调用res= read_log_event(buf, data_len, &error, description_event)

原型:

Log_event* Log_event::read_log_event(const char* buf, uint event_len,
				     const char **error,
                                     const Format_description_log_event *description_event)
在这里会根据事件的类型来调用相应的构造函数,这里我们只关心ROW模式的事件处理:

 case WRITE_ROWS_EVENT:
      ev = new Write_rows_log_event(buf, event_len, description_event);
      break;
 case UPDATE_ROWS_EVENT:
      ev = new Update_rows_log_event(buf, event_len, description_event);
      break;
 case DELETE_ROWS_EVENT:
      ev = new Delete_rows_log_event(buf, event_len, description_event);
      break;


OK,形成一个事件暂时不是我所关心的,因为在最终的ev中,并没有把我想要的数据从relay log中解析出来,也就是说没有对读取的ROW数据进行分析提取。

3.执行事件

不同的事件类型对应不同的处理函数,以delete、update和insert为例:

(gdb) b Update_rows_log_event::do_exec_rowBreakpoint 24 at 0x65ec84: file log_event.cc, line 9290.

(gdb) b Delete_rows_log_event::do_exec_rowBreakpoint 25 at 0x65ef7c: file log_event.cc, line 9166.

(gdb) b Write_rows_log_event::do_exec_rowBreakpoint 26 at 0x65f98a: file log_event.cc, line 8703.

这里我只关心update操作,因此继续跟进:

首先调用  int error= find_row(rli); 在表中找到相应的行(binlog row模式会记录旧版本),若不存在则返回error(在 mysqld的输出信息中可以看到sql线程报错error)

然后,解析数据,并将新的行数据存储到record[0]中

最后调用存储引擎接口函数handler::ha_update_row


其实,通过分析,不难发现,对binlog最终处理方式,都会在存储引擎之上,调用相应的接口,例如在statement模式下,就会模拟一个新的SQL请求提交给mysql_parse函数


现在还没有达到我们的目的,在最初分析的时候。将大量精力放在寻找mysql何时才会将其记录在ev中的record[0](新版本数据)和record[1]进行解析,但一直执行到ha_innobase::update_row都没有对其中的数据做任何处理

无奈之下,继续观察结构体数据,结果在执行ha_update_row之前,观察到m_table->field被赋值。其类型为FIELD**,显然,这是个结构体数组,尝试着打印其中的信息,发现数组中每个元素分别对应表的各个字段,并且存储的是新版本数据

现在的问题是:找到在什么地方、如何解析行数据的?

gdb的过程中,发现一个诡异的现象:

(gdb) p m_table->field[2]->ptr
Cannot access memory at address 0x0
(gdb) n
3569        if (m_curr_row_end > m_rows_end)
(gdb) p m_table->field[2]->ptr
Cannot access memory at address 0x0
(gdb) n
9323      if ((error= unpack_current_row(rli, abort_on_warnings)))
(gdb) p m_table->field[2]->ptr
Cannot access memory at address 0x0
(gdb) n
9335      DBUG_PRINT("info",("Updating row in table"));
(gdb) p m_table->field[2]->ptr
Cannot access memory at address 0x0
(gdb) p m_table->field[2]->ptr
Cannot access memory at address 0x0
(gdb) n
9336      DBUG_DUMP("old record", m_table->record[1], m_table->s->reclength);
(gdb) p m_table->field[2]->ptr
Cannot access memory at address 0x0
(gdb) n
9337      DBUG_DUMP("new values", m_table->record[0], m_table->s->reclength);
(gdb) p m_table->field[2]->ptr
$258 = (uchar *) 0x2aaab800628a "\rhello,world!!", ' ' <repeats 87 times>
(gdb) 


看到没? 在经过一次DEBUG_DUMP后,m_table被神奇的赋值了,百思不得其解(???),也许是本人C++功底不行,哎


问题总得要解决,反复gdb跟踪,发现m_table与unpack_row 函数中的table参数内存地址完全一致,尽管不知道何时赋值给m_table的……

那么,解决问题的关键就是unpack_row函数了。大致浏览了下代码,也跟预想中的很符合,下一步将深入进去看看解析的具体流程,验证猜想!









  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值