请你谈谈对binlog的理解?

binlog用于记录数据库执行的写入性操作(不包括查询)信息,以二进制的形式保存在磁盘中。binlog是mysql的逻辑日志,并且由Server层进行记录,使用任何存储引擎的mysql数据库都会记录binlog日志。

逻辑日志:可以简单理解为记录的就是sql语句。
物理日志:因为mysql数据最终是保存在数据页中的,物理日志记录的就是数据页变更。

binlog是通过追加的方式进行写入的,可以通过max_binlog_size参数设置每个binlog文件的大小,当文件大小达到给定值之后,会生成新的文件来保存日志。

在实际应用中,binlog的主要使用场景有两个,分别是主从复制和数据恢复。

主从复制:在Master端开启binlog,然后将binlog发送到各个Slave端,Slave端重放binlog从而达到主从数据一致。
数据恢复:通过使用mysqlbinlog工具来恢复数据。

对于InnoDB存储引擎而言,只有在事务提交时才会记录biglog,此时记录还在内存中,那么biglog是什么时候刷到磁盘中的呢?mysql通过sync_binlog参数控制biglog的刷盘时机,取值范围是0-N:

0:不去强制要求,由系统自行判断何时写入磁盘;
1:每次commit的时候都要将binlog写入磁盘;
N:每N个事务,才会将binlog写入磁盘。
从上面可以看出,sync_binlog最安全的是设置是1,这也是MySQL 5.7.7之后版本的默认值。但是设置一个大一些的值可以提升数据库性能,因此实际情况下也可以将值适当调大,牺牲一定的一致性来获取更好的性能。

binlog日志格式

binlog日志有三种格式,分别为STATMENT、ROW和MIXED。

在 MySQL 5.7.7之前,默认的格式是STATEMENT,MySQL 5.7.7之后,默认值是ROW。日志格式通过binlog-format指定。

STATMENT:
Statement模式记录的是所有数据库操作对应的SQL语句,如INSERT、UPDATE 、DELETE 等DML语句,CREATE 、DROP 、ALTER 等DDL,所以,从理论上讲,只要按顺序执行这些SQL 语句,就可以实现不同数据库间的数据复制。

优点:不需要记录每一行的变化,减少了binlog日志量,节约了IO, 从而提高了性能;
缺点:在某些情况下会导致主从数据不一致,比如执行sysdate()、slepp()等。

ROW:
Row模式更关心每一行的变更,这种在实际应用中会更普遍一点,因为有时候更关心数据的变化情况,例如一个订单被创建出来,司机通过App接收了某个运输任务等。

优点:不会出现某些特定情况下的存储过程、或function、或trigger的调用和触发无法被正确复制的问题;
缺点:会产生大量的日志,尤其是alter table的时候会让日志暴涨

MIXED:
Statement模式和Row模式的混合体,因为Statement模式和Row模式都有各自的不足,前者可能会导致数据不一致,而后者则会占用大量的存储空间。一般的复制使用STATEMENT模式保存binlog,对于STATEMENT模式无法复制的操作使用ROW模式保存binlog

mysql 为什么不能用binlog来做数据恢复?

首先要知道 redo log 仅仅是在 msyql的InnoDB 引擎下存在。 在 myisam引擎下是没有的。而 binlog是 mysql 层面上的。其次,redo log是用于没有写入binlog就崩溃的情形下恢复数据的。

只要我们写的是DML语句(insert,update,delete,create)等等,那么我们在数据库服务端执行的时候就会涉及到 redo log(重做日志) 和 binlog(归档日志) 两个日志文件的变动。两个日志的主要区别:redo log 是物理日志,记录的是“在某个数据页上做了什么修改”;binlog 是逻辑日志,记录的是这个语句的原始逻辑,比如“给 ID=2 这一行的 c 字段加 1 ”。

redo log 是循环写的,空间固定会用完;binlog 是可以追加写入的。“追加写”是指 binlog 文件写到一定大小后会切换到下一个,并不会覆盖以前的日志。redo log 是 InnoDB 引擎特有的;binlog 是 MySQL 的 Server 层实现的,所有引擎都可以使用。

关于update操作的内部流程:

执行器先找引擎取 ID=2 这一行。ID 是主键,引擎直接用树搜索找到这一行。如果 ID=2 这一行所在的数据页本来就在内存中,就直接返回给执行器;否则,需要先从磁盘读入内存,然后再返回。执行器拿到引擎给的行数据,把这个值加上 1,比如原来是 N,现在就是 N+1,得到新的一行数据,再调用引擎接口写入这行新数据。

引擎将这行新数据更新到内存中,同时将这个更新操作记录到 redo log 里面,此时 redo log 处于 prepare 状态。然后告知执行器执行完成了,随时可以提交事务。执行器生成这个操作的 binlog,并把 binlog 写入磁盘。执行器调用引擎的提交事务接口,引擎把刚刚写入的 redo log 改成提交(commit)状态,更新完成。

两阶段提交:

将 redo log 的写入拆成了两个步骤:prepare 和 commit,这就是"两阶段提交"。这是为了让两份日志之间的逻辑一致。可以认为,redo是物理的,binlog是逻辑的;现在由于redo是属于InnoDB引擎,所以必须要有binlog,因为你可以使用别的引擎。保证数据库的一致性,必须要保证2份日志一致,使用的2阶段式提交;其实感觉像事务,不是成功就是失败,不能让中间环节出现,也就是一个成功,一个失败。

Bin log 用于记录了完整的逻辑记录,所有的逻辑记录在 bin log 里都能找到,所以在备份恢复时,是以 bin log 为基础,通过其记录的完整逻辑操作,备份出一个和原库完整的数据。如果不是两阶段提交,而是各自先后完成:若 redo log 写入成功,bin log 写入失败,则后续通过 bin log 恢复时,恢复的数据将会缺失一部分。

(如 redo log 执行了 update t set status = 1,此时原库的数据 status 已更新为 1,而 bin log 写入失败,没有记录这一操作,后续备份恢复时,其 status = 0,导致数据不一致)。

若是两阶段提交,这时候redolog只是完成了prepare, 而binlog又失败,那么事务本身会回滚,这个库里面status的值也会是0。若先写入 bin log,当 bin log 写入成功,而 redo log 写入失败时,原库中的 status 仍然是 0 ,但是当通过 bin log 恢复时,其记录的操作是 set status = 1,也会导致数据不一致。

猜你喜欢

转载自blog.csdn.net/zs18753479279/article/details/119964407