《朱子语类》里面有句话:“知其然知其所以然!”,笔下回曰:“然也!” 。

前两天看到一张图,应该是大学毕业不久后保存的,很现实,当然,也有点黑色幽默的感觉,一起来看一看。

感慨什么的,就不吐槽了,过去的生活中或许会有遗憾,未来的日子里都会充满期待与幻想,不过么,数风流人物,还看今朝!

干了这碗鸡汤,咱们继续。MySQL数据库,大家很早应该就接触到了,毕竟数据存储,是一切业务必不可少的阶段。而MySQL的写入操作,都离不开事务这个东东,当然也不离开redo log这个“中间商”。

数据存储自然是持久的,大家用最朴实的思想想一下,也明白,数据最终一定是存储到了磁盘里面。

笔下要把“个人精通java字符串的编写!”这句话写到磁盘的文件中,首先是在JVM堆中创建一个字符串,然后写入到“简历.txt”中,历程如下:

先从应用程序缓存到OS缓存,再到磁盘,而且,把这句话写入文件中还是非常快的。但是,如果磁盘中已经有了数万乃至数十万的文件数据,用户突然对其中的某一条或者某几条数据进行写操作,那样速度还会很快么?定位数据的位置,分配空间之类的估计就会话费不少的时间。 从度娘那里截了个图,大家可以瞅瞅,有兴趣的话,可以深入下,这里就不重点说了。

对于MySQL来说,每次对数据库file文件的修改都是一次事务,当然直接一句insert 或 update后台就默认帮你提交了,不需要你关注那么多细节,而且这种简单的事务几乎不存在事务并发问题,毕竟耗时短,提交快,而且总有一个先后(下面说为什么有先后)。

那么复杂的事务呢?MySQL是如何做到高速读写,不仅实现事务的提交回滚,而且可以保证数据的一致性呢?我们模拟个事务并发来看看。

首先建立一张简单的数据表:


