数据库事务概念及实现原理

一.事务基本概念及特性

1. 事务定义与详解

事务是数据库系统中重要的概念,事务的定义:是构成单一逻辑工作单元的操作集合。
一个典型的数据库事务如下所示:
BEGIN TRANSACTION //事务开始
SQL1
SQL2
COMMIT/ROLLBACK //事务提交或回滚

关于定义的解释:
数据库事务可以包含一个或多个数据库操作,这些操作构成一个逻辑上的整体。
对于一个事务来说,上述构成的逻辑整体具有原子性,要么全部执行,要么均不 执行。
数据库出现故障以及并发事务存在的情况下依然成立

2.事务4大特性(ACID)

原子性(Atomicity)
事务中所有操作可作为一个整体像原子一样不可分割,要么全部执行成功,要么
全部执行失败。
一致性(Consistency)
指系统数据在事务执行前后,若事务执行成功从一个正确状态迁移到另一个正确 状态。如果失败,回滚到初始时的正确状态。正确状态是指数据当前的状态满足预定的约束,约束既有数据完整性约束(主键等)也有开发者制定业务上的。
隔离性(Isolation)
指在并发环境下,当不同的事务同时操作相同的数据时,每个事务都有各自完整的数据空间,多个事务之间要相互隔离,隔离级别下文再详细介绍。
持久性(Durability)
指事务一旦被提交,它对数据库中的数据的改变就必须永久保存下来,即使发生系统崩溃,重启数据库系统仍能恢复到事务成功结束的状态

**备注:**原子性关注状态,全部成功或全部失败,不存在部分成功。一致性关注数据的可见性,中间状态的数据对外部不可见,只有最初状态和最终状态的数据时对外可见的

3.常见的并发异常

(1)脏读
一个事务读到了另一个事务修改但未提交的数据。
(2)不可重复读
一个事务过程中多次读取同一数据,但在多次读取间隔发生数据更新,此时读取数据之间数据值不同,无法重复读取。
(3)幻读
一个事务读取到了另一个事务提交的增加或删除的目前数据情况,之前没有现在突然出现。

不可重复读的重点是修改 !!!
同样的条件, 你读取过的数据,再次读取出来发现值不一样了
幻读的重点在于新增或者删除!!!
同样的条件, 第 1 次和第 2 次读出来的记录数不一样

4.事务隔离级别
数据库事务隔离级别有4个等级,由低到高分别为:
(1) 读未提交(Read UnCommitted)
最低的的隔离级别,一个事务可以读取到另一个事务,并未提交的结果,可能引起脏
读、不可重复读、幻读。
(2)读已提交(Read Committed)
多数数据库采用的默认隔离级别,一个事务更新操作只有在该事务提交之后,才可以
读取到同一笔数据更新后的结果。可能引起不可重复读和幻读
(3)可重复读(Repeatable Read) mysql数据库默认事务级别!!
针对同一个事务过程,同一笔数据的读取结果整个事务过程中都是相同的,即使其他
事务已经进行了更新。可能引起幻读
(4)序列化(Serializable)
最高的隔离级别,所有事务操作依次顺序执行。但其可能引发并发读下降问题,效率
最低。

二、事务实现原理

事务的实现就是要确保事务的ACID四大特性,并发控制技术保证了事务的隔离性,日志恢复技术保证了事务的原子性,已提交的数据修改不会丢失实现了持久性。

1.知识准备
以MySQL中的InnoDB存储引擎为例,其提供了两种事务日志:
redo log(重做日志):保证事务持久性
undo log(回滚日志):保证事务原子性和隔离性

2.事务原子性的实现原理
undo log是实现原子性的关键,当事务回滚时可以撤销所有已经成功执行的sql语句。当事务对数据库进行修改时,InnoDB会生成对应的undo log,如果事务执行失败或调用rollback,可利用undo log中将数据回滚到原始状态。
undo log属于逻辑日志记录了sql执行相关的信息,发生回滚时会根据undo log内容做与之相反的工作:对于insert回滚执行delete,对于delete回滚执行insert,对于update回滚执行相反的update(此处就利用日志中记录的被修改数据主键,修改列以及修改前后值信息。)

3.事务持久性实现原理

3.1 redo log背景

redo log是实现持久性的关键,先介绍下redo log的存在背景。
数据是存放在磁盘中,每次读写数据需要磁盘IO,时间长效率低,InnoDB提供了缓存(Buffer Pool),Buffer Pool包含了磁盘中部分数据也映射作为访问数据库的缓冲。当读数据时先从Buffer Pool中读取,若没有从磁盘读取后放入Buffer Pool中。当写数据时,先写入Buffer Pool,然后定期刷到磁盘中。但当Mysql宕机时,Buffer Pool修改的数据未刷到磁盘会导致数据丢失,无法保证持久性。

