mysql日志log及主从同步

184 阅读7分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第26天,点击查看活动详情

MySQL 整体分为 Server 层和数据引擎层,而每层也对应了自己的日志文件。如果选用的是 InnoDB 引擎,对应的是 redo log 文件。Server 层则对应了 binlog 文件。

在MySQL中有三个比较重要的日志:

    bin log(二进制日志) 
    redo log(重做日志) 
    undo log(回滚日志)

bin log: 归档日志

bin log是逻辑日志,记录的是mysql语句的原始逻辑,属于mysql 的server层。

只要是数据表发生了数据的变化,都会记录到bin log中。Bin Log的作用是数据库之间的数据同步和保持数据一致性。像主从,主主,主备这些都离不开bin log,需要依靠binlog来同步数据,保证数据一致性。

事务执行过程中,先把日志写到binlog cache,事务提交的时候,再把binlog cache(系统为每个线程分配一个,当数据大于默认参数,将暂存磁盘)写到binlog文件中。

Bin Log日志有三种格式: statement row mixed

  • Statement:直接记录mysql 原始语句,在同步数据的时候会执行mysql语句。但有个问题是有些语句会和当前的时间有关联,比如update time = now或获取当前的系统时间,在同步数据的时候会操成数据的不一致。为了应对这种情况,我们还有一种格式,就是row。

  • Row:Row不仅记录mysql语句,还记录一些具体的数据,这样的话就解决了数据不一致性的问题。但是这些格式需要记录的信息太多,比较占用空间。

  • Mixed:Mixed是statment和row的混合体。MySQL会判断,如果一条语句会造成数据不一致,那么就用row,如果没问题,就直接使用statment。

使用场景:

binlog 的主要使用场景有两个,分别是 主从复制数据恢复

  • 主从复制 :在 Master 端开启 binlog ,然后将 binlog发送到各个 Slave 端, Slave 端重放 binlog 从而达到主从数据一致。

  • 数据恢复 :通过使用 mysqlbinlog 工具来恢复数据。

另外,常见的一些同步 MySQL 数据到其他数据源的工具(比如 canal)的底层一般也是依赖 binlog。

redo log:重做日志

redo log 它是物理日志,记录内容是“在某个数据页上做了什么修改”,属于 InnoDB 存储引擎特有日志,让mysql有奔溃后恢复的crash-safe能力。

我们都知道,事务的四大特性里面有一个是 持久性 ,具体来说就是只要事务提交成功,那么对数据库做的修改就被永久保存下来了,不可能因为任何原因再回到原来的状态。

mysql按页读取数据,先查询Buffer Pool ,没有命中再去硬盘加载。mysql更新数据时,发现 Buffer Pool 里存在要更新的数据,就直接在 Buffer Pool 里更新。然后会把“在某个数据页上做了什么修改”记录到重做日志缓存(redo log buffer)里,接着刷盘到 redo log 文件里。

image.png

sync_binlog刷盘时机:
  • 0 :设置为 0 的时候,表示每次事务提交时不进行刷盘操作

  • 1 :设置为 1 的时候,表示每次事务提交时都将进行刷盘操作(默认值)

  • 2 :设置为 2 的时候,表示每次事务提交时都只把 redo log buffer 内容写入 page cache

innodb_flush_log_at_trx_commit 参数默认为 1 ,也就是说当事务提交时会调用 fsync 对 redo log 进行刷盘。

另外,InnoDB 存储引擎有一个后台线程,每隔1秒,就会把 redo log buffer 中的内容写到文件系统缓存(page cache),然后调用 fsync 刷盘。

一个没有提交事务的 redo log 记录,也可能会刷盘(因为在事务执行过程 redo log 记录是会写入redo log buffer 中,这些 redo log 记录会被后台线程刷盘||还有一种情况,当 redo log buffer 占用的空间即将达到 innodb_log_buffer_size 一半的时候,后台线程会主动刷盘)。

image.png

redo log 的出现,除了在效率上有了很大的改善,还保证了 MySQL 具有了 crash-safe 的能力,在发生异常情况下,不会丢失数据。

在具体实现上 redo log 的大小是固定的,可配置一组为4 个文件,每个文件 1GB,更新时对四个文件进行循环写入

image.png

write pos 记录当前写入的位置,写完就后移,当第写入第 4 个文件的末尾时,从第 0 号位置重新写入。

check point 表示当前可以擦除的位置,当数据更新到磁盘时,check point 就向后移动

write pos 和 check point 之间的位置,就是可以记录更新操作的空间。当 write pos 追上 check point ,不在能执行新的操作,先让 check point 去写入一些数据。

可以将 innodb_flush_log_at_trx_commit 设置成 1,开启 redo log 持久化的能力。

binlog 和 redo 区分:
  1. 所有者不同,binlog 是 Server 层,所有引擎都可使用。redo log 是 InnoDB 特有的。
  2. 类型不同,binlog 是逻辑日志,记录的是语句的原始逻辑(比 statement)。redo log 是物理日志,记录某个数据页被做了怎样的修改。
  3. 数据写入的方式不同,binog 日志会一直追加,而 redo log 是循环写入。
  4. 功能不同,binlog 用于归档,而 redo log 用于保证 crash-safe.

undo log:回滚日志

数据库事务四大特性中有一个是 原子性 ,具体来说就是 原子性是指对数据库的一系列操作,要么全部成功,要么全部失败,不可能出现部分成功的情况

实际上, 原子性底层就是通过 undo log 实现的。undo log主要记录了数据的逻辑变化, 比如一条 INSERT 语句,对应一条DELETE 的 undo log ,对于每个 UPDATE 语句,对应一条相反的 UPDATE 的 undo log ,这样在发生错误时,就能回滚到事务之前的数据状态。

同时, undo log 也是 MVCC(多版本并发控制) 实现的关键。

每条数据修改(insert、update或delete) 操作都伴随一条undo log的生成,并且回滚日志必须先于数据持久化到磁盘上。

mysql锁和多版本控制保证数据一致性

主从同步

MySQL binlog(binary log 即二进制日志文件) 主要记录了 MySQL 数据库中数据的所有变化(数据库执行的所有 DDL 和 DML 语句)。因此,我们根据主库的 MySQL binlog 日志就能够将主库的数据同步到从库中。

    1. master提交完事务后,写⼊binlog
    1. slave连接到master,获取binlog
    1. master创建dump线程,推送binglog到slave
    1. slave启动⼀个IO线程读取同步过来的master的binlog,记录到relay log中继⽇志中
    1. slave再开启⼀个sql线程读取relay log事件并在slave执⾏,完成同步
    1. slave记录⾃⼰的binglog

image.png

由于mysql默认的复制⽅式是异步的,主库把⽇志发送给从库后不关⼼从库是否已经处理,这样会产⽣⼀个问题就是假设主库挂了,从库处理失败了,这时候从库升为主库后,⽇志就丢失了。由此产⽣两个概念:

全同步复制

主库写⼊binlog后强制同步⽇志到从库,所有的从库都执⾏完成后才返回给客户端,但是很显然这个⽅式的话性能会受到严重影响。

半同步复制

和全同步不同的是,半同步复制的逻辑是这样,从库写⼊⽇志成功后返回ACK确认给主库,主库收到⾄少⼀个从库的确认就认为写操作完成。

主从的延迟怎么解决呢?

  1. 针对特定的业务场景,读写请求都强制⾛主库

  2. 读请求⾛从库,如果没有数据,去主库做⼆次查询