一、什么是事务?
简单说就是逻辑上的一组操作,要么都执行,要么都不执行。
举个例子,假如小明要给小红转账100元,这个转账会涉及到两个关键操作:①将小明的余额减少100元。 ②将小红的余额增加100元 。但是万一再这两个操作之间突然出现错误,比如说银行系统崩溃,导致小明余额减少而小红的余额没有增加,这样就不对了。事务就是保证这两个关键操作要么都成功,要么都失败。
二、事务的特性ACID
1、原子性:事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么全不起作用。
2、一致性:执行事务前后,数据保持一致。例如转账业务中,无论事务是否成功,转账者和收款人的总额应该是不变的。
3、隔离性:并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间的数据是独立的。
4、持久性:一个事务被提交之后,他对数据库的改变是持久的,即使数据库发生故障也不应该对其有任何影响。
实现:
持久性:通过 redo log来保证的
原子性:通过 undo log来保证的
隔离性:通过 MVCC 或锁机制来保证的
一致性:通过持久性+原子性+隔离性来保证
三、并发事务带来的问题
在典型的应用程序中,多个事务并发运行,经常会操作相同的数据来完成各自的任务(多个用户对统一数据进行操作)。并发虽然是必须的,但是可能会导致以下的问题。
- 脏读(Dirty Read):当一个事务正在访问数据并且对数据进行了修改,此时还未提交到数据库中,这时另一个事务也访问并使用了这个数据,由于上个事务还未提交,此时他读到的就是“脏数据”,根据“脏数据”所做的操作可能时不正确的。
- 丢失修改(Lost of Modify):指一个事务读取到一个数据,另一个事务也访问了该数据。那么在第一个事务修改了这个数据后,第二个事务也进行了修改,此时第一个事务的修改结果就被覆盖了,也就是丢失了,因此称为丢失修改。 例如:事务1读取某表中的数据A=20,事务2也读取A=20,事务1修改A=A-1,事务2也修改A=A-1,最终结果A=19,事务1的修改被丢失。
- 死锁(Deadlock):两个或多个事务相互等待对方释放资源,导致系统无法继续执行。这种情况下,只能通过终止其中一个事务或者回滚来解决死锁。
- 不可重复读(Unrepeatableread):一个事务在读取某个数据后,另一个事务修改了该数据并提交。当第一个事务再次读取同一数据时,得到的结果与之前不一致。因此称为不可重复读。
- 幻读(Phantom read):幻读与不可重复读类似,它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。
不可重复度和幻读的区别:
不可重复读的重点是修改,幻读的重点在于新增或者删除。
例1(同样的条件, 你读取过的数据, 再次读取出来发现值不一样了 ):事务1中的A先生读取自己的工资为 1000的操作还没完成,事务2中的B先生就修改了A的工资为2000,导 致A再读自己的工资时工资变为 2000;这就是不可重复读。
例2(同样的条件, 第1次和第2次读出来的记录数不一样 ):假某工资单表中工资大于3000的有4人,事务1读取了所有工资大于3000的人,共查到4条记录,这时事务2 又插入了一条工资大于3000的记录,事务1再次读取时查到的记录就变为了5条,这样就导致了幻读。
四、事务隔离级别
SQL 标准定义了四个隔离级别:
-
READ-UNCOMMITTED(读取未提交): 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。
-
READ-COMMITTED(读取已提交): 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。
-
REPEATABLE-READ(可重复读): 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
-
SERIALIZABLE(可串行化): 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。
隔离级别 | 脏读 | 不可重复读 | 幻影读 |
---|---|---|---|
READ-UNCOMMITTED | √ | √ | √ |
READ-COMMITTED | × | √ | √ |
REPEATABLE-READ | × | × | √ |
SERIALIZABLE | × | × | × |