MySQL事务的基本特性(ACID)实现及隔离级别

1.什么是事务

事务是一个最小的,不可再分的工作单元,也就是在一个事务中的操作要么同时成功,要么同时失败

2.事务的基本特性(ACID)

(1)原子性 A (Atomicity) :一个事务是一个不可分割的工作单位,一个事务中的所有操作,要么同时成功,要么同时失败,不可能成功一半,失败一半。当事务在执行过程中如果失败的话,会进行回滚操作

(2)一致性 C (Consistency) :事务在执行完后的结果是和预期数据一致的,符合逻辑的;其他三个特性都是为了保证一致性的成立

(3)隔离性 I(Isolation) :事务与事务之间是相互隔离的,互不影响的

(4)持久性 D(Durability) :在事务成功完成之后,会立即将更改的数据写入数据库中,实现持久性的操作,接下来发生的错误都无法改变这一结果

3.事务特性(ACID)的实现与保证

(1)原子性的实现与保证

原子性的实现的关键是undo log,即回滚日志,在这个日志里面记录了一些事务需要回滚的信息,也就是一些反向执行的sql语句

例如:当我们要delete一条语句时,undo log里记录这一行的数据的一个insert语句,在事务失败需要回滚的时候会执行这个insert语句,其他的修改语句同理

(2)一致性的实现与保证

一致性的保证要从两个层面来说

在数据库层面,一致性其实是由其他三大特性来保证的,一致性是目的,其他三大特性是手段。也就是说,只要其他三大特性得到保证,那么数据库层面的一致性就能得到保证,那三大特性只要有一个没有保证,那么一致性就得不到保证

在业务代码层面,如果你的代码不符合日常的逻辑或者没有一些约束,那么数据的一致性也无法得到保证

例如:当A向B转账100元,但是你的代码只写了A的账户 -100 元,而B的账户 +50元或者不给B的账户加钱,那这就是不符合日常逻辑的代码,自然保证不了一致性

还有当A向B转账100元,但A的账户只有50元,那么就不应该转账成功,这个时候就需要程序员对数据做一些约束

(3)隔离性的实现与保证
1.事务的隔离级别

(1)未提交读(Read uncommitted):表示一个事务可以读到另一个事务还没有提交的数据,可能发生脏读、不可重复读等。

(2)已提交读(Read committed):也叫不可重复读,表示一个事务只能等另一个事务提交才能读到它的数据。当一个事务读取一个数据后,其他事务对这个数据进行操作修改,之后这个事务再去读取这个数据的话,前后两次读取到的数据不一致,也就是不可重复读

(3)可重复读(Repeatable read)(默认级别):事务两次读取到的结果一致,在这个隔离级别下,MySQL的幻读其实是可以防止的,因为MySQL里有间隙锁,它会把这个范围锁起来,其他事务不可以修改,这就防止了幻读的发生

(4)可串行化(Serializable):在这个隔离级别下,事务是按顺序,串行化执行的,这样虽然可以防止脏读,幻读,不可重复读的问题发生,但是它的效率是非常低的,不建议使用

2.隔离性的保证

隔离性的实现与保证其实是通过锁和MVCC来实现的

(1)什么是MVCC?
MVCC(Multi Version Concurrency Control):多版本并发控制,是指在数据库中为了实现高并发的数据访问,对数据进行多版本控制,并通过事务的可见性来保证事务能看到自己应该看到的数据版本

作用是当某条记录正在被修改,则可以并发读取该数据的历史版本,而不必阻塞等待锁的释放。其实现是使用undo log 和ReadView实现的,可以读取undo log里的历史版本,ReadView则用来控制哪个版本是对当前事务可见的

在MVCC的并发控制中,读操作可以分为两类:快照读(snapshot read)与当前读(current read)

快照读:其实就是普通读,读取的记录的可见版本,不用加锁。(select)

当前读:读取的是记录的最新版本,并且当前读返回的记录都会加上锁,保证其他的事务不会再并发修改这条记录。(insert、update、delete、select for update)

所以MySQL为什么写不阻塞读?

答案其实是因为MySQL利用MVCC的方式来读历史版本

ReadView就是事务进行快照读操作的时候产生的读视图(Read View),在RC隔离级别下,是每个快照读都会生成并获取最新的Read View;在RR的隔离级别下,只有同一个事务中的第一次快照读才会才会创建Read View,之后的快照读获取的是同一个Read View,所以在RR隔离级别下能读取到历史版本其实是因为它产生的是同一个ReadView

(4)持久性的实现与保证

持久性是由WAL(write ahead log 预写log) + redo log保证的,redo log有两部分组成,redo log Buffer(在内存中)与redo log file(在磁盘中)。

Buffer Pool是在内存里,存的有一些脏页数据,也就是当我们进行一些修改的操作,例如update,insert等操作时,在内存里的数据和在磁盘上的数据不一致,所以叫脏页数据。那正常来说,我们在执行完上述这些操作后,执行commit,数据就直接写入磁盘更新数据不就是行了吗?但是这样的话会有一些问题;那就是频繁的进行IO操作,性能就会降低

所以MySql的操作就是先存到redo log Buffer(内存)里,相当于做一个缓冲,当redo log Buffer里打上一个commit标志的时候,然后才会写入redo log file(磁盘)中,当数据库宕机,数据丢失的时候,会从redo log中恢复。

(1)那么真正的数据是在什么时候写入磁盘呢?其实是依据checkpoint择时刷新脏页,也就是写入磁盘,这个机制叫Double Write(双写机制)
在这里插入图片描述
redo log落盘机制(写入磁盘):
在这里插入图片描述
InnoDB提供了三种落盘机制,通过配置一个
innodb_flush_log_at_trx_commit 的参数来控制

配置值描述
0表示当事务提交时,由主线程写入redo log Buffer里,之后再由另外的线程每隔固定的时间从redo log Buffer刷新到磁盘。
1默认参数,表示事务提交时,立即会通过主线程写入缓存并刷新到磁盘。
2表示当事务提交时,有主线程写入OS Buffer(操作系统缓存),之后再由其他线程每隔固定的时间刷新到磁盘。

当设置为0时,性能最高,但是会丧失事务的一致性

(2)那么当我们还没有打上commit标志时,数据库就挂了,该如何回滚呢?

如下图,事务为了回滚(RollBack),会在undo log里面会记录一些原始值,除了有一些基本数据,还会有RowID,事务ID和回滚指针三个值
在这里插入图片描述
当我们再进行一次事务操作时,更新了数据,那么它的事务ID就会+1,回滚指针就会指向前面的一条记录,此时若是commit没有成功,那么它就会回滚到上一条记录
在这里插入图片描述
如果commit成功后,此时再来一次事务的话,它的事务ID继续+1,回滚指针依然会指向上一条记录
在这里插入图片描述

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值