MySQL是如何保证不丢失数据的呢?

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS MySQL,高可用系列 2核4GB
简介: 大家好,我是Leo,从事Java后端开发。之前的文章大概介绍了WAL机制,如果不太清楚的小伙伴下面第一部分我们可以再回顾一下。今天这里主要介绍一下WAL的安全性这一块。

介绍


大家好,我是Leo,从事Java后端开发。之前的文章大概介绍了WAL机制,如果不太清楚的小伙伴下面第一部分我们可以再回顾一下。今天这里主要介绍一下WAL的安全性这一块。


写作思路


根据读者与朋友的反馈,所以从这篇文章开始我会加一个写作的思路。可以先让读者了解到学完这一篇下来之后能收获到哪些知识,以防看了半个小时最后啥也没学到,这样的确挺气人的。

image.png


步入正题


binlog  写入机制

binlog写入日志这个是比较简单的。提到binlog,必然提到binlog cache。那么binlog cache是什么?

我们有必要了解一些前提知识,再学习binlog。会的人可以跳过这段,照顾一下不懂的朋友。

binlog cache是一个二进制日志文件的缓冲区,他是由一个参数 binlog_cache_size 控制大小的缓冲区。

一个事务在执行是时候是不允许被拆开的,因此无论事务多大,都是要一次性保存执行的。那么这个就涉及到了binlog cache 的保存问题。如果所占的内存大小超过了这个binlog_cache_size 参数的设定。就会采用暂存到磁盘。事务在提交的时候,会先把binlog cache里的数据写入到binlog中,并清空binlog cache数据。

如下图,我们可以从图中了解一下。

image.png

每个binlog_cache是由单独的一个线程享有的。也就是说多个线程带着多个binlog_cache去进入write操作的时候是写入到一个binlog 文件的。效率是非常快的,因为并没有涉及到磁盘IO的开销。

当进行到了fsync的时候,才是将数据持久化到磁盘操作。这个时候才会占用磁盘IO,也就是我们常说的IOPS。

我们我们可以深入讨论一个问题,什么时候进行write,什么时候需要fsync操作呢?


下面我们介绍一下write与fsync的时机。

int sync_binlog=0;
if(sync_binlog==0){
    每次提交事务都只 write,不 fsync
}
if(sync_binlog==1){
    每次提交事务都会执行 fsync
}
if(sync_binlog>1){
    每次提交事务都 write,但累积 N 个事务后才 fsync。
}


因此,在出现 IO 瓶颈的场景里,将 sync_binlog 设置成一个比较大的值,可以提升性能。在实际的业务场景中,考虑到丢失日志量的可控性,一般不建议将这个参数设成 0,比较常见的是将其设置为 100~1000 中的某个数值。

但是,将 sync_binlog 设置为 N,对应的风险是:如果主机发生异常重启,会丢失最近 N 个事务的 binlog 日志。

扩展:上面我们介绍了通过binlog_cahe_size 控制一个参数的大小。进行磁盘与内存缓存区的抉择。那么还有哪些参数是控制这一类问题的。你能想到几个?

下面我们延伸介绍一下sort_buffer_size ,从前几篇文章中引用的一段。详细的可以去order by那篇文章回顾一下。

image.png

binlog 写入机制大概就是这样了,以后技术的进步优化会再完善的。有不对的地方也可以加以指出!互相探讨学习!

redo log  写入机制

还是老样子吧。我感觉MySQL的设计很多地方都很相像。比如change buffer,binlog cache都是一些缓存区,内存临时存放的地方。那么redolog的缓冲区是什么呢? redo log buffer

我们可以先介绍一下 redo log buffer

redo log buffer 要做的是一个事务在插入一条数据的时候,需要先写入日志。但是又不能在还没有提交事务的时候直接写到redo log文件中。这个日志的临时存放处就是redo log buffer。真正在写入redo log文件的过程是在commit这一步完成的。

这里是开启一个事务下执行的。如果我们只执行一条insert 语句它又是如何实现的呢?

单独执行一个更新语句的时候,InnoDB 会自己启动一个事务,在语句执行完成的时候提交。过程跟上面是一样的,只不过是“压缩”到了一个语句里面完成。


上面我们说了 buffer是他的一个临时缓存区,那么 是不是所有buffe都要持久化到磁盘呢?

