有头有尾就靠他了:事务详解

基本特性

事务的基本特性,概括说来就4点:ACID

A:atomicity事务的原子性,保证了一个事务要不全部成功,要不回滚,全部失败
C:consistency事务的一致性,保证了数据库总是从一个一致性的状态转换到另一个一致性的状态
I:isolation事务的隔离性,保证了一个事务执行完毕前,对其他事务不可见
D:durability事务的持久性,保证事务一旦提交,做的修改会永久保存在数据库之中

举几个栗子:
比如小胡打算给阿伟转账100块软妹币,不巧的是:当小胡账户刚减少100元时,而这100元还没有到阿伟账户时,系统不争气的崩溃了。小胡并不用急,这时,事务的原子性会使得事务进行回滚,回到事务未开始的状态,在这个转账的过程中,将会回到小胡账户未更改的时刻;
小胡仍然不死心,再次向阿伟进行转账,在同一时刻阿伟等不及了,去看了一下自己的账户余额,发现账户金额还没有增长,便去质问小胡,此时小胡去看了一下自己的账户,发现余额确实减少了,两人就此争论了起来,但事务的隔离性就能避免这种情况的发生;
在转账完成后,系统又一次的宕机了,这时候,阿伟也并不用担心转账会消失,因为事务的持久性保证了转账是永久有效的,不会因为故障导致数据丢失。

隔离级别

当然,虽然说事务拥有隔离性,但是并不是说所有的事务都是完成隔离的,隔离是有四个不同的级别的:
1读未提交(read uncommit):能读到其他事务未提交的数据,也叫脏读

时间点事务1事务2
1开始事务开始事务
2账户开始为0元,加100元
3查询账户,结果为100元
4提交

比如在该表中,事务1未提交的数据被事务2所读取到,如果在事务1提交之前,系统崩溃,事务进行回滚,事务2读取到的显然是错误的信息,这就是脏读

2读已提交(read commit):只能读取其他事务已提交的数据,但会导致多次读取到的结果不一样,叫做不可重复读

时间点事务1事务2
1开始事务开始事务
2查询账户,为0元
3账户加100元
4提交事务
5查询账户余额,为100元

如上表,事务1读取了两次数据,很明显这两次的结果是不同的,说明事务1在执行过程中有其它事务对该数据的修改,这样肯定也是不合适的结果,需要对这种不可重复读的机制进行进一步的改善。

3可重复读(repeatable read):多次读取的数据一致,这是mysql的默认级别,但会导致另一个问题:幻读。幻读是指事务在前后读取一个数据时,两次查询的行数不同

时间点事务1事务2
1开始事务开始事务
2查询账户余额为100的账户,为1
3增加一个新账户,余额为100
4提交事务
5再次查询账户余额为100的账户,为2

4串行(serializable):也叫序列化,是最极端的一种隔离机制,所有事务会一个一个的按顺序执行,对每一个数据进行加锁,会导致大量的超时问题和锁竞争的问题

那么,事务是如何保证自己的几个特性呢

ACID的实现

原子性:由undo log日志保证,undo log记录了需要回滚的日志信息,事务回滚时撤销已经执行成功的sql语句
undo log:名为回滚日志,是实现原子性的关键,当事务回滚时能够撤销所有已经成功执行的sql语句,他需要记录你要回滚的相应日志信息。
例如
当插入时需要记录这条数据,回滚时删除这条数据
当删除时记录删除的该条数据,回滚时进行插入
当修改时记录之前的旧值,回滚时修改为原值

隔离性:MVCC保证
首先,了解MVCC是什么东西:MVCC全称:Multi-Version Concurrency Control,多版本并发控制。能够很好的处理读写冲突。
其主要依赖于每行记录的隐藏字段:
自增ID,若记录没有主键,将会将其作为记录的主键
最近修改事务ID:记录了最后修改该事务的ID
回滚指针:记录这条记录的上一个版本
所以,对于每一条记录,完整的表格应该是这样:

namemoneyDB_ROW_ID主键DB_TRX_ID事务IDDB_ROLL_PTR回滚指针
小胡1001nullnull
阿伟1002nullnull

除此之外,还需要undo log(日志)和read view(读视图)

我们用一次转账来解释undo日志的使用:

按照上述表格,如果小胡账户增加100时:
首先数据库会对该行加锁
然后把该行数据拷贝到undo log中,作为旧记录,既在undo log中有当前行的拷贝副本
拷贝完毕后,修改该行money为200,并且修改隐藏字段的事务ID为当前事务1的ID, 我们默认从1开始,之后递增,回滚指针指向拷贝到undo log的副本记录,既表示上一个版本就是它
事务提交后,释放锁

namemoneyDB_ROW_ID主键DB_TRX_ID事务IDDB_ROLL_PTR回滚指针
小胡10011*
阿伟1002nullnull

而undo log中表为:

namemoneyDB_ROW_ID主键DB_TRX_ID事务IDDB_ROLL_PTR回滚指针
小胡1001nullnull

而如果该行记录再次进行修改时,undo log会继续增加

namemoneyDB_ROW_ID主键DB_TRX_ID事务IDDB_ROLL_PTR回滚指针
小胡10011*
namemoneyDB_ROW_ID主键DB_TRX_ID事务IDDB_ROLL_PTR回滚指针
小胡1001nullnull

事务对同一条记录的更改,在undo日志中会是一条链表的形式储存undo log的链首就是最新的旧记录,链尾就是最早的旧记录。

read view:
read view就是事务进行快照读操作的时候生产的读视图,在该事务执行的快照读的那一刻,会生成数据库系统当前的一个快照,记录并维护系统当前活跃事务的ID
read view拥有三个全局属性
一个list列表,储存系统正活跃着的ID
up_limit_id,记录列表中事务ID最小的ID
low_limit_id,readview生成时刻系统尚未分配的下一个事务ID
read view可以判断事务的可见性,对于任意一个事务,将会判断其能见到哪一个版本的记录,为此,执行以下判断:
1、判断DB_TRX_ID是否小于up_limit_id,小于则表示当前事务能看到DB_TAX_ID所表示的该版本的记录,否则进入下一步
2、判断DB_TRX_ID是否大于low_limit_id,大于或等于则说明比当前系统中事务的最大ID都要大,表示事务无法观测到当前记录,否则进入下一步
3、如果DB_TRX_ID在当前列表中,表示记录还未提交,事务也是无法看到该记录,反之,说明该记录所在事务已提交,可以观测到记录

持久性:由内存和redo log来保证,mysql修改数据时会在内存和redo log中记录这次操作,事务提交redo log进行记录,宕机时从redo log中恢复。

一致性:由代码层面保证,和其他三者都有一定关系

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值