事务(Transaction)是并发控制
![](/icons/65160de.gif)
基本单位
![](/icons/65160dou2.gif)
所谓
![](/icons/65160de.gif)
事务
![](/icons/65160dou.gif)
它是
![](/icons/65160yi.gif)
个操作序列
![](/icons/65160dou.gif)
这些操作要么都执行
![](/icons/65160dou.gif)
要么都不执行
![](/icons/65160dou.gif)
它是
![](/icons/65160yi.gif)
个不可分割
![](/icons/65160de.gif)
工作单位
![](/icons/65160dou2.gif)
例如
![](/icons/65160dou.gif)
银行转账工作:从
![](/icons/65160yi.gif)
个账号扣款并使另
![](/icons/65160yi.gif)
个账号增款
![](/icons/65160dou.gif)
这两个操作要么都执行
![](/icons/65160dou.gif)
要么都不执行
![](/icons/65160dou2.gif)
所以
![](/icons/65160dou.gif)
应该把它们看成
![](/icons/65160yi.gif)
个事务
![](/icons/65160dou2.gif)
事务是数据库维护数据
![](/icons/65160yi.gif)
致性
![](/icons/65160de.gif)
单位
![](/icons/65160dou.gif)
在每个事务结束时
![](/icons/65160dou.gif)
都能保持数据
![](/icons/65160yi.gif)
致性
针对上面
![](/icons/65160de.gif)
描述可以看出
![](/icons/65160dou.gif)
事务
![](/icons/65160de.gif)
提出主要是为了解决并发情况下保持数据
![](/icons/65160yi.gif)
致性
![](/icons/65160de.gif)
问题
事务具有以下4个基本特征
l Atomic(原子性):事务中包含
![](/icons/65160de.gif)
操作被看做
![](/icons/65160yi.gif)
个逻辑单元
![](/icons/65160dou.gif)
这个逻辑单元中
![](/icons/65160de.gif)
操作要么全部成功
![](/icons/65160dou.gif)
要么全部失败
l Consistency(
![](/icons/65160yi.gif)
致性):只有合法
![](/icons/65160de.gif)
数据可以被写入数据库
![](/icons/65160dou.gif)
否则事务应该将其回滚到最初状态
l Isolation(隔离性):事务允许多个用户对同
![](/icons/65160yi.gif)
个数据进行并发访问
![](/icons/65160dou.gif)
而不破坏数据
![](/icons/65160de.gif)
正确性和完整性
![](/icons/65160dou2.gif)
同时
![](/icons/65160dou.gif)
并行事务
![](/icons/65160de.gif)
修改必须和其他并行事务
![](/icons/65160de.gif)
修改相互独立
l Durability(持久性):事务结束后
![](/icons/65160dou.gif)
事务处理
![](/icons/65160de.gif)
结果必须能够得到固化
数据库肯定是要被广大客户所共享访问
![](/icons/65160de.gif)
![](/icons/65160dou.gif)
那么在数据库操作过程中很可能出现以下几种不确定情况
l 更新丢失(Lost update):两个事务都同时更新
![](/icons/65160yi.gif)
行数据
![](/icons/65160dou.gif)
但是第 2个事务却中途失败退出
![](/icons/65160dou.gif)
导致对数据
![](/icons/65160de.gif)
两个修改都失效了
![](/icons/65160dou2.gif)
这是
![](/icons/65160yinwei.gif)
系统没有执行任何
![](/icons/65160de.gif)
锁操作
![](/icons/65160dou.gif)
因此并发事务并没有被隔离开来
l 脏读取(Dirty Reads):
![](/icons/65160yi.gif)
个事务开始读取了某行数据
![](/icons/65160dou.gif)
但是另外
![](/icons/65160yi.gif)
个事务已经更新了此数据但没有能够及时提交
![](/icons/65160dou2.gif)
这是相当危险
![](/icons/65160de.gif)
![](/icons/65160dou.gif)
![](/icons/65160yinwei.gif)
很可能所有
![](/icons/65160de.gif)
操作都被回滚
l 不可重复读取(Non-repeatable Reads):
![](/icons/65160yi.gif)
个事务对同
![](/icons/65160yi.gif)
行数据重复读取两次
![](/icons/65160dou.gif)
但是却得到了区别
![](/icons/65160de.gif)
结果
![](/icons/65160dou2.gif)
例如
![](/icons/65160dou.gif)
在两次读取
![](/icons/65160de.gif)
中途
![](/icons/65160dou.gif)
有另外
![](/icons/65160yi.gif)
个事务对该行数据进行了修改
![](/icons/65160dou.gif)
并提交
l 两次更新问题(Second lost updates problem):无法重复读取
![](/icons/65160de.gif)
特例
![](/icons/65160dou2.gif)
有两个并发事务同时读取同
![](/icons/65160yi.gif)
行数据
![](/icons/65160dou.gif)
然后其中
![](/icons/65160yi.gif)
个对它进行修改提交
![](/icons/65160dou.gif)
而另
![](/icons/65160yi.gif)
个也进行了修改提交
![](/icons/65160dou2.gif)
这就会造成第
![](/icons/65160yi.gif)
次写操作失效
l 虚读(Phantom Reads):事务在操作过程中进行两次查询
![](/icons/65160dou.gif)
第 2次查询
![](/icons/65160de.gif)
结果包含了第
![](/icons/65160yi.gif)
次查询中未出现
![](/icons/65160de.gif)
数据(这里并不要求两次查询
![](/icons/65160de.gif)
SQL语句相同)
![](/icons/65160dou2.gif)
这是
![](/icons/65160yinwei.gif)
在两次查询过程中有另外
![](/icons/65160yi.gif)
个事务插入数据造成
![](/icons/65160de.gif)
l 数据库
![](/icons/65160de.gif)
隔离级别
为了避免上面出现
![](/icons/65160de.gif)
几种情况
![](/icons/65160dou.gif)
在标准SQL规范标准中
![](/icons/65160dou.gif)
定义了4个事务隔离级别
![](/icons/65160dou.gif)
区别
![](/icons/65160de.gif)
隔离级别对事务
![](/icons/65160de.gif)
处理区别
ü 未授权读取(Read Uncommitted):允许脏读取
![](/icons/65160dou.gif)
但不允许更新丢失
![](/icons/65160dou2.gif)
如果
![](/icons/65160yi.gif)
个事务已经开始写数据
![](/icons/65160dou.gif)
则另外
![](/icons/65160yi.gif)
个数据则不允许同时进行写操作
![](/icons/65160dou.gif)
但允许其他事务读此行数据
![](/icons/65160dou2.gif)
该隔离级别可以通过“排他写锁”实现
ü 授权读取(Read Committed):允许不可重复读取
![](/icons/65160dou.gif)
但不允许脏读取
![](/icons/65160dou2.gif)
这可以通过“瞬间共享读锁”和“排他写锁”实现
![](/icons/65160dou2.gif)
读取数据
![](/icons/65160de.gif)
事务允许其他事务继续访问该行数据
![](/icons/65160dou.gif)
但是未提交
![](/icons/65160de.gif)
写事务将会禁止其他事务访问该行
ü 可重复读取(Repeatable Read):禁止不可重复读取和脏读取
![](/icons/65160dou.gif)
但是有时可能出现幻影数据
![](/icons/65160dou2.gif)
这可以通过“共享读锁”和“排他写锁”实现
![](/icons/65160dou2.gif)
读取数据
![](/icons/65160de.gif)
事务将会禁止写事务(但允许读事务)
![](/icons/65160dou.gif)
写事务则禁止任何其他事务
ü 序列化(Serializable):提供严格
![](/icons/65160de.gif)
事务隔离
![](/icons/65160dou2.gif)
它要求事务序列化执行
![](/icons/65160dou.gif)
事务只能
![](/icons/65160yi.gif)
个接着
![](/icons/65160yi.gif)
个地执行
![](/icons/65160dou.gif)
但不能并发执行
![](/icons/65160dou2.gif)
如果仅仅通过“行级锁”是无法实现事务序列化
![](/icons/65160de.gif)
![](/icons/65160dou.gif)
必须通过其他机制保证新插入
![](/icons/65160de.gif)
数据不会被刚执行查询操作
![](/icons/65160de.gif)
事务访问到
隔离级别越高
![](/icons/65160dou.gif)
越能保证数据
![](/icons/65160de.gif)
完整性和
![](/icons/65160yi.gif)
致性
![](/icons/65160dou.gif)
但是对并发性能
![](/icons/65160de.gif)
影响也越大
![](/icons/65160dou2.gif)
对于多数应用
![](/icons/65160chengxu.gif)
![](/icons/65160dou.gif)
可以优先考虑把数据库系统
![](/icons/65160de.gif)
隔离级别设为Read Committed
![](/icons/65160dou.gif)
它能够避免脏读取
![](/icons/65160dou.gif)
而且具有较好
![](/icons/65160de.gif)
并发性能
![](/icons/65160dou2.gif)
尽管它会导致不可重复读、虚读和第 2类丢失更新这些并发问题
![](/icons/65160dou.gif)
在可能出现这类问题
![](/icons/65160de.gif)
个别场合
![](/icons/65160dou.gif)
可以由应用
![](/icons/65160chengxu.gif)
采用悲观锁或乐观锁来控制
通过前面
![](/icons/65160de.gif)
介绍已经知道
![](/icons/65160dou.gif)
通过选用区别
![](/icons/65160de.gif)
隔离等级就可以在区别程度上避免前面所提及
![](/icons/65160de.gif)
在事务处理中所面临
![](/icons/65160de.gif)
各种问题
![](/icons/65160dou2.gif)
所以
![](/icons/65160dou.gif)
数据库隔离级别
![](/icons/65160de.gif)
选取就显得尤为重要
![](/icons/65160dou.gif)
在选取数据库
![](/icons/65160de.gif)
隔离级别时
![](/icons/65160dou.gif)
应该注意以下几个处理
![](/icons/65160de.gif)
原则:
首先
![](/icons/65160dou.gif)
必须排除“未授权读取”
![](/icons/65160dou.gif)
![](/icons/65160yinwei.gif)
在多个事务的间使用它将会是非常危险
![](/icons/65160de.gif)
![](/icons/65160dou2.gif)
事务
![](/icons/65160de.gif)
回滚操作或失败将会影响到其他并发事务
![](/icons/65160dou2.gif)
第
![](/icons/65160yi.gif)
个事务
![](/icons/65160de.gif)
回滚将会完全将其他事务
![](/icons/65160de.gif)
操作清除
![](/icons/65160dou.gif)
甚至使数据库处在
![](/icons/65160yi.gif)
个不
![](/icons/65160yi.gif)
致
![](/icons/65160de.gif)
状态
![](/icons/65160dou2.gif)
很可能
![](/icons/65160yi.gif)
个已回滚为结束
![](/icons/65160de.gif)
事务对数据
![](/icons/65160de.gif)
修改最后却修改提交了
![](/icons/65160dou.gif)
![](/icons/65160yinwei.gif)
“未授权读取”允许其他事务读取数据
![](/icons/65160dou.gif)
最后整个
![](/icons/65160cuowu.gif)
状态在其他事务的间传播开来
其次
![](/icons/65160dou.gif)
绝大部分应用都无须使用“序列化”隔离(
![](/icons/65160yi.gif)
般来说
![](/icons/65160dou.gif)
读取幻影数据并不是
![](/icons/65160yi.gif)
个问题)
![](/icons/65160dou.gif)
此隔离级别也难以测量
![](/icons/65160dou2.gif)
目前使用序列化隔离
![](/icons/65160de.gif)
应用中
![](/icons/65160dou.gif)
![](/icons/65160yi.gif)
般都使用悲观锁
![](/icons/65160dou.gif)
这样强行使所有事务都序列化执行
剩下
![](/icons/65160de.gif)
也就是在“授权读取”和“可重复读取”的间选择了
![](/icons/65160dou2.gif)
我们先考虑可重复读取
![](/icons/65160dou2.gif)
如果所有
![](/icons/65160de.gif)
数据访问都是在统
![](/icons/65160yi.gif)
![](/icons/65160de.gif)
原子数据库事务中
![](/icons/65160dou.gif)
此隔离级别将消除
![](/icons/65160yi.gif)
个事务在另外
![](/icons/65160yi.gif)
个并发事务过程中覆盖数据
![](/icons/65160de.gif)
可能性(第 2个事务更新丢失问题)
![](/icons/65160dou2.gif)
这是
![](/icons/65160yi.gif)
个非常重要
![](/icons/65160de.gif)
问题
![](/icons/65160dou.gif)
但是使用可重复读取并不是解决问题
![](/icons/65160de.gif)
唯
![](/icons/65160yi.gif)
途径
假设使用了“版本数据”
![](/icons/65160dou.gif)
Hibernate会自动使用版本数据
![](/icons/65160dou2.gif)
Hibernate
![](/icons/65160de.gif)
![](/icons/65160yi.gif)
级Session缓存Cache和版本数据已经为你提供了“可重复读取隔离”绝大部分
![](/icons/65160de.gif)
特性
![](/icons/65160dou2.gif)
特别是
![](/icons/65160dou.gif)
版本数据可以防止 2次更新丢失
![](/icons/65160de.gif)
问题
![](/icons/65160dou.gif)
![](/icons/65160yi.gif)
级Session缓存Cache可以保证持久载入数据
![](/icons/65160de.gif)
状态和其他事务对数据
![](/icons/65160de.gif)
修改隔离开来
![](/icons/65160dou.gif)
因此如果使用对所有
![](/icons/65160de.gif)
数据库事务采用授权读取隔离和版本数据是行得通
![](/icons/65160de.gif)
“可重复读取”为数据库查询提供了更好
![](/icons/65160de.gif)
效率(仅对那些长时间
![](/icons/65160de.gif)
数据库事务)
![](/icons/65160dou.gif)
但是由于幻影读取依然存在
![](/icons/65160dou.gif)
因此没必要使用它(对于Web应用来说
![](/icons/65160dou.gif)
![](/icons/65160yi.gif)
般也很少在
![](/icons/65160yi.gif)
个数据库事务中对同
![](/icons/65160yi.gif)
个表查询两次)
也可以同时考虑选择使用Hibernate
![](/icons/65160de.gif)
2级缓存Cache
![](/icons/65160dou.gif)
它可以如同底层
![](/icons/65160de.gif)
数据库事务
![](/icons/65160yi.gif)
样提供相同
![](/icons/65160de.gif)
事务隔离
![](/icons/65160dou.gif)
但是它可能弱化隔离
![](/icons/65160dou2.gif)
假如在 2级缓存Cache大量使用缓存Cache并发策略
![](/icons/65160dou.gif)
它并不提供重复读取语义(例如
![](/icons/65160dou.gif)
后面章节中将要讨论
![](/icons/65160de.gif)
读写
![](/icons/65160dou.gif)
特别是非严格读写)
![](/icons/65160dou.gif)
很容易可以选择默认
![](/icons/65160de.gif)
隔离级别:
![](/icons/65160yinwei.gif)
无论如何都无法实现“可重复读取”
![](/icons/65160dou.gif)
因此就更没有必要拖慢数据库了
![](/icons/65160dou2.gif)
另
![](/icons/65160yi.gif)
方面
![](/icons/65160dou.gif)
可能对关键类不采用 2级缓存Cache
![](/icons/65160dou.gif)
或者采用
![](/icons/65160yi.gif)
个完全
![](/icons/65160de.gif)
事务缓存Cache
![](/icons/65160dou.gif)
提供“可重复读取隔离”
![](/icons/65160dou2.gif)
那么在业务中需要使用到“可重复读取”吗?如果你喜欢
![](/icons/65160dou.gif)
当然可以那样做
![](/icons/65160dou.gif)
但更多
![](/icons/65160de.gif)
时候并没有必要花费这个代价
SQL99有关事务
要想让多个用户共享同
![](/icons/65160yi.gif)
个数据库
![](/icons/65160dou.gif)
首先要解决
![](/icons/65160de.gif)
问题是用户的间
![](/icons/65160de.gif)
相互隔离
![](/icons/65160dou.gif)
即每个用户在访问数据库时
![](/icons/65160dou.gif)
都感觉自己是在独占数据库
似
![](/icons/65160de.gif)
![](/icons/65160dou.gif)
这有点象我们在操作系统中说
![](/icons/65160de.gif)
“分时”
![](/icons/65160dou2.gif)
在SQL99中
![](/icons/65160dou.gif)
为了实现事务
![](/icons/65160de.gif)
完全隔离
![](/icons/65160dou.gif)
定义了 3种必须避免
![](/icons/65160de.gif)
现象:
1、读“脏”数据(dirty read):即事务在运行中读到了其它事务未提交
![](/icons/65160de.gif)
数据
2、不可重复读(unrepeatable read):即事务在运行中再次读取同
![](/icons/65160yi.gif)
数据时
![](/icons/65160dou.gif)
发现其它事务
![](/icons/65160de.gif)
更新
3、“幻象”读(phantom read):即事务在运行中再次执行同
![](/icons/65160yi.gif)
查询时
![](/icons/65160dou.gif)
发现其它事务
![](/icons/65160de.gif)
更新
只有避免上面 3种现象
![](/icons/65160dou.gif)
才能实现真正意义上
![](/icons/65160de.gif)
隔离
![](/icons/65160dou.gif)
或者称是可串行
![](/icons/65160de.gif)
![](/icons/65160dou2.gif)
但是
![](/icons/65160dou.gif)
此时事务的间
![](/icons/65160yinwei.gif)
冲突而等待
![](/icons/65160de.gif)
机会非常高
![](/icons/65160dou.gif)
系统
![](/icons/65160de.gif)
性能
可能难以达到预期
![](/icons/65160de.gif)
目标
为了实现隔离性和系统性能的间
![](/icons/65160de.gif)
平衡
![](/icons/65160dou.gif)
SQL99定义了 4种隔离级别
![](/icons/65160dou.gif)
允许应用根据实际需要选择合适
![](/icons/65160de.gif)
隔离级别
![](/icons/65160dou.gif)
这 4种隔离级别
![](/icons/65160de.gif)
含义如下:读“脏”数据 不可重复读 “幻象”读
未提交读 N N N
提交读 Y N N
可重复读 Y Y N
可串行 Y Y Y
其中“N”表示在这种隔离级别下
![](/icons/65160dou.gif)
对应
![](/icons/65160de.gif)
现象不可避免
![](/icons/65160dou.gif)
而“Y”则
表示可以避免
当然
![](/icons/65160dou.gif)
不管怎样
![](/icons/65160dou.gif)
必须保证事务是可恢复
![](/icons/65160de.gif)
![](/icons/65160dou.gif)
所以SQL99还规定在未提交读这种隔离级别下
![](/icons/65160dou.gif)
事务必须是只读
![](/icons/65160de.gif)
在DB2中
![](/icons/65160dou.gif)
隔离级别是面向应用
![](/icons/65160de.gif)
![](/icons/65160dou.gif)
用户只有在应用被预编译(PREP)或联编(BIND)时才能设置隔离级别;而ORACLE
![](/icons/65160de.gif)
隔离级别则是面向用户
![](/icons/65160de.gif)
![](/icons/65160dou.gif)
用户可以在每次事务开始时重新设置隔离级别
![](/icons/65160dou2.gif)
由于后者更为灵活
![](/icons/65160dou.gif)
SQL99最终采用了这种方式
相比的下
![](/icons/65160dou.gif)
DB2
![](/icons/65160de.gif)
并发控制显得“火候不足”
![](/icons/65160dou.gif)
仅仅是最基本
![](/icons/65160de.gif)
2阶段锁协议
![](/icons/65160dou.gif)
并发度的低可以预料;按照我们做数据库
![](/icons/65160de.gif)
人
![](/icons/65160de.gif)
想法
![](/icons/65160dou.gif)
最起码也得做个多版本什么
![](/icons/65160de.gif)
![](/icons/65160dou.gif)
这样至少可以实现只读事务不等待(在实际数据库系统中
![](/icons/65160dou.gif)
只读事务出现
![](/icons/65160de.gif)
概率要远远高于更新事务)
![](/icons/65160dou2.gif)
真不明白IBM是如何想
![](/icons/65160de.gif)
![](/icons/65160dou2.gif)
而ORACLE
![](/icons/65160de.gif)
并发控制则感觉是“做过了头”
![](/icons/65160dou.gif)
通过实现读
![](/icons/65160yi.gif)
致性
![](/icons/65160dou.gif)
不仅只读事务不必等待
![](/icons/65160dou.gif)
连只读操作都不必等待
![](/icons/65160dou.gif)
当然由此而付出
![](/icons/65160de.gif)
代价是在可串行这种隔离级别(默认
![](/icons/65160de.gif)
隔离级别)下
![](/icons/65160dou.gif)
更新操作
![](/icons/65160yinwei.gif)
冲突而使得整个事务被滚回
![](/icons/65160de.gif)
几率大大增加
![](/icons/65160dou2.gif)
搞不清楚为什么到现在还没人提意见
![](/icons/65160dou.gif)
大概是等到他们发现这个问题时
![](/icons/65160dou.gif)
已经无路可走了
不过话又说回来
![](/icons/65160dou.gif)
想我们这种无名小辈
![](/icons/65160dou.gif)
又有什么资格对这些大公司说 3道 4呢?人家纵然有千般不是
![](/icons/65160dou.gif)
毕竟还是个实实在在可用
![](/icons/65160de.gif)
东西
![](/icons/65160dou2.gif)
而中国人搞研究
![](/icons/65160de.gif)
最大缺点就是:老是觉得
![](/icons/65160yi.gif)
些事情没有理论研究
![](/icons/65160de.gif)
价值
![](/icons/65160dou.gif)
而不愿去做
![](/icons/65160yi.gif)
些具体
![](/icons/65160de.gif)
工作
延伸阅读
最新评论