概念
事务是数据库中的一个概念,为保证安全地访问数据库
而提出。是并发控制的基本单位
,是满足 ACID 特性的一组操作
,可以通过 Commit 提交一个事务,也可以使用 Rollback 进行回滚。
实现目标
为保证数据库操作的安全,事务需要实现:
1、为数据库操作提供了一个从失败
中恢复
到正常状态的方法,同时提供了数据库即使在异常
状态下仍能保持一致性
的方法。
2、当多个应用程序在并发访问数据库时,可以在这些应用程序之间提供一个隔离
方法,以防止
彼此的操作互相干扰
。
事务的四大特性
- 原子性(Atomicity):事务作为一个整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都不执行。
- 一致性(Consistency):事务应确保数据库的状态从一个一致状态转变为另一个一致状态。一致状态的含义是数据库中的数据应
满足完整性约束
。 - 隔离性(Isolation):多个事务并发执行时,一个事务的执行不应影响其他事务的执行。
- 持久性(Durability):一个事务一旦提交,他对数据库的修改应该永久保存在数据库中。
举例:
举例
用一个常用的“A账户向B账号汇钱”的例子来说明如何通过数据库事务保证数据的准确性和完整性。熟悉关系型数据库事务的都知道从帐号A到帐号B需要6个操作:
1、从A账号中把余额读出来(500)。
2、对A账号做减法操作(500-100)。
3、把结果写回A账号中(400)。
4、从B账号中把余额读出来(500)。
5、对B账号做加法操作(500+100)。
6、把结果写回B账号中(600)。
- 原子性:
保证1-6所有过程要么都执行,要么都不执行。一旦在执行某一步骤的过程中发生问题,就需要执行回滚操作。 假如执行到第五步的时候,B账户突然不可用(比如被注销),那么之前的所有操作都应该回滚到执行事务之前的状态。- 一致性
在转账之前,A和B的账户中共有500+500=1000元钱。在转账之后,A和B的账户中共有400+600=1000元。也就是说,数据的状态在执行该事务操作之后从一个状态改变到了另外一个状态。同时一致性还能保证账户余额不会变成负数等。- 隔离性
在A向B转账的整个过程中,只要事务还没有提交(commit),查询A账户和B账户的时候,两个账户里面的钱的数量都不会有变化。
如果在A给B转账的同时,有另外一个事务执行了C给B转账的操作,那么当两个事务都结束的时候,B账户里面的钱应该是A转给B的钱加上C转给B的钱再加上自己原有的钱。- 持久性
一旦转账成功(事务提交),两个账户的里面的钱就会真的发生变化(会把数据写入数据库做持久化保存)!
ACID 特性的理解:
事务的 ACID 特性概念简单,但不是很好理解,主要是因为这几个特性不是一种平级关系
:
- 只有满足一致性,事务的执行结果才是正确的。
- 在无并发的情况下,事务串行执行,隔离性一定能够满足。此时只要能满足原子性,就一定能满足一致性。
- 在并发的情况下,多个事务并行执行,事务不仅要满足原子性,还需要满足隔离性,才能满足一致性。
- 事务满足持久化是为了能应对数据库崩溃的情况。
四大特性的实现机制:回滚日志、重做日志、锁
-
回滚日志(undo log)
- 实现过程:
所有事务进行的修改都会先记录
到这个回滚日志
中,再对数据库
中的对应行进行写入
,即让回滚日志必须先于数据持久化到磁盘上
。 - 应用场景:
用户执行 ROLLBACK、发生错误、数据库进程直接被杀死 、整个系统发生崩溃时,用户再次启动数据库进程后,能够通过查询回滚日志将之前未完成的事务进行回滚。
- 实现过程:
-
重做日志(redo log)
- 组成
- 内存中的重做日志缓冲区,因为重做日志缓冲区在内存中,所以它是易失的
- 在磁盘上的重做日志文件,它是持久的在磁盘上的重做日志文件,它是持久的
- 实现原理
当我们在一个事务中尝试对数据进行修改时,它会先将数据从磁盘读入内存,并更新内存中缓存的数据,然后生成一条重做日志并写入重做日志缓存,当事务真正提交时,MySQL 会将重做日志缓存中的内容刷新到重做日志文件,再将内存中的数据更新到磁盘上,图中的第 4、5 步就是在事务提交时执行的。
在 InnoDB 中,重做日志都是以 512 字节的块的形式进行存储的,同时因为块的大小与磁盘扇区大小相同,所以重做日志的写入可以保证原子性,不会由于机器断电导致重做日志仅写入一半并留下脏数据。 - 应用场景
除了所有对数据库的修改
会产生重做日志,回滚日志
也是需要持久存储的,它们也会创建对应的重做日志,在发生错误后,数据库重启时会从重做日志中找出未被更新到数据库磁盘中的日志重新执行以满足事务的持久性。
隔离级别
当多个线程
都开启事务,操作数据库中的数据时,数据库系统要能进行隔离操作,以保证各个线程获取数据的准确性。
并发情况下,由隔离性诱发的一致性问题
在并发
环境下,事务的隔离性
很难保证,因此会出现很多并发一致性
问题。
- 丢失修改
T1 和 T2 两个事务都对一个数据进行修改,T1 先修改,T2 随后修改,T2 的修改覆盖
了 T1 的修改。
- 脏读
T1 修改一个数据,T2 随后读取这个数据。如果 T1 撤销了这次修改,那么 T2 读取的数据是脏数据。
即T2读取
了T1未提交的更新数据
。
- 不可重复读
T2 读取一个数据,T1 对该数据做了修改。如果 T2 再次读取这个数据,此时读取的结果和第一次读取的结果不同。
即,T2读取了T1已提交的update数据
,使得T2读取结果前后不一致
。
- 虚读(幻读)
T1 读取某个范围的数据,T2 在这个范围内插入新的数据,T1 再次读取这个范围的数据,此时读取的结果和和第一次读取的结果不同。
即,T1读取了T2已提交的insert数据
,使得T1读取结果前后不一致
。
事务的MySQL控制语句
注意事项
- MySQL 默认采用自动提交模式。也就是说,如果不显式使用START TRANSACTION语句来开始一个事务,那么每个查询都会被当做一个事务自动提交。
参考文献
https://github.com/CyC2018/CS-Notes/blob/master/notes/%E6%95%B0%E6%8D%AE%E5%BA%93%E7%B3%BB%E7%BB%9F%E5%8E%9F%E7%90%86.md#%E4%B8%80%E4%BA%8B%E5%8A%A1 事务概念和 ACID 特性的理解
http://www.hollischuang.com/archives/898 事务产生的目的和特性举例
https://blog.csdn.net/sinat_33536912/article/details/51200630 为什么需要事务
https://www.cnblogs.com/fjdingsd/p/5273008.html 事务的隔离级别
https://draveness.me/mysql-transaction 事务四大特性实现机制
https://github.com/CyC2018/CS-Notes/blob/master/notes/%E6%95%B0%E6%8D%AE%E5%BA%93%E7%B3%BB%E7%BB%9F%E5%8E%9F%E7%90%86.md#%E4%BA%8C%E5%B9%B6%E5%8F%91%E4%B8%80%E8%87%B4%E6%80%A7%E9%97%AE%E9%A2%98 并发情况下,由隔离性诱发的一致性问题