不需要 如果事务执行期间 MySQL 发生异常重启,那这部分日志就丢了。由于事务并没有提交,所以这时日志丢了也不会有损失。

那么事务还没提交的时候,redo log buffer 中的部分日志有没有可能被持久化到磁盘呢?

确实会有。这个问题,要从 redo log 可能存在的三种状态说起 如下图

image.png

  • redo log buffer:物理上这是MySQL的进程内存
  • FS page cache:写入到磁盘,但是还没有进行持久化。物理上是page cache文件系统。
  • hard disk,这个就是持久化到磁盘了。

图中的红色区域是内存操作,不涉及到磁盘IO。所以性能的非常快的。write也是非常快的,也就是图中的黄色部分。fsync的速度就慢了很多。因为持久化到磁盘。


MySQL没那么简单,这里的redo log是有一个写入策略的。我们下面介绍一下策略与案例。

写入策略这个就涉及到了一个参数innodb_flush_log_at_trx_commit 这个参数控制写入redo log,写入到磁盘的走向。为了提供更好的性能保障!

  • 设置为 0 的时候,表示每次事务提交时都只是把 redo log 留在 redo log buffer 中 ;
  • 设置为 1 的时候,表示每次事务提交时都将 redo log 直接持久化到磁盘;
  • 设置为 2 的时候,表示每次事务提交时都只是把 redo log 写到 page cache。

InnoDB 有一个后台线程,每隔 1 秒,就会把 redo log buffer 中的日志,调用 write 写到文件系统的 page cache,然后调用 fsync 持久化到磁盘。


下面我们可以细想一下,redo log buffer帮redo log解决了那么大 的一个难题。那么redo log buffer又是绝对的安全或者说绝对的性能吗?

如果我事务正在执行,还没有提交。那么MySQL肯定会把数据从redo log写入到redo log buffer!上面我们介绍了每隔一秒会把redo log buffer里的数据做一边写入。那么有没有可能事务没执行完,可能已经写盘了呢?

答案是肯定的。下面我们介绍一下redo log buffer的刷新策略

控制这个策略的参数是innodb_log_buffer_size

  • redo log buffer 占用的空间即将达到 innodb_log_buffer_size 一半的时候,后台线程会主动写盘。

(注意,由于这个事务并没有提交,所以这个写盘动作只是 write,而没有调用 fsync,也就是只留在了文件系统的 page cache。)

  • 另一种是,并行的事务提交的时候,顺带将这个事务的 redo log buffer 持久化到磁盘

(假设一个事务 A 执行到一半,已经写了一些 redo log 到 buffer 中,这时候有另外一个线程的事务 B 提交,如果 innodb_flush_log_at_trx_commit 设置的是 1,那么按照这个参数的逻辑,事务 B 要把 redo log buffer 里的日志全部持久化到磁盘。这时候,就会带上事务 A 在 redo log buffer 里的日志一起持久化到磁盘。)

image.png

说明: 如上图所示, 在做两阶段提交的时候会有一个prepare。先写入redolog处于prepare阶段。再写binlog。最后再commit。

假设: 如果innodb_log_buffer_size 设置成1,那么redo log在prepare就要持久化一次,因为有一个崩溃恢复的逻辑还要依赖prepare的redo log,再加上binlog来恢复。

每秒一次后台轮询刷盘,再加上崩溃恢复这个逻辑,InnoDB 就认为 redo log 在 commit 的时候就不需要 fsync 了,只会 write 到文件系统的 page cache 中就够了。

通常我们说 MySQL 的“双 1”配置,指的就是 sync_binlog 和 innodb_flush_log_at_trx_commit 都设置成 1。也就是说,一个事务完整提交前,需要等待两次刷盘,一次是 redo log(prepare 阶段),一次是 binlog。


还远远不止这些,有些时候我们听说大厂的TPS每秒两万。也就是说每秒就会写四万次磁盘。但是,我用工具测试出来,磁盘能力也就两万左右,怎么能实现两万的 TPS? 组提交机制


虽然是最后一个模块,不过还是少不了概念

这里,先介绍日志逻辑序列号(log sequence number,LSN)的概念。LSN 是单调递增的,用来对应 redo log 的一个个写入点。每次写入长度为 length 的 redo log, LSN 的值就会加上 length。LSN 也会写到 InnoDB 的数据页中,来确保数据页不会被多次执行重复的 redo log。

