一 三種模式介紹
1.1 查看mysql主庫的binlog格式
binlog僅在主庫設置即可,從庫無需設置
binlog的默認方式為STATEMENT ( show variables like '%binlog_format%'; )
mysql> mysql> show variables like '%binlog_format%'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | binlog_format | ROW | #默認ROW +---------------+-------+
1.2 mysql主庫的binlog有三種方式
statement #最古老的方式,基於sql的主從復制。binlog里面保存的都是sql語句,binlog日志量小。但如果像uuid()的話,數據會不一致
優點:statement level 下的優點,首先就是解決了row level下的缺點,不需要記錄每一行數據的變化,減少bin‐log 日志量,節約io,提高性能。因為他只需要記錄在master 上所執行的語句的細節,以及執行語句時候的上下文的信息。
缺點:由於它是記錄的執行語句,所以為了讓這些語句在slave 端也能正確執行,那么他還必須記錄每條語句在執行的時候的一些相關信息,也就是上下文信息,以保證所有語句在slave 端被執行的時候能夠得到和在master 端執行時候相同的結果。另外就是,由於mysql 現在發展比較快,很多的新功能加入,使mysql 的復制遇到了不小的挑戰,自然復制的時候涉及到越復雜的內容,bug 也就越容易出現。在statement level 下,目前已經發現的就有不少情況會造成mysql 的復制問題,主要是修改數據的時候使用了某些特定的函數或者功能的時候會出現,比如sleep()在有些版本就不能正確復制。
row #基於行的主從方式,保證數據一致性,binlog里面保存的是更改信息。binlog日志量大。阿里雲默認使用這種方式(5.7以后默認的方式)
優點:bin‐log 中可以不記錄執行的sql 語句的上下文相關的信息,僅僅只需要記錄哪一條記錄被修改了,修改成什么樣了。所以row level 的日志的內容會非常清楚的記錄下每一行數據修改的細節。
缺點:row level 下,所有的執行的語句當記錄到日志中的時候,都將以每行記錄的修改記錄,這樣可能會產生大量的日志內容,比如有這樣一條update 語句:update product set owner_member_id='d',執行之后,日志中記錄的不是這條update 語句所對應的event 事件(mysql 是以事件的形式來記錄bin‐log 日志),而是這條語句所更新的每一條記錄的變化情況,這樣就記錄了很多條記錄被更新的事件。因此,bin‐log日志的量會很大。
mixed :
實際上就是前兩種模式的結合,在mixed 模式下,mysql 會根據執行的每一條具體的sql 語句來區分對待記錄的日志形式,也就是在statement 和row 之間選一種。新版本中的statement level 還是和以前一樣,僅僅記錄執行的語句。而新版本的mysql 中對row level 模式被做了優化,並不是所有的修改都會以row level 來記錄,像遇到表結構變更的時候就會以statement 模式來記錄,如果sql 語句確實就是update或者delete 等修改數據的語句,那么還是會記錄所有行的變更。
二 三種模式驗證
主從復制的方式可以動態更改,無需重啟mysql
2.1 statement模式
把模式改為statement
mysql> set global binlog_format = STATEMENT; #針對當前的會話,會話退出后就會還原成STATEMENT Query OK, 0 rows affected (0.00 sec) mysql> set binlog_format = statement #針對當前的會話,會話退出后就會還原成STATEMENT -> ; Query OK, 0 rows affected (0.00 sec) mysql> show variables like '%binlog_format%'; +---------------+-----------+ | Variable_name | Value | +---------------+-----------+ | binlog_format | STATEMENT | +---------------+-----------+
mysql配置文件新增binlog_format = STATEMENT #永久把binlog格式替換為statement
[mysqld] bind-address=0.0.0.0 port=3306 datadir=/data/mysql socket=/data/mysql/mysql.sock user=mysql skip-name-resolve slow_query_log=on long_query_time=1 slow_query_log_file=/data/mysql/mysql-slow.log innodb-file-per-table=1 innodb_flush_log_at_trx_commit = 2 log_warnings = 1 connect_timeout = 60 net_read_timeout = 120 performance_schema_max_table_instances = 400 server-id = 1 log-bin binlog_format = STATEMENT
使用命令,查看binlog記錄的內容
mysql> show master logs; +-------------------+-----------+ | Log_name | File_size | +-------------------+-----------+ | mater1-bin.000001 | 31050 | +-------------------+-----------+ 1 row in set (0.00 sec) mysql> flush logs; Query OK, 0 rows affected (0.01 sec) mysql> show master logs; +-------------------+-----------+ | Log_name | File_size | +-------------------+-----------+ | mater1-bin.000001 | 31098 | | mater1-bin.000002 | 154 | +-------------------+-----------+ 2 rows in set (0.00 sec) mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | darren | | mysql | | performance_schema | | sys | +--------------------+ 5 rows in set (0.00 sec) mysql> create database darren1; Query OK, 1 row affected (0.00 sec) mysql> use darren1; Database changed mysql> create table test (id int); Query OK, 0 rows affected (0.01 sec) mysql> insert into test values (1); Query OK, 1 row affected (0.01 sec) mysql> update test set id=3 where id = 1; Query OK, 1 row affected (0.01 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> select * from test; +------+ | id | +------+ | 3 | +------+ 1 row in set (0.00 sec) mysql> show binlog events in 'mater1-bin.000002'; +-------------------+------+----------------+-----------+-------------+--------------------------------------------------+ | Log_name | Pos | Event_type | Server_id | End_log_pos | Info | +-------------------+------+----------------+-----------+-------------+--------------------------------------------------+ | mater1-bin.000002 | 4 | Format_desc | 1 | 123 | Server ver: 5.7.26-log, Binlog ver: 4 | | mater1-bin.000002 | 123 | Previous_gtids | 1 | 154 | | | mater1-bin.000002 | 154 | Anonymous_Gtid | 1 | 219 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS' | | mater1-bin.000002 | 219 | Query | 1 | 322 | create database darren1 | | mater1-bin.000002 | 322 | Anonymous_Gtid | 1 | 387 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS' | | mater1-bin.000002 | 387 | Query | 1 | 493 | use `darren1`; create table test (id int) | | mater1-bin.000002 | 493 | Anonymous_Gtid | 1 | 558 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS' | | mater1-bin.000002 | 558 | Query | 1 | 643 | BEGIN | | mater1-bin.000002 | 643 | Query | 1 | 750 | use `darren1`; insert into test values (1) | | mater1-bin.000002 | 750 | Xid | 1 | 781 | COMMIT /* xid=659 */ | | mater1-bin.000002 | 781 | Anonymous_Gtid | 1 | 846 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS' | | mater1-bin.000002 | 846 | Query | 1 | 931 | BEGIN | | mater1-bin.000002 | 931 | Query | 1 | 1044 | use `darren1`; update test set id=3 where id = 1 | | mater1-bin.000002 | 1044 | Xid | 1 | 1075 | COMMIT /* xid=660 */ | +-------------------+------+----------------+-----------+-------------+--------------------------------------------------+
從端也同步
mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | darren | | darren1 | | mysql | | performance_schema | | sys | +--------------------+ 6 rows in set (0.00 sec) mysql> show slave status\G; *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 192.168.132.121 Master_User: replication Master_Port: 3306 Connect_Retry: 60 Master_Log_File: mater1-bin.000002 Read_Master_Log_Pos: 1075 Relay_Log_File: slave1-relay-bin.000004 Relay_Log_Pos: 1290 Relay_Master_Log_File: mater1-bin.000002 Slave_IO_Running: Yes Slave_SQL_Running: Yes
row格式主要是記錄了更改,statement記錄的是現實的sql語句,數據不安全
ROW格式數據安全,但binlog的日志量比較大,試想,如果刪除、更新了上百萬行的話
statement會導致數據不一致,如下面語句
mysql> alter table test add age varchar(50); Query OK, 0 rows affected (0.04 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> insert into test values (2,UUID()); #插入一個隨機數 Query OK, 1 row affected, 1 warning (0.01 sec) mysql> select * from test; +------+--------------------------------------+ | id | age | +------+--------------------------------------+ | 3 | NULL | | 2 | 2d64d412-9e72-11e9-a2f9-000c2991dd19 | +------+--------------------------------------+ 從端: mysql> select * from darren1.test; +------+--------------------------------------+ | id | age | +------+--------------------------------------+ | 3 | NULL | | 2 | 2d666978-9e72-11e9-8aca-000c2963fd11 | +------+--------------------------------------+
發現數據不一致
2.2 使用ROW格式
ysql> delete from test; Query OK, 2 rows affected (0.01 sec) mysql> select * from test; Empty set (0.00 sec) mysql> insert into test values (2,UUID()); Query OK, 1 row affected (0.00 sec) mysql> select * from test; +------+--------------------------------------+ | id | age | +------+--------------------------------------+ | 2 | 74ee4ad0-9e73-11e9-a2f9-000c2991dd19 | +------+--------------------------------------+ 從端: mysql> select * from darren1.test; +------+--------------------------------------+ | id | age | +------+--------------------------------------+ | 2 | 74ee4ad0-9e73-11e9-a2f9-000c2991dd19 | +------+--------------------------------------+
完全一致的
使用mysqlbinlog命令可以查看row格式的binlog
[root@master mysql]# mysqlbinlog -vv --base64-output=decode-rows master-bin.000001
# at 1857 #190704 11:50:33 server id 1 end_log_pos 1922 CRC32 0x365a53d7 Anonymous_GTID last_committed=7 sequence_number=8 rbr_only=yes /*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/; SET @@SESSION.GTID_NEXT= 'ANONYMOUS'/*!*/; # at 1922 #190704 11:50:33 server id 1 end_log_pos 1997 CRC32 0x9d17ec5e Query thread_id=133 exec_time=0 error_code=0 SET TIMESTAMP=1562255433/*!*/; BEGIN /*!*/; # at 1997 #190704 11:50:33 server id 1 end_log_pos 2050 CRC32 0x91bb97b1 Table_map: `darren1`.`test` mapped to number 110 # at 2050 #190704 11:50:33 server id 1 end_log_pos 2127 CRC32 0x8c5aca9e Write_rows: table id 110 flags: STMT_END_F ### INSERT INTO `darren1`.`test` ### SET ### @1=2 /* INT meta=0 nullable=1 is_null=0 */ ### @2='74ee4ad0-9e73-11e9-a2f9-000c2991dd19' /* VARSTRING(50) meta=50 nullable=1 is_null=0 */ #記錄的是一個變化值 # at 2127 #190704 11:50:33 server id 1 end_log_pos 2158 CRC32 0x7f33c342 Xid = 695 COMMIT/*!*/; # at 2158 #190704 11:53:53 server id 1 end_log_pos 2206 CRC32 0xd55138bd Rotate to mater1-bin.000003 pos: 4 SET @@SESSION.GTID_NEXT= 'AUTOMATIC' /* added by mysqlbinlog */ /*!*/; DELIMITER ; # End of log file
2.3 MIXED模式
混合格式的binlog,想讓binlog日志量小,而且數據一致還有保證
主端操作
mysql> set binlog_format = 'MIXED'; Query OK, 0 rows affected (0.00 sec) mysql> flush logs; Query OK, 0 rows affected (0.00 sec) mysql> create database darren; Query OK, 1 row affected (0.00 sec) mysql> use darren; Database changed mysql> create table test( id int, name varchar(100) ); Query OK, 0 rows affected (0.01 sec) mysql> insert into test values (1, 'darren'); Query OK, 1 row affected (0.00 sec) mysql> update test set id = 3 where id = 1; Query OK, 1 row affected (0.01 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> insert into test values (2, UUID()); Query OK, 1 row affected (0.00 sec) mysql> select * from test; +------+--------------------------------------+ | id | name | +------+--------------------------------------+ | 3 | darren | | 2 | b86c02cf-9e74-11e9-a2f9-000c2991dd19 | +------+--------------------------------------+
從端對比
mysql> select * from darren.test; +------+--------------------------------------+ | id | name | +------+--------------------------------------+ | 3 | darren | | 2 | b86c02cf-9e74-11e9-a2f9-000c2991dd19 | +------+--------------------------------------+
數據也是一致的
日志記錄
mysql> mysql> show binlog events in 'mater1-bin.000004'; +-------------------+------+----------------+-----------+-------------+--------------------------------------------------------------+ | Log_name | Pos | Event_type | Server_id | End_log_pos | Info | +-------------------+------+----------------+-----------+-------------+--------------------------------------------------------------+ | mater1-bin.000004 | 4 | Format_desc | 1 | 123 | Server ver: 5.7.26-log, Binlog ver: 4 | | mater1-bin.000004 | 123 | Previous_gtids | 1 | 154 | | | mater1-bin.000004 | 154 | Anonymous_Gtid | 1 | 219 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS' | | mater1-bin.000004 | 219 | Query | 1 | 319 | create database darren | | mater1-bin.000004 | 319 | Anonymous_Gtid | 1 | 384 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS' | | mater1-bin.000004 | 384 | Query | 1 | 508 | use `darren`; create table test( id int, name varchar(100) ) | | mater1-bin.000004 | 508 | Anonymous_Gtid | 1 | 573 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS' | | mater1-bin.000004 | 573 | Query | 1 | 656 | BEGIN | | mater1-bin.000004 | 656 | Query | 1 | 771 | use `darren`; insert into test values (1, 'darren') | | mater1-bin.000004 | 771 | Xid | 1 | 802 | COMMIT /* xid=728 */ | | mater1-bin.000004 | 802 | Anonymous_Gtid | 1 | 867 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS' | | mater1-bin.000004 | 867 | Query | 1 | 950 | BEGIN | | mater1-bin.000004 | 950 | Query | 1 | 1063 | use `darren`; update test set id = 3 where id = 1 | | mater1-bin.000004 | 1063 | Xid | 1 | 1094 | COMMIT /* xid=729 */ | | mater1-bin.000004 | 1094 | Anonymous_Gtid | 1 | 1159 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS' | | mater1-bin.000004 | 1159 | Query | 1 | 1233 | BEGIN | | mater1-bin.000004 | 1233 | Table_map | 1 | 1285 | table_id: 114 (darren.test) | | mater1-bin.000004 | 1285 | Write_rows | 1 | 1362 | table_id: 114 flags: STMT_END_F | | mater1-bin.000004 | 1362 | Xid | 1 | 1393 | COMMIT /* xid=730 */ | +-------------------+------+----------------+-----------+-------------+--------------------------------------------------------------+
根據SQL調整記錄方式