3.2 持久性实现原理

redo log引入可以解决上述问题,当数据修改除了写入Buffer Pool外还会在redo log记录此次操作,当事务提交会调用fsync接口对redo log进行刷盘。如果MySQL宕机,下次重启时可以读取redo log中的数据,对数据进行恢复。先写入redo log后写入Buffer Pool。

3.3 redo log与binlog

MySQL中还存在binlog(二进制日志)也同样可以记录写操作和数据恢复,不同点:
作用不同:redo log保证宕机也不会影响持久性,binlog保证服务器基于时间点
恢复数据,用于主从复制
层次不同:redo log是InnoDB存储引擎实现,而binlog是MySQL服务器层实现。
内容不同:redo log是物理日志,内容基于磁盘Page;binlog内容是二进制,根
据binlog_format参数的不同,可能基于sql语句、基于数据本身或者
二者的混合。

4.事务隔离性的实现原理

并发控制技术是实现事务隔离性以及不同隔离级别的关键,按照其对可能发生冲突的操作采取的不同策略可分为乐观并发控制和悲观并发控制两类。隔离性其实就是并发控制,不同隔离级别的实现就是采用了不同的并发控制机制。
乐观并发控制
对于并发可能发生冲突的操作,假定其不会真的冲突允许并发执行,直到有真正的冲突发生时才会解决冲突,如事务回滚。
悲观并发控制
对于并发可能发生冲突的操作,假定其一定会发生,通过让事务等待(锁)或终止回滚(时间戳排序)的方式使并行的操作串行化执行

备注:MyIsam只支持表锁,而InnoDB同时支持表锁和行锁,InnoDB默认的隔离级别是RR,在SQL标准中,RR是无法避免幻读问题的,但是InnoDB实现的RR避免了幻读问题。

4.1基于封锁的并发控制(悲观并发控制)

核心思想:对于可能发生冲突的操作,通过加锁进行互斥执行,将并发变为串行执行
锁分为两种:
共享锁(S):事务T对数据A加共享锁,其他事务只能对A加共享锁
排它锁(X):事务T对数据A加排他锁,其他事务对A既不能加共享锁也不能加排它锁

基于锁的并发流程:
首先事务根据自己对数据的操作类型申请相应的锁
(读操作申请共享锁,写操作申请排它锁)
之后发送申请锁请求至锁管理器,根据当前请求的数据项判断是否已经有锁、目
前锁是否冲突来决定是否为该请求授予锁
最后若锁被授予,申请锁的事务可以继续执行后续操作,若被拒绝,事务将等待
其他事务将锁释放。
可能出现问题:
死锁:多个事务循环等待 饥饿:A一致被加共享锁,始终无法加排它锁

4.2基于时间戳的并发控制(悲观并发控制)

核心思想:对于可能发生冲突的操作,基于时间戳排序规则选定某事务继续执行,其他事务回滚。与基于锁本质一样将并行执行变为串行执行
时间戳定义
时间戳针对事务维度,每个事务在开始时赋予其一个时间戳,其可以是系统时钟也可以是计数器等,事务回滚会赋予其新的时间戳,事务开始时间越晚时间戳越大。
数据项两个时间戳相关字段
W-timestamp:成功执行write(Q)的所有事务的最大时间戳
R-timestamp:成功执行read(Q)的所有事务的最大时间戳
时间戳排序规则:
假设事务T发出read(Q),T的时间戳为TS
若TS<W-timestamp,说明需要读取的数据项已经被其他事务修改,此时拒绝
read操作,事务回滚。
若TS>=W-timestamp,事务读取发生在修改之后可以执行read操作,同时把
R-timestamp设置为TS与R-timestamp之间最大值。
假设事务T发出write(Q)
若TS<R-timestamp,说明写操作发生在读之后,说明未使用最新数据进行
更新,write操作拒绝,事务回滚
若TS<W-timestamp,说明写操作发生其他事务写之后,数据已经更改,
write操作拒绝,事务回滚
其他情况,系统执行write操作,将W-timestamp(Q)设置为TS(T)。

4.3基于有效性检查的并发控制(乐观并发控制)

核心思想:事务对数据更新首先在自己的工作空间进行(类似于工作内存,副本),等到要写回数据库时才进行有效性检查,对不符合要求的事务进行回滚。
基于有效性检查的阶段:
读阶段
数据项被读入并保存在事务的局部变量中。所有write操作都是对局部变量进行
的,并不是对数据库进行真正的更新。
有效性检查阶段
判断事务有效性,是否可以执行write操作而不违反可串行性,失败则回滚事务。
写阶段
若通过有效性检查,将临时变量中的结果更新到数据库中。
备注:
有效性检查也是通过对事务时间戳进行比较完成的,不过时间戳排序规则不同。
有效性检查允许可能发生冲突的操作并发执行,因为每个事务有自己工作空间局部变量,直到有效性检查阶段才解决冲突,是一种乐观的并发策略