后续会介绍一下lsn,redo log,checkpoint他们三者的区别。checkpoint这个还没忘记吧。就是前面介绍的刷内存的时候利用的就是这个checkpoint。redo log和LSN就是正在介绍的。

如下图所示,trx1,trx2,trx3三个并发事务在prepare阶段,都写完了redo log buffer持久化到磁盘的过程。

image.png

由图中可以得知

  • trx1是最先到达的,会被选为这组的leader。
  • 等 trx1 要开始写盘的时候,这个组里面已经有了三个事务,这时候 LSN 也变成了 160;
  • trx1 去写盘的时候,带的就是 LSN=160,因此等 trx1 返回时,所有 LSN 小于等于 160 的 redo log,都已经被持久化到磁盘;
  • 这时候 trx2 和 trx3 就可以直接返回了。

所以,一次组提交里面,组员越多,节约磁盘 IOPS 的效果越好。但如果只有单线程压测,那就只能老老实实地一个事务对应一次持久化操作了。

在并发更新场景下,第一个事务写完 redo log buffer 以后,接下来这个 fsync 越晚调用,组员可能越多,节约 IOPS 的效果就越好。

优化:

为了提升MySQL的性能,一般会选择在这个地方进行延迟,因为这样可以用节省更多的IOPS。

我们借助上文的两阶段提交的图!这里把写binlog日志这一过程分成了两步。

  • 先把 binlog 从 binlog cache 中写到磁盘上的 binlog 文件;
  • 调用 fsync 持久化。

根据这里的优化改一下。MySQL为了让组提交效果更好如下图。

image.png

这么一来,binlog也可以组提交了。为什么这么说呢。可以看上图的第二步。如果多个事务都已经write了(也就是说写入到redo log buffer了),再到第四步的时候就可以一起持久化到磁盘了。不是提升IOPS嘛的这个优化过程嘛!

不过通常情况下第 3 步执行得会很快,所以 binlog 的 write 和 fsync 间的间隔时间短,导致能集合到一起持久化的 binlog 比较少,因此 binlog 的组提交的效果通常不如 redo log 的效果那么好。

如果你想提升 binlog 组提交的效果,可以通过设置 binlog_group_commit_sync_delaybinlog_group_commit_sync_no_delay_count 来实现。这两个只要有一个满足条件就会调用 fsync。

  • binlog_group_commit_sync_delay 参数,表示延迟多少微秒后才调用 fsync;
  • binlog_group_commit_sync_no_delay_count 参数,表示累积多少次以后才调用 fsync。

所以,当 binlog_group_commit_sync_delay 设置为 0 的时候,binlog_group_commit_sync_no_delay_count 也无效了。


回到上文的WAL机制那里我们继续讨论一下。WAL机制主要得以于两方面

  • redo log 和 binlog 都是顺序写,磁盘的顺序写比随机写速度要快;
  • 组提交机制,可以大幅度降低磁盘的 IOPS 消耗。


实战案例


如果你的 MySQL 现在出现了IO性能瓶颈,可以通过哪些方法来提升性能呢?


  • 设置 binlog_group_commit_sync_delay 和 binlog_group_commit_sync_no_delay_count 参数,减少 binlog 的写盘次数。这个方法是基于“额外的故意等待”来实现的,因此可能会增加语句的响应时间,但没有丢失数据的风险。
  • 将 sync_binlog 设置为大于 1 的值(比较常见是 100~1000)。这样做的风险是,主机掉电时会丢 binlog 日志。
  • 将 innodb_flush_log_at_trx_commit 设置为 2。这样做的风险是,主机掉电的时候会丢数据。

我不建议你把 innodb_flush_log_at_trx_commit 设置成 0。因为把这个参数设置成 0,表示 redo log 只保存在内存中,这样的话 MySQL 本身异常重启也会丢数据,风险太大。而 redo log 写到文件系统的 page cache 的速度也是很快的,所以将这个参数设置成 2 跟设置成 0 其实性能差不多,但这样做 MySQL 异常重启时就不会丢数据了,相比之下风险会更小。


总结


今天我们已经介绍完了两大日志的写入机制。以及日志的两阶段提交的优缺点。组提交机制的流程与性能


