事务的ACID特性
原子性(Atomicity): 事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用。
一致性(Consistency): 执行事务前后,数据保持一致,多个事务对同一个数据读取的结果是相同的。
隔离性(Isolation): 并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的。
持久性(Durability): 一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。
并发事务的问题
- 丢失修改(Lost to modify): 事务A、B同时访问数据,A修改了这个数据后,B也修改了,则A的修改结果就丢失了。
- 脏读(Dirty read): 事务A对数据进行了修改,修改没有提交时,事务B读取这个未提交的“脏数据”。
- 不可重复读(Unrepeatableread): 事务A内两次读同一数据期间,事务B修改了该数据,则A内两次读到的数据不一样。
- 幻读(Phantom read): 事务A读取数据期期间,事务B插入或删除了几行记录。在随后的查询中,A发现记录多了火烧了,就好像发生了幻觉一样,所以称为幻读。
不可重复读和幻读区别:
出现不一致的细粒度不同,不可重复读是A第二次读一条记录发现某些列的值被修改,幻读的重点在于A发现增多或减少几行记录。
隔离级别
在数据库事务中,为保证并发数据读写的正确性而提出的定义,它并不是MySQL专有的概念,而是源于ANSI/ISO制定的SQL-92标准。
每种关系型数据库都提供了各自特色的隔离级别实现,以最常见的MySQL InnoDB引擎为例,它是基于MVCC(Multi-Versioning Concurrency Control)和锁的复合实现,按照隔离程度从低到高,MySQL事务隔离级别分为四个不同层次:
- 读未提交(Read uncommitted):一个事务能够看到其他事务尚未提交的修改,是最低的隔离水平,可能出现脏读、幻读或不可重复读。。
- 读已提交(Read committed):保证不会看到未提交前的中间性状态,不保证再次读取时能够获取同样的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。
- 可重复读(Repeatable reads):保证同一个事务 对同一字段的多次读取结果是一致的,是MySQL InnoDB引擎的默认隔离级别,可以阻止脏读和不可重复读,但幻读仍有可能发生。
- 串行化(Serializable),所有的事务依次逐个执行,是最高的隔离级别,完全服从ACID,可以防止脏读、不可重复读以及幻读。意味着读要获取共享读锁,更新要获取排他写锁,如果SQL使用WHERE语句,还会获取区间锁 (MySQL以GAP锁形式实现,可重复读级别中默认也会使用,所以MySQ的默认隔离级别也可以防止幻读)。
悲观锁和乐观锁
悲观锁一般利用类似SELECT … FOR UPDATE这样的语句对数据加锁,避免其他事务修改数据。乐观锁则与Java并发包中的AtomicFieldUpdater类似,也是利用CAS机制,并不会对数据加锁,而是通过对比数据的时间戳或者版本号,来实现乐观锁需要的版本判断。 MVCC本质就可以看作乐观锁机制,而排他性的读写锁、双阶段锁等则是悲观锁的实现。
参考
《Java核心技术36讲》
《MySQL必知必会》