#151111 7:39:22 server> BINLOG '
qvBCVg8BAAAAZwAAAAAAAAAAAAQANS41LjI0LXRteXNxbC0xLjQtbG9nAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAEzgNAAgAEgAEBAQEEgAAVAAEGggAAAAICAgCAA==
'/*!*/;
DELIMITER ;
# End of log file
ROLLBACK /* added by mysqlbinlog */;
/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/;
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/;
可以在我stop slave的时候因为I/O thread也被停掉了,看到这个时候relay-log才写到152,还没写到255,那么255肯定是还没读到的位置咯。 (二)、什么是sql_slave_skip_counter
这是一个global级别的参数,一般我们在stop slave之后,通过set global sql_slave_skip_counter=N,再start slave,来恢复因为错误而中断的主从关系。简单来说,这是mysql主从同步时,从库上遇到错误偶尔会用的一个参数,这个参数会控制从库跳过主从同步中遇到的错误,但是因为可能会导致数据不一致,所以个人比较建议用好懂的slave_exec_mode,有错误针对错误跳过错误,slave_exec_mode=IDEMPOTENT只跳过有影响的sql或者row
听了个人之言,下面来看看那么我们的bible----manual是怎么定义这个参数的呢?
SET GLOBAL sql_slave_skip_counter = N
This statement skips the next N events from the master. This is useful for recovering from replication stops caused by a statement.
This statement is valid only when the slave threads are not running. Otherwise, it produces an error.
When using this statement, it is important to understand that the binary log is actually organized as a sequence of groups known as event groups. Each event group consists of a sequence of events.
For transactional tables, an event group corresponds to a transaction.
For nontransactional tables, an event group corresponds to a single SQL statement.
Note
A single transaction can contain changes to both transactional and nontransactional tables.
When you use SET GLOBAL sql_slave_skip_counter to skip events and the result is in the middle of a group, the slave continues to skip events until it reaches the end of the group. Execution then starts with the next event group.
首先明确这么几点:
1)N是事件数,那么什么是事件,通俗一点来说(你看上面的我扣出来的relay-log,相邻两个POS之间记录的那些东东叫做一个事件
2)对于事务型表一个事件组才是一个事务(这么说吧,一个事务有begin,正文(Insert。。。那些实际操作),commit,而begin和commit也是一个事件,所以呢至少要三个事件才能构成一个事务)
3)对于非事务型表,一个sql一个事件
最后最最重要的是NOTE:
如果跳过了事件,还没有到达该事务的尾部(没有读到commit;)那么没关系我也会跳过这个事务的。
所以对于事务表而言:SET GLOBAL sql_slave_skip_counter = 2;这个2的效果不等于1+1的(SET GLOBAL sql_slave_skip_counter = 2和SET GLOBAL sql_slave_skip_counter = 1效果一样,都只跳过了一个事务,但是执行了两次SET GLOBAL sql_slave_skip_counter = 1;会跳过两个事务) (三)、让我们来好好解释:
场景是这样的:现在的错误是t1主键重复(因为1,2,3);
我分别设置参数为sql_slave_skip_counter=1,2,3,4,5,6
进行了测试,下面我把测试情况和解释集成在同一个relay-log里面(几次测试relay-log的内容结构都差不多)
relay-log长成下面这样:
# at 255
#151218 8:14:33 server> SET TIMESTAMP=1450426473/*!*/;
SET @@session.pseudo_thread_id=2859/*!*/;
SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=0, @@session.unique_checks=1, @@session.autocommit=1/*!*/;
SET @@session.sql_mode=0/*!*/;
SET @@session.auto_increment_increment=1, @@session.auto_increment_offset=1/*!*/;
/*!\C latin1 *//*!*/;
SET @@session.character_set_client=8,@@session.collation_connection=8,@@session.collation_server=33/*!*/;
SET @@session.lc_time_names=0/*!*/;
SET @@session.collation_database=DEFAULT/*!*/;
BEGIN
/*!*/;
# at 323-----------SET GLOBAL sql_slave_skip_counter = 1;生效后从这个事件开始读起,可是因为还是在同一个事务中,所以跳到这个事务结束。
#151218 8:14:33 server> use `test`/*!*/;
SET TIMESTAMP=1450426473/*!*/;
insert into t1 value (1)
/*!*/;
# at 410-----------SET GLOBAL sql_slave_skip_counter = 2;生效后从这个事件开始读起,可是因为还是在同一个事务中,所以跳到这个事务结束。
#151218 8:14:33 server> COMMIT/*!*/;
SET GLOBAL sql_slave_skip_counter = 1;跳过一个事件,还是在事务中间,结果跳到了这里
SET GLOBAL sql_slave_skip_counter = 2;跳过两个个事件,还是在事务中间,结果跳到了这里
SET GLOBAL sql_slave_skip_counter = 3;------------------------------跳过了上面那个事务,打算从下一个事件开始读起
start slave后:
Relay_Log_Pos: 437
# at 437
#151218 8:15:16 server> SET TIMESTAMP=1450426516/*!*/;
BEGIN
/*!*/;
# at 505-----------SET GLOBAL sql_slave_skip_counter = 4;上面那个事务只有三个事件,跳过那个事务,生效后从这个事件开始读起,可是因为还是在同一个事务中,所以跳到这个事务结束。
#151218 8:15:04 server> SET TIMESTAMP=1450426504/*!*/;
insert into t1 value (2)
/*!*/;
# at 592-----------SET GLOBAL sql_slave_skip_counter = 5;上面那个事务只有三个事件,跳过那个事务,生效后从这个事件开始读起,可是因为还是在同一个事务中,所以跳到这个事务结束。
#151218 8:15:06 server> SET TIMESTAMP=1450426506/*!*/;
insert into t1 value (3)
/*!*/;
# at 679-----------SET GLOBAL sql_slave_skip_counter = 6;上面那个事务只有三个事件,跳过那个事务,生效后从这个事件开始读起,可是因为还是在同一个事务中,所以跳到这个事务结束。
#151218 8:15:16 server> COMMIT/*!*/;
SET GLOBAL sql_slave_skip_counter = 4;完整跳过了一个事务,停在了第二个事务中间,结果跳到了这里
SET GLOBAL sql_slave_skip_counter = 5;完整跳过了一个事务,停在了第二个事务中间,结果跳到了这里
SET GLOBAL sql_slave_skip_counter = 6;完整跳过了一个事务,停在了第二个事务中间,结果跳到了这里
SET GLOBAL sql_slave_skip_counter = 7;-----------------------------跳过了上面两个事务,打算从下一个事件开始读起
start slave后:
Relay_Log_Pos: 706
# at 706
#151218 8:15:29 server> SET TIMESTAMP=1450426529/*!*/;
BEGIN
/*!*/;
# at 774
#151218 8:15:29 server> SET TIMESTAMP=1450426529/*!*/;
insert into t1 value (4)
/*!*/;
# at 861
#151218 8:15:29 server> COMMIT/*!*/; (四)总结
重要的事情再说一遍:
1)set global sql_slave_skip_counter=2;不等于进行两次set global sql_slave_skip_counter=1;(前者只能跳过一个事务,后者每次设置为一都能跳过一个事务,所以两次能跳过两个事务),所以建议尽量不用使用set global sql_slave_skip_counter=N(N>1);
2)刚刚上面看到了,set global sql_slave_skip_counter=N;这是依次跳过事件的,那么对于一个事务有多个事件,即使是set global sql_slave_skip_counter=1;也有可能使得一个多事件的事务(其中有有问题事件和没问题事件)里面的有用事件被跳过造成数据不一致,所以建议用slave_exec_mode来跳过错误。