MySQL - 日志

  • undo log 回滚日志:Innodb引擎层生成的日志,实现了事务中的原子性,主要用于事务回滚和MVCC
  • redo log 重做日志:Innodb引擎层生成的日志,实现了事务中的持久性,主要用于掉电等故障恢复
  • bin log 归档日志:Server层生成的日志,只要用于数据备份和主从复制

为什么需要undo log

两大作用:

  1. 实现事务回滚,保证事务的原子性
  2. 实现MVCC关键因素之一

undo log是一种用于撤销回退的日志。在事务没提交前,MySQL会记录更新前的数据到undo log日志文件里面,当事务回滚时,可以利用undo log回滚

  • 在插入一条记录时,要把这条记录的主键值记录下来,回滚时只需要把对应的记录删掉
  • 在删除一条记录时,要把这条记录中的内容都记录下来,回滚之后把这些内容重新插入到表中
  • 在更新一条记录时,要把被更新的旧值记录下来,回滚之后再把这些列更新为旧值

一条记录的每次更新操作产生的undo log格式都有一个roll_pointer指针和一个trx_id事务id

  • 通过trx_id可以知道该记录是被哪个事务修改的
  • 通过roll_pointer指针可i将这些undo log串成一个链表,这些链表就被称为版本链

image.png

为什么需要Buffer Pool

MySQL的数据是存在磁盘中的,我们更新一条记录时,需要先从磁盘中读取该记录,然后在内存中修改着这条记录,修改完放入一个缓冲池Buffer Pool,以减少磁盘的I/O

有了Buffer Pool之后

  • 当读取数据时,如果数据存在于Buffer Pool中,客户端就会直接读取Buffer Pool中的数据,否则再去磁盘中读取
  • 当修改数据时,如果数据存在于Buffer Pool中,那就直接修改Buffer Pool中数据所在的页,然后将这个页设置为脏页(该页的内存数据和磁盘数据已经不一致),为了减少磁盘I/O,不会立即将脏页写入磁盘,后续由后台线程选择一个合适的时机将脏页写入到磁盘

为什么需要redo log

两大作用

  1. 实现事物的持久性,让MySQL有crash-safe的能力
  2. 将写操作从随机写变成顺序写,提升了MySQL写入磁盘的能力

Buffer Pool能提到读写效率,但是Buffer Pool是基于内存的,而内存总是不可靠的,万一断电重启,还没来得及落盘的脏数据页就会丢失
为了防止数据丢失的问题,当有一条记录需要更新时,InnDB引擎会先更新内存(同时标记为脏页),然后将本次对这个页的修改以redo log的形式记录下来
后续,InnoDB引擎会在适当的时机,由后台线程将Buffer Pool中的脏页刷新到磁盘中,这就是WAL(Write-Ahead Logging)技术
image.png

redo log是物理日志,记录某个数据页做了什么修改,比如对XXX表空间中YYY数据页ZZZ偏移量的地方做了AAA更新,每当执行一个事物就会产生这样的一条或者多条物理日志
在提交事物时,只要先将redo log持久化磁盘即可,不需要等到将Buffer Pool中的脏页持久化到磁盘
当系统崩溃时,虽然脏页数据没有持久化,但是redo log已经持久化,MySQL重启之后可以根据redo log的内容,将所有数据恢复到最新状态


redo log和undo log的区别

  • redo log记录了此次事物「完成后」的数据状态,记录的是更新之后的值
  • undo log记录了此次事物「开始前」的数据状态,记录的是更新之前的值

redo log要写到磁盘,数据也要写到磁盘,为什么要多此一举?
写入redo log是追加写,而写入数据要先找到写入位置,然后才能写入到磁盘,是随机写。
磁盘的追加写比随机写高效的多,因此redo log写入磁盘的开销更小


产生的redo log是直接写入磁盘吗?
不是的,redo log也有自己的缓存——redo log buffer,每当产生一条redo log时,会先写入到redo log buffer,后续再持久化到硬盘
image.png
redo log刷盘时机

  • MySQL正常关闭时
  • 当redo log buffer中记录的写入量大于redo log buffer内存空间的一半时,会触发落盘
  • InnoDB的后台线程每隔1秒,将redo log buffer持久化到磁盘

