mysql binlog 日志详解,恢复测试

二进制日志常用参数:

max_binlog_size :单个二进制日志文件的最大值,超过该值,则产生新的二进制日志文件,并记录到.index文件,默认1G。
binlog_cache_size:  使用InnoDB时,所有未提交的事务会记录到一个缓存中,等待事务提交时,直接将缓冲中的二进制日志写入二进制日志文件, 而该缓冲的大小由binlog_cache_size决定,binlog_cache_size是基于会话的,即:当一个线程开始一个事务时,mysql会自动分配一个大小为binlog_cache_size大小的缓存,因此该值得设置需要相当小心,可以通过show global status 查看binlog_cache_use、binlog_cache_disk_use的状态,来判断当前binlog_cache_size的设置是否合适。


sync_binlog:参数sync_binlog=[N]表示每写缓存多少次就同步到磁盘,如果将N设置为1,则表示采用同步写磁盘的方式来写二进制日志。该参数设置为1时,也应该将innodb_support_xa设为1来解决,这可以确保二进制日志和InnoDB存储引擎数据文件的同步。
binlog-do-db:该binlog-do-db=test参数表示的是,二进制日志记录test库。
binlog-ignore-db:该binlog-ignore-db=test,表示忽略test库,不写入二进制日志。
log-slave-update:搭建master=>slave=>slave的架构时,需要配置,也就是说,应用主的binlog也写入自己的二进制日志中,方便自己的slave来同步日志。
binlog_format:表示的是,二进制日志模式:ROW,STATEMENT,MIXED。
log_bin:二进制日志的路径和名称。
expire_logs_days    = 7    binlog过期清理时间。

二进制日志格式比较:

1.Statement:每一条会修改数据的sql都会记录在binlog中。
优点:不需要记录每一行的变化,减少了binlog日志量,节约了IO,提高性能。(相比row能节约多少性能与日志量,这个取决于应用的SQL情况,正常同一条记录修改或者插入row格式所产生的日志量还小于Statement产生的日志量,但是考虑到如果带条件的update操作,以及整表删除,alter表等操作,ROW格式会产生大量日志,因此在考虑是否使用ROW格式日志时应该跟据应用的实际情况,其所产生的日志量会增加多少,以及带来的IO性能问题。)

缺点:由于记录的只是执行语句,为了这些语句能在slave上正确运行,因此还必须记录每条语句在执行的时候的一些相关信息,以保证所有语句能在slave得到和在master端执行时候相同的结果。另外mysql 的复制,像一些特定函数功能,slave可与master上要保持一致会有很多相关问题 (如sleep()函数, last_insert_id(),以及user-defined functions(udf)会出现问题)。

还有一点,statement只记录了SQL语句,我们通过日志也只能看到他执行的SQL,但无法获知SQL修改之前的数据。

使用以下函数的语句也无法被复制:
  LOAD_FILE()、 UUID()、 USER()、 FOUND_ROWS()、 SYSDATE() ,now()(除非启动时启用了 --sysdate-is-now 选项)
  同时在INSERT ...SELECT 会产生比 RBR 更多的行级锁。

2.Row:不记录sql语句上下文相关信息,仅保存哪条记录被修改。此时可以将InnoDB的事务隔离基本设为READ COMMITTED,以获得更好的并发性。

优点: binlog中可以不记录执行的sql语句的上下文相关的信息,仅需要记录那一条记录被修改成什么了。所以rowlevel的日志内容会非常清楚的记录下每一行数据修改的细节。而且不会出现某些特定情况下的存储过程,或function,以及trigger的调用和触发无法被正确复制的问题。

缺点:所有的执行的语句当记录到日志中的时候,都将以每行记录的修改来记录,这样可能会产生大量的日志内容,比如一条update语句,修改多条记录,则binlog中每一条修改都会有记录,这样造成binlog日志量会很大,新版本的MySQL中对row level模式也被做了优化,并不是所有的修改都会以row来记录,像遇到表结构变更的时候就会以statement模式来记录。至于update或者delete等修改数据的语句,还是会记录所有行的变更。

