自导自演的面试现场之--你竟然不了解MySQL的组提交?

Hi,大家好!我是白日梦!本文是MySQL专题的第 26 篇。

下文还是白日梦以自导自演的方式,围绕“组提交”展开本话题。看看你能抗到第几问吧

换一种写作风格,自导自演面试现场!感觉这样还是比较有趣的,欢迎大家订阅我的MySQL专题,公众号首发!持续更新中~

点击阅读原文,格式会好看一点哦~

点击阅读原文,格式会好看一点哦~

点击阅读原文,格式会好看一点哦~

公众号后台回复:数据库 可以参与抽奖活动,3本《MySQL技术内幕-InnoDB存储引擎》

欢迎关注白日梦,公众号首发!持续连载中



库尼奇瓦,同学,听说你上一面表现的还可以,这一面要不咱继续?


嗯,好啊!




好,你了解MySQL的两阶段提交不?说说看


嗯,了解。 简单来说两阶段提交就是将事务提交分成两部分,经过这两步处理后,我们称这个事务被提交了。




嗯,那两个阶段呢?你说说看!


可以看下面这张图:我们当执行commit命令时,会按照如下进行。


第一阶段:写redo log,并标识上prepare。

-- 中间穿插写binlog --

第二阶段:写redo log,完成事务的最终提交。




嗯,那你知不知道为什么事务的提交要分成两个阶段?


嗯,你看上图中的两阶段是redolog-prepare、redolog-commit这两个阶段,然后在这两个阶段中又写了binlog。


这个redolog主要提供的能力是对事务进行rollback、并且redolog是存储引擎层面记录的日志。


而binlog的作用是方面我们进行数据的备份以及MySQL集群主从之间的数据同步使用,并且binlog是MySQL上层,也就是Server层面记录的日志。


综上:MySQL的两阶段提交是为了保证redolog、binlog两者在逻辑上的一致性。才能进一步保证。事务的回滚、数据备份、已经MySQL集群之间的数据才是安全可靠的




嗯,那你能不能举个例子,更直观的告诉我,redolog、binlog两者在逻辑一致的必要性以及用途?


嗯,好啊!,比如这个例子:


在这个例子中,进行完第三步之后,没来得及写binlog,然后主库就挂了,事务也没来得及提交。


这时主库从新启动之后会选择将这个事务丢弃,因为如果它将事务提交了,从库们相对主库来说就少了这条数据,造成了主从数据不一致问题




嗯,在看这个例子

在这个例子中主库宕机前虽然也没有完成对事务对提交,但是它已经写了redolog-prepare、还写了binlog,binlog写完之后很可能从库已经收到这个最新的binlog,并且将这个binlog中的数据回放到自己身上了。


所以主库重新启动之后呢,会选择按照redolog将宕机前的事务状态从内存中恢复出来,也就是说把内存中的缓存页改成脏数据页,然后提交事务


这时主库如果不提交事务,那主库就比从库少了一条数据,同时会造成主从不一致的情况出现。



白日梦补充:
本小节我们重点看MySQL的组提交,而不是两阶段提交,想更多的两阶段提交的故事,可以点击查看白日梦写的:全网最牛X的两阶段提交笔记


嗯,你说的没错。我有个问题哈,你看你画的这张图,事务提交时写了好几次日志。


而且一般我们线上的数据库都有这两个配置

# 该参数控制binlog的落盘时机# 设置为1表示当事务被提交时将binlog落盘# 设置为0表示由文件系统自己控制binlog的落盘时机sync_binlog=1# 该参数控制redolog的落盘时机  # 设置为1表示当事务被提交时将redolog落盘innodb_flush_log_at_trx_commit=1

那你有没有想过:即使写日志是磁盘的顺序IO速度极快,即使固态硬盘的性能很🐂,那终究也还是在跟磁盘打交道啊! 


那你是否了解MySQL针对这个现状有什么优化的落地实现方案吗?


我提醒你一下,比如:组提交


哦!我还真了解一些group commit的概念!


在MySQL早期的设计中,所谓的组提交,也就是 group commit 实际上提交的是group commit redolog。(而不是binlog)



而且MySQL写redolog并不是产生一条redolog就写一条redo log的!而是以redo log block为单位写入磁盘,而且在redo log block 之上还有个redo log buffer的概念


总结来说就是:写redolog要分成2大步:


Step1:将redolog写入内存buffer中。

Step2:将redolog fsync到磁盘中。



白日梦补充:
点击查看白日梦的redolog笔记:传说中的redo log到底是什么?


点击查看白日梦笔记:fsync系统调用到底是什么?


而组提交的概念是说:当进行fsync时,一下子将多个事务日志写入到磁盘中。 通俗来讲,假如说你写一次磁盘需要10ms,如果我们按条每个redolog都写一次磁盘的话,10条redolog就需要100ms的耗时。


现在我们有了组提交,可以一次fsync将10条redolog一次磁盘IO就持久化到磁盘中,耗时可能也就十几ms的样子。性能是有所提升的。




嗯,你说的没错!而且我还注意到了你说的一个细节问题,你上面说,原来的group commit针对的是redolog,而不是binlog。这个缘由你能展开说一下吗?


.....


好的,这个问题就得从binlog的作用说起了。MySQL不可能无缘无故的就写binlog,它之所以记录binlog是因为它想获取到写binlog带给它的红利。



那什么红利呢?其实前面说了很多遍了,主要的红利就是:binlog给了MySQL搭建集群的可能性,以及数据备份的能力。 


