1. 事务
事务是逻辑上的一组操作,要么都执行,要么都不执行。
以银行转账为例。A向B转账1000元,第一个操作是A的资金数减少1000,第二个操作是B的资金数增加1000。如果两个操作之间发生了异常,导致只有第一个操作执行,第二个操作不执行,那么这样就带来了问题。在事务存在的情况下,两个操作构成了一组操作,要么都执行,要么在出现异常的情况下都不执行。
2. 事务的特性
事务的特性简称为ACID
- 原子性(Atomicity):事务是最小的执行单位,不可分割。事务中的操作要么都成功执行,要么全部失败回滚。
- 一致性(Consistency)事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。还是以转账为例,A和B两者的钱加起来一共是5000,那么不管A和B之间如何转账,转几次账,事务结束后A和B两者的钱相加起来应该还得是5000,这就是事务的一致性。
- 隔离性(Isolation):当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。
- 持久性(Durability):一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。
3. 不考虑隔离性,并发事务带来的问题
- 脏读:事务A访问一个数据,并修改了数据,但是没有提交修改。这时事务B也访问了这个数据,这时的数据是事务A修改后(A未提交修改)的数据,事务B使用这个数据进行了一系列操作。这时事务A回滚操作,那么事务B执行操作时所使用的数据是错误的数据。
- 丢失修改:事务A读取数据,事务B也读取该数据。事务A对数据进行了修改,接着事务B也对该数据进行了修改,那么事务A的修改就丢失了,最后数据的值是事务B修改后的结果。
- 不可重复读:同一个事务多次读取数据,得到的值不同。事务A读取数据,此时事务B也读取数据,并修改数据。此时事务A再读取数据时,所得到的值和上次可能会有所不同。
- 幻影读:与不可重复读类似。事务A读取了几行数据,接着另一个事务B插入了一些数据时。在随后的查询中,事务A就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。
不可重复读关注的是修改,幻影读关注于新增或者删除。
4. 事务的隔离级别
SQL 标准定义了四个隔离级别:
READ-UNCOMMITTED
(读取未提交): 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。
READ-COMMITTED
(读取已提交): 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。
REPEATABLE-READ
(可重复读): 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
SERIALIZABLE
(可串行化): 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。
读提交级别下出现幻影读
可重复度级别下避免幻影读
MySQL InnoDB 存储引擎默认的隔离级别是 REPEATABLE-READ
。通过SELECT @@tx_isolation;
命令来查看存储引擎默认的隔离级别,MySQL 8.0 该命令改为SELECT @@transaction_isolation;
与标准SQL不同,MySQL的InnoDB存储引擎在默认隔离级别REPEATABLE-READ
下,可以使用 Next-Key Lock 锁算法来避免幻读的产生。在该隔离级别下,使用加锁读(例如 select * from table for update
语句)来保证不会产生幻读,而这个加锁度使用到的机制就是 Next-Key Lock 锁算法。从而达到了标准SQL的SERIALIZABLE
隔离级别。