3.Mixed: 是以上两种level的混合使用,一般的语句修改使用statment格式保存binlog,如一些函数,statement无法完成主从复制的操作,则采用row格式保存binlog,MySQL会根据执行的每一条具体的sql语句来区分对待记录的日志形式,也就是在Statement和Row之间选择一种。


清除slave复制信息:
reset slave all;

MySQL [(none)]> show binary logs;
+------------+-----------+
| Log_name   | File_size |
+------------+-----------+
| bin.000001 |    100139 |
| bin.000002 |    100139 |
| bin.000003 |    101198 |
| bin.000004 |       929 |
+------------+-----------+
4 rows in set (0.03 sec)

查看binlog日志:
show binlog events in 'mysqld-bin.000002'\G;    查看mysqld-bin.000002文件所有二进制日志。
SHOW BINLOG EVENTS [IN 'log_name'] [FROM pos] [LIMIT [offset,] row_count]

MySQL [(none)]> show binlog events in 'bin.000002' from 99536 limit 0,3\G;
*************************** 1. row ***************************
   Log_name: bin.000002
        Pos: 99536
 Event_type: Query
  Server_id: 11
End_log_pos: 99687
       Info: use `mysql`; CREATE USER 'root'@'localhost' IDENTIFIED WITH 'mysql_native_password'
*************************** 2. row ***************************
   Log_name: bin.000002
        Pos: 99687
 Event_type: Anonymous_Gtid
  Server_id: 11
End_log_pos: 99752
       Info: SET @@SESSION.GTID_NEXT= 'ANONYMOUS'
*************************** 3. row ***************************
   Log_name: bin.000002
        Pos: 99752
 Event_type: Query
  Server_id: 11
End_log_pos: 99903
       Info: use `mysql`; GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
3 rows in set (0.00 sec)

文件查看:
mysqlbinlog /usr/local/mysql/binlog/mysql-bin.000042 >/tmp/000042.sql
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
# at 467      事件起点。
#151113 12:31:56 server id 30  end_log_pos 1073         Query   thread_id=73185 exec_time=0     error_code=0      #151113 12:31:56事件发生时间点。事件pos,thread_id,执行时间,错误代码等信息
use `ads_uuid`/*!*/;          执行的SQL
SET TIMESTAMP=1447389116/*!*/;      事务执行的时间戳。
SET @@session.pseudo_thread_id=73185/*!*/;
SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=0, @@session.unique_checks=1, @@session.autocommit=1/*!*/;
SET @@session.sql_mode=2097152/*!*/;
SET @@session.auto_increment_increment=1, @@session.auto_increment_offset=1/*!*/;
/*!\C utf8 *//*!*/;
SET @@session.character_set_client=33,@@session.collation_connection=33,@@session.collation_server=33/*!*/;
SET @@session.lc_time_names=0/*!*/;
SET @@session.collation_database=DEFAULT/*!*/;          执行语句session相关。
UPDATE device SET id=893390,me='867622020128842',pn='Qualcomm',md='2014811' where id='893390'    执行SQL语句
/*!*/;
# at 1073
#151113 12:31:56 server id 30  end_log_pos 1100         Xid = 120978366
COMMIT/*!*/;

\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
1.开始事物的时间:
SET TIMESTAMP=1350355892/*!*/;
BEGIN

2.sql event起点
#at 1643330 :为事件的起点,是以1643330字节开始。

3.sql event 发生的时间点
#151113 12:31:56 是事件发生的时间,

4.serverId
server id 30 :为master 的serverId

5.sql event终点及花费时间,错误码
end_log_pos 1643885:为事件的终点,其中1643885 就是下一个#at事件发生的起点。
execTime 0: 花费的时间
error_code=0:错误码
Xid:事件指示提交的XA事务

Mixed日志说明:

在slave日志同步过程中,对于使用now这样的时间函数,MIXED日志格式,会在日志中产生对应的unix_timestamp()*1000的时间字符串,slave在完成同步时,取用的是sqlEvent发生的时间来保证数据的准确性。另外对于一些功能性函数slave能完成相应的数据同步,而对于上面指定的一些类似于UDF函数,导致Slave无法知晓的情况,则会采用ROW格式存储这些Binlog,以保证产生的Binlog可以供Slave完成数据同步。
================================================================================================

 