4.4基于快照隔离的并发控制(乐观并发控制)

快照隔离时多版本并发控制(mvcc)的一种实现方式。
核心思想:数据库为每个数据项维护多个版本(快照),每个事务只对属于自己的私有快照进行更新,在事务真正提交前进行有效性检查,使得事务正常提交更新或失败回滚
快照隔离导致事务看不到其他事务对数据项的更新,避免丢失更新,采用以下两种方案
先提交者获胜
对于执行该检查的事务T,判断其他事务是否已经将更新写入数据库,若是回滚否则提交
先更新者获胜
通过锁机制保证第一个获得锁的事务提交其更新,之后试图更新的事务中止。

4.5.具体四种隔离级别的实现

4.5.1 锁实现

隔离级别 操作 锁 生命周期
读未提交 读 否
读未提交 写 行级排它锁 立即释放
读已提交 读 行级共享锁 立即释放
读已提交 写 行级排它锁 事务结束
可重复读 读 行级共享锁 事务结束
可重复读 写 行级排它锁 事务结束
可串行化 读 范围锁/表级锁 事务结束
可串行化 写 行级排它锁 事务结束

4.5.2 可重复读RR实现原理

InnoDB默认的隔离级别是RR。在SQL标准中,RR是无法避免幻读问题的,但是InnoDB实现的RR避免了幻读问题。
原理:
MVCC多个版本的数据可以共存,主要依靠数据的隐藏列和undo log,其中隐藏列包括了该数据的版本号、删除时间、只想undo的指针;当读取数据时,MySQL可以通过隐藏列判断是否需要回滚,并找到需要的undo log,进而避免脏读、不可重复读、幻读(InnoDB的RR可以避免)问题
避免脏读

当事务A在T3时间节点读取zhangsan的余额时,会发现数据已被其他事务修改,且状态为未提交。此时事务A读取最新数据后,根据数据的undo log执行回滚操作,得到事务B修改前的数据,从而避免了脏读。

避免不可重复读

当事务A在T2节点第一次读取数据时,会记录该数据的版本号(数据的版本号是以row为单位记录的),假设版本号为1;当事务B提交时,该行记录的版本号增加,假设版本号为2;当事务A在T5再一次读取数据时,发现数据的版本号(2)大于第一次读取时记录的版本号(1),因此会根据undo log执行回滚操作,得到版本号为1时的数据,从而实现了可重复读

避免幻读
InnoDB实现的RR通过next-key lock机制避免幻读。
next-key lock是行锁的一种,实现相当于record lock(记录锁) + gap lock(间隙锁);其特点是不仅会锁住记录本身(record lock的功能),还会锁定一个范围(gap lock的功能)。当然,这里我们讨论的是不加锁读:此时的next-key lock并不是真的加锁,只是为读取的数据增加了标记(标记内容包括数据的版本号等);准确起见称之为类next-key lock机制

当事务A在T2节点第一次读取0<id<5数据时,标记的不只是id=1的数据,而是将范围(0,5)进行了标记,这样当T5时刻再次读取0<id<5数据时,便可以发现id=2的数据比之前标记的版本号更高,此时再结合undo log执行回滚操作,避免了幻读。
备注:InnoDB的RR实现了一定程度的隔离性,可以满足大多数场景的需要。不过需要说明的是,RR虽然避免了幻读问题,但是毕竟不是Serializable,不能保证完全的隔离

5.事务一致性的实现原理

一致性是事务追求的目标,原子性、持久性、隔离性都是为了保存数据库状态一致性。
实现一致性措施
保证原子性、持久性和隔离性,如果这些特性无法保证,事务一致性也无法保证数据库本身提供保障,例如不允许向整形列插入字符串值、字符串长度不能超过列的限制等(数据约束)
应用层面进行保障,例如如果转账操作只扣除转账者的余额,而没有增加接收者的余额,无论数据库实现的多么完美,也无法保证状态的一致(业务约束)

参考文档:
事务定义及隔离级别实现:https://www.cnblogs.com/takumicx/p/9998844.html
MySQL事务级别的实现原理:https://www.cnblogs.com/cjsblog/p/8365921.html
事务级别具体锁实现:https://www.jianshu.com/p/d75ecc545fda
深入学习MySql事务:https://www.cnblogs.com/kismetv/p/10331633.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值