1. 前言
事务是数据库管理系统中非常重要的概念,本文主要介绍的是事务的四大特性,并发事务可能引发的问题以及事务的隔离级别.
2. 事务简介
事务是一组操作的集合,它是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求,即这些操作要么同时成功,要么同时失败。
事务的使用场景的典型案例就是转账.转账是不允许出现任何差错的,出现一点都会造成巨大的损失.例如此时用户A给用户B转了200块钱.那么在数据库中执行的操作就是让用户A的余额减200,让用户B的余额加200.
如果不使用事务,则可能会出现以下问题:用户A的余额在减200之后,数据库在执行给用户B的余额加200时,数据库挂了或者出现异常情况了,此时用户B的余额并没有加200.那么A就平白无故少了200元.此时问题就很大了.
但如果使用事务时,用户A的余额减200,用户B的余额加200这两个操作是一个整体. 结果就两个,要么成功,要么失败. 那么上述出现的问题就很可以很容易进行处理了. 如果出现上述的问题,那么此时数据库就会进行事务的回滚,可以理解为撤销操作.将数据恢复到数据执行前. 由此可见事务的重要性.
3. 事务操作
事务操作的操作主要有三个:开启事务,提交事务和回滚事务.
在MySQL中开启事务有三种方式:
使用 BEGIN语句可以手动开启 使用 SET AUTOCOMMIT=0/1 语句(0为手动提交,1为默认提交) 使用 START TRANSACTION语句
提交事务和回滚事务:
COMMIT; # 提交事务 ROLLBACK; # 回滚事务
MySQL的事务是默认提交的,也就是说,当执行一条DML语句时,MySQL会隐式提交事务.
以刚才提到的转账示例来说:其实主要的步骤就是修改用户A和用户B的余额两步,当然追求严谨的话,转账前可以判断一下余额是否大于转账金额之类的.我这边就不写了.
初始状态下用户A和用户B余额:
接下来我们使用事务来处理数据库执行过程中可能出现的上述问题:
例如我这里把SQL语句改成错的.来模拟数据库出错的情况.
执行完SQL语句之后,我们看到用户A的余额变了.但是用户B的余额并没有变.
此时数据已经发生错误了.我们接下来就要回滚事务来恢复数据.
回滚事务之后我们可以看到用户A和用户B的数据已经恢复到修改数据之前的了.
4. 事务四大特性
事务具有以下四个特性,也被称为 ACID 特性:
原子性(Atomicity):事务是一个原子操作,不可分割。该操作的所有步骤要么全部完成,要么全部不完成,不会存在部分完成的情况。如果有任何一个操作失败,事务会被回滚到开始前的状态,所有修改过的数据都会被撤销。
一致性(Consistency):事务执行前和执行后,数据库的状态都必须是一致的。事务执行前的数据库状态是一个合法的状态,执行后的数据库状态也必须是合法的状态。换句话说,事务执行过程中不会产生矛盾和冲突,保证数据的完整性和正确性。
隔离性(Isolation):多个事务在并发执行时,每个事务的操作应该与其他事务的操作相互隔离,不能互相干扰。每个事务所做的修改必须与其他事务所做的修改相互独立,不会产生互相干扰的情况。保证每个事务独立执行,不会产生并发问题。
持久性(Durability):一旦事务提交,它所做的修改就会永久保存到数据库中,即使系统崩溃也不会丢失。数据的持久性保证了事务的提交结果不会被撤销,即使在系统故障或崩溃的情况下也能够恢复。
事务的四个特性保证了数据库操作过程的可靠性、一致性和持久性,是保证数据完整性和正确性的重要手段。
5. 并发事务的问题
事务是并发控制的基本单位,而并发事务可能会使事务的ACID特性遭到破坏.并发事务可能出现以下三个问题:
脏读:脏读是指一个事务在读取另一个未提交的事务的数据时,读取到了未提交的数据。如果另一个事务回滚了,这个事务读到的数据就是无效的。脏读会导致数据的不一致性。
不可重复读:不可重复读是指一个事务多次读取同一组数据,但在此期间,另一个事务更新了这组数据,从而导致第一个事务两次读取的数据不一致。不可重复读会导致数据的不一致性。
幻读:幻读是指一个事务在读取了一组数据后,另一个事务插入了一条新数据,导致第一个事务再次读取同一组数据时,发现数据增加了一条,从而产生了幻觉。幻读会导致数据的不一致性。
以上三种问题都是由于多个事务并发执行导致的,解决这些问题的方法包括:
加锁:通过对共享数据加锁,保证每个事务对共享数据的访问是互斥的,解决脏读和不可重复读问题。
MVCC(多版本并发控制):通过在每个事务的视图中记录数据的版本信息,保证每个事务读取到的数据都是一致的,解决不可重复读和幻读问题。
串行化:通过将并发事务串行执行,保证每个事务独立执行,不会产生并发问题。但是串行化会降低系统的并发性和吞吐量,不适合高并发的场景。
6. 事务隔离级别
事务的隔离级别是指多个并发事务之间相互隔离的程度,MySQL中支持4种隔离级别,从低到高分别为:
读未提交(Read Uncommitted):最低的隔离级别,允许一个事务读取另一个事务未提交的数据,可能会导致脏读、不可重复读和幻读问题。
读已提交(Read Committed):允许一个事务读取另一个事务已提交的数据,避免了脏读问题,但仍可能导致不可重复读和幻读问题。
可重复读(Repeatable Read):保证在同一个事务中多次读取同一组数据时,读取到的数据始终一致,避免了脏读和不可重复读问题,但仍可能导致幻读问题。
串行化(Serializable):最高的隔离级别,通过将并发事务串行执行,避免了所有并发问题,但会导致性能降低。
隔离级别 脏读 不可重复读 串行化
Read Uncommitted √ √ √
Read Committed × √ √
Repeatable Read × × √
Serializable × × ×
查看事务的隔离级别:
select @@transaction_isolatio;
设置事务的隔离级别:
SET [SESSION/GLOBAL] TRANSACTION ISOLATION LEVEL <隔离级别>; # SESSION: 针对当前的会话窗口有效 # GLOBAL: 对所有客户端的会话窗口有效
隔离级别越高,相互隔离的程度越高,但同时也会导致性能降低和死锁等问题。在实际应用中,需要根据不同的业务需求和系统性能来选择适当的隔离级别。MySQL 默认的隔离级别是可重复读(Repeatable Read)。
7. 总结
事务对于数据库管理系统的正确性、可靠性、完整性和安全性都非常重要,是保障数据质量的关键手段。理解并发事务可能引起的问题以及这些问题是什么含义,并且要学会合理设置事务的隔离级别处理并发事务可能带来的问题.