一、基于RBR行复制是与记录的位置有关,binlog里只记录了了相关表发生变化的列的数据,对此引入了4个事件:
  Table_map、Write_rows、Update_rows、Delete_rows。

Table_map事件(包含表的ID、表名和列的类型,没有列名),再后面是3个事件,最后是结束标志为STMT_END_F。

如果是ROW格式,日志是这样的:
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
BEGIN
/*!*/;
# at 1110
# at 1278
#151113 11:10:27 server id 105  end_log_pos 1278        Table_map: `mysql`.`user` mapped to number 4
#151113 11:10:27 server id 105  end_log_pos 1676        Delete_rows: table id 4 flags: STMT_END_F

BINLOG '
o1RFVhNpAAAAqAAAAP4EAAAAAAQAAAAAAAEABW15c3FsAAR1c2VyACz+/v7+/v7+/v7+/v7+/v7+
/v7+/v7+/v7+/v7+/v7+/v78/PwDAwMD/vz+/kz+tP7w/in3AfcB9wH3AfcB9wH3AfcB9wH3AfcB
9wH3AfcB9wH3AfcB9wH3AfcB9wH3AfcB9wH3AfcB9wH3AfcB9wECAgL+QAL3AfcBAAAAAAAA
o1RFVhlpAAAAjgEAAIwGAAAAAAQAAAAAAAEALP///////wAAAAAA8AkxMjcuMC4wLjEEY21vbikq
RTc0ODU4REI4NkVCQTIwQkMzM0QwQUVDQUU4QTgxMDhDNTZCMTdGQQICAgICAgICAgICAgICAgIC
AgICAgICAgICAgICAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQAAAAAA8Alsb2NhbGhvc3QE
Y21vbikqRTc0ODU4REI4NkVCQTIwQkMzM0QwQUVDQUU4QTgxMDhDNTZCMTdGQQICAgICAgICAgIC
AgICAgICAgICAgICAgICAgICAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQAAAAAA8A0xOTIu
MTY4LjEuMjI1BGNtb24pKkU3NDg1OERCODZFQkEyMEJDMzNEMEFFQ0FFOEE4MTA4QzU2QjE3RkEC
AgICAgICAgICAgICAgICAgICAgICAgICAgICAgEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQE=
'/*!*/;     ------这中间是一段代码
# at 1676
#151113 11:10:27 server id 105  end_log_pos 1746        Query   thread_id=11    exec_time=0     error_code=0
SET TIMESTAMP=1447384227/*!*/;
/*!\C utf8mb4 *//*!*/;
SET @@session.character_set_client=33,@@session.collation_connection=33,@@session.collation_server=45/*!*/;
COMMIT
/*!*/;
# at 1746
#151113 11:38:08 server id 106  end_log_pos 1784        GTID 0-106-346
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
以上我们可以清楚的看到。没有任何SQL语句,也没有看到修改的具体数据。

MariaDB [(none)]> show binlog events in 'mysql-bin.000042' from 1278 limit 4 \G
*************************** 1. row ***************************
   Log_name: mysql-bin.000042
        Pos: 1278
 Event_type: Delete_rows_v1
  Server_id: 105
End_log_pos: 1676
       Info: table_id: 4 flags: STMT_END_F
*************************** 2. row ***************************
   Log_name: mysql-bin.000042
        Pos: 1676
 Event_type: Query
  Server_id: 105
End_log_pos: 1746
       Info: COMMIT
*************************** 3. row ***************************
   Log_name: mysql-bin.000042
        Pos: 1746
 Event_type: Gtid
  Server_id: 106
End_log_pos: 1784
       Info: GTID 0-106-346
*************************** 4. row ***************************
   Log_name: mysql-bin.000042
        Pos: 1784
 Event_type: Query
  Server_id: 106
End_log_pos: 1866
       Info: use `db`; truncate table t

如何让ROW格式的日志,显示他的庐山真面目呢:

mysqlbinlog --base64-output=decode-rows -v /usr/local/mysql/binlog/mysql-bin.000042 >/tmp/000042.sql