相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
19天前
|
canal 消息中间件 关系型数据库
Canal作为一款高效、可靠的数据同步工具,凭借其基于MySQL binlog的增量同步机制,在数据同步领域展现了强大的应用价值
【9月更文挑战第1天】Canal作为一款高效、可靠的数据同步工具,凭借其基于MySQL binlog的增量同步机制,在数据同步领域展现了强大的应用价值
115 4
|
28天前
|
关系型数据库 MySQL 数据库
RDS MySQL灾备服务协同解决方案构建问题之数据库备份数据的云上云下迁移如何解决
RDS MySQL灾备服务协同解决方案构建问题之数据库备份数据的云上云下迁移如何解决
|
3天前
|
存储 关系型数据库 MySQL
技术解析:MySQL中取最新一条重复数据的方法
以上提供的两种方法都可以有效地从MySQL数据库中提取每个类别最新的重复数据。选择哪种方法取决于具体的使用场景和MySQL版本。子查询加分组的方法兼容性更好,适用于所有版本的MySQL;而窗口函数方法代码更简洁,执行效率可能更高,但需要MySQL 8.0及以上版本。在实际应用中,应根据数据量大小、查询性能需求以及MySQL版本等因素综合考虑,选择最合适的实现方案。
24 6
|
3天前
|
关系型数据库 MySQL 数据处理
针对MySQL亿级数据的高效插入策略与性能优化技巧
在处理MySQL亿级数据的高效插入和性能优化时,以上提到的策略和技巧可以显著提升数据处理速度,减少系统负担,并保持数据的稳定性和一致性。正确实施这些策略需要深入理解MySQL的工作原理和业务需求,以便做出最适合的配置调整。
27 6
|
22天前
|
SQL 存储 缓存
MySQL是如何保证数据不丢失的?
文章详细阐述了InnoDB存储引擎中Buffer Pool与DML操作的关系。在执行插入、更新或删除操作时,InnoDB为了减少磁盘I/O,会在Buffer Pool中缓存数据页进行操作,随后将更新后的“脏页”刷新至磁盘。为防止服务宕机导致数据丢失,InnoDB采用了日志先行(WAL)机制,通过将DML操作记录为Redo Log并异步刷新到磁盘,结合双写机制和合理的日志刷新策略,确保数据的持久性和一致性。尽管如此,仍需合理配置参数以平衡性能与数据安全性。
MySQL是如何保证数据不丢失的?
|
20天前
|
存储 关系型数据库 MySQL
|
20天前
|
SQL 关系型数据库 MySQL
SQL Server、MySQL、PostgreSQL:主流数据库SQL语法异同比较——深入探讨数据类型、分页查询、表创建与数据插入、函数和索引等关键语法差异,为跨数据库开发提供实用指导
【8月更文挑战第31天】SQL Server、MySQL和PostgreSQL是当今最流行的关系型数据库管理系统,均使用SQL作为查询语言,但在语法和功能实现上存在差异。本文将比较它们在数据类型、分页查询、创建和插入数据以及函数和索引等方面的异同,帮助开发者更好地理解和使用这些数据库。尽管它们共用SQL语言,但每个系统都有独特的语法规则,了解这些差异有助于提升开发效率和项目成功率。
89 0
|
28天前
|
SQL 关系型数据库 MySQL
mysql误删数据后,你会怎么办?
mysql误删数据后,你会怎么办?
44 0
|
30天前
|
Kubernetes 关系型数据库 MySQL
k8s练习--通过NFS+PV+PVC+POD,部署一个MySQL服务,并将MySQL的数据进行持久化存储
本文档介绍了如何使用Kubernetes (K8s)、NFS、PersistentVolume (PV)、PersistentVolumeClaim (PVC)和Pod来部署并实现MySQL服务的数据持久化存储。Kubernetes是一个用于自动化部署、扩展和管理容器化应用的强大平台。NFS作为一种网络文件系统协议,能够使Kubernetes集群中的Pod跨节点访问共享文件。PV和PVC机制则提供了持久化的存储解决方案,确保数据即使在Pod生命周期结束后仍得以保留。
|
1月前
|
SQL 关系型数据库 MySQL
mysql不等于<>取特定值反向条件的时候字段有null值或空值读取不到数据
对于数据库开发的专业人士来说,理解NULL的特性并知道如何正确地在查询中处理它们是非常重要的。以上所介绍的技巧和实例可以帮助你更精准地执行数据库查询,并确保数据的完整性和准确性。在编写代码和设计数据库结构时,牢记这些细节将有助于你避免许多常见的错误,提高数据库应用的质量与性能。
33 0

热门文章

最新文章