MySQL事务四大特性实现:面试官的终究拷问

MySQL事务四大特性实现:面试官的终究拷问

估计大家对事务的四大特性ACID、事务的四种隔离机制的概念了熟于心,有些面试官问完这些就不追问了,但是有些面试官会来个终究拷问,你了解MySQL事务四大特性的实现原理吗?这时候就该祭出本文了。

前言知识

具体详细可以参考博客MySQL事务的隔离级别

数据库事务的四大特性

  • Atomic,原子性,事务的所有SQL操作作为原子工作单元执行,要么全部执行,要么全部不执行;

  • Consistent,一致性,事务完成后,所有数据的状态都是一致的,即A账户只要减去了100,B账户则必定加上了100;

  • Isolation,隔离性,如果有多个事务并发执行,每个事务作出的修改必须与其他事务隔离;

  • Duration,持久性,即事务完成后,对数据库数据的修改被持久化存储。

事务的四种隔离级别

隔离级别 脏读 不可重复读 幻读
未提交读(Read uncommitted) 可能 可能 可能
已提交读(Read committed) 不可能 可能 可能
可重复读(Repeatable read) 不可能 不可能 可能
可串行化(Serializable) 不可能 不可能 不可能

原子性的实现

MySQL数据库事务的原子性是通过undo log实现的。

事务的所有修改操作(增、删、改)的相反操作都会写入undo log,比如事务执行了一条insert语句,那么undo log就会记录一条相应的delete语句。所以**undo log是一个逻辑文件,记录的是相应的SQL语句**一旦由于故障,导致事务无法成功提交,系统则会执行undo log中相应的撤销操作,达到事务回滚的目的。

undo log也可以实现MVCC(多个版本行控制),下文的隔离性的实现将会详细介绍

持久性的实现

MySQl数据库事务的持久性是通过redo log实现的。

事务的所有修改操作(增、删、改),数据库都会生成一条redo日志记录到redo log.区别于undo log记录SQL语句、redo log记录的是事务对数据库的哪个数据页做了什么修改,属于物理日志。

redo日志应用场景:数据库系统直接崩溃,需要进行恢复,一般数据库都会使用按时间点备份的策略,首先将数据库恢复到最近备份的时间点状态,之后读取该时间点之后的redo log记录,重新执行相应记录,达到最终恢复的目的。

日志文件的刷新策略

undo logredo log并不是直接写到磁盘,而是写下入log buffer.再等待合适的时机同步到OS buffer,再由操作系统决定刷新到磁盘的时间。如图

MySQL主要由三种日志刷新策略,默认为第一种。三种策略,安全性依次下降,效率依次上升

  • 每次事务提交写入OS buffer,并调用fsync刷新到磁盘
  • 每秒写入OS buffer,并调用fsync刷新到磁盘
  • 每秒提交写入OS buffer,然后每秒调用fync刷新到磁盘

隔离性的实现

已提交读(允许可重复读)

实现策略:数据的读取不加锁,数据的写入、修改、删除需要加行锁,可以克服脏读,但无法避免不可重复读

如下图是一个脏读场景,事务T2读取了T1未提交的数据。

使用加锁策略后,T1写数据x时,先获取了x的锁,导致T2的读操作等待,T1进行数据回滚后,释放锁,T2可以继续读取原来数据,不存在读取到脏数据的可能。

可重复读(允许幻读)

实现策略:MVCC(多个版本行控制)策略

下图是一个不可重复读的场景。由于T1的更新操作,导致T2两次读取的数据不一致。

单纯加行锁是无法解决的,T2先读取x值,T1之后经过加锁、解锁步骤,更新x的值,提交事务。T2再读的话,读出来的是T1更新后的值,两次读取结果不一致。

是时候祭出MVCC这个大招了

前面讲的行级锁是一个悲观锁,而MVCC是一个乐观锁,乐观锁在一定程度上可以避免加锁操作,因此开销更低。InnoDB的MVCC实现,是通过保存数据在某个时间点的快照来实现的。一个事务,不管其执行多长时间,其内部看到的数据是一致的。也就是事务在执行的过程中不会相互影响。

具体实现如下:

MVCC,通过在每行记录后面保存两个隐藏的列来实现:一个保存了行的创建时间,一个保存行的过期时间(删除时间),当然,这里的时间并不是时间戳,而是系统版本号,每开始一个新的事务,系统版本号就会递增

  • selelct操作

    • 只查找版本早于(包含等于)当前事务版本的数据行。可以确保事务读取的行,要么是事务开始前就已存在,或者事务自身插入或修改的记录。
    • 行的删除版本要么未定义,要么大于当前事务版本号。可以确保事务读取的行,在事务开始之前未删除。
  • insert操作。将新插入的行保存当前版本号为行版本号。

  • delete操作。将删除的行保存当前版本号为删除标识。

  • update操作。变为insert和delete操作的组合,insert的行保存当前版本号为行版本号,delete则保存当前版本号到原来的行作为删除标识。

通过MVCC策略,可以确保一个事务里面读取的是同一个数据库版本快照。

终究难题

MVCC可以解决幻读吗?思考完,mysql什么都难不倒你了。

参考文章

MVCC 能解决幻读吗?

Innodb中的事务隔离级别和锁的关系

面试官:MySQL事务是怎么实现的

发布了208 篇原创文章 · 获赞 847 · 访问量 18万+

猜你喜欢

转载自blog.csdn.net/zycxnanwang/article/details/105742160
今日推荐