--base64-output=decode-rows:就是解析BINLOG '后面那一长串字符的。-v显示解析结果。
===========================================================================================================
# at 1904
# at 1945
#151113 11:38:35 server id 106  end_log_pos 1945        Table_map: `db`.`t` mapped to number 92
#151113 11:38:35 server id 106  end_log_pos 1982        Write_rows: table id 92 flags: STMT_END_F
### INSERT INTO `db`.`t`
### SET
###   @1=2
###   @2='ll'
# at 1982
#151113 11:38:35 server id 106  end_log_pos 2009        Xid = 6968
COMMIT/*!*/;
# at 2009
#151113 15:12:51 server id 106  end_log_pos 2047        GTID 0-106-348
/*!100001 SET @@session.gtid_seq_no=348*//*!*/;
BEGIN
/*!*/;
# at 2047
# at 2088
#151113 15:12:51 server id 106  end_log_pos 2088        Table_map: `db`.`t` mapped to number 92
#151113 15:12:51 server id 106  end_log_pos 2125        Write_rows: table id 92 flags: STMT_END_F
### INSERT INTO `db`.`t`
### SET
###   @1=4
###   @2='oo'
# at 2125
#151113 15:12:51 server id 106  end_log_pos 2152        Xid = 6978
COMMIT/*!*/;
DELIMITER ;
=========================================================================================================================
主要是方便我们分析,解析出了执行的数据。


查看日志:/usr/local/mysql/3307/bin/mysqlbinlog --no-defaults --base64-output=decode-rows -v /log/binlog/3307/bin.000004 >/tmp/3307.sql
# at 2202
#160523 17:37:59 server id 11  end_log_pos 2246 CRC32 0xf5999189        Table_map: `test`.`t` mapped to number 211
# at 2246
#160523 17:37:59 server id 11  end_log_pos 2286 CRC32 0x308897a3        Write_rows: table id 211 flags: STMT_END_F
### INSERT INTO `test`.`t`
### SET
###   @1=1001
# at 2286
#160523 17:37:59 server id 11  end_log_pos 2317 CRC32 0xdc9367e9        Xid = 201
COMMIT/*!*/;
# at 2317
#160523 17:40:19 server id 11  end_log_pos 2382 CRC32 0xc99d11d2        GTID    last_committed=9        sequence_number=10
SET @@SESSION.GTID_NEXT= '3c7ce82b-18be-11e6-ba0a-0050569e70f2:143'/*!*/;
# at 2382
#160523 17:40:19 server id 11  end_log_pos 2459 CRC32 0x2ddc09f8        Query   thread_id=18    exec_time=0     error_code=0
SET TIMESTAMP=1463996419/*!*/;
BEGIN
/*!*/;
# at 2459
# at 2509
#160523 17:40:19 server id 11  end_log_pos 2553 CRC32 0xe2ad52d0        Table_map: `test`.`t` mapped to number 212
# at 2553
#160523 17:40:19 server id 11  end_log_pos 2593 CRC32 0xffb7d75d        Write_rows: table id 212 flags: STMT_END_F
### INSERT INTO `test`.`t`
### SET
###   @1=9999
# at 2593
#160523 17:40:19 server id 11  end_log_pos 2624 CRC32 0x2b09846c        Xid = 223
COMMIT/*!*/;
# at 2624
#160523 17:40:27 server id 11  end_log_pos 2689 CRC32 0x46dd7674        GTID    last_committed=10       sequence_number=11
SET @@SESSION.GTID_NEXT= '3c7ce82b-18be-11e6-ba0a-0050569e70f2:144'/*!*/;
# at 2689
#160523 17:40:27 server id 11  end_log_pos 2766 CRC32 0x657dd679        Query   thread_id=18    exec_time=0     error_code=0
SET TIMESTAMP=1463996427/*!*/;
BEGIN
/*!*/;
# at 2766
# at 2816
#160523 17:40:27 server id 11  end_log_pos 2860 CRC32 0xe0b8b86f        Table_map: `test`.`t` mapped to number 212
# at 2860
#160523 17:40:27 server id 11  end_log_pos 2900 CRC32 0x83236ed8        Write_rows: table id 212 flags: STMT_END_F
### INSERT INTO `test`.`t`
### SET
###   @1=8888
# at 2900
#160523 17:40:27 server id 11  end_log_pos 2931 CRC32 0xbf47abdd        Xid = 224
COMMIT/*!*/;

