对 mysql 事务的认识,再不懂我就捶死我自己!

发布于: 2020 年 07 月 06 日
对mysql事务的认识,再不懂我就捶死我自己!

微信搜索BGM7756,免费获取本文提及资料!



引言

今天回头继续讲讲数据库系列的文章。这篇文章属于mysql数据库系列,我们来谈谈事务方面的常见面试题。那么,具体题目有下面这些:1、讲讲为什么用事务?事物的四大特性?事务的隔离级别知道吧,你们生产用哪种?2、Innodb中ACID具体是如何实现的?3、redo log和binlog的一致性如何保证?4、大事务有哪些坏处?生产上遇到过大事务么?你怎么排查和解决的?5、你有遇到过数据库宕机重启,事务丢失的情况么?6、可重复读是怎么实现的?



再三强调,每个问题都仔细看!都是高频题!切勿遗漏!



正文

1、讲讲为什么用事务?事物的四大特性?事务的隔离级别知道吧,你们生产用哪种?回答:为什么用事务?



这个问题从事务的四大特性:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability),这四个角度去答。例如原子性的角度,张三给李四转账,只有当张三账户的钱转走了,并且李四账户的钱收到了之后转账事务才能提交。如果原子性无法保证,就会出现张三的钱转走了,李四却没收到钱的情况!



Come on!这种easy回答,我就不一个个举例了,过!



至于生产用哪种隔离级别?答Read Commited或者Repeatable都行,有道理即可。例如,我用了Read Commited,因为这个隔离级别够用了,用不上间隙锁!详情可以参照《互联网项目中mysql应该选什么事务隔离级别》这篇文章!



另外,额外记住Repeatable是默认的隔离级别即可!至于另外两个隔离级别,Read uncommitted,一个事务能读到另一个事务未提交的数据,隔离性都无法满足,不用这个隔离级别。另外一个隔离级别Seriallzable,在这个隔离级别下,MVCC机制都无法满足,数据库并发性非常差,不用这个隔离级别。



ps:这个问题其实考察的是你对各个隔离级别的理解,所以务必牢记各个隔离级别的区别!



2、Innodb中ACID具体是如何实现的?回答:老题了,详细版,可以看这篇文章《程序员,知道Mysql中事务ACID的原理吗?》这里给出简单回答,



  • (1)利用undo log保证原子性

  • (2)利用redo log保证持久性

  • (3)利用锁和MVCC机制保证隔离性

  • (4)通过原子性、持久性、隔离性来保证一致性



3、redo log和binlog的一致性如何保证?回答:此题,先回忆一下redo log和binlog的区别!redo log 记录的是数据的物理变化,所以叫物理日志,记录的是是物理修改的内容(xxxx页修改了xxx)。当我们修改数据的时候,写完内存了,但数据还没真正写到磁盘的时候。此时我们的数据库挂了,我们可以根据redo log来对数据进行恢复!



binlog 记录的是数据的逻辑变化,所以又叫逻辑日志,statement模式下记载的是update/delete/insert这样的SQL语句,主要用来主从复制和恢复数据用。



这二者功能很像,都是用作”恢复“的!因此这二两个日志必须保证逻辑上一致,否则就会出现数据错乱。例如,我们先写redo log再写binlog。在redo log写完后,宕机了,此时binlog来不及写。那么重启后,数据能够根据redo log进行恢复,但是binlog没记录这个语句。那么,我们在利用这个binlog恢复数据的时候,就会出现丢失数据的情形!



mysql怎么解决的?这里考察的是mysql的内部XA事务!俗称日志的两阶段提交协议!也就是说,将事务提交分为了两个阶段,prepare阶段和commit阶段!



prepare:写入redo log,并将回滚段置为prepared状态,此时binlog不做操作。



commit:innodb释放锁,释放回滚段,设置提交状态,写入binlog,然后存储引擎层提交。



mysql数据库怎么进行崩溃恢复的?1> 崩溃恢复时,扫描最后一个Binlog文件,提取其中的xid;2> InnoDB维持了状态为Prepare的事务链表,将这些事务的xid和binlog中记录的xid做比较,如果在binlog中存在,则提交,否则回滚事务。



最后,这道题蓝绿大厂,开水团,宇宙条都问过!



4、大事务有哪些坏处?生产上遇到过大事务么?你怎么排查和解决的?回答:大事务,有的文章又称之为长事务,顾名思义,执行时间很长的事务!



至于坏处,例如事务执行时间太长,会造成大量的阻塞和锁超时,容易造成主从延迟.另外,大事务如果执行失败,回滚也会很耗时…(省略一千字)



