前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >一个简单 sql 的问题,差点把同事问出了内伤。【技术创作101训练营】

一个简单 sql 的问题,差点把同事问出了内伤。【技术创作101训练营】

原创
作者头像
付威
修改2020-09-22 10:26:04
4440
修改2020-09-22 10:26:04
举报
文章被收录于专栏:老付的网络博客

在一个闲暇的周五下午,和旁边的同事小A 边看报表边聊天(划水)。

小A : 没事我给你出一个题怎么样?你看能不能答上来?

我: 我怎么没事,我这么多事,哪像你天天划水?!嗯, 什么题,你说吧。

小A: 就知道你没有事,那我可出了啊。

我: 随便,你这水平能难倒我,我喊你爸爸。

小A:你说一说,B+树和 B 树的区别?

我: 这个题目太简单的吧,他们两个最大的区别,B+ 树数据存储在叶子节点,B 树的节点在所有的节点上。

我又babalala说了一堆他们的各自的优点和缺点,心想估计他自己都不知道这么多。

小A :厉害厉害,你再说说下, B+数如果来了一个新节点,是怎么旋转的?

这个题一步小心就掉坑里了,很可能会联想到二叉树的旋转,好在我还算是一个老司机。

我: 你小子不老实,B+ 插入的时候不是旋转,是靠节点分裂,少来误导我。

小A : 可以啊,老哥,这都被你猜破了。

我高兴的笑了笑,你这个问题太基础,要么我给你出一个"基础题"?

小A: 来啊来啊。

看这个小A一副骄傲的表情,我心想这次一定让你知道什么是真正的技术。

01.jpg
01.jpg

我 : 在InnerDB中,如果更新 1000w+ 数据,在更新的过程中服务器宕机了,会产生什么结果?

这个时候,我身后的小B 也来了兴趣,抢着说:这么简单的问题,事务没有提交,肯定是直接回滚啊

我:给你一个表情,你自己体会。

02.png
02.png

这个时候小 A,也陷入了沉思,我估计是和小B 的答案一致。

他们俩又私下捣鼓了一阵,最后只好举手投降。

看着他们俩茫然的表情,我满意的挖苦到:这个都是很基础的知识,你们竟然不知道?!

小A : 要么你说下答案,让我们也学习下。

心想这下让你们开开眼界,我轻轻的喝了口水,然后慢慢的说道:

想要说明这个问题,首先要理解事务的原子性是怎么保证的?

小A:是 redo log 和 undo log?

我:回答正确!

但是仅仅知道这个还不够,还要了解他们的过程。例如:我们把数据库中 A=1 改成 A=2 经历了什么?

  1. start transaction
  2. 记录 A=1 到 undo log;
  3. Update A=2 修改内存数据
  4. 记录 A=2 到 redo log
  5. 将 redo log 刷新到磁盘
  6. Commit
  7. 真实数据写入磁盘

小B: 没图你说个毛线?!

我:......

说完后,我又给他们画出了一个流程图:

update流程图.png
update流程图.png

小A:流程我是看懂了,可是还是有点迷糊,如果事务不提交,数据还是需要回滚啊?redolog 又能有什么关键性的作用?

我:先别急,我们还需要再深入了解下 redo log。

我继续说道:

redo log 分为两个部分:日志缓冲(redo log buffer,简称 log buffer)和日志文件(redo log file),log buffer 日志在内存中存储是易丢失; log file 是存入了磁盘,是持久化存储。

我们再从 redo log 的角度看下问题更新的流程:

  1. 事务开启时,事务中的操作,都会先写入存储引擎的日志缓冲。
  2. 在事务提交之前,缓冲的日志都需要提前刷新到磁盘上持久化,这就常说的“日志先行”(Write- Ahead-Logging)。
  3. 当事务提交之后,在内存映射的数据文件才会慢慢刷新到磁盘。

所以,在事务提交前我们已经修改的数据都 "记录"了下来,即使后面有故障我们也能回滚或者继续执行

说道这里,我渐渐觉得他们的有点明白了 ,看来是时候再让他们再深入一下。

我:咳咳,问题还没有结束,到这里才能够回答上面那个问题的一半。

小A: TM 还有??

我:当然,这都是基础知识。。

下面我们还需要聊一下 redo log 的持久化策略,因为 redo log 保证了事务的原子性,怎么能保证 redo log 持久化到磁盘呢?

Redo log 的日志写入是调用操作系统的 fsync 操作。因为 MySQL 是工作在用户空间,MySQL 的 log buffer 处于用户空间的内存中。如果要写入到磁盘上的 log file 中 ,中间还要经过操作系统内核空间的 os buffer(系统缓冲区),调用 fsync() 的作用就是将 OS buffer 中的日志刷到磁盘上的 log file 中。

听到这个他们两个有点懵,为了不让小B 说话,我赶紧找了张图:

log buffer 写入方式.png
log buffer 写入方式.png

MySQL 支持用户修改参数,来控制 log buffer 如何写入到磁盘中。这个参数有三个值,分别是 0,1,2。

  • 当 0 的时候,事务提交时候,不会直接把 log buffer 写入到 os buffer(系统缓冲区), 而是 os buffer 每 1s 去同步 log buffer的内容,并同时写入到磁盘中
  • 当 1 的时候,事务提交会直接把 log buffer 的数据同步写入 os buffer ,并同时写入到磁盘中
  • 当 2 的时候,每次提交都仅写入到 os buffer ,然后是每秒调用 fsync() 将 os buffer 中的日志写入到 log file on disk。

log buffer 写入方式
log buffer 写入方式

上面的 3 个参数,会对数据可靠性和性能带来不同的影响。

性能

可靠性

0

低,MySQL崩溃会丢失 1 s数据

1

高,不会丢失数据

2

中,MySQL 崩溃不会丢失数据,系统崩溃会丢失 1s数据

说道这里,你们应该都明白了吧。我说道

小B: 你快赶紧说完答案下班, 小B 不耐烦的说道

我们再回到问题本身:如果更新 1000w+ 数据,在更新的过程中服务器宕机了,会产生什么结果?

首先,log buffer 的写入方式的参数默认值是 1 ,我们默认没有修改过。

其次,我们需要知道宕机的类型,服务器僵死?还是 MySQL 崩溃?

最后,确定在更新的哪一步发生了崩溃。

update 流程图.png
update 流程图.png

我们在看这几个过程, 由于采用的 log buffer 参数为 1 ,即可靠性最高的等级,所以 MySQL 崩溃和服务器僵死产生的效果是一样的。

最终,可以得到结论:

如果崩溃发生在 5 之前,则事务没有提交,只能数据回滚

如果发生在 5 之后,则在重启后,可以选择继续提交事务或者回滚

如果发生在 6 之后,事务已经提交,重启后 MySQL 根据 redo log 把数据写入内存和刷入磁盘。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云数据库 SQL Server
腾讯云数据库 SQL Server (TencentDB for SQL Server)是业界最常用的商用数据库之一,对基于 Windows 架构的应用程序具有完美的支持。TencentDB for SQL Server 拥有微软正版授权,可持续为用户提供最新的功能,避免未授权使用软件的风险。具有即开即用、稳定可靠、安全运行、弹性扩缩等特点。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档