我们这里知道现在数据库中的数据是1001最后一条,所以,这里我们就知道从哪里开始恢复。

/data0/mysql/bin/mysqlbinlog --start-datetime='2017-12-20 18:00:00' --base64-output=decode-rows -vv /data0/mysql/data/mysql-bin.000020 >20.sql

 /usr/local/mysql/3307/bin/mysqlbinlog --no-defaults --start-position=2317 /log/binlog/3307/bin.000004 | mysql -uroot -p -S /usr/local/mysql/3307/mysql.sock
执行之后,发现并没有恢复。

实际上,是gtid的问题:
/usr/local/mysql/3307/bin/mysqlbinlog --no-defaults --skip-gtids --stop-position=1498 /log/binlog/3307/bin.000010 | mysql -uroot -p -S /usr/local/mysql/3307/mysql.sock
添加--skip-gtids就可以完成。

[root@test11 ~]# /usr/local/mysql/3307/bin/mysqlbinlog --no-defaults --base64-output=decode-rows -v --start-position=2317 -vv /log/binlog/3307/bin.000004 | grep "insert into"
# insert into t values(9999)
# insert into t values(8888)

[root@test11 ~]# /usr/local/mysql/3307/bin/mysqlbinlog --no-defaults --base64-output=decode-rows --start-position=2317 -vv /log/binlog/3307/bin.000004 | grep "insert into" | sed -e 's/#//g'|sed -e 's/)/);/g'
 insert into t values(9999);
 insert into t values(8888);

Mariadb10.0是OK的,但是mysql 5.7.12恢复不了。


5.7.12  指定--stop-position的时候,需要注意:指定的pos位置。
   Log_name: bin.000010
        Pos: 947
 Event_type: Query
  Server_id: 11
End_log_pos: 1024
       Info: BEGIN
*************************** 17. row ***************************
   Log_name: bin.000010
        Pos: 1024
 Event_type: Rows_query
  Server_id: 11
End_log_pos: 1084
       Info: # insert into tab(name) values ('pol')
*************************** 18. row ***************************
   Log_name: bin.000010
        Pos: 1084
 Event_type: Table_map
  Server_id: 11
End_log_pos: 1133
       Info: table_id: 112 (test.tab)
*************************** 19. row ***************************
   Log_name: bin.000010
        Pos: 1133
 Event_type: Write_rows
  Server_id: 11
End_log_pos: 1177
       Info: table_id: 112 flags: STMT_END_F
*************************** 20. row ***************************

像上面这样,指定的--stop-position只能是1177这个位置。否则要报告警:
WARNING: The range of printed events ends with a row event or a table map event that does not have the STMT_END_F flag set. This might be because the last statement was
not fully written to the log, or because you are using a --stop-position or --stop-datetime that refers to an event in the middle of a statement. The event(s) from the partial statement
have not been written to output.
也就是说,需要指定到STMT_END_F flag的位置。

====================
最好不要这样恢复:
shell> mysqlbinlog binlog.000001 | mysql -u root -p
shell> mysqlbinlog binlog.000002 | mysql -u root -p
处理二进制日志使用的服务器不同的连接这样可能会导致问题,如果第一个日志文件包含一个CREATE TEMPORARY TABLE语句和第二日志包含使用临时表的语句。
当第一个mysql进程终止时,服务器会删除临时表。当第二个mysql进程尝试使用该表时,服务器报告“unknown table”。
为了避免这样的问题,使用一个连接来执行想要处理的所有二进制日志中的内容。这里就是这样做的一种方法:

shell> mysqlbinlog binlog.000001 binlog.000002 | mysql -u root -p

另一种方法是写的所有日志到一个文件中,然后处理文件:
shell> mysqlbinlog binlog.000001 >  /tmp/statements.sql
shell> mysqlbinlog binlog.000002 >> /tmp/statements.sql
shell> mysql -u root -p -e "source /tmp/statements.sql"

 

你可能感兴趣的:(备份恢复)