MySQL是否会丢失数据

要搞明白mysql是否会丢失数据,我们先学习下InnoDB中的redo log日志。

一、redo log日志

首先要明确,在操作数据时,直观感受就是每次直接把数据写到磁盘或者直接从磁盘读取,这是不完全的,在这个过程中都用到了缓存,缓存,缓存

1.1从磁盘读取数据:

图片

数据在缓存中都是一页的形式保存的,一页大小为16kb,buffer pool的默认大小是128M,所以可以将buffer pool理解为一个数组,元素就是页数据。当查询语句的数据不在buffer pool中的页上时,需要去磁盘读取数据,数据读取也是按照一页的格式读取。读取后再放到buffer pool中。

在这里有几个问题需要考虑:

  • 我将数据从磁盘中读取出来,放到buffer pool的空闲位置,怎么放,也就是,我怎么知道buffer pool的空闲?

  • 修改数据(后面会讲)后,我怎么明确是把哪些页修改了,即怎么管理修改的数据页?

  • buffer pool满了之后,新的数据页要来,怎么处理?

free链表:

在下图中,我标注了数据页的控制块(每一个数据页都有的,主要控制数据页),简单标注了数字1,2,3,4,他们都是空闲的数据页位置,可以将从磁盘中读取的数据写到这些位置。为了管理这些空闲数据位置,mysql采用free链表。

图片

flush链表:

使用flush链表,记录脏页信息,以便后续将脏页写回磁盘中。

图片

LRU链表:

数据结构和上面的free链表一样,区别在于,新操作的数据页,要移动链表头部,数据不足时,删除链表尾部。

设想这样一个场景:buffer pool满了,这个时候执行select * from table语句,根据LRU思路,是会把所有的数据页都清除掉,然后再加载磁盘中的数据,考虑到这个全表扫描语句不会经常执行,这样把之前的缓存删除掉,是不太合理的。

为了解决这类问题,LRU进行升级,mysql中提出了冷热数据分离的思想。

设置5/8的数据是热数据,3/8的数据是冷数据,当新的数据页需要插入LRU链表时,会先插入冷数据区域的头部,只有间隔1s后,这个数据页再次被访问,才会把这个数据页放入热数据区域。

图片

因为设置的是5:3的区域比例,这样在执行select *时,不会影响全部的buffer pool数据,而且select *语句会在短时间内访问同一个数据页,通过设置1s这个属性,也可以确保数据页不会升到热数据区域。

1.2更新数据到磁盘:

图片

我们更新完数据页后,最朴素的想法就是把它持久化到磁盘中,但是一个数据页中可能保存多条记录,他们在逻辑上是连续的,我现在只是更新其中一个记录,整个数据页都变成了脏页,然后持久化到磁盘,真实的每条记录在磁盘中不是顺序保存的,可能在不同的磁道,这个时候会频繁切换磁针,效率低下,这也是随机IO的坏处。

所以后面引入了redolog日志,它是在磁盘中的连续空间,保存着具体的操作命令。这样如果脏页数据没有写到磁盘,在mysql崩溃后,可以先将旧数据读取,再通过redolog日志,就可以恢复数据。针对redolog,InnoDB也引入了缓存,即log buffer。将数据在缓存更新后,我们要写入redo log中,本质上也是写到log buffer缓存中,然后再在特定的时机,写到磁盘中,中间也要经过操作系统的缓存page cache。

图片

同时会有一个后台线程,会每隔一秒将log buffer中的数据写到操作系统的page cache中,并刷盘,持久化到磁盘中。

图片

图片来源:MySQL三大日志(binlog、redo log和undo log)详解 | JavaGuide(Java面试 + 学习指南)

1.3刷盘时机

1. 事务提交,innodb_flush_log_at_trx_commit参数控制

图片来源:MySQL三大日志(binlog、redo log和undo log)详解 | JavaGuide(Java面试 + 学习指南)

  • 0:每次事务提交时不进行刷盘操作,只是保存在log buffer中。这种方式性能最高,但是也最不安全,因为如果 MySQL 挂了或宕机了,可能会丢失最近 1 秒内的事务(也就是后台线程会每隔一秒刷盘)。

    图中左边黑线,只是写到redolog buffer

    图片

    图片来源:MySQL三大日志(binlog、redo log和undo log)详解 | JavaGuide(Java面试 + 学习指南)

  • 1:每次事务提交时都将进行刷盘操作。这种方式性能最低,但是也最安全,因为只要事务提交成功,redo log 记录就一定在磁盘里,不会有任何数据丢失,就是会先把log buffer中的数据写到pagecache中,然后再写到磁盘中。

    图中左边的红线是一直连到磁盘的

    图片

    图片来源:MySQL三大日志(binlog、redo log和undo log)详解 | JavaGuide(Java面试 + 学习指南)

  • 2:事务提交时都只把 log buffer 里的 redo log 内容写入 page cache(文件系统缓存)。但是page cache没有写到磁盘中,需要等后台线程的下一次操作再写到磁盘中。

    图片

2.redolog buffer 空间不足

一般是在redolog buffer的使用空间达到最大值的一半时,就会把日志写到page cache,然后刷盘到磁盘。

3. check point(检查点)

在一些特殊的点,会将数据刷盘到磁盘。

4. 后台刷新线程

之前也说过,InnoDB有一个后台线程,他会每隔一秒刷盘一次。

5. 正常关闭服务器

1.4checkpoint

redolog在磁盘中是按照日志文件组的形式出现的,每个redolog文件大小一样,组成一个循环结构。

图片

图片来源:MySQL三大日志(binlog、redo log和undo log)详解 | JavaGuide(Java面试 + 学习指南)

我们假设,每个文件只能保存一个记录,在0号文件保存第一个更新的记录,在1号文件保存第二个更新的记录。我们怎么知道该写到几号文件?需要用write pos记录当前记录的位置,一边写一边后移。

假设在3号文件记录了第四个更新的记录,现在又有一个更新的记录,将写到文件0中。但是文件0中还保存着更新的第一个记录,这将会被覆盖,怎么办?既然我们要把最新的数据写到文件0,是不是就是把第一个更新的记录写到它该去的地方,也就是把它写到磁盘中,这样文件0就空闲出来了。所以我们使用checkpoint记录哪些记录已经写到磁盘中了,也就是可以将记录擦除的地方的指针,checkpoint也是往后推移的。

图片

如果 write pos 追上 checkpoint ,表示日志文件组满了,这时候不能再写入新的 redo log 记录,MySQL 得停下来,将一些脏页数据写到磁盘中,把 checkpoint 推进一下。

图片

二、mysql中的WAL机制

WAL,全称是Write-Ahead Logging, 预写日志系统。指的是 MySQL 的写操作并不是立刻更新到磁盘上,而是先记录在日志上,然后在合适的时间再更新到磁盘上。这样的好处是错开高峰期。日志主要分为 undo log、redo log、binlog。

三、binlog日志

。。。。。。

MySQL三大日志(binlog、redo log和undo log)详解 | JavaGuide(Java面试 + 学习指南)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值