事务ACID属性与隔离级别
概念
数据库领域中的事务指的是一系列对数据库的操作集合,是数据库管理系统(DBMS)定义的一个执行单位。事务的作用体现在两个方面:
- 在并发访问数据库的场景中,利用事务来隔离多个应用程序的操作,避免多个操作彼此之间相互影响
- 提供一种从失败中恢复到正常状态的方法,同时提供数据库即使在异常状态仍能保持一致性的方法
当然以上两条是事务理论上应该持有的特性,但是实际应用过程中,由于业务需求的不同或配置方式不同,事务对以上两个方面的满足程度也不尽相同。
ACID 属性
事务是 DBMS 定义的一个逻辑概念,其作为一个执行单元,内部包含了一系列数据库操作。但是并非所有的数据库操作序列都可以被归纳为事务,可以通过四个属性来描述事务:
- 原子性(Atomicity):事务中包含的操作集合,要么全部操作执行完成,要么全部都不执行。即当事务执行过程中,发生了某些异常情况,如系统崩溃、执行出错,则需要对已执行的操作进行回滚,清除所有执行痕迹。
- 一致性(Consistency):事务执行前和事务执行后,数据库的完整性约束不被破坏。即事务的执行是从一个有效状态转移到另一个有效状态。
- 隔离性(Isolation):多个事务并发执行时,彼此之间不应该存在相互影响。隔离程度不是绝对的,每个数据库都提供有自己的隔离级别,每个数据库的默认隔离级别也不尽相同。
- 持久性(Durability):事务正常执行完毕后,对数据库的修改是永久性的。即事务的修改操作已经记录到了存储介质中。
note:
原子性和一致性约束的内容不同,事务中的操作,全部执行或全部不执行是原子性约束,一致性要求的是数据库完整性约束条件不被破坏,例如在 表上建立对 表的外键关联约束,则向 表插入记录时,若关联的 表不存在对应记录,就是违反了一致性约束。
事务所能体现出的作用就是通过其所具有的属性定义的,隔离性保证了数据库的并发访问中,多个事务之间彼此隔离,避免相互影响;原子性则保证了即使事务执行失败,仍然能够将数据库恢复到执行前状态;一致性则是在数据库操作执行异常时,维护健康的关系约束条件不被破坏掉。持久性则是保证数据的修改被记录到持久化设备上,不受系统异常或设备断电的影响。
隔离级别
在实际应用中,对数据库的并发访问是必然的,如何在多个事务的同时操作下保证每个业务流都能获取正确的结果,依靠的就是 DBMS 提供的不同程度的隔离级别。ANSI/ISO SQL 定义的标准隔离级别如下:
- 未提交读(Read Uncommitted):一个事务过程中可以读取到其他事务对数据的未提交修改。即事务的修改阶段未加排他锁,对其他事务可见。例如事务 可能读取到只是事务 中某一步的修改状态,即存在脏读的现象。
脏读指的是,事务读取到的数据可能是不正确、不合理或者处于非法状态的数据,例如在事务 读取后,事务 可能又对数据做了修改,或者事务 中某些操作违反了一致性约束,作了回滚操作,该情况下事务 读取到的数据称之为脏数据,该行为称之为脏读。
- 提交读(Read Committed):一个事务过程中只能读取到其他事务对数据的提交后修改。即事务的修改阶段加了排它锁,直到事务结束才释放,执行读命令那一刻加了共享锁,读完即释放,以此维持事务修改阶段对其他事务的不可见。例如事务 读取到的只能是事务 提交完成后的状态。该隔离级别避免了脏读现象,但正是由于事务 可能读取到的是事务 修改完成后的数据,以致出现了不可重复读现象。
不可重复读指的是,对于同一个事务的前后两次读取操作,读取到的内容不同。例如在事务 读取操作后,事务 可能对数据做了修改,事务 修改完成提交后,事务 又做了读取操作,因为内容已被修改,导致读取到的内容与上一次不同,即存在不可重复读现象。
- 可重复读(Repeatable Reads):一个事务过程中不允许其他事务对数据进行修改。即事务的读取过程加了共享锁,事务的修改过程加了排它锁,并一直维持锁定状态直到事务结束。因为事务的读取或修改都需要维持整个阶段的锁定状态,所以避免了脏读和不可重复读现象。但是因为只对现有的记录上进行了锁定,并未维持间隙锁/范围锁,导致某些数据记录的插入未受阻拦,即存在幻读现象。
幻读指的是,事务中前后相同的查询语句,返回的结果集不同。例如在事务 查询表记录后,事务 向表中增加了一条记录,当事务 再次执行相同的查询时,返回的结果集可能不同,即存在幻读现象。
- 可串行化(Serializable):一个事务过程中不允许其他事务对指定范围数据进行修改。即事务过程中若指定了操作集合的范围,则在可重复读的锁基础上增加了对操作集合的范围锁,通过增加范围锁避免了幻读现象。
锁的使用是为了在并发环境中保持每个业务流处理结果的正确性,这样的概念在计算机领域中很普遍,但是都必须要基于一个前提,或者称之为约定:在执行操作前,首先尝试去获取锁,获取成功则可以执行,若获取失败,则不执行或等待重复获取。因为无论任何类型的操作,有没有锁都不影响程序本身的执行流程,但只有遵从这个约定才能体现出其价值。就像红绿灯并不影响车辆本身的行驶能力,只有声明所有个体皆遵守相同的规则,所以一切才变得有序。当然在数据库的并发环境下,隔离程度越高,也就意味着并发程度越低,所以各个数据库中一般设置的都是一个折中的隔离级别。