登录
/
注册
首页
论坛
其它
首页
科技
业界
安全
程序
广播
Follow
关于
博客
发1篇日志+1圆
记录
发1条记录+2圆币
发帖说明
登录
/
注册
账号
自动登录
找回密码
密码
登录
立即注册
搜索
搜索
关闭
CSDN热搜
程序园
精品问答
技术交流
资源下载
本版
帖子
用户
软件
问答
教程
代码
VIP网盘
VIP申请
网盘
联系我们
道具
勋章
任务
设置
我的收藏
退出
腾讯QQ
微信登录
返回列表
首页
›
业界区
›
业界
›
MySQL 23 MySQL是怎么保证数据不丢的?
MySQL 23 MySQL是怎么保证数据不丢的?
[ 复制链接 ]
仁夹篇
10 小时前
只要redo log和binlog保证持久化到磁盘,就能确保MySQL异常重启后,数据可以恢复。本文讲讲MySQL写入binlog和redo log的流程。
binlog的写入机制
binlog的写入逻辑比较简单:事务在执行过程中,先把日志写到binlog cache,事务提交的时候,再把binlog cache写到binlog文件。
一个事务的binlog不能拆开,因此不论这个事务多大,也要确保一次性写入,这涉及到binlog cache的保存问题。系统给binlog cache分配了一片内存,每个线程一个,参数binlog_cache_size用于控制单个线程内binlog cache所占内存大小,如果超过参数大小,就要暂存到磁盘。
事务提交时,执行器把binlog cache里的完整事务写入binlog并清空binlog cache。
上图中:
write指的是把日志写入到文件系统的page cache,速度较快;
fsync指的是把数据持久化到磁盘。
write和fsync的时机,由参数sync_binlog控制:
sync_binlog=0,表示每次提交事务都只write,不fsync;
sync_binlog=1,表示每次提交事务都会fsync;
sync_binlog=N>1,表示每次提交事务都write,但累积N个事务后才fsync。
因此,在IO瓶颈的场景里,一般将参数设置为较大的值。在实际业务场景中,考虑到丢失日志量的可控性,比较常见的设置为100-1000中的某个数值。其风险是如果主机发生异常重启,会丢失最近N个事务的binlog日志。
redo log的写入机制
事务在执行过程中,生成的redo log先写到redo log buffer。redo log buffer里的内容,不需要每次生成后都直接持久化到磁盘,但也有可能在事务还没提交的时候被持久化到磁盘。
这个问题,需要从redo log可能存在的三种状态说起:
三种状态为:
存在redo log buffer,物理上是在MySQL进程内存,即图中红色;
写到磁盘,但是没有持久化(fsync),物理上是在文件系统的page cache,即图中黄色;
持久化到磁盘,对应的是hard disk,即图中绿色。
日志写到redo log buffer,write到page cache都很快,但持久化到磁盘的速度会慢很多。
为了控制redo log的写入策略,InnoDB提供innodb_flush_log_at_trx_commit参数:
设置为0,表示每次事务提交时都只是把redo log留在redo log buffer中;
设置为1,表示每次事务提交时都将redo log持久化到磁盘;
设置为2,表示每次事务提交时都只是把redo log写到page cache。
InnoDB有一个后台线程,每隔一秒会把redo log buffer中的日志调用write写到文件系统的page cache,然后调用fsync持久化到磁盘。
事务执行中间过程的redo log也是直接写在redo log buffer中,这些redo log也会被后台线程一起持久化到磁盘,因此一个没有提交的事务的redo log也有可能已经被持久化到磁盘。
除了后台线程每秒一次的轮询操作外,还有两种场景会让一个没有提交的事务的redo log写入到磁盘:
redo log buffer占用的空间即将达到innodb_log_buffer_size一半的时候,后台线程会主动写盘。这个写盘动作只是write,而没有调用fsync;
并行的事务提交的时候,顺带将这个事务的redo log buffer持久化到磁盘。假设一个事务A执行到一半,已经写了一些redo log到buffer,这时另一个线程的事务B提交,如果innodb_flush_log_at_trx_commit=1,那么事务B要把redo log buffer里的日志全部持久化到磁盘,这时会带上事务A在redo log buffer里的日志一起持久化。
两阶段提交的时序是redo log先prepare,再写binlog,最后再把redo log commit。如果innodb_flush_log_at_trx_commit=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。
这时,可能有一个疑问,这意味着从MySQL看到的TPS是每秒两万的话,每秒就会写四万次磁盘,但磁盘能力也就两万左右,怎么实现两万的TPS。
解释该问题需要用到组提交机制。先介绍日志逻辑序列化(LSN)的概念。LSN是单调递增的,用来对应redo log的一个个写入点,每次写入长度为length的redo log,LSN的值就会加上length。LSN也会写到InnoDB数据页,来确保数据页不会被多次执行重复的redo log。
下图是三个并发事务在prepare阶段,都写完redo log buffer持久化到磁盘的过程,对应的LSN分别是50、120和160:
解释该过程:
trx1第一个到达,会被选为这个组的leader;
当trx1要开始写盘,组里有三个事务,此时LSN变为160;
trx1去写盘时,带的是LSN=160,等trx1返回,所有LSN小于等于160的redo log都已经被持久化到磁盘;
此时trx2和trx3可以直接返回了。
所以一次组提交里,组员越多,节约磁盘IOPS的效果越好。在并发更新场景下,第一个事务写完redo log buffer后,接下来fsync越晚调用,组员可能越多,节约IOPS的效果就越好。
而为了让一次fsync带的组员更多,MySQL也有优化。在两阶段提交的过程,如果将binlog拆分,可以分为write和fsync,整个两阶段提交过程实际上为:
如上,MySQL调整了时间步骤,这样使得binlog也可以组提交,在执行第4步时,如果有多个事务的binlog已经写完,也是一起持久化的,这样也减少了IOPS的消耗。
不过通常第3步很快,所以第2步和第4步间隔很短,导致能集合到一起持久化的binlog比较少,因此binlog组提交效果通常不如redo log。如果想提升binlog组提交效果,可以设置两个参数:
binlog_group_commit_sync_delay:表示延迟多少微秒后才调用fsync;
binlog_group_commit_sync_no_delay_count:表示累积多少次后才调用fsync。
这里可以总结,WAL机制主要得益于两方面:
redo log和binlog都是顺序写,比随机写速度要快;
组提交机制大幅降低磁盘的IOPS消耗。
最后再回答一下,如果MySQL出现了IO方面的性能瓶颈,该怎么去提升性能:
设置binlog_group_commit_sync_delay和binlog_group_commit_sync_no_delay_count参数,减少binlog的写盘次数;
将sync_binlog设置成大于1,风险是主机掉电时会丢binlog日志;
将innodb_flush_log_at_trx_commit设为2,风险是主机掉电会丢数据。
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复
使用道具
举报
提升卡
置顶卡
沉默卡
喧嚣卡
变色卡
千斤顶
照妖镜
高级模式
B
Color
Image
Link
Quote
Code
Smilies
您需要登录后才可以回帖
登录
|
立即注册
回复
本版积分规则
回帖并转播
回帖后跳转到最后一页
浏览过的版块
安全
签约作者
程序园优秀签约作者
发帖
仁夹篇
10 小时前
关注
0
粉丝关注
19
主题发布
板块介绍填写区域,请于后台编辑
财富榜{圆}
敖可
9984
凶契帽
9990
黎瑞芝
9990
4
杭环
9988
5
猷咎
9988
6
鲫疹
9988
7
接快背
9988
8
里豳朝
9988
9
处匈跑
9988
10
氛疵
9988
查看更多