怎么排查?so easy!监控information_schema.Innodb_trx表,设置长事务阈值,超过就报警 / 或者 kill;



下面语句是查询持续时间超过60s的事务



 select * from information_schema.innodb_trx where TIME_TO_SEC(timediff(now(),trx_started))>60;



一般在生产中,会将监控大事务的语句,配成定时脚本,进行监控。



至于怎么解决?结合业务场景,优化SQL,将一个大事务拆成多个小事务执行,或者缩短事务执行时间即可。



5、你有遇到过数据库宕机重启,事务丢失的情况么?回答:此题考查的是,sync_binlog配置(控制binlog刷盘时机)和innodb_flush_log_at_trx_commit配置(配置redolog刷盘时机)由于这两个配置的搭配组合很多种,一一举例,太麻烦了,我随意举几个例子(1)innodb_flush_log_at_trx_commit=1 和 sync_binlog=0这是mysql的默认配置,表示每次事务提交时都将 redo log 直接持久化到磁盘.但是MySQL不控制binlog的刷新,由操作系统自己控制它的缓存的刷新。



如果你答的是这套配置,那风险就是一旦系统宕机,在binlog_cache中的所有binlog信息都会被丢失。于是乎,你可以再结合上自己的公司背景来答。例如,我们公司规模不大,大家没有技术沉淀,因此没有修改过mysql的默认配置。但是,我私底下有过了解(突出自己勤奋好学),在该配置情况下,这两个参数的值为XXX,理论上操作系统宕机,有XXX的风险。但是,操作系统宕机的几率很低,因此我们也没怎么遇见过事务丢失的情况。



(2)innodb_flush_log_at_trx_commit=1 和 sync_binlog=1很多文章提到的双1配置。表示每次事务提交时都将redolog直接持久化到磁盘,binlog也会持久化到磁盘。



无疑,这个配置一致性最好,不会丢数据,但是性能是最差的。如果你答的是这套配置,你可以这么答,我们系统涉及到一些金钱相关的业务逻辑,宁愿慢,也不能一致性出错,因此我们的配置为XXX。至于性能问题,我们可以通过一些缓存,或者异步化设计进行改良。



(3)innodb_flush_log_at_trx_commit=2 和 sync_binlog=0性能最好的一套配置,表示每次事务提交时,只是把redolog写到OS cache,隔一秒,MySQL主动将OS cache中的数据批量fsync。而MySQL不控制binlog的刷新,由操作系统自己控制它的缓存的刷新。风险就是,操作系统一旦宕机,会丢数据。



这套配置下并发性最好。如果答这个配置,你可以说我们业务对并发的要求相对高一些,因此修改过XX参数。但是有操作系统宕机,丢数据的风险。但是,在实际环境中,操作系统崩溃的概率相比MySQL应用程序崩溃的概率,小很多…(自由发挥)



不一一举例,言之有理即可!记住,全靠一张嘴,说得通就是你的!说不通,回去总结一下,换一家继续说!无他,唯熟尔!



6、可重复读是怎么实现的?回答:这道题坦白说,我不懂面试官想听哪个角度的回答。我只能说一下我的理解,主要有以下两个原因(1)利用间隙锁,防止幻读的出现,保证了可重复读幻读的问题存在是因为新增或者更新操作,这时如果进行范围查询的时候(加锁查询),会出现不一致的问题,这时使用不同的行锁已经没有办法满足要求,需要对一定范围内的数据进行加锁。



(2)MVCC的快照生成时机不同在可重复读这个隔离级别下,遇到当事务中的第一个SELECT请求才创建read view,因此你是无法读到其他事务提交的更改。而在读已提交这个隔离级别下,每个SELECT都会获取最新的read view,因此你能读到其他事务提交的数据。



因为,这个View生成时机不同,所以实现了可重复读。此题答案,有不同见解可以讨论一下。反正我是没懂,这一题究竟是想问什么!



总结

OK,希望本文大家有所收获!



本文其实对事务方面的问题没讲全,因为在面试的时候通常会结合spring的事务和分布式事务来问,例如什么情况下spring事务会失效啊,巴拉巴拉。

微信搜索BGM7756,免费获取以下面试资料!



关于面试我还通过一些渠道整理了需要大厂真实面试主要有:蚂蚁金服、拼多多、阿里云、百度、唯品会、携程、等初级,中级,高级Java面试题集合,附带超详细答案,希望能帮助到大家。





用户头像

还未添加个人签名 2020.07.02 加入

还未添加个人简介

评论

发布
暂无评论
对mysql事务的认识,再不懂我就捶死我自己!