由于二进制日志是公共资源,所有线程都要写二进制日志,所以一定要避免两个线程同时更新二进制日志。因此,在事件组写二进制日志时,二进制日志将获得一个互斥锁LOCK_log,然后在事件组写完后释放,由于服务器所有的会话都向二进制日志写事务,所以这个锁经常会阻塞某些会话线程。
1 写入DML语句
通常是指DELETE/INSERT/UPDATE语句,为保证一致性,MySQL在写入二进制日志时同时获得事务级锁,写完后释放锁
在表上锁释放前,在语句提交前同时需要将该语句写入二进制日志,这样就保证了二进制日志始终与语句对表的改动保持一致,如果记录日志没有作为语句的一部分时,有可能其他语句会插在这条语句对数据库的修改和记录二进制两个动作中间。
2 写入DDL语句
通常指的是CREATE TABLE/ALTER TABLE语句,DDL语句会在文件系统中创建或改变对象。为了保护表中的内部数据结构(.frm)得到更新,在修改表定义钱需要先获得一个内部锁LOCK_open
3 写入查询
对于SBR来说,最常见的就binlog事件就是QUERY事件,它将master上执行的语句写入日志,除了实际执行的语句本身,该语句还包含执行语句需要的附加信息
对服务器执行语句时必须知道隐式,隐式包括
1)当前数据库
向QUERY事件添加一个特殊字段,记录当前数据库,处理LOAD DATA INFILE语句的事件也有这个字段。
2)当前时间
有5个函数需要利用到时间NOW CURDATE CURTIME UNIX_TIMESTAMP SYSDATE,前4个返回开始执行的时间,而SYSDATE返回函数执行时的时间。
[root@localhost][(none)]> SELECT SYSDATE(),NOW(),SLEEP(2),SYSDATE(),NOW();
+---------------------+---------------------+----------+---------------------+---------------------+
| SYSDATE() | NOW() | SLEEP(2) | SYSDATE() | NOW() |
+---------------------+---------------------+----------+---------------------+---------------------+
| 2017-03-29 08:08:37 | 2017-03-29 08:08:37 | 0 | 2017-03-29 08:08:39 | 2017-03-29 08:08:37 |
+---------------------+---------------------+----------+---------------------+---------------------+
row in set (2.05 sec)
为了正确处理时间,事件将存储一个时间戳,表明事件何时开始执行的。然后将这个时间戳的值从事件复制到SLAVE执行进程,在计算时间函数的时候,将这个时间戳作为事件开始执行的时间
SYSDATE直接从操作系统获取时间,这个对于SBR来说是不安全的,有可能会导致master和slave上执行的返回值不一样。最好慎用这个函数
3)上下文
有些语句也包含隐式信息,需要满足一些条件
a)语句包含对用户定义变量的引用
b)包含RAND函数调用
c)包含LAST_INSERT_ID调用
d)需要向表中插入AUTO_INCREMENT类型的字段
无论上述那种情况,在写入包含查询时间之前,需要向二进制日志写入一个或多个上下文事件,由于一个QUERY事件之前可能有多个上下文时间,所以二进制日志同时要处理多个用户定义变量和RAND函数。二进制日志通过下面事件来存储必要的上下文信息
User_var 记录单个用户自定义变量的变量名及其值
Rand 记录RAND函数的随机数种子,种子取自会话颞部状态
Intvar 如果语句要插入AUTO_INCREMENT类型字段,在执行插入语句钱,该事件会设置表中自动增量计算器的值,如果语句包含LAST_INSERT_ID函数调用,该事件记录这个函数在该语句中的返回值
[root@localhost][(none)]> set @foo=12;
Query OK, 0 rows affected (0.00 sec)
[root@localhost][(none)]> set @bar = 'fdjkfjdjfkdjfkdf';
Query OK, 0 rows affected (0.00 sec)
[root@localhost][(none)]> use boss
Database changed
[root@localhost][boss]> create table t1(b int auto_increment primary key,c varchar(20));
Query OK, 0 rows affected (0.43 sec)
[root@localhost][boss]> insert into t1(b,c) values(@foo,@bar),(RAND(),'random');
Query OK, 2 rows affected (0.11 sec)
Records: 2 Duplicates: 0 Warnings: 0
[root@localhost][boss]> insert into t1(b) values(LAST_INSERT_ID());
ERROR 1062 (23000): Duplicate entry '13' for key 'PRIMARY'
[root@localhost][boss]> insert into t1(b) values(LAST_INSERT_ID()+1);
Query OK, 1 row affected (0.07 sec)
[root@localhost][boss]> show master status;
+------------------+----------+--------------+------------------+--------------------------------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+--------------------------------------------+
| mysql_bin.000023 | 2878 | | | a0c06ec7-fef0-11e6-9f85-525400a7d662:1-193 |
+------------------+----------+--------------+------------------+--------------------------------------------+
row in set (0.00 sec)
[root@localhost][boss]> show binlog events in 'mysql_bin.000023'\G;