SQL SERVER 2008 数据库隔离级别代码演示
个隔离级别(其实这是SQL 工业标)
种隔离级别,本身没有优劣之分,完全取决于应用的场景。 本质上,他们是在 我的理解是:如果不太计较数据出现的少许紊乱,可以使用用最低级别的隔离性(保持高并发性) 同样的,发奖金这样的事情是极其敏感的。你不能因为来了几个新人,就要求把老员工的奖金拿去平均了(总奖金/员工数量=单人奖金 ,计算奖金的时候应该隔离新员工的INSERT,请刚入职的人不要恨我),这时候要强调隔离性(绝不能乱!),并发慢一点我们无所谓(反正有时间等,大家也理解,当然也不能太慢了)。 世界就是如此,不存在又灵活又能保持一致的数据库解决方案 我按照自己的理解来说明,这些隔离级别存在的问题。 先建立原料,创建一个Testtb
未提交读的问题
所谓未提交读,可以这么简单理解:查询语句不发出锁。这是个与世无争的存在,速度也是最快的。
但是,他存在一个问题(快是有代价的),我们用2个并发查询(SQL SERVER 新建2个查询,事务1先执行)
请注意这个ROLLBACK ,事务1 是回滚的。你猜事务2读到了什么,事务最后回滚的,他居然读出了一个数据,这种情况就叫脏读 (可以这么理解,你想在微信上吐槽,已经点了发送,但突然想到可能得罪某人,连忙点了取消;刚好此时某个家伙刷一下微信,居然读出那条吐槽。就这么个感觉) 前面说了,对于微信这样的要求高并发的系统,未提交读是无所谓的。但是作为企业应用你可能想解决这个问题。
我们做下面这个测试
我们的事务2的结果是怎么样的呢? 我们发现有趣的事情
问题的关键就在这个阻塞上面(没错,如果你工作中碰到用户说现场阻塞了,就是这么回事)。SQL SERVER数据库使用了 已提交读 作为隔离级别的时候。会发出一个共享锁 (S锁,oracle ,mysql,sybase 我不知道,但估计是一个意思:一份文件正在被写的时候,其他人不能读。) 也就是说,这个SELECT 正在等待 INSERT 事务提交。 INSERT 完了,它才执行 (如果你有 windows 核心编程背景,就理解为INSERT 产生了一个信号量,SELECT 之前 要用WaitForSIngleObject 判断有无信号) 使用已提交读,就解决了这个脏读的问题。SQL SERVER 默认用这个隔离级别。所以,你没有看到WITH (xxx) 这样的锁\隔离级别声明。
已提交读的问题已提交读能在大多数情况下工作良好,但是,他也有郁闷的缺陷。 我们先往Testtb中插入一条数据
让我们来看下面的并发事务
事务1执行结果是怎样的? 什么情况?事务1,读到2次不同的值。这就出现了前后不一致的情况。这就是不可重复读。 这种感觉就是:你通过淘宝买了iphone6 ,商家给你发了个iphone6 (事务开始了),中间某个环节出了差错。你收到货了(事务结束),打开一看,是iphon4。就这种感觉
在企业应用中,前后状态不一致,可能是不允许的。 那么我们看看如何解决
看看事务1的输出结果 而且执行事务2的时候,他一直在阻塞,直到事务1提交,才运行。 这个阻塞,一样的,就好读比一个文档,我加了一条规矩(隔离级别):哥们在读的时候,旁的人都给我等着!于是事务2就老老实实的等着。 对于前面 已提交读就没这个功能,他的规矩是:别人在写的时候,我等着。 而未提交读的规矩是啥呢?: 写啥呢?我瞅瞅! 没写完不要紧的,我就拍个照,绝不浪费您的时间!
就这样,通过WITH(REPEATABLEREAD) 隔离级别提示,我们解决了不可重复读的问题。
可重复读的问题很完美了 对吗? 有了可重复读 隔离级别,我们能保持前后数据一致。 现在我们来修改需求,我们要求计算表中记录数量。我们看下面的代码
至今为止,我们数据库里面只有一条记录对吗? 我们看看事务1输出结果
即使我们增加了REPEATABLEREAD 关键字,但是得到结果前后数量不一致。这就是幻读 运用你的直觉(我认为这是一种天赋)!你感觉到了 REPEATABLEREAD 好像没有阻塞INSERT ,但在上一个例子中,他成功的阻塞了UPDATE. why? 我是这么理解的,UPDATE 和 DELETE 有一个共同点是,对一个已经存在的记录进行操作。也就是说能被SELECT 到。因此可以加锁。但是INSERT在执行的时候数据是不存在的。你无法给记录加锁!所以,你无法阻塞任何INSERT语句。虽然INSERT 语句也发出一个排它锁,但是数据产生之前,你只能尝试阻塞这空气一般的存在。因为,你不知道它在哪儿。
解决幻读
SQL 在4个隔离级别中,*隔离别就是Serializable (可序列化),老实说我真不理解这个名字。他和JAVA /C# 中的 来,我们修改我们的隔离级别。
再次运行你的直觉! 上一次INSERT 之后,我们有4条数据了! 这次依次执行执行事务1,2之后,我们得到结果是多少呢? 我认为是2条都是4. 果然,WITH(SERIALIZABLE) 隔离级别解决了幻读的问题。保证了前后读取一致。这个隔离级别是最严格的。他连INSERT 都给阻塞了(它是怎么做到的,原理是什么?参考 总结上面的例子分析了 脏读,不可重复度,幻读 。 分别解释了 他们是什么,以及简单的原因。并用代码做出了演示。 事实上,这些隔离级别是层层递进的。下面这幅图来源于《企业应用架构模式》P 52 |