概念
事务可以被认为是一系列数据库操作(如插入、更新、删除等)的集合,这些操作要么全部成功执行,要么全部回滚(撤销),以确保数据库的状态在操作期间不会出现异常或不一致的情况。
事务的四大特性
事务通常具有以下四个标准特性,通常被称为ACID特性:
1. 原子性(Atomicity)
事务是一个不可分割的操作单位,要么全部成功,要么全部失败。如果其中一个操作失败,整个事务将被回滚,以确保数据库不会处于部分更新的状态。
2. 一致性(Consistency)
在事务开始之前和结束之后,数据库必须保持一致性状态。这意味着事务的执行不会破坏数据库中的约束、规则和关系。
例如用户A和用户B两者的钱加起来一共是10000元,那么不管A和B之间如何转账,转几次账,事务结束之后两个用户的钱相加起来还是10000元,这就是事务的一致性。
3. 隔离性(isolation)
多个事务可以并发执行,但每个事务的操作应该与其他事务隔离,以防止干扰。事务的隔离性确保一个事务的操作不会影响其他事务。
4. 持久性(Durability)
一旦事务提交,其所做的更改将永久保存在数据库中,即使在系统崩溃之后也是如此。系统故障不应该影响已提交的事务。
隔离级别中的现象
1. 脏读(Dirty Read):
脏读是指一个事务读取了另一个事务尚未提交的数据。在Read Uncommitted(读取未提交)隔离级别下,一个事务可以读取到其他事务尚未提交的数据,可能导致读取了不准确的、未经验证的数据。
脏读现象发生在数据库事务的隔离级别中较低的级别,如Read Uncommitted(读取未提交)级别。在这个级别下,事务可以读取其他事务尚未提交的数据,从而导致脏读。
为了避免脏读问题,可以采取以下措施:
- 提高隔离级别
: 将事务的隔离级别设置为更高的级别,如Read Committed(读取已提交)或更高级别。这些级别能够避免脏读问题,但可能会引入其他并发问题。 - 使用事务:
在读取和修改数据时,始终使用事务来包装操作,以确保数据的一致性和完整性。 - 使用乐观锁或悲观锁:
使用锁机制来控制并发访问,乐观锁假设不会有并发冲突,但会在更新时检查数据是否已被修改,而悲观锁则在访问数据之前就加锁,防止其他事务修改数据。 - 避免长事务:
长时间运行的事务增加了脏读的风险,因为其他事务可能在其执行期间修改相同的数据。
2. 不可重复读(Non-repeatable Read):
不可重复读是指一个事务在同一个事务内两次读取同一数据时,得到的结果不一致。在Read Committed(读取已提交)隔离级别下,一个事务在多次读取同一数据时,可能会发现其他事务已经修改了该数据。
考虑以下情况:
事务A开始,读取某一数据。
事务B开始,修改了该数据并提交。
事务A再次读取同一数据。
解决不可重复读问题的方法之一是使用更高的隔离级别,如Repeatable Read(可重复读)或Serializable(串行化)。
3. 幻读(Phantom Read):
幻读是指一个事务在同一个事务内两次查询时,得到的结果集不一致。在Read Committed(读取已提交)隔离级别下,当一个事务查询一组数据后,另一个事务插入了一些数据,这会导致第一个事务的第二次查询返回不同的结果。
为了解决幻读问题,可以使用更高的隔离级别,如Repeatable Read(可重复读)或Serializable(串行化)
4. 丢失更新(Lost Update):
丢失更新是指两个事务同时修改同一数据时,后提交的事务的修改覆盖了前一个事务的修改。在Read Committed(读取已提交)隔离级别下,如果不使用锁机制或乐观锁,可能会发生丢失更新。
锁机制可以在事务修改数据时锁定数据,阻止其他事务的修改操作。乐观锁则是在事务提交时检查数据是否被其他事务修改过,如果有修改,则不提交,从而避免丢失更新。
隔离级别
同的隔离级别提供了不同的事务隔离和并发控制水平。常见的隔离级别包括:
1. Read Uncommitted(读取未提交):
最低隔离级别,允许一个事务读取另一个事务尚未提交的数据。可能导致脏读、不可重复读和幻读问题。
2. Read Committed(读取已提交):
默认的隔离级别,一个事务只能读取已经提交的数据。可以避免脏读,但仍可能出现不可重复读和幻读问题。
3. Repeatable Read(可重复读):
在一个事务内,多次读取同一数据会保证返回一致的结果。可以避免脏读和不可重复读,但仍可能出现幻读问题。
4. Serializable(串行化):
最高的隔离级别,确保每个事务在读取和修改数据时都会对数据加锁,避免了脏读、不可重复读和幻读问题。但可能导致并发性能降低。
级别越高,执行效率就越低。
在MySQL数据库中,支持上面四种隔离级别,默认的为Repeatable read (可重复读);而在Oracle数据库中,只支持Serializable (串行化)级别和Read committed (读已提交)这两种级别,其中默认的为Read committed级别。