而且默认情况下binlog是不会开启的。其实说白了,如果你不需要binlog带给我们的能力,比如你偏偏就不需要做数据的备份、或者搭建集群,那么其实你直接使用默认配置不开启binlog就好了。此时的两阶段提交也不复存在。


但是事实并不是这样的,线上的数据库都是以集群的形式存在、并且也需要数据备份。也就是说binlog是开启的状态。事务的提交也是走的两阶段提交。


 那,回到我们组提交的话题来看,其实在早期的MySQL中,并没有一个很好的机制让 组提交和两阶段提交共存的!




嗯,为什么这么说?


嗯,其实你大概一想就知道为什么了。根本原因就在用 binlog是MySQL的上层(Server层)记录的。而redolog是下层的存储引擎层记录的。


所以你突然整一个redolog的组提交出来,那两阶段提交怎么办?你为了性能哐当一下将内存中的redolog全落盘了,但是两阶段提交中每一步写日志的顺序是有要求的呀。它俩正好相违背。


所以其实在InnoDB1.2版本之前,当开启binlog使用两阶段提交时,组提交会失效....




嗯,你说的没错。那后来没有比较好的解决方式呢?


嗯,其实是有的,早在2010年facebook的MySQL技术组,Percona公司就给出了解决方案。后来由MariaDB数据库的开发人员Kristian Nielsen完成了最终的完美解决方案。


所以如果你使用的是MySQL5.6或者更好的版本,它就在两阶段提交的概念上,还加持上了组提交的buff,性能相对之前有了很多的提升。




嗯,那你可否大概说一下如何实现的?


嗯嗯,想了解它的具体的实现可以看这张图:

我稍微解读一下这张图:总体来说分为三个部分,分别是Flush阶段、Sync阶段、Commit阶段


并且它引入了一个新的概念:队列,事务按提交的顺序进入队列中,队列有先进先出的概念,最先进入队列的事务被成为leader


在Flush阶段就是将当前队列中的所有事务的binlog统一写入内存中,Sync就是将当前队列中的所有事务的binlog通过一次fsync统一写入到磁盘中。(相当于批量将binlog落盘)


最后的commit阶段,由队列中的leader根据一定的顺序调用存储引擎层的事务提交,完成两阶段的第二阶段提交。事务结束...



 这时就会形成一个比较理想的状态,当一组事务发生Commit时,新的事务可以去Flush。让整个过程中的组提交不断的生效。




嗯,你继续说


但是如果每次队列中都只有一个事务,那可能会导致性能还不如之前没有这种队列的设计




嗯,你继续往下说。


还有就是我还了解关于组提交相关的一个控制参数

binlog_max_flush_queue_time

这个参数可以控制Flush阶段的等待时间。默认情况下该参数设置为0,推荐设置也为0。


如果我们不把它设置为0,意思就是说想让队列中的事务攒一攒,攒多了再统一处理,想获取到一次fsync将大量日志落盘带来的速度快的红利。但是这样同样会导致事务的响应时间变慢。




嗯!你说的没问题,整体上看你的回答的还可以!


我没有问题了,你还有什么想问我的吗?


没有问题了,感谢大佬百忙抽空来给我面试!




哈哈,客气!好好准备,期待你下一面优秀的表现


推荐阅读

  1. MySQL的修仙之路,图文谈谈如何学MySQL、如何进阶!(已发布)
  2. 面前突击!33道数据库高频面试题,你值得拥有!(已发布)
  3. 大家常说的基数是什么?(已发布)
  4. 讲讲什么是慢查!如何监控?如何排查?(已发布)
  5. 对NotNull字段插入Null值有啥现象?(已发布)
  6. 能谈谈 date、datetime、time、timestamp、year的区别吗?(已发布)
  7. 了解数据库的查询缓存和BufferPool吗?谈谈看!(已发布)
  8. 你知道数据库缓冲池中的LRU-List吗?(已发布)
  9. 谈谈数据库缓冲池中的Free-List?(已发布)
  10. 谈谈数据库缓冲池中的Flush-List?(已发布)
  11. 了解脏页刷回磁盘的时机吗?(已发布)
  12. 用十一张图讲清楚,当你CRUD时BufferPool中发生了什么!以及BufferPool的优化!(已发布)
  13. 听说过表空间没?什么是表空间?什么是数据表?(已发布)
  14. 谈谈MySQL的:数据区、数据段、数据页、数据页究竟长什么样?了解数据页分裂吗?谈谈看!(已发布)
  15. 谈谈MySQL的行记录是什么?长啥样?(已发布)
  16. 了解MySQL的行溢出机制吗?(已发布)
  17. 说说fsync这个系统调用吧! (已发布)
  18. 简述undo log、truncate、以及undo log如何帮你回滚事物! (已发布)
  19. 我劝!这位年轻人不讲MVCC,耗子尾汁! (已发布)
  20. MySQL的崩溃恢复到底是怎么回事? (已发布)
  21. MySQL的binlog有啥用?谁写的?在哪里?怎么配置 (已发布)
  22. MySQL的bin log的写入机制 (已发布)
  23. 删库后!除了跑路还能干什么?(已发布)
  24. 自导自演的面试现场,趣学数据库的10种文件(已发布)
  25. 大型面试现场:一条update sql执行都经历什么?(已发布)
  26. 大型翻车现场:如何实现记录存在的话就更新,如果记录不存在的话就插入。(已发布)

最后,欢迎关注白日梦的公号哦~

换一种写作风格,自导自演面试现场!感觉这样还是比较有趣的,欢迎大家订阅我的MySQL专题,公众号首发!持续更新中~

posted @ 2021-03-15 18:31  赐我白日梦  阅读(557)  评论(1编辑  收藏  举报