面试官:谈谈你对mysql事务的认识?
引言
今天回头继续讲讲数据库系列的文章。这篇文章属于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;
相关文章