欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

实现MySQL事务的四大功能:面试官的最后拷问

最编程 2024-03-11 17:47:49
...

文章目录

  • 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事务是怎么实现的
mysql事务隔离级别之锁实现原理,脏读、不可重复读、幻读出现原因及解决方案

视图的概念