因为mysql的主从复制是逻辑复制,所以slave在apply relay log的过程中,经常会遇到错误,
而参数sql_slave_skip_counter可以设置跳过多少个event,让slave sql继续。
因为mysql的binlog是由event组成的。一个transaction是一个group,其中包括多个event,
所以sql_slave_skip_counter参数设置的时候需要达到两个效果:
- 既可以跳过多少个event。
- 又保证了group的原子性,即如果一个group中的一个event跳过,那么整个group也skip。
下面就来看下MySQL中的源码是如何处理的:
1. 相关数据结构:
enum enum_skip_reason { /**Don't skip event.*/ EVENT_SKIP_NOT, /** Skip event by ignoring it. This means that the slave skip counter will not be changed. */ EVENT_SKIP_IGNORE, /** Skip event and decrease skip counter. */ EVENT_SKIP_COUNT };
apply_event的过程中,是否需要skip,以及是否递减sql_slave_skip_counter都是依据这个枚举变量。
2. 测试case:
master: insert into test(2, 'xxx'); commit
2.1. binlog格式
根据row格式,上面的sql产生如下的binlog,一个包括4个event。
begin:
table_map:
write_row:
xid:
2.2. 设置slave
因为主键冲突,slave停止恢复
case 1: set global sql_slave_skip_counter=1; start slave
case 2: set global sql_slave_skip_counter=10; start slave
2.3. 关键函数:
下面四个函数对应着4个event是否skip,以及如何skip。
1. query_log::do_shall_skip()
2. table_map::do_shall_skip()
3. write_row::do_shall_skip()
4. xid::do_shall_skip()
但这里有一个非常关键的处理判断:
Log_event::continue_group(Relay_log_info *rli) { if (rli->slave_skip_counter == 1) return Log_event::EVENT_SKIP_IGNORE; return Log_event::do_shall_skip(rli); } Log_event::do_shall_skip(Relay_log_info *rli) rli->slave_skip_counter == 1 && rli->is_in_group()) return EVENT_SKIP_IGNORE; else if (rli->slave_skip_counter > 0) return EVENT_SKIP_COUNT; else return EVENT_SKIP_NOT;
其整体的逻辑如下:
1. 如果slave_skip_counter =1 ,那么一直skip,直到遇到xid,然后递减成0.
2. 如果slave_skip_counter =10, 每一个event都skip,并且递减counter,直到xid结束,递减成6.
3. 结论:
所以, slave_skip_counter包含了两层意思:
1. 跳过多少个event
2. 保证一个group的完整性