引言
今天回头继续讲讲数据库系列的文章。这篇文章属于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数据库怎么进行崩溃恢复的?
- 崩溃恢复时,扫描后一个Binlog文件,提取其中的xid;
- 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的刷新,由操作系统自己控制它的缓存的刷新。
如果你答的是这套配置,那风险就是一旦系统Crash,在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事务会失效啊,巴拉巴拉。
但是将这两块内容都放在这个文章中,此文过长,因此回头补上!希望大家多多关注!