binary log是逻辑日志。主要记录alter,drop,create,insert,update,delete
语句
记录了对mysql数据库的执行更改操作,但是不会记录select,show
语句。
但像select,show这种查询语句可以在general_log中查看。
这个参数的含义是binlog刷写到磁盘的最大文件大小。在mysql 5.0之后是1G。超出则会创建文件,后缀名+1的文件。
此参数会记录所有未提交事务所产生的binlog大小。可以理解它就是一个binlog的缓冲区。当有commit操作时,就会将缓冲区的binlog(binlog cache)刷到binlog 文件。默认是32k. binlog_cache_size是基于会话的。如果当一个用户写入数据所产生的binlog大于binlog_cache_size时,mysql就会将缓冲中的binlog日志写入到临时文件中,所以此值不能设置的太小。
我们可以通过binlog_cache_disk_use
和binlog_cache_use
判断该binlog_cache_size设置的值是否合适。
binlog_cache_disk_use
: 用于记录binlog采用临时文件存储日志的次数。
binlog_cache_use
: 用于记录了使用缓冲写(binlog_cache_size
)二进制日志的次数。
双一参数其中的一个.用于记录binlog刷盘的时机。
如上图所示:
当使用innodb存储引擎时,commit事物之前,由于sync_binlog=1,且设置的binlog_cache_size也足以容纳update语句所产生的binlog时,这时binlog就会马上刷写到磁盘,而假如在这时server crash,数据库在下次重启时进行恢复,会将update语句所在事务回滚,但是二进制日志已经写入,这就会造成内存和磁盘的数据不一致的情况。可以通过innodb_support_xa=1
来解决。
记录binlog的格式。可选择值为STATEMENT,ROW,MIXED
.在mysql5.1版本之前,没有这个参数,所有的binlog都是记录的STATEMENT。
STATEMENT
二进制日志文件中记录的是SQL语句。
ROW
在mysql5.1版本之后,默认是ROW模式,二进制日志文件中记录的是数据的变化,这是逻辑的。一般设置为ROW后,会比设置为statement文件更大。那是因为statement模式记录的只是一条sql语句,例如update t1 set t1.name = 'xx' where t1.id =1;
,而row模式,则是将t1表将所有字段都显示出来(需要使用mysqlbinlog工具解析一下)。
MIXED
是上面两种模式的混合。MySQL采用默认的STATEMENT模式记录二进制日志,但是在某些情况下,MySQL会采用ROW模式。
a. 表的引擎为NDB
b. 使用UUID(),NOW(),CURRENT_USER()等不确定的函数
c. 使用insert delay语句
d. 使用了临时表
e. 使用了用户定义函数(UDF)
mysql复制相关参数,表示主库中有哪些库可以同步到从库
mysql复制相关参数,表示主库中有哪些库不可以同步到从库
如果当前数据库是slave角色,则表示 要将从master取得并执行的二进制日志写入自己的二进制日志文件中去。
查看当前数据库已经运行的位置点
show master status
查看指定binlog 文件的events
show binlog events in 'mysql-bin.00001'
查看指定binlog 文件的events
pager less
: 在mysql内部使用,表示开启less的方式查看以后的内容
解析binlo
mysqlbinlog --base64-output=decode-rows -vv mysql-bin.00001
场景:假如在数据库中这样一段数据
create database bin; --> positionc 4
use bin;
create table t(id int);
insert into t values (1);
insert into t values (2);
insert into t values (3); -->position 66
# 执行误操作
drop database bin;
需要恢复到删库之前的状态
mysqlbinlog --start-position=4 --stop-position=66 xxx >/tmp/bak.sql
mysqlbinlog --start-datetime=xxx --stop-datetime=xxx filename > /tmp/bak.sql
set sql_log_bin=0;-- 在当前窗口中以后的操作都不记录二进制日志
source /tmp/bak.sql;
set sql_log_bin=1;
- 日志跨度时间长,量比较大
如果要恢复的日志起始点和结束点数据多,且相隔的时间比较长,DBA很难定位 。
解决方法:我们可以利用备份+日志的方式进行恢复,如果没有备份的话,就比较麻烦了- 跨多个文件
恢复的日志起始位置和结束位置横跨多个文件
解决方法:
a. 假如使用Position号恢复数据的话,需要分段恢复,比较麻烦
b. 假如按时间截取的话,恢复的日志会不准确 。因为/tmp/bak.sql里面存储的不只是这一张表的数据 ,而是有很多张表,在这一段日志中肯定会有其他表的数据,并且这个文件中包含其他表的事务也不一定是完整的。而且如果其他表没有主键的话,再恢复时也会报错。
c. 按gtid截取
d. 使用my2sql工具分析/tmp/bak.sql文件,这个工具可以将表级别的数据转换成sql
gtid-mode=on
enforce-gtid-consistency=true
log-slave-updates=1
开启gtid后,截取二进制日志方法如下:
--include-gtids='xxx' # 包含哪个gtid的信息
--exclude-gtids='xxx' # 除去该gtid,将剩余的信息输出
--skip-gtids # gtid幂等性(将备份的gtid删除,开启此选项后,输出的备份文件就不会包含gtid_next的信息了。只要开启gtid,无论用哪种方式将数据导出,都要加这个选项)
position方式备份数据也要加这个参数
expire_logs_days
expire_logs_days
(binlog 过期时间,以天为单位)binlog_expire_logs_seconds
expire_logs_days
选一个。purge binary logs to 'log name'
reset master
我们知道,innodb存储引擎是以页来存储数据的,当对页进行增删改时,首先先将相关页面加载到buffer pool中,使其成为脏页,再根据某种策略将脏页刷新到磁盘。假如在这时脏页还没有刷写到磁盘时,数据库宕机,那么修改的数据岂不就丢失了吗!!!故此,innodb存储引擎引用redo,当在buffer pool中修改数据页后,先将修改的页面日志写入到redo中(即redo里存储的是数据页的变化),即使数据库宕机,也可以利用redo log将数据库恢复。
redo日志会把事务在执行过程中对数据库所做的所有修改都记录下来,在这后系统因崩溃而重启后,可以把事务所做的任何修改都恢复过来。
是指每个redo log file的大小,默认是48M。
是指在内存中redo buffer 的大小,默认是16M,innodb将redo buffer分为多个512字节大小的块,也就是有32768个这样大小的块。
是指redo log file的家目录,也就是当前实例的数据目录
是指redo log file的个数,默认为2,最大为100。从ib_logfile0开始,以此类推。
redo log刷写磁盘的时机,可以为0,1,2
当此值为0时:
表示当事务提交时,不做日志写入操作,而是每秒钟将日志缓冲的数据写入到os cache中并且每秒fsync到磁盘一次。意思就是说在1秒钟之内不管生成多少个事务都会将数据先写入到os cache中然后再写入到磁盘,就算是丢失数据也只是会丢失1秒钟的数据 。
当此值为1时:
每次事务提交执行commit之后就将日志写入,先从内存中写入到os cache中(这个过程叫做commit os缓冲),再将数据从os cache立即flush到磁盘。当值为1时,不会丢失数据,不过访问磁盘的次数有些增多。
当此值为2时:
每次事务提交引起写入os cache中,并每秒写入到磁盘(只要是在1秒钟生成的数据都会写入到磁盘中)
字段 | 说明 |
---|---|
type | 这条redo的类型 |
space ID | 表空间ID |
page number | 页号 |
data | 真实数据 |
InnoDB存储引擎采用的是第一种方式,因为第二种有点浪费空间了。
我们知道,log buffer
是由多个512字节大小的块组成,所以在磁盘中的log file
也是由多个512字节大小的块组成,redo log
写入log buffer
是顺序写入的,所以从log buffe
r写入log file
也应该是顺序写入的。
如上图所示:在log file header
字段中比较重要的有:
LOG HEADER START LEN:
标记本redo 日志文件偏移量为2048字节处所对应的lsn值;
LOG BLOCK CHECKSUM:
该block块的checksum校验值 .
字段 | 说明 |
---|---|
LOG CHECKPOINT NO |
服务器执行checkpoint的编号,每执行一次,该值加1 |
LOG CHECKPOINT LSN |
log buffer向log file最后刷写的lsn值,也是数据库崩溃恢复的起点 |
LOG CHECKPOINT OFFSET |
上个属性中的lsn值在redo日志文件组中的偏移量 |
LOG CHECKPOINT LOG BUF SIZE |
服务器在执行checkpoint 时对应的log buffer 的大小 |
LOG BLOCK CHECKSUM |
block的校验值 |
checkpoint1和checkpoint2包含的字段相同。
innodb_log_file_size
,innodb_log_group_dir,innodb_log_files_in_group
innodb_log_file_size
innodb_log_group_dir
innodb_log_files_in_group
INNODB存储引擎一共有53种redo日志的类型。在这里只拿几种常见的举例。
MLOG_8BYTE
我们知道,在innodb系统表中维护一个MAX_ROW_ID
的全局变量,每当向某个包含row_id
的隐藏列中插入一条数据时,就会更新MAX_ROW_ID
的值。当这个全局变量是256的倍数时,就会更新到表空间中页号为7的页面里,当再次引用时,就会加此次的row_id
加上256得出最大的row_id
.MAX_ROW_ID
占用8个字节,所以在写入页面时就会记录一条redo日志,而这条redo日志的redo类型就是MLOG_8BYTE
。
MLOG_MULTI_REC_END
把该组事务中最后一条redo日志后面加这一个特殊类型,当系统崩溃进行恢复时会检测这组事务,如果在这组事务中最后一个redo日志的类型不是MLOG_MULTI_REC_END
的话,则会认为这组事务不是一个完整的事务,会回滚掉。
我们知道,当我们对表中的一条数据进行更改,例如一条insert语句在插入到数据页时,如果这个数据页的大小刚好够这条记录的话,那么InnoDB就不必再开启一个新页面了。因为开启一个新的页面,意味着需要改多处,叶子节点,内节点(会产生页分裂),可能还需要改槽,这种“牵一发而动全身”的做法在数据库的领域称为“悲观插入
”,前者称为“乐观插入
”。
在页面上写数据也就是在一棵B+树上写数据。当我们创建了一张表,表中多少个索引,就代表多少个B+树,当对B+树中的叶子节点进行更新时,同理也要更新内节点的信息,否则我们就会认为这棵B+树的状态是不正确的。
我们都知道,redo是为了数据库的崩溃恢复而提出的,如果在悲观插入的过程中,只记录一部分的redo日志,那么在重启时B+树会是一个不正确的状态。那么我们通过什么来保证redo日志的原子性呢?
有一种redo日志类型,叫MLOG_MULTI_REC_END
,在前面我们已经说过这种类型的含义了,在此就不在赘述了。