bin log 是 MySQL 的一种二进制日志,记录引起或可能(更新删除没有匹配的记录)引起数据库变动的事件信息。
bin log 以事件形式记录,不是事务日志。对于非事务表的操作,每当语句执行完成则直接写入;对于事务表的操作则会在事务提交时(先记录到缓存中)一次性写入。
主要用于数据备份和恢复、主从同步、审计、数据异构、基于数据的任务分发等等。
slave 从服务器 I/O 线程将主服务器的二进制日志读取过来记录到从服务器本地文件,然后SQL线程会读取 relay log 日志的内容并应用到从服务器,从而使从服务器和主服务器的数据保持一致。
把数据按需(数据结构、存取方式、存取形式)异地构建存储。
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
MySQL 中查看二进制日志的方法主要有几种。
格式: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*/;
用于查看当前使用了哪些二进制日志文件。
用于查看日志中进行了哪些操作,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)
可使用命令:mysqladmin -uroot -p refresh
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 来指定。
以本节第二小节内容为例,各种格式如下所示:
记录内容不可直接阅读,需要加 -vv 选项。记录的非常详细,所以日志文件相对大。
默认会采用 statement 的方式记录,部分特殊情况会采用row的形式来记录日志。
mysqlbinlog mysql-bin.000001 -vv
事务执行过程中,先把日志写到 bin log cache,事务提交的时候,再把 bin log cache 写到 bin log 文件中。
一个事务的 bin log 不能被拆开,无论这个事务多大,也要确保一次性写入,所以系统会给每个线程分配一个块内存作为 bin log cache。刷盘流程如下:
需要注意上图中标红的两个写操作,设置不当可能导致日志丢失。两个写入时机由参数:sync_binlog 控制。
#指定是否启用记录二进制日志或者指定一个日志路径(路径不能加.否则.后的被忽略).
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"