一 前言
bin log 是 MySQL 的一种二进制日志,记录引起或可能(更新删除没有匹配的记录)引起数据库变动的事件信息。
bin log 以事件形式记录,不是事务日志。对于非事务表的操作,每当语句执行完成则直接写入;对于事务表的操作则会在事务提交时(先记录到缓存中)一次性写入。
二 用途
主要用于数据备份和恢复、主从同步、审计、数据异构、基于数据的任务分发等等。
2.1 主从同步
slave 从服务器 I/O 线程将主服务器的二进制日志读取过来记录到从服务器本地文件,然后SQL线程会读取 relay log 日志的内容并应用到从服务器,从而使从服务器和主服务器的数据保持一致。
2.2 数据异构
把数据按需(数据结构、存取方式、存取形式)异地构建存储。
2.3 基于数据的任务分发
多服务依赖相同数据而不侵占其他服务的实现。
三 开启 binlog
MariaDB/MySQL默认没有启动二进制日志,要启用二进制日志使用 --log-bin=[on|off|file_name] 或配置文件中 log-bin=[on|filename]选项指定。
如果没有给定file_name,则默认为datadir下的主机名加"-bin",并在后面跟上一串数字表示日志序列号,如果给定的日志文件中包含了后缀(logname.suffix)将忽略后缀部分。
mysqld 会创建一个二进制日志索引文件,当二进制日志文件滚动的时候会向该文件中写入对应的信息。所以该文件包含所有使用的二进制日志文件的文件名。
默认情况下该文件与二进制日志文件的文件名相同,扩展名为’.index’。要指定该文件的文件名使用 --log-bin-index[=file_name] 选项。
当 mysqld 在运行时不应手动编辑该文件,免得 mysqld 变得混乱或重启失败。
-rw-r----- 1 mysql mysql 340 Jun 26 09:25 bin-log.000003
-rw-r----- 1 mysql mysql 17 Jun 26 09:25 bin-log.index
- 最大值通过变量 max_binlog_size 设置(默认值为1G)。但由于二进制日志可能是基于事务来记录的(如innodb表类型),而事务是绝对不可能也不应该跨文件记录的,如果正好二进制日志文件达到了最大值但事务还没有提交则不会滚动日志,而是继续增大日志,所以 max_binlog_size 指定的值和实际的二进制日志大小不一定相等。
- 出于安全和性能考虑,极不建议将二进制日志和datadir放在同一磁盘上。
四 查看 binlog
MySQL 中查看二进制日志的方法主要有几种。
4.1 mysqlbinlog 命令
格式:mysqlbinlog [option] log-file1 log-file2…
# 常用选项
-d,--database=name:只查看指定数据库的日志操作
-o,--offset=#:忽略掉日志中的前n个操作命令
-r,--result-file=name:将输出的日志信息输出到指定的文件中,使用重定向也一样可以。
-s,--short-form:显示简单格式的日志,只记录一些普通的语句,会省略掉一些额外的信息如位置信息和时间信息以及基于行的日志。可以用来调试,生产环境千万不可使用
--set-charset=char_name:在输出日志信息到文件中时,在文件第一行加上set names char_name
--start-datetime,--stop-datetime:指定输出开始时间和结束时间内的所有日志信息
--start-position=#,--stop-position=#:指定输出开始位置和结束位置内的所有日志信息
-v,-vv:显示更详细信息,基于row的日志默认不会显示出来,此时使用-v或-vv可以查看
在一个新开的实例中创建一个 test 数据库、test 数据表并插入一条数据为例子查看 bin log 记录情况。
root@565bf1f8399a:/var/lib/mysql# mysqlbinlog bin-log.000002
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=1*/;
/*!40019 SET @@session.max_insert_delayed_threads=0*/;
/*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE,COMPLETION_TYPE=0*/;
DELIMITER /*!*/;
1. 初始信息,可以看到记录了时间和位置信息(at 4)。
# at 4
#220626 9:43:32 server id 1 end_log_pos 256 CRC32 0x1485ea02 Start: binlog v 4, server v 10.5.12-MariaDB-1:10.5.12+maria~focal-log created 220626 9:43:32 at startup
# Warning: this binlog is either in use or was not closed properly.
ROLLBACK/*!*/;
BINLOG '
RCq4Yg8BAAAA/AAAAAABAAABAAQAMTAuNS4xMi1NYXJpYURCLTE6MTAuNS4xMittYXJpYX5mb2Nh
bC1sb2cAAAAAAAAAAABEKrhiEzgNAAgAEgAEBAQEEgAA5AAEGggAAAAICAgCAAAACgoKAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAEEwQADQgICAoKCgEC6oUU
'/*!*/;
# at 256
#220626 9:43:32 server id 1 end_log_pos 299 CRC32 0x6468dccc Gtid list [0-1-6]
# at 299
#220626 9:43:32 server id 1 end_log_pos 340 CRC32 0x418a9234 Binlog checkpoint bin-log.000002
# at 340
#220626 9:44:47 server id 1 end_log_pos 382 CRC32 0x5ad3b3b6 GTID 0-1-7 ddl
/*!100101 SET @@session.skip_parallel_replication=0*//*!*/;
/*!100001 SET @@session.gtid_domain_id=0*//*!*/;
/*!100001 SET @@session.server_id=1*//*!*/;
/*!100001 SET @@session.gtid_seq_no=7*//*!*/;
# at 382
#220626 9:44:47 server id 1 end_log_pos 471 CRC32 0xd1fa3319 Query thread_id=4 exec_time=0 error_code=0
SET TIMESTAMP=1656236687/*!*/;
SET @@session.pseudo_thread_id=4/*!*/;
SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=0, @@session.unique_checks=1, @@session.autocommit=1, @@session.check_constraint_checks=1, @@session.sql_if_exists=0/*!*/;
SET @@session.sql_mode=1411383296/*!*/;
SET @@session.auto_increment_increment=1, @@session.auto_increment_offset=1/*!*/;
/*!\C utf8mb4 *//*!*/;
SET @@session.character_set_client=45,@@session.collation_connection=45,@@session.collation_server=45/*!*/;
SET @@session.lc_time_names=0/*!*/;
SET @@session.collation_database=DEFAULT/*!*/;
2. 创建 test 数据库。
CREATE DATABASE `test`
/*!*/;
# at 471
#220626 9:45:20 server id 1 end_log_pos 513 CRC32 0x54820548 GTID 0-1-8 ddl
/*!100001 SET @@session.gtid_seq_no=8*//*!*/;
# at 513
#220626 9:45:20 server id 1 end_log_pos 711 CRC32 0x408eb615 Query thread_id=4 exec_time=0 error_code=0
3. 创建 test 数据表。
use `test`/*!*/;
SET TIMESTAMP=1656236720/*!*/;
CREATE TABLE `test`.`test` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL DEFAULT '',
PRIMARY KEY (`id`)
)
/*!*/;
# at 711
#220626 9:45:46 server id 1 end_log_pos 753 CRC32 0x29248341 GTID 0-1-9 trans
/*!100001 SET @@session.gtid_seq_no=9*//*!*/;
开启事务
START TRANSACTION
/*!*/;
# at 753
#220626 9:45:46 server id 1 end_log_pos 785 CRC32 0x1538c98f Intvar
自增锁
SET INSERT_ID=1/*!*/;
# at 785
#220626 9:45:46 server id 1 end_log_pos 903 CRC32 0xb1a7f07c Query thread_id=4 exec_time=0 error_code=0
SET TIMESTAMP=1656236746/*!*/;
4. 插入一条数据
INSERT INTO `test`.`test`(`name`) VALUES ('测试')
/*!*/;
# at 903
#220626 9:45:46 server id 1 end_log_pos 934 CRC32 0x503162f1 Xid = 51
COMMIT/*!*/;
DELIMITER ;
# End of log file
ROLLBACK /* added by mysqlbinlog */;
/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/;
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/;
4.2 show binary logs
用于查看当前使用了哪些二进制日志文件。
- cat mysql-bin.index
- show {binary | master} logs
4.3 show binlog events
用于查看日志中进行了哪些操作,show binlog events in ‘bin-log’;
mysql> show binlog events in 'bin-log.000002';
+----------------+-----+-------------------+-----------+-------------+-------------------------------------------------------------------------------------------------------------------------------------------------+
| Log_name | Pos | Event_type | Server_id | End_log_pos | Info |
+----------------+-----+-------------------+-----------+-------------+-------------------------------------------------------------------------------------------------------------------------------------------------+
| bin-log.000002 | 4 | Format_desc | 1 | 256 | Server ver: 10.5.12-MariaDB-1:10.5.12+maria~focal-log, Binlog ver: 4 |
| bin-log.000002 | 256 | Gtid_list | 1 | 299 | [0-1-6] |
| bin-log.000002 | 299 | Binlog_checkpoint | 1 | 340 | bin-log.000002 |
| bin-log.000002 | 340 | Gtid | 1 | 382 | GTID 0-1-7 |
| bin-log.000002 | 382 | Query | 1 | 471 | CREATE DATABASE `test` |
| bin-log.000002 | 471 | Gtid | 1 | 513 | GTID 0-1-8 |
| bin-log.000002 | 513 | Query | 1 | 711 | use `test`; CREATE TABLE `test`.`test` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL DEFAULT '',
PRIMARY KEY (`id`)
) |
| bin-log.000002 | 711 | Gtid | 1 | 753 | BEGIN GTID 0-1-9 |
| bin-log.000002 | 753 | Intvar | 1 | 785 | INSERT_ID=1 |
| bin-log.000002 | 785 | Query | 1 | 903 | use `test`; INSERT INTO `test`.`test`(`name`) VALUES ('测试') |
| bin-log.000002 | 903 | Xid | 1 | 934 | COMMIT /* xid=51 */ |
+----------------+-----+-------------------+-----------+-------------+-------------------------------------------------------------------------------------------------------------------------------------------------+
# 可以指定起始位置。同样,起始位置必须指定正确,不能指定不存在的位置。
mysql> show binlog events in 'bin-log.000002' from 711;
+----------------+-----+------------+-----------+-------------+-----------------------------------------------------------------+
| Log_name | Pos | Event_type | Server_id | End_log_pos | Info |
+----------------+-----+------------+-----------+-------------+-----------------------------------------------------------------+
| bin-log.000002 | 711 | Gtid | 1 | 753 | BEGIN GTID 0-1-9 |
| bin-log.000002 | 753 | Intvar | 1 | 785 | INSERT_ID=1 |
| bin-log.000002 | 785 | Query | 1 | 903 | use `test`; INSERT INTO `test`.`test`(`name`) VALUES ('测试') |
| bin-log.000002 | 903 | Xid | 1 | 934 | COMMIT /* xid=51 */ |
+----------------+-----+------------+-----------+-------------+-----------------------------------------------------------------+
4. show master status
用于显示主服务器中的二进制日志信息。如果是主从结构,它只会显示主从结构中主服务器的二进制日志信息。
可以查看到当前正在使用的日志及下一事件记录的开始位置,还能查看到哪些数据库需要记录二进制日志,哪些数据库不记录二进制日志。
mysql> show master status;
+----------------+----------+--------------+------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+----------------+----------+--------------+------------------+
| bin-log.000002 | 934 | | |
+----------------+----------+--------------+------------------+
1 row in set (0.00 sec)
五 刷新 binlog
可使用命令:mysqladmin -uroot -p refresh
六 删除 binlog
1. reset master
删除所有日志,并让日志文件重新从000001开始。
2. PURGE { BINARY | MASTER } LOGS { TO ‘log_name’ | BEFORE datetime_expr }
purge master logs to “binlog_name.00000X” 将会清空00000X之前的所有日志文件,正在使用中的日志文件中,将无法进行 purge。
1.例如删除000004之前的日志文件:
mysql> purge master logs to "mysql-bin.000004";
2.删除指定日期之前的所有日志:
purge master logs before '2022-06-25 12:36:40';
3. --expire_logs_days=N
使用–expire_logs_days=N选项指定过了多少天日志自动过期清空。
七 记录格式
MySQL 5.1之前,只有一种基于语句 statement 形式的日志记录格式。即将所有的相关操作记录为SQL语句形式。
但是这样的记录方式对某些特殊信息无法同步记录,例如uuid,now()等这样动态变化的值。
MySQL 5.1开始,MySQL支持 statement、row、mixed 三种形式的记录方式,由变量 binlog_format 来指定。
- row 形式是基于行来记录,也就是将相关行的每一列的值都在日志中保存下来,这样的结果会导致日志文件变得非常大,但是保证了动态值的确定性。 innodb引擎的创始人之一推荐使用row格式。
- mixed 形式,如何记录日志由MySQL自己来决定。
以本节第二小节内容为例,各种格式如下所示:
7.1 statement
7.2 row格式
记录内容不可直接阅读,需要加 -vv 选项。记录的非常详细,所以日志文件相对大。
7.3 mixed格式
默认会采用 statement 的方式记录,部分特殊情况会采用row的形式来记录日志。
- 存储引擎为NDB,对表的DML操作都会以row的格式记录。
- 使用了uuid()、user()、current_user()、found_rows()、row_count()等不确定函数。
- 使用了insert delay语句。
- 用了临时表。
mysqlbinlog mysql-bin.000001 -vv
八 刷新时机
事务执行过程中,先把日志写到 bin log cache,事务提交的时候,再把 bin log cache 写到 bin log 文件中。
一个事务的 bin log 不能被拆开,无论这个事务多大,也要确保一次性写入,所以系统会给每个线程分配一个块内存作为 bin log cache。刷盘流程如下:
需要注意上图中标红的两个写操作,设置不当可能导致日志丢失。两个写入时机由参数:sync_binlog 控制。
- sync_binlog=0 :每次提交事务都只写到 page cache,由系统自行判断什么时候执行fsync 到 binlog 文件。若机器宕机,page cache 里面的 binglog 会丢失。
- sync_binlog=1:每次提交事务都会执行 fsync,要保证事务的一致性和持久性的时候,必须将sync_binlog的值设置为1。
- sync_binlog=N(N >= 1):每次提交事务都只写到 page cache,但累积N个事务后才fsync。若机器宕机,page cache 里面的 N个事务的 binglog 会丢失。
九 相关参数
#指定是否启用记录二进制日志或者指定一个日志路径(路径不能加.否则.后的被忽略).
log_bin = {on | off | base_name}
#指定是否启用记录二进制日志,只有在log_bin开启的时候才有效
sql_log_bin ={ on | off }
#指定自动删除二进制日志的时间,即日志过期时间
expire_logs_days =
#明确指定要记录日志的数据库
binlog_do_db =
#指定不记录二进制日志的数据库
binlog_ignore_db =
#指定mysql-bin.index文件的路径
log_bin_index =
#指定二进制日志基于什么模式记录
binlog_format = { mixed | row | statement }
# MySQL5.6.2添加了该变量,当binlog format为row时,默认不会记录row对应的SQL语句,设置为1或其他true布尔值时会记录,但需要使用mysqlbinlog -v查看,这些语句是被注释的,恢复时不会被执行。
binlog_rows_query_log_events = { 1|0 }
#指定二进制日志文件最大值,超出指定值将自动滚动。但由于事务不会跨文件,所以并不一定总是精确。
max_binlog_size =
#基于事务类型的日志会先记录在缓冲区,当达到该缓冲大小时这些日志会写入磁盘。
binlog_cache_size = 32768
#指定二进制日志缓存最大大小,硬限制。默认4G,建议不要改。
max_binlog_cache_size =
#一般等同于且决定binlog_cache_size大小,所以修改缓存大小时只需修改这个而不用修改binlog_cache_size
binlog_stmt_cache_size = 32768
#binlog_cache_size的值
binlog_stmt_cache_disk_use:
#使用缓存写二进制日志的次数(这是一个实时变化的统计值)
binlog_cache_use:
#使用临时文件写二进制日志的次数,当日志超过了binlog_cache_size的时候会使用临时文件写日志,如果该变量值不为0,则考虑增大binlog_cache_size的值。
binlog_cache_disk_use:
#使用缓存写二进制日志的次数
binlog_stmt_cache_use:
#使用临时文件写二进制日志的次数,当日志超过了binlog_cache_size的时候会使用临时文件写日志,如果该变量值不为0,则考虑增大
#这个参数直接影响mysql的性能和完整性
sync_binlog = { 0 | n }
sync_binlog=0: 不同步,日志何时刷到磁盘由FileSystem决定,这个性能最好。
sync_binlog=n: 每写n次事务(注意,对于非事务表来说,是n次事件,对于事务表来说,是n次事务,而一个事务里可能包含多个二进制事件),MySQL将执行一次磁盘同步指令fdatasync()将缓存日志刷新到磁盘日志文件中。Mysql中默认的设置是sync_binlog=0,即不同步,这时性能最好,但风险最大。一旦系统奔溃,缓存中的日志都会丢失。
要保证事务的一致性和持久性的时候,必须将sync_binlog的值设置为1。
因为每次事务提交都会写入二进制日志,设置为1就保证了每次事务提交时二进制日志都会写入到磁盘中,从而立即被从服务器复制过去。
要保证事务的一致性和持久性的时候,必须将sync_binlog的值设置为1。
因为每次事务提交都会写入二进制日志,设置为1就保证了每次事务提交时二进制日志都会写入到磁盘中,从而立即被从服务器复制过去。
十 恢复重做
指定二进制日志的起始位置(可指定终止位置)并将其保存到 sql 文件中,由 mysql 命令来载入恢复即可。当然也可以直接通过管道送给mysql。
基于位置来恢复还是基于时间点来恢复,这两种行为都可以。选择时间点来恢复比较直观些,并且跨日志文件恢复时更方便。
mysqlbinlog --stop-datetime="2022-06-25 12:17:44" /tmp/mysql-bin.000005 | mysql -u user -p password
# 恢复多个二进制日志文件时:
mysqlbinlog mysql-bin.[*] | mysql -uroot -p password
# 入到一个文件中后恢复。
mysqlbinlog mysql-bin.000001 > /tmp/1.sql
mysqlbinlog mysql-bin.000002 >>/tmp/2.sql
mysql -u root -p password -e "source /tmp/1.sql"