CREATE TABLE `student`  (`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键id',`stu_name` varchar(10) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '学生姓名',`stu_code` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '学生编号',`school_code` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '学校编码',`create_time` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',PRIMARY KEY (`id`) USING BTREE,INDEX `idx_code`(`stu_code`) USING BTREE COMMENT '学生编码索引'
) ENGINE = InnoDB AUTO_INCREMENT = 22 CHARACTER SET = latin1 COLLATE = latin1_swedish_ci COMMENT = '学生表' ROW_FORMAT = Dynamic;-- ----------------------------
-- Records of student
-- ----------------------------
INSERT INTO `student` VALUES (23, '张三1', 'ce1482d6b2d211ebaa2abce92fdc3ad6', '20', '2021-05-12 11:33:24');
INSERT INTO `student` VALUES (24, '张三2', 'ce14a163b2d211ebaa2abce92fdc3ad6', '20', '2021-05-12 11:33:24');
INSERT INTO `student` VALUES (25, '张三3', 'ce14b888b2d211ebaa2abce92fdc3ad6', '20', '2021-05-12 11:33:24');
INSERT INTO `student` VALUES (26, '张三4', 'ce14ebb6b2d211ebaa2abce92fdc3ad6', '20', '2021-05-12 11:33:24');
INSERT INTO `student` VALUES (27, '张三5', 'ce151bc1b2d211ebaa2abce92fdc3ad6', '20', '2021-05-12 11:33:24');
INSERT INTO `student` VALUES (28, '张三6', 'ce1538d0b2d211ebaa2abce92fdc3ad6', '20', '2021-05-12 11:33:24');
INSERT INTO `student` VALUES (29, '张三7', 'ce155866b2d211ebaa2abce92fdc3ad6', '20', '2021-05-12 11:33:24');
INSERT INTO `student` VALUES (30, '张三8', 'ce1578beb2d211ebaa2abce92fdc3ad6', '20', '2021-05-12 11:33:24');
INSERT INTO `student` VALUES (31, '张三9', 'ce1591b5b2d211ebaa2abce92fdc3ad6', '20', '2021-05-12 11:33:24');
INSERT INTO `student` VALUES (32, '张三10', 'ce15ac92b2d211ebaa2abce92fdc3ad6', '20', '2021-05-12 11:33:24');
INSERT INTO `student` VALUES (33, '张三11', 'ce15c86ab2d211ebaa2abce92fdc3ad6', '20', '2021-05-12 11:33:24');
INSERT INTO `student` VALUES (34, '张三12', 'ce15e20eb2d211ebaa2abce92fdc3ad6', '20', '2021-05-12 11:33:24');
INSERT INTO `student` VALUES (35, '张三13', 'ce15fa18b2d211ebaa2abce92fdc3ad6', '20', '2021-05-12 11:33:24');
INSERT INTO `student` VALUES (36, '张三14', 'ce1612cab2d211ebaa2abce92fdc3ad6', '20', '2021-05-12 11:33:24');
INSERT INTO `student` VALUES (37, '张三15', 'ce1628e3b2d211ebaa2abce92fdc3ad6', '20', '2021-05-12 11:33:24');
INSERT INTO `student` VALUES (38, '张三16', 'ce163cedb2d211ebaa2abce92fdc3ad6', '20', '2021-05-12 11:33:24');
INSERT INTO `student` VALUES (39, '张三17', 'ce16524bb2d211ebaa2abce92fdc3ad6', '20', '2021-05-12 11:33:24');
INSERT INTO `student` VALUES (40, '张三18', 'ce166a5fb2d211ebaa2abce92fdc3ad6', '20', '2021-05-12 11:33:24');
INSERT INTO `student` VALUES (41, '张三19', 'ce1680fbb2d211ebaa2abce92fdc3ad6', '20', '2021-05-12 11:33:24');
INSERT INTO `student` VALUES (42, '张三20', 'ce169964b2d211ebaa2abce92fdc3ad6', '20', '2021-05-12 11:33:24');
INSERT INTO `student` VALUES (43, '张三21', 'ce16b0b9b2d211ebaa2abce92fdc3ad6', '20', '2021-05-12 11:33:24');
INSERT INTO `student` VALUES (44, '张三22', 'ce16c723b2d211ebaa2abce92fdc3ad6', '20', '2021-05-12 11:33:24');
INSERT INTO `student` VALUES (45, '张三23', 'ce16decab2d211ebaa2abce92fdc3ad6', '20', '2021-05-12 11:33:24');
INSERT INTO `student` VALUES (46, '张三24', 'ce170106b2d211ebaa2abce92fdc3ad6', '20', '2021-05-12 11:33:24');
INSERT INTO `student` VALUES (47, '张三25', 'ce171d10b2d211ebaa2abce92fdc3ad6', '20', '2021-05-12 11:33:24');
INSERT INTO `student` VALUES (48, '张三26', 'ce173b93b2d211ebaa2abce92fdc3ad6', '20', '2021-05-12 11:33:24');
INSERT INTO `student` VALUES (49, '张三27', 'ce1759e3b2d211ebaa2abce92fdc3ad6', '20', '2021-05-12 11:33:24');
INSERT INTO `student` VALUES (50, '张三28', 'ce177224b2d211ebaa2abce92fdc3ad6', '20', '2021-05-12 11:33:24');

启动事务一(操作的数据id是34):

start transaction;select * from student where stu_code = 'ce15e20eb2d211ebaa2abce92fdc3ad6';
update student set school_code = 30 where stu_code ='ce15e20eb2d211ebaa2abce92fdc3ad6';
-- update student set school_code = 40 where stu_code ='ce15e20eb2d211ebaa2abce92fdc3ad6';
select * from student where stu_code = 'ce15e20eb2d211ebaa2abce92fdc3ad6';
commit;

可以看到最后查询的 school_code = 30 ,再启动事务2:

start transaction;select * from student where stu_code = 'ce15e20eb2d211ebaa2abce92fdc3ad6';
update student set school_code = 50 where stu_code = 'ce15e20eb2d211ebaa2abce92fdc3ad6';

我们可以看到school_code = 20,而且,修改的操作陷入了等待,也就是一直在执行中。也就是说发生了事务并发,触发了行锁,事务2进入了锁等待。

此时对于数据库来说school_code = 20,对于事务1来说,school_code = 30。如果事务一提交,那么事务二修改完成后,再查询,school_code = 50

在启动一个事务三,如果仅仅事务一提交,查询的话结果也是school_code = 30;

start transaction;
select * from student where stu_code = 'ce15e20eb2d211ebaa2abce92fdc3ad6';

说到这里,就不得不说下MySQL的一个机制了,MVCC(Multi-Version Concurrency Control ),也就是多版本并发控制。

事物的隔离级别分为Read uncommitted,Read committed,Repeatable read,Seralizable这四种,Mysql在可重复读隔离级别下保证事务较高的隔离性,其中必不可少的机制就是MVCC!其中一个重要部分就是undo log!

undo log实际上是就是一个日志版本链,比如,某一行数据被多个事务进行了修改,那么每次修改都会记录一个undo log 回滚日志,如果某次修改最终rollback了,那么就根据undo log日志,进行数据回滚。每条undo log 有三个关键字段,row_id、trx_id和roll_pointer,也就是该条数据主键id、事务的id和这条数据上一个版本的指针,通过事务id将undo log 串联成一个历史记录版本链,那么,事务的任何SQL查询结果从对应版本链里的最新数据开始逐条对比从而获取最终的快照结果。

根据上面的两个事务,衍生一个undo log版本链。

通俗的说下,就是在当前事务进行查询的时候,从当前记录向上查找,直到找到当前数据的提交的那条undo log,在这段记录中,如果没有本事务对该条数据的操作,那么就返回已经提交的数据,如果有当前事务对这条数据的修改,那么久返回当前事务修改的数据。

对着上面的栗子来看下:

就看事务二,第一次查询的时候,已提交的数据,也就是数据库的数据,school_code=20,查询的结果也就是20。

第二次查询的时候,事务一提交了,数据库的数据是school_code=40,但是当前事务对school_code=40进行了修改,改成了50,那就需要根据当前事务修改后的数据为主,故返回50,是不是挺清晰的?就是MVCC和MySQL的锁保证了事务的正确执行,是不是挺牛掰?

undo log 是事务数据查询,回滚等用到的,那么,redo log是干啥的?

MySQL数据库最终是以文件的形式存储在磁盘中,为了保证数据的持久性,对数据进行新增修改的时候,肯定要把数据写入到磁盘中的。而对磁盘数据进行修改的时候,也就是我们操作数据库的时候,肯定是随机修改其中的数据,这样的话,修改速度肯定满足不了我们的要求。

随机写性能比较差,肯定有优化方法呀!比如,计算机,CPU读写速度肯定都远远超过磁盘,如果每次操作都是直接对磁盘进行操作,无疑会拖慢CPU的读写速率,于是有了CPU缓存,内存条等中间存储设备!

MySQL也是采取类似的方法,对读写速率进行了优化,不过更为复杂,而且为了保证MySQL中数据的持久性和一致性,数据每次或者每隔很短的时间就要写入磁盘。而redo log就在其中扮演了一个十分重要的角色!(redo log 是innerDB独有的,下文的逻辑默认MySQL使用了innerDB引擎哈)

当MySQL事务提交时,首先会把数据写入缓存,也就是redo log buffer,调用了一个函数叫做WriteRedoLog。这个是每次事务提交都必须要写入的。

接着,肯定是要把应用级缓存写入到操作系统缓存呀,也就是要发起系统调用写文件write。当然这个什么时候写就看配置了,可以每次事务完成都写入到操作系统级别的缓存,也可以每隔一段时间写一次。不过这个时候文件还是没有进入磁盘的,如果发生宕机或者断电之类的,数据自然也就丢失了哦。

最后,再由操作系统将操作系统中的缓存(OS cache)数据,最终fsync到磁盘上,也就是顺序写到redo log,速度自然超过随机写一大截!这个时候,停电宕机什么也不会影响到数据了。

当然,这个时候也仅仅是将数据也到了redo 日志中,并未真正的写到数据库文件中,后面还有操作将redo log file同步到数据库,不过这一步已经影响不到数据的安全性了,不多说。

为了保证数据的持久性,也就是需要三步,如下图:

如果在写入OS cache之前,应用程序崩溃了,数据就丢失;

如果在写入redo log file 之前,机器宕机或者断电之类的,数据也丢失了。

所以,MySQL默认的是每次事务提交,都会写入到redo log file。

redo log file的写入虽然是顺序写,但是每次事务提交都写入磁盘,对性能的影响仍然是很大的。每隔1-2s批量写一次行不行?

行!

MySQL里面有个参数,innodb_flush_log_at_trx_commit,就是控制事务提交的时候,刷新数据的策略。

默认innodb_flush_log_at_trx_commit=1, 强一致性,也就是每次事务提交,既要刷新到OS cache,也要写入到redo log中去,不用担心数据丢失。

如果要性能最佳的话,设置innodb_flush_log_at_trx_commit=0,提交后,只是写入到redo log buffer,每隔1s,才将redo log buffer 中的数据批量写入OS cache,同时写入redo log。单次写改成批量写,速度肯定快很多的。当然,如果应用程序挂了或者操作系统挂了,就要丢失1s的数据。

再有就是折中一下,设置innodb_flush_log_at_trx_commit=2,提交后,每次写入到redo log buffer,write到OS cache,每隔一秒,OS cache 中的数据批量刷新到redo log文件。效率或许会比innodb_flush_log_at_trx_commit=0 差一点,但也不会相差太多,因为就是多一步内存写操作,速度不会有太大影响的。当然,如果操作系统宕机或者停电什么的,也是会丢失1s的数据的。

一般来说,innodb_flush_log_at_trx_commit=0与innodb_flush_log_at_trx_commit=2,效率相差几倍,与innodb_flush_log_at_trx_commit=1相比,效率要相差几十倍甚至上百倍。

不过,哪怕应用程序崩溃,操作系统一般也不会轻易宕机,停电什么的,对一般的大型机房很少会发生这种问题, innodb_flush_log_at_trx_commit=2已经足够,而且可以更容易的适应高并发业务。

当然,如果对数据一致性要求特别高,也可以设置innodb_flush_log_at_trx_commit=1,应对高并发的时候,可以加机器么~,分库分表什么的,就是业务麻烦了一点。

除了redo log之外,MySQL在执行事务提交的时候,还会将提交的SQL语句,写入到一个叫做binlog的文件中。

binlog是MySQL数据库上层产生的,记录了MySQL数据库执行更改的所有操作,不包括select和show这类操作,因为这类操作本身并没有修改数据。

redo log是在innerDB存储引擎产生,是innerDB独有,而且记录的是物理日志,而且大小是有限制的,超过固定大小之后,会返回起点,循环使用。

binlog 不循环使用的,在写满或者重启之后,会生成新的binlog文件,一般来说binlog文件比较大,用于恢复数据、主从复制搭建,而redo log 则是作为异常宕机或者介质故障后数据恢复使用。下面copy一张SQL执行 的逻辑图,大家可以瞅一瞅哈!

好了,今天的分享就到这里吧,感觉还行的话,点个赞呗~

人贵有志,学贵有恒!

若有志何须三更睡五更起?最无益只怕一日曝十日寒!

 no sacrifice ,no victory~

一文看懂 redo log 与undo log相关推荐

  1. 一文聊透binlog、redo log、undo log

    今日推荐 Web登录很简单?开玩笑!知乎热问:国家何时整治程序员的高薪现象?太可怕了!注解+反射优雅的实现Excel导入导出(通用版)Fluent Mybatis 牛逼!Nginx 常用配置清单这玩意 ...

  2. undolog 是binlog_mysql日志redo log、undo log、binlog以及作用看这篇就可以啦

    什么是事务日志? 事务要保证ACID的完整性必须依靠事务日志做跟踪,每一个操作在真正写入数据数据库之前,先写入到日志文件中如要删除一行数据会先在日志文件中将此行标记为删除,但是数据库中的数据文件并没有 ...

  3. 精讲 MySQL 事务日志:redo log 和 undo log

    来源:https://blog.csdn.net/demonson/article/details/104369733 innodb事务日志包括redo log和undo log.redo log是重 ...

  4. 必须了解的MySQL三大日志:binlog、redo log和undo log

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 作者: 六点半起床 juejin.im/post/68602522 ...

  5. 必须了解的mysql三大日志-binlog、redo log和undo log

    来源:https://juejin.im/post/6860252224930070536 日志是 mysql 数据库的重要组成部分,记录着数据库运行期间各种状态信息.mysql日志主要包括错误日志. ...

  6. 一文看懂 “极大似然估计” 与 “最大后验估计” —— 极大似然估计篇

    参考: 唐宇迪<人工智能数学基础>第8章 Richard O. Duda <模式分类>第三章 白板机器学习 P2 - 频率派 vs 贝叶斯派 频率学派还是贝叶斯学派?聊一聊机器 ...

  7. 一文看懂WS2812的呼吸灯实现

    一文看懂WS2812呼吸灯实现 1. 相关资料   WS2812是一个集控制电路与发光电路于一体的智能外控LED光源,外形一般为5050封装,每个LED灯珠为一个像素点,支持RGB无极调色,同时每颗灯 ...

  8. php-fpm进程的用户组,一文看懂PHP进程管理器php-fpm

    php-fpm是什么 php-fpm是PHP的一个进程管理器.php下面的众多work进程皆有php-fpm进程管理器管理. php-fpm的工作原理 php-fpm全名是PHP FastCGI进程管 ...

  9. 一文看懂推荐系统:召回07:双塔模型——正负样本的选择,召回的目的是区分感兴趣和不感兴趣的,精排是区分感兴趣和非常感兴趣的

    一文看懂推荐系统:召回07:双塔模型--正负样本的选择,召回的目的是区分感兴趣和不感兴趣的,精排是区分感兴趣和非常感兴趣的 提示:最近系统性地学习推荐系统的课程.我们以小红书的场景为例,讲工业界的推荐 ...

最新文章

  1. 《机器学习与数据科学(基于R的统计学习方法)》——2.11 R中的SQL等价表述...
  2. 物联网的层次结构如何划分?
  3. C盘满了怎么办?如何清理
  4. Modeling Filters and Whitening Filters
  5. 2020 年 Node.js 开发者调查报告
  6. oracle中forall in,oracle10g的forall功能加强
  7. 超实用的 Nginx 极简教程,覆盖了常用场景
  8. mysql+control+user_mysql的基本使用
  9. 【面试题22】栈的压入、弹出序列
  10. sql server 跟踪_SQL Server作业性能–跟踪
  11. java集合类(三)About Iterator Vector(Stack)
  12. 十代主板改win7_微星b460主板装win7系统及bios设置教程(支持十代usb驱动)
  13. ensp VRRP配置2
  14. 微信开放平台之第三方授权开发
  15. 马化腾绝地逢生:山重水复疑无路,柳暗花明又一村
  16. river歌曲表达的意思_Lost Rivers这首歌到底想表达什么?
  17. 视觉SLAM十四讲学习笔记——第十三讲 实践:设计SLAM系统
  18. iscc2022弱雪wp
  19. js统计字符串中字母个数以及统计最多字母数
  20. 海淘时代全面来临,海淘转运公司为何风生水起?

热门文章

  1. 安装centos6.5没有网,右上角也没有网络图标
  2. 畅联“5机”,华为云WeLink勇当数字化联接器
  3. error: ‘stol’ was not declared in this scope
  4. centos7 设置外部访问
  5. c语言编程求pai的近似值,c语言:求π的近似值
  6. 用自己打工挣的钱,他创办广告公司,总资产已达3000多万
  7. python点云可视化工具_救命!点云可视化(不需配置PCL)
  8. Linux系统Word转换PDF,文档字体乱码不显示问题解决
  9. 网页文字无法复制,怎么办?
  10. VBA:MsgBox函数