事务是MySQL等关系型数据库区别于NoSQL的重要方面,是保证数据一致性的重要手段。
事务ACID四大特性
原子性(Atomicity)
- 定义
指一个事务是一个不可分割的工作单位,其中的操作要嘛都做,要嘛都不做;如果事务中一个sql语句执行失败,则已执行的语句也必须回滚,数据库退回到事务前的状态。 - 实现原理:undo log(回滚日志)
undo log是实现原子性的关键,是当事务回滚时能够撤销所有已经成功执行的sql语句。
InnoDB存储引擎实现回滚,靠的就是undo log:当事务对数据库进行修改时,InnoDB会生成对应的undo log;如果事务执行失败或调用rollback,导致事务需要回滚,便可以利用undo log中的信息将数据回滚到修改之前的样子。
undo log属于逻辑日志,它记录的是sql执行相关的信息。当发生回滚时,InnoDB会根据undo log的内容做与之前相反的工作;对于每个insert,回滚时会执行delete;对于每个delete,回滚时会执行insert;对于每个update,回滚时会执行一个相反的update,把数据改回去。
以update操作为例:当事务执行update时,其生成的undo log中会包含被修改行的主键(以便知道修改了哪些行),修改了哪些列,这些列在修改前后的值等信息,回滚时便可以使用这些信息将数据还原到update之前的状态。 -
案例
A给B转账100元
image.png
- 分析
在事务中的扣款和加款两条语句,要嘛都执行,要嘛都不执行。否则如果只执行了扣款语句,就提交,此时如果突然断电,A账户已经发生了扣款,B账号缺没有收到加款,在生活中就会引起纠纷。
一致性(Consistency)
指事务执行结束后,数据库的完整性约束没有被破坏,事务执行的前后都是合法的数据状态。数据库的完整性约束包括但不限于:实体完整性(如行的主键存在且唯一)、列完整性(如字段的类型、大小、长度要符合要求)、外键约束、用户自定义完整性(如转账前后、两个账户余额的和应不变)
- 实现一致性的措施
- 保证原子性、持久性和隔离性,如果这些特性无法保证,事务的一致性也无法保证
- 数据库本身提供保障,例如不允许向整行列插入字符串值、字符串长度不能超过列的限制等。
- 应用层面进行保障,例如如果转账操作只扣除转账者的余额,而没有增加接收者的余额,无论数据库实现的多么完美,无无法保证状态的一致。
隔离性(Isolation)
研究的是不同事务之间的相互影响。指事务内部的操作与其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
考虑最简单的读操作和写操作,可以分为两个方面:
- (一个事务)写操作对(另一个事务)写操作的影响:锁机制保证隔离性。
- (一个事务)写操作对(另一个事务)读操作的影响:MVCC保证隔离性。(多版本并发控制(MVCC,Multiversion Concurrency Control)解决了幻读的问题)
写操作之间的相互影响
- 锁机制
InnoDB存储引擎通过锁机制来保证同一时刻只能有一个事务对数据进行写操作。
锁机制的基本原理:事务在修改数据之前,需要先获得相应的锁;获得锁之后,事务便可以修改数据;该事务操作期间,这不是数据是锁定的,其他事务如果需要修改数据,需要等待当前事务提交或回滚后释放锁。 - 行锁与表锁
- 表锁在操作数据时会锁定整张表,并发性较差。
- 行锁则只锁定需要操作的数据,并发性能好。
因为加锁本身需要消耗资源(获得锁、检查锁、释放锁等都需要消耗资源),因此在锁定数据较多情况下使用表锁可以节省大量资源,但出于性能考虑 ,绝大多数情况下使用的都是行锁。
写操作对读操作的影响
- 并发情况下,读操作可能存在的三类问题:
-
脏读: 当前事务(A)中可以读到其他事务(B)未提交的数据(脏数据),这种现象是脏读。
image.png -
不可重复读:在事务A中先后两次读取同一个数据,两次读取的结果不一样,这种现象称为不可重复读。脏读与不可重复读的区别在于:前者读到的是其他事务未提交的数据,后者读到的是其他事务已提交的数据。
image.png -
幻读
在事务A中按照某个条件先后两次查询数据库,两次查询的条数不同,这种现象称为幻读。不可重复读与幻读的区别可以理解为:前者数据变了,后者是数据的行数变了。
image.png
4) 丢失更新
两个事务同时读取同一条记录,A先修改记录,B也修改记录(B是不知道A修改过),B提交数据后B的修改结果覆盖了A的修改结果。
-
MVCC
MVCC全称Multi-Version Concurrency Control,即多版本的并发控制协议,用来解决脏读、不可重复读、幻读等问题。
举例说明MVCC的特点:在同一时刻,不同的事务读取到的数据可能是不同的(即多版本)---在T5时刻,事务A和事务C可以读取到不同版本的数据。
image.png
MVCC最大的优点:读不加锁,因此读写不冲突,并发性能好。InnoDB实现MVCC即多个版本的数据可以共存,主要依靠数据的隐藏列(标记位)和undo log。其中数据的隐藏列包括该行数据的版本号、删除时间、指向undo log的指针等;当读取数据时,MySQL可以通过隐藏列判断是否需要回滚并找出回滚需要的undo log,从而实现MVCC。
-
MVCC 怎么解决脏读
image.png
当事务在T3时间节点读取zhangsan的余额时,会发现数据已被其他事务修改,且状态为未提交。此时事务A读取最新数据后,根据数据的undo log执行回滚操作,得到事务B修改前的数据,从而避免了脏读。
-
MVCC 怎么解决不可重复读
image.png
当事务A在T2节点第一次读取数据时,会记录该数据的版本号(数据的版本号是以row为单位记录的)。假设版本号为1,当事务B提交时,该行记录的版本号增加,假设版本号为2;当事务A在T5再一次读取数据时,发现数据的版本号(2)大于第一次读取时记录的版本号(1),因此会根据undo log执行回滚操作,得到版本号为1时的数据,从而实现可重复读。
- MVCC 怎么解决幻读
InnoDB实现的可重复读是通过next-key lock机制避免幻读现象
next-key lock是行锁的一种,实现相当于record lock(记录锁)+gap lock(间隙锁);其特点是不仅会锁住记录本身还会锁定一个范围
image.png
当事务A在T2节点第一次读取0<id<5的数据时,标记的不只是id=1的数据,而是将范围(0,5)进行标记,这样当T5时刻再次读取0<id<5的数据时,便可以发现id=2的数据比之前标记的版本号更高,此时再结合undo log执行回滚操作,避免幻读。
持久性(Durability)
事务一旦提交,它对数据库的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。
事务隔离级别
SQL标准中定义4种隔离级别,并规定每种隔离级别下下述几个问题是否存在。一般来说,隔离级别越低,系统开销越低,可支持的并发越高,但隔离性也越差。隔离级别与读问题的关系如下:
image.png
未提交读: 在读数据时不会检查或使用任何锁。因此,在这种隔离级别中可能读取到没有提交的数据。
已提交读: 只读取提交的数据并等待其他事务释放排他锁。读数据的共享锁在读操作完成后立即释放。已提交读是SQL Server的默认隔离级别。
可重复读: 像已提交读级别那样读数据,但会保持共享锁直到事务结束。
可串行读: 工作方式类似于可重复读。但它不仅会锁定受影响的数据,还会锁定这个范围。这就阻止了新数据插入查询所涉及的范围。
网友评论