04-三种复制方式
二进制日志文件Binlog的格式有以下3种。
Statement:基于SQL语句级别的Binlog,每条修改数据的SQL都会保存到Binlog里。
Row:基于行级别,记录每一行数据的变化,也就是将每行数据的变化都记录到Binlog里面,记录得非常详细,但是并不记录原始SQL;在复制的时候,并不会因为存储过程或触发器造成主从库数据不一致的问题,但是记录的日志量较Statement格式要大得多。
Mixed:混合Statement和Row模式,默认情况下采用Statement模式记录,某些情况下会切换到Row模式,例如SQL中包含与时间、用户相关的函数等。
同时也对应了MySQL复制的3种技术。
binlog_format=Statement:基于 SQL 语句的复制,也叫 Statement-Based Replication (SBR),MySQL 5.1.4或之前版本仅提供基于SQL语句的复制。
binlog_format=Row:基于行的复制,也叫Row-Based Replication(RBR)。
binlog_format=Mixed:混合复制模式,混合了基于SQL语句的复制和基于行的复制。
注意:MySQL 5.1.11和之前版本,MySQL的默认设置是基于SQL语句的复制,MySQL 5.1.29和之后的版本,MySQL的默认设置也是基于SQL语句的复制;只有从MySQL 5.1.12到MySQL 5.1.28之间的版本,默认设置是混合模式。
下面通过例子来看Statement格式和Row格式的区别,这里以Update操作为例。在Statement格式下,Binlog记录的是原始操作SQL:
mysql> show variables like '%binlog%format%';
+---------------+-----------+
| Variable_name | Value |
+---------------+-----------+
| binlog_format | STATEMENT |
+---------------+-----------+
1 row in set (0.00 sec)
mysql> update emp set sal = sal + 1000;
Query OK, 8 rows affected (0.03 sec)
Rows matched: 8 Changed: 8 Warnings: 0
通过SHOW BINLOG EVENTS命令查看到Update操作在Binlog日志文件 ip83-bin.000003中对应的开始位置为6980:
mysql> show binlog events in 'ip83-bin.000003' from 6912\G
1. row
Log_name: ip83-bin.000003
Pos: 6912
Event_type: Query
Server_id: 2
End_log_pos: 6980
Info: BEGIN
2. row
Log_name: ip83-bin.000003
Pos: 6980
Event_type: Query
Server_id: 2
End_log_pos: 7074
Info: use demo; update emp set sal = sal + 1000
3. row
Log_name: ip83-bin.000003
Pos: 7074
Event_type: Xid
Server_id: 2
End_log_pos: 7101
Info: COMMIT / xid=115 /
3 rows in set (0.00 sec)
通过mysqlbinlog工具分析对应的Binlog日志,会发现在Statement模式下,Binlog日志文件中(从6980位置开始)记录了实际发生的SQL:
at 6980
130704 19:21:13 server id 2 end_log_pos 7074 Query thread_id=12 exec_time=0
error_code=0
SET TIMESTAMP=1372936873/!/;
update emp set sal = sal + 1000
/!/;
调整Binlog格式为Row,再做一个Update操作检查一下:
mysql> show variables like '%binlog%format%';
+---------------+-----------+
| Variable_name | Value|
+---------------+-----------+
| binlog_format | STATEMENT |
+---------------+-----------+
1 row in set (0.00 sec)
mysql> set binlog_format = 'ROW';
Query OK, 0 rows affected (0.00 sec)
mysql> show variables like '%binlog%format%';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| binlog_format | ROW |
+---------------+-------+
1 row in set (0.00 sec)
mysql> update emp set sal = sal - 300;
Query OK, 8 rows affected (0.05 sec)
Rows matched: 8 Changed: 8 Warnings: 0
同样地,通过 SHOW BINLOG EVENTS 命令查看到 Update 操作在 Binlog 日志文件ip83-bin.000003中对应开始位置为7169:
mysql> show binlog events in 'ip83-bin.000003' from 7101\G
1. row
Log_name: ip83-bin.000003
Pos: 7101
Event_type: Query
Server_id: 2
End_log_pos: 7169
Info: BEGIN
2. row
Log_name: ip83-bin.000003
Pos: 7169
Event_type: Table_map
Server_id: 2
End_log_pos: 7226
Info: table_id: 39 (demo.emp)
3. row
Log_name: ip83-bin.000003
Pos: 7226
Event_type: Update_rows
Server_id: 2
End_log_pos: 7996
Info: table_id: 39 flags: STMT_END_F
4. row
Log_name: ip83-bin.000003
Pos: 7996
Event_type: Xid
Server_id: 2
End_log_pos: 8023
Info: COMMIT / xid=119 /
4 rows in set (0.00 sec)
此时再通过 mysqlbinlog检查对应的Binlog日志文件,就会发现显示出的是乱码,需要通过Base64解码后才能看:
at 7169
at 7226
130704 19:30:25 server id 2 end_log_pos 7226 Table_map: demo.emp mapped to number 39
130704 19:30:25 server id 2 end_log_pos 7996 Update_rows: table id 39 flags: STMT_END_F
BINLOG '
0VzVURMCAAAAOQAAADocAAAAACcAAAAAAAEABGRlbW8AA2VtcAAJAwMPDwMKBQUDBgoACQAICP4B
…
'/!/;
at 7996
通过增加参数 -vv --base64-output=DECODE-ROWS再看Binlog就清晰多了:
$ mysqlbinlog -vv ip83-bin.000003 --base64-output=DECODE-ROWS --start-pos=7169
/!40019 SET @@session.max_insert_delayed_threads=0/;
/!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE,COMPLETION_TYPE=0/;
DELIMITER /!/;
at 7169
at 7226
130704 19:30:25 server id 2 end_log_pos 7226 Table_map: demo.emp mapped to number 39
130704 19:30:25 server id 2 end_log_pos 7996 Update_rows: table id 39 flags: STMT_END_F
UPDATE demo.emp
WHERE
@1=1 / INT meta=0 nullable=0 is_null=0 /
@2=7369 / INT meta=0 nullable=1 is_null=0 /
@3='SMITH' / VARSTRING(10) meta=10 nullable=1 is_null=0 /
@4='CLERK' / VARSTRING(9) meta=9 nullable=1 is_null=0 /
@5=7902 / INT meta=0 nullable=1 is_null=0 /
@6='1980:12:17' / DATE meta=0 nullable=1 is_null=0 /
@7=1800 / DOUBLE meta=8 nullable=1 is_null=0 /
@8=NULL / DOUBLE meta=8 nullable=1 is_null=1 /
@9=20 / INT meta=0 nullable=1 is_null=0 /
SET
@1=1 / INT meta=0 nullable=0 is_null=0 /
@2=7369 / INT meta=0 nullable=1 is_null=0 /
@3='SMITH' / VARSTRING(10) meta=10 nullable=1 is_null=0 /
@4='CLERK' / VARSTRING(9) meta=9 nullable=1 is_null=0 /
@5=7902 / INT meta=0 nullable=1 is_null=0 /
@6='1980:12:17' / DATE meta=0 nullable=1 is_null=0 /
@7=1500 / DOUBLE meta=8 nullable=1 is_null=0 /
@8=NULL / DOUBLE meta=8 nullable=1 is_null=1 /
@9=20 / INT meta=0 nullable=1 is_null=0 /
…
能够清晰地看到,在binlog_format设置为Row格式时,MySQL实际上在Binlog中逐行记录数据的变更,Row格式比Statement格式更能保证从库数据的一致性(复制的是记录,而不是单纯操作SQL)。当然,Row格式下的Binlog的日志量很可能会增大非常多,在设置时需要考虑到磁盘空间问题。
参数binlog_format可以在全局设置或者在当前Session动态设置:在全局设置会影响所有Session,而在当前Session设置则仅仅影响当前Session。可以通过SET命令来实时修改二进制日志文件(Binlog)的格式,例如,当前Binlog格式为语句Statement,通过SET GLOBAL命令调整数据库全局Binlog格式为Row:
mysql> set global binlog_format = 'ROW';
Query OK, 0 rows affected (0.00 sec)