innodb_flush_log_at_trx_commit参数

  • 0,表示事务提交时不会主动出发写入磁盘操作
  • 1,表示事务提交时,直接将redo log buffer里的redo log持久化到磁盘,保证数据不丢失,默认值
  • 2,表示事务提交时,都只是将缓存再redo log buffer里的redo log写到redo log文件,**并不意味着写入到磁盘,**相对是一个折中的方案,只要操作系统不宕机,即使数据库崩溃了,也不会丢失数据。

redo log文件写满了怎么办?
默认情况下,InnoDB存储引擎有一个重做日志组redo log group,重做日志组由两个redo log文件组成,这两个redo log日志的文件名叫:ib_logfile0ib_logfile1
重做日志文件组是以循环写的方式工作的,从头开始写,写到末尾又从头开始,相当于一个环形
redo log是为了防止Buffer Pool中脏页丢失而设计的,随着系统的运行,Buffer Pool的脏页刷新到了磁盘中,那么redo log对应的记录也就没用了,这时候我们擦除这些旧记录,以腾出空间记录新的更新操作
redo log 是循环写的方式,相当于一个环形,InnoDB 用 write pos 表示 redo log 当前记录写到的位置,用 checkpoint 表示当前要擦除的位置,如下图:
image.png
图中的:

  • write pos和checkpoint的移动方向都是顺时针方向
  • write pos ~ checkpoint红色部分,用来记录新的更新操作
  • checkpoint ~ write pos蓝色部分,待落盘的脏数据页记录

如果write pos追上了checkpoint,就意味着redo log文件满了,这时MySQL不能再执行新的更新操作,也就是说MySQL会被阻塞,此时会将Buffer Pool中的脏数据页刷新到磁盘

为什么需要bin log

bin log由Server层产生,事务提交的时候,会_将该事务执行过程中产生的所有bin log统一写入bin log文件_
bin log文件记录了所有数据库表结构和表数据的修改日志,不会记录查询类的操作,比如SELECTSHOW操作
为什么有了bin log还要有redo log?
这个问题跟 MySQL 的时间线有关系。
最开始 MySQL 里并没有 InnoDB 引擎,MySQL 自带的引擎是 MyISAM,但是 MyISAM 没有 crash-safe 的能力,binlog 日志只能用于归档。
而 InnoDB 是另一个公司以插件形式引入 MySQL 的,既然只依靠 binlog 是没有 crash-safe 能力的,所以 InnoDB 使用 redo log 来实现 crash-safe 能力。


bin log和redo log区别

  1. 适用对象不同
    1. bin log由Server产生,所有引擎都可以使用
    2. redo log是InnoDB引擎产生的
  2. 文件格式不同
    1. bin log有3种文件格式,分别是Statement(默认)、Row、Mixed
      1. Statement:修改数据的SQL会被记录到bin log中,主从复制中slave端根据SQL语句重现。但Statement有动态函数的问题,可能会导致数据的不一致。
      2. Row:记录行数据最终被修改成什么样了,缺点是每行数据的变化结构都会被记录,比如执行批量update语句,更新多少行数据就会产生多少条记录,使bin log过大。
      3. Mixed:包含了StatementRow模式。
    2. redo log是物理日志,记录的是在某个数据页做了什么修改,比如对XXX表空间的YYY数据页ZZZ偏移量的地方做了AAA的修改。
  3. 写入方式不同
    1. bin log是追加写,写满一个文件就创建一个新的继续写,不会覆盖以前的日志,保存的是全量的日志。
    2. redo log是循环写,日志空间大小是固定的。
  4. 用途不同
    1. bin log用于备份恢复、主从复制
    2. redo log用于掉电等故障恢复

如果不小心整个数据库的数据被删除了,能使用redo log文件恢复数据吗?
不可以使用redo log恢复,只能使用bin log恢复
因为redo log文件是循环写,边写边擦除,bin log保存的是全量的日志


