Transaction 事务简单详解 (2)

那第 2 条,不可重复读又怎么解释呢?还是用类似的例子来说明:

时间   事务 A(存款)   事务 B(取款)  
T1   开始事务      
T2       开始事务  
T3       查询余额(1000 元)  
T4   查询余额(1000 元)      
T5       取出 1000 元(余额 0 元)  
T6       提交事务  
T7   查询余额(0 元)      

事务 A 其实除了查询了两次以外,其他什么事情都没有做,结果钱就从 1000 变成 0 了,这就是重复读了。可想而知,这是别人干的,不是我干的。其实这样也是合理的,毕竟事务 B 提交了事务,数据库将结果进行了持久化,所以事务 A 再次读取自然就发生了变化。

这种现象基本上是可以理解的,但在有些变态的场景下却是不允许的。毕竟这种现象也是事务之间没有隔离所造成的,但我们对于这种问题,似乎可以忽略。

最后一条,幻读。我去!Phantom 这个单词不就是“幽灵、鬼魂”吗?刚看到这个单词时,真的把我的小弟弟都给惊呆了。怪不得这里要翻译成“幻读”了,总不能翻译成“幽灵读”、“鬼魂读”吧。其实意义就是鬼在读,不是人在读,或者说搞不清楚为什么,它就变了,很晕,真的很晕。还是用一个示例来说话吧:

时间   事务 A(统计总存款)   事务 B(存款)  
T1   开始事务      
T2       开始事务  
T3   统计总存款(10000 元)      
T4       存入 100 元  
T5       提交事务  
T6   统计总存款(10100 元)      

银行工作人员,每次统计总存款,都看到不一样的结果。不过这也确实也挺正常的,总存款增多了,肯定是这个时候有人在存钱。但是如果银行系统真的这样设计,那算是玩完了。这同样也是事务没有隔离所造成的,但对于大多数应用系统而言,这似乎也是正常的,可以理解,也是允许的。银行里那些恶心的那些系统,要求非常严密,统计的时候,甚至会将所有的其他操作给隔离开,这种隔离级别就算非常高了(估计要到 SERIALIZABLE 级别了)。

归纳一下,以上提到了事务并发所引起的跟读取数据有关的问题,各用一句话来描述一下:

脏读:事务 A 读取了事务 B 未提交的数据,并在这个基础上又做了其他操作。

不可重复读:事务 A 读取了事务 B 已提交的更改数据。

幻读:事务 A 读取了事务 B 已提交的新增数据。

第一条是坚决抵制的,后两条在大多数情况下可不作考虑。

这就是为什么必须要有事务隔离级别这个东西了,它就像一面墙一样,隔离不同的事务。看下面这个表格,您就清楚了不同的事务隔离级别能处理怎样的事务并发问题:

事务隔离级别   脏读   不可重复读   幻读  
READ_UNCOMMITTED   允许   允许   允许  
READ_COMMITTED   禁止   允许   允许  
REPEATABLE_READ   禁止   禁止   允许  
SERIALIZABLE   禁止   禁止   禁止  

根据您的实际需求,再参考这张表,最后确定事务隔离级别,应该不再是一件难事了。

JDBC 也提供了这四类事务隔离级别,但默认事务隔离级别对不同数据库产品而言,却是不一样的。我们熟知的 MySQL 数据库的默认事务隔离级别就是 READ_COMMITTED,Oracle、SQL Server、DB2等都有有自己的默认值。我认为 READ_COMMITTED 已经可以解决绝大多数问题了,其他的就具体情况具体分析吧。

若对其他数据库的默认事务隔离级别不太清楚,可以使用以下代码来获取:

1 DatabaseMetaData meta = DBUtil.getDataSource().getConnection().getMetaData(); 2 int defaultIsolation = meta.getDefaultTransactionIsolation();

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zzwxyd.html