首页 > 代码库 > 事务隔离级别

事务隔离级别

事务并发所带来的问题

  1. 脏读:一个事务读取到另一个事务尚未提交的数据。

  2. 不可重复读:同一事务,两次读取同一数据,得到不同的结果。

  3. 幻读:同一事务,用相同的条件读取两次,得到的结果集数据条数不同(数据条数多了或者少了)。    

补充:详解上面的术语。

  1. 脏读:当一个事务1读取到了事务2没有提交的数据,那么如果事务2出错,发生了回滚,这将导致事务1得到的是错误的数据。比如小明的工资为1000,事务2将小明的工资更改为2000,但是数据没有提交,事务1去查询小明的工资,得到的结果为2000。如果这时,事务2更改不成功,事务2回滚了。那么事务1得到的结果2000就是错误的。

  2. 不可重复读:事务1第一次读取了一条记录,这时,事务2更改了同一条记录。事务1再次读该条记录,那么事务1中第二次读取到的数据和第一次读取到的数据将是不同的。

  3. 幻读:事务1第一次按一个条件查询,得到一个结果集。事务2插入或者删除一条记录,这条记录满足事务1所查询的条件。当事务1第二次再按同一个条件查询时,由于事务2插入或者删除了数据,那么得到的结果集(数据条数变多了或者变少了)将不同。

为了解决上面这些并发问题,数据库提供了4种事务隔离级别对之进行处理。

4种事务隔离级别

  1. TRANSACTION_READ_UNCOMMITTED:防止更新丢失,允许脏读、不可重复读、幻读。

  2. TRANSACTION_READ_COMMITTED:防止脏读,允许不可重复读、幻读,这也是多数数据库默认的隔离级别。

  3. TRANSACTION_REPEATABLE_READ:防止脏读、不可重复读,允许幻读。

  4. TRANSACTION_SERIALIZABLE:防止脏读、不可重复读,幻读。(事务完全串行化执行,事务一个一个按顺序依次执行,可以不会产生并发问题。)


丢失更新 脏读 不可重复读 幻读
未提交读 N Y Y Y
已提交读 N N Y Y
可重复读 N N N Y
串行化 N N N N

锁就是防止其他事务访问指定的资源的手段。锁是实现并发控制的主要方法,是多个用户能够同时操纵同一个数据库中的数据而不发生数据不一致现象的重要保障。 一般来说,锁可以防止脏读、不可重复读和幻觉读。

用锁的方式处理脏读、不可重复读、幻读问题

为了防止更新丢失(允许脏读、不可重复读、幻读),可以采用”排他锁“实现。当事务1正在更新某条记录时,那么其它事务想要更新该条记录则需要阻塞等待锁,当事务1释放了锁并且事务2得到该锁之后,事务2才能对该条记录进行更新操作。所以可以防止更新丢失。这种情况下会发生脏读,因为对数据只加了排他写锁,这个锁只能防止其它写事务的执行。而读数据是不需要锁的,所以数据在修改的过程中,其它读事务还是能够读取数据的。这样就会发生脏读。同理,也会发生不可重复读、幻读。

为了防止脏读(允许不可重复读,幻读),可以采用”排他锁“和”共享锁“实现,对读事务加”共享锁“(查询执行完就释放,不需要等到事务结束),对写事务加“排他锁”。比如,当事务2在修改某条数据时,事务1想要去读该条记录。由于事务2给该条数据加了“排他锁”并且在未修改成功之后不会释放锁,所以此时事务1将不能在这条记录上获取到共享锁,事务1将阻塞起来,直到事务2的修改动作执行完成,释放了锁,此时,事务1才能获取到“共享锁”去读数据。所以事务1是读不到没有提交的数据的,所以这样能够防止脏读。(为什么不能防止不可重复读?)比如:事务1第一次读取某条记录,读的时候对数据加了“共享锁”,读取完之后就释放了锁,此时,事务2对该条记录进行修改,修改的时候加“排他锁”,修改完成之后就释放锁。之后事务1再一次读取该条记录,这时能够正常获得”共享锁“并读取数据的。但此时读到的数据是修改之后的数据。那么两次读到的数据就不一样了。这就发生了不可重复读。

为了防止不可重复读(允许幻读),可以采用“排他锁”和“共享锁”实现,对读事务加”共享锁“,对写事务加“排他锁”。两种锁都是在事务完成之后再释放。比如,事务1第一次读取某条记录,对该数据加了“共享锁”,在事务没完成之前,事务1不会释放该锁,此时,事务2想要修改该条记录,那么事务2需要去申请获取“排他锁”,由于事务1没完成,锁没有释放,所以事务2会被阻塞等待锁。直到事务1完成释放锁之后事务2才有机会获得锁执行。所以事务1中不管有多少次查询,返回的结果都是一样的。这就防止了不可重复读。(为什么不可防止幻读?)虽然读数据和修改数据都加了相应的锁进行并发控制,但是,在向数据库添加数据时,数据是新添加进去的,新的数据上并没有什么锁的限制。所以一个事务两次查询可能会得到不同的结果集(记录条数不同)。

为了防止幻读,采用的是范围锁RangeS RangeS_S模式,锁定检索范围为只读,这样,对于这个范围内的数据都不能添加或者删除,这样就防止了幻读的产生。

(PS:用锁的方式处理脏读、不可重复读、幻读问题全是文本描述,可能看起来有点吃力。不过还是可以尽力看看,我也是理解了很久这里的锁才理解到这些)