一、ACID
- 原子性( A ):事务是最小的单元,要么全部成功,要么全部回滚。
- 一致性( C ):事务开始之前和事务结束以后,数据库的完整性约束没有被破坏。事务前后保持处于一致的状态,不管在任何给定的时间并发事务有多少。一个事务是数据状态的切换,因此,如果事务并发对个,系统也必须串行执行这些事务操作。
- 隔离性( I ):多个事务并发访问时,事务之间是隔离的,一个事务不应该影响其它事务运行效果。完全的隔离性是不现实的,完全的隔离性要求数据库同一时间只执行一条事务,这样会严重影响性能。
- 持久性( D ):事务的结果不能丢失。
在并发环境下,事务的隔离性很难保证,从而引发并发一致性问题。
-
脏读:T1修改了一个数据,未提交之前被T2读取到了。随后T1撤销了修改,T2读到了脏数据。
-
不可重复读:T1读取->T2修改->T1再读。
-
幻读:T1select count()->T2insert->T1数据不准确
-
丢失更改 ::T1更改->T2更改 T1更改失效
对于简单的事务隔离性问题,我们可以通过设置事务隔开级别来保证数据一致性,通常通过@Transational注解来管理事务。其默认使用数据库引擎默认的隔离级别。
- READ_UNCOMMITTED:未授权读取级别
- 读事务允许其他读事务和写事务,未提交的写事务禁止其他写事务(但允许其他读事务)。可以防止丢失更改问题。
- READ_COMMITTED:授权读取级别
- 读事务允许其他读事务和写事务,未提交的写事务禁止其他读事务和写事务。可以防止丢失更改,脏读。
- REPEATABLE_READ:可重复读取级别
- 读事务禁止其他写事务(但允许其他读事务),未提交的写事务禁止其他读事务和写事务。 可以防止丢失更改,脏读,不可重复读。
- SERIALIZABLE:序列化级别
- 一个一个走,并行转串行。
数据库的事务隔离级别的实现通常是通过加锁或 undo log+MVCC实现。
锁实现
-
未提交读:一个update事务A只有在对数据修改时才加write lock,一旦写完马上释放write lock,即使事务A还没有提交。因此事务B在读取同一行时,才能读到事务A修改过的数据。
-
提交读:一个update事务A只有在对数据修改时才加write lock,但直到事务A commit时才释放写锁。因此,同时进行的事务B希望读取同一行数据时,会被事务A的write lock堵塞,所以解决了脏读的问题。
-
可重复读:这个隔离等级的条件下,除了执行提交读的写锁方式,还会在读取一行数据后,为这行数据添加read lock直至事务commit。例如,事务A读取ID=1这一行数据,然后为ID=1添加read lock。事务B同时希望update ID=1,此时获取写锁失败,因此在事务A执行完之前,没有其他任何事务可以对ID=1这一行做修改,因此解决了重复读的问题