1、binlog文件
Binlog的日志格式可以在命令行中设置
show variables like '%log%'; //要重新登入一次才会看到该变量生效
set global binlog_format='row'/'statement'/'mixed'
binlog有三种格式:Statement, Row和Mixed.
- 基于SQL语句的复制(statement-based replication, SBR)
- 基于行的复制(row-based replication, RBR)
- 混合模式复制(mixed-based replication, MBR),一般的语句修改使用statment格式保存binlog,如一些函数,statement无法完成主从复制的操作,则采用row格式保存binlog,MySQL会根据执行的每一条具体的sql语句来区分对待记录的日志形式,也就是在Statement和Row之间选择一
通过命令
show binlog EVENTS in "mysql-bin.000001";
或者
可以查看binlog的具体sql内容
具体内容如下:
BEGIN
/*!*/;
# at 4123
#190526 19:04:08 server id 1 end_log_pos 4238 CRC32 0x0c948921 Table_map: `account_17_db`.`t_company_account_snap_2` mapped to number 72
# at 4238
#190526 19:04:08 server id 1 end_log_pos 4526 CRC32 0x76cdfa0a Update_rows: table id 72 flags: STMT_END_F
BINLOG '
qHLqXBMBAAAAcwAAAI4QAAAAAEgAAAAAAAEADWFjY291bnRfMTdfZGIAGHRfY29tcGFueV9hY2Nv
dW50X3NuYXBfMgARCA8PDwH2EgP29vb2DxISCAEVYABgAMAAIAQAIAQgBCAEIAT9AgAAAAAAIYmU
DA==
qHLqXB8BAAAAIAEAAK4RAAAAAEgAAAAAAAEAAgAR////////AAD+AgAAAAAAAAADd3F3AAAAgAAA
AAAAAAAAAAAAAAAAmQLCAAAAAAAAgAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAgAAAAAAA
AAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAACZAsIAAJkCwgAAAAAAAAAAAAAAAAD+AgAAAAAAAAAD
d3F3AAJzZACAAAAAAAAAAAAAAAAAAACZAsIAAAAAAACAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAA
AAAAAACAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAJkCwgAAmQLCAAAAAAAAAAAAAAAK
+s12
'/*!*/;
# at 4526
#190526 19:04:08 server id 1 end_log_pos 4557 CRC32 0x2abea70f Xid = 139
COMMIT/*!*/;
# at 4557
#190526 19:06:01 server id 1 end_log_pos 4654 CRC32 0xf30dd244 Query thread_id=5 exec_time=0 error_code=0
SET TIMESTAMP=1558868761/*!*/;
BEGIN
/*!*/;
# at 4654
#190526 19:06:01 server id 1 end_log_pos 4829 CRC32 0xc246c0a7 Query thread_id=5 exec_time=0 error_code=0
use `account_17_db`/*!*/;
SET TIMESTAMP=1558868761/*!*/;
UPDATE `t_company_account_snap_2` SET `Ftrans_id`='34' WHERE (`Findex`='4') LIMIT 1
/*!*/;
# at 4829
#190526 19:06:01 server id 1 end_log_pos 4860 CRC32 0x7f835a25 Xid = 151
COMMIT/*!*/;
xid 139是按照行数据的形式存储的变更,xid 151则是按照statement存储的变更
2、主从同步的过程
主库执行sql,记录到二进制的文件中(记录的过程在事务中),从库启动一个工作线程,和主库建立客户端连接,主库也会启动二进制转储的线程,该转储线程读取binlog中的事件,发送到从库的线程中并接收从库的ack(串行)。如果已经追赶到了最新数据,该转储线程会休眠,等待主库有新事务后唤醒之。如果是一主多从的情况,主库对每个从库都有对应的线程。
ps:5.7之后主库有专门的线程接收从库的ack
从库的工作线程将收到的事件记录到中继日志中,从库的另外的sql线程会读取中继日志,并执行之。同时也会将该事件写入自己的binlog中。一个线程的执行可能会变为瓶颈
3、主从同步的方式
1、语句复制
在mysql 5.0之前只支持语句复制,将主库执行的sql在从库执行一次。
优点:简单,sql更新的数据量很大,但是sql的大小只有几个字节
缺点:无法完全还原主库的执行sql的环境,例如 CURRENT_USER()函数。并且sql在主从的执行时间不一样,5.1和5.2的mysql对触发器和存储过程的支持不够
2、行复制
将binlog中的行数据复制到从库
优点:可以覆盖任何场景的数据变更
缺点:看不出数据变更的逻辑
4、主从同步
1、异步复制:
- MySQL的异步复制是MySQL自带的数据同步功能,在公司里面也是也就最为常见的。
- 主库的事务执行不会管备库的同步进度,如果备库落后,主库不幸crash,那么就会导致数据丢失。
2、同步复制 (>=5.7):
全同步复制,当主库提交事务之后,所有的从库节点必须收到、APPLY并且提交这些事务,然后主库线程才能继续做后续操作。但缺点是,主库完成一个事务的时间会被拉长,性能降低。
3、半同步复制 (>=5.5):
- 半同步复制是由谷歌研发的一种数据库主从复制方式。
- 与传统的异步复制相比,半同步复制在多个Slave节点中会选取一个节点进行半同步复制。也就是说,当Master提交一个事务的时候,在这个半同步复制的Slave端返回一个同步完成的Ack包之后,服务器才会向用户返回事务提交成功,而其他的节点则是采用传统的异步复制方式进行同步。
- 如果主库在等待备库ack时候,如果超时会退化为异步,这就可能导致数据丢失。
rpl_semi_sync_master_wait_point、sync_binlog、sync_relay_log配置半同步
rpl_semi_sync_master_wait_point的配置:
WAIT_AFTER_COMMIT:
如上图所示。即在等待Slave ACK时候,虽然没有返回当前客户端,但事务已经提交,其他客户端会读取到已提交事务。如果Slave端还没有读到该事务的events,同时主库发生了crash,然后切换到备库。那么之前读到的事务就不见了,出现了幻读
WAIT_AFTER_SYNC:
在调用binlog sync之后,engine层commit之前等待Slave ACK。这样只有在确认Slave收到事务events后,事务才会提交。在commit之前等待Slave ACK,同时可以堆积事务,利于group commit(一次ack多个事务),有利于提升性能。
使用方式:
主库要安装插件,并启用之:
INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
SET GLOBAL VARIABLES rpl_semi_sync_master_enabled=1;
从库也一样:
mysql> INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';
mysql> SET GLOBAL VARIABLES rpl_semi_sync_slave_enabled=1;