主从复制是怎样实现的?
MySQL主从复制依赖于bin log,复制的过程就是将bin log的数据从主库传输到从库上。
这个过程一般是异步的,也就是主库执行事务的线程不会等待复制bin log的线程同步完成
image.png

  1. MySQL主库在收到客户端请求后,会先写入bin log,再提交事务。
  2. 从库创建一个专门的I/O线程,连接主库的log dump线程,来接受主库的bin log日志,再把bin log信息写入relay log的中继日志里,返回给主库复制成功的相应。
  3. 从库创建一个用于回放bin log的线程,读取中继日志的内容,最终实现主从的一致性。

主从复制的三种模型:

  • 同步复制:主库提交事务的线程要等待所有从库的响应,才返回客户端结果。
    • 性能很差
    • 可用性很差,主库和从库任何一个出现问题都会影响业务
  • 异步复制(默认):不等待bin log同步到从库
    • 一旦主库宕机,数据就会丢失
  • 半同步复制:一部分从库复制成功就行

从库是不是越多越好?
不是的,因为从库数量增加,从库连接上来的I/O线程也比较多,主库也需要创建同样数量的log dump线程来处理复制请求,对主库资源消耗较高。


bin log什么时候刷盘?

MySQL会给每个线程都分配一个bin log cache,事务执行过程中,先把日志写到bin log cache,事务提交的时候,再把bin log cache写到bin log文件中。
一个事务bin log是不能被拆开的,因此无论这个事务多大,也要保证一次性写入,如果事务的bin log被拆开,从库的多个事务就会分段执行,不能保证原子性

事务提交时,执行器把bin log cache里完整的事务写入到bin log文件中,并清空bin log cache
image.png
虽然每个线程都有自己的bin log cache,但最终都写到一个bin log文件中。
图中的write就是把日志写到bin log文件中,但是还没有把数据持久化到磁盘
MySQL提供一个参数sync_binlog来控制bin log刷写频率:

  • sync_binlog = 0:表示每次提交事务都只write,不fsync,后续由操作系统决定何时将数据持久化到磁盘
  • sync_binlog = 1:表示每次提交事务都会马上write,然后马上执行fsync
  • sync_binlog = N:表示每次提交事务都write,但累积到N个事务才执行fsync

为什么要有两阶段提交?

事务提交后,redo log和bin log都要持久化到磁盘,但是这两个是独立的逻辑,可能出现半成功的状态

  • redo log刷入磁盘,MySQL宕机,bin log没来得及写入。主库重启后,主库从redo log中恢复更改之后的值,但是bin log丢失了这条语句,导致主从数据不一致。
  • bin log刷入磁盘,MySQL宕机,redo log没来得及写入。主库重启后,由于redo log没写,相当于这个事务无效,但是bin log被从机读取到,导致数据不一致

两阶段提交把单个事务的提交拆成两个阶段,分别是准备(Prepare)和提交(Commit)阶段,每个阶段都由协调者和参与者共同完成。
为了保证两个日志的一致性,MySQL使用了内部XA事务,内部XA事务由bin log作为协调者,存储引擎作为参与者。
image.png

  • prepare阶段:将XID(内部XA事务的ID)写到redo log,同时将redo log对应的事务状态设置为prepare,然后将redo log持久化到磁盘
  • commit阶段:将XID写入bin log,然后将bin log持久化到磁盘,接着调用引擎的提交事务接口,将redo log状态设置为commit。

异常重启会出现什么现象?
image.png
A、B时刻,redo log都处于prepare阶段
MySQL重启后会按顺序扫描redo log,碰到处于prepare状态的redo log,就拿着redo log中的XID去bin log中查看是否存在此XID

  • 如果bin log中没有当前内部XA事务的XID,说明redo log完成刷盘,但bin log还没有刷盘,则回滚事务。对应时刻A
  • 如果bin log中有当前内部XA事务的XID,说明redo log 和 bin log都已经完成刷盘。对应时刻B

对于prepare阶段的redo log,既可以提交事务又可以回滚事务,这取决于是否能在bin log中查找到与redo log相同的XID
两阶段提交是以bin log写成功为事务提交成功的标识,因为bin log写入成功了,就意味着能在bin log中找到与redo log中相同的XID

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值