论坛首页 综合技术论坛

数据库事务隔离级别及误读的简单介绍

浏览 2922 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2012-08-23   最后修改:2012-08-23


先说点废话,这篇文章的第一段是2010年写的,因为包括懒在内的各种原因,一直没写完,今天第一次开始night shift.没啥事。把这个翻了出来。唉.....    本来一直想像coolshell那样。 等水平够了,发布的都是精品。 今天一想,大牛们都是靠积累出来的,皓哥也不是一生下来对编程有那么多见解。 先积累吧。 本来想写一些其它的,看着既然是10年的残篇,不如就把他继续写下去吧,无论文笔还是内容都无爆点,不过就把这当成一个开始吧。


——————————————我是可爱的分割线—————————————————————————


强烈为想学习oracle的朋友推荐一本书:oracle9i&10g编程艺术:深入数据库体系架构 

先回顾一下事务的属性(简称ACID原则) 
1.原子性(
Atomicity)  :一个事务应该是一个原子操作,要么成功,要么失败,不能存在部分成功部分失败的中间状态。 

2.一致性(Consistency):事务无论成败不能破坏业务规则,必须使用系统从一个一致性状态到另一个一致性状态。例:银行转帐,A向B转1000元,事务完成之后,必须是A-1000 B+1000或AB余额不变,既事务前后都满足转帐规则 -- 双方余额之和不变。 

3.隔离性(Isolation)  :每个事务必须独立于其它事务执行。 

4.持久性(Durability) :执行结果的值/状态持久。 


之后再了解一下数据并发操作的三种误读 
1.Dirty Read[脏读]
      打开一个"别人"正读写的资源的数据。既能读到未提交的数据。 
2.Nonrepeatable Read[不可重复读]
      在一个原子操作中,时间点t1去读取一个值与时间点t2再次读取这个值发生了变化,可以是更新,删除。 
3.Phantom Read[幻象读]
      在时间点t1执行一个查询,得到的结果集与在时间点t2执行相同查询的结果集不一样,与不可重复读有点类似,不过不可重复读强调的同一个数据在随时间变化,而幻象读强调的是两次查询[结果集行数]增加。而不是指[数据值]的变化。 

根据三种读允许出现的情况,正好可以划分ANSI/ISO SQL规定的四种隔离级别 


隔离级别 脏读 不可重复读 幻像读
Read Uncommit(未提交读) 允许 允许 允许
Read Commit(提交读) 禁止 允许 允许
Repeatable Read(可重复读) 禁止 禁止 允许
Serializable(串行读) 禁止 禁止 禁止


注:oracle支持上面规定提交读与串行读之外还支持自身特殊的read-only只读事务 

[未提交读]的[脏读]演示 

时间点 事务 1 事务 2
t1 事务启动,向帐户A(无余额)存入1000元
t2 存入 查询余额
t3 (未提交) 查询结果1000元
t4 回滚


    这里演示的是[未提交读]出现[脏读]的情况,事实上未提交读也可以很"顺利"的出现不可重复读与幻像读,这里就不一一举例了。 
    未提交虽然有非阻塞的快速读写,但是它被认为是不可接受的事务级别,其真正原因并非在"读"上面(2 3两种级别仍然会出现误读的情况,但是他们被划为可接受的)。真正原因是因为未提交读完全破坏了ACID原则,不仅是展现(读)出来的数据有问题,更不能让人接受的是物理数据混乱(如果将上表中t2时的"查询余额"改成"网上购买支付1000元")以至业务规则被破坏。业务的成功与失败靠并发量决定,相信谁不会不接受。 

[提交读]的[不可重复读]演示

时间点 统计在职员工平均工资 修改员工状态
t1 计算在职员工总工资
t2 (等待) 开始事务,锁表,将其中一名在职员工的状态修改为[离职],提交.
t3 计算在职员工总人数
t4 计算员工平均工资

*平均工资不正确,t1的查询到的

员工会比t3时查询到的多一个。



    先说[提交读]的机制,提交读是指任何时候对表有写入操作时,对该表进行读操作都将阻塞,等待提交。这样就能避免脏读的出来。
    这里展示的是[提交读]会引发的[不可重复读]的问题,可以这样理解不可重复读:假设那名离职职的员工叫张三, 在执行t1那条查询的时候, 张三的状态是在职,而在执行到t3的时候,此时张三的状态已经是离职了,也就是说在同一个事务中对同一个数据读两次,结果不一致(第二次不可重现第一次的查询到的值)。这就叫不可重复读。

这种隔离级别目前看来仍然是不被接受的,但是不会造成太多阻塞(相比后两种),效率也算较好。Oracle默认就是采用的这隔离级别,只过Oracle还在此基础上增加了[多版本控制]的机制(ps:这是一个天才的机制!),这让Oracle能尽可能的高效率,并且能保持事务间,数据的独立性。 感兴趣的朋友可以去看看, 就算不用oracle,当成一种解决方案来了解一下也不错。


[可重复读]的[幻像读]演示

时间点 统计在职员工平均工资 新录入一个员工
t1

计算所有员工的总工资

对[读到的行]增加一个共享读锁必须等到

该锁释放其它事务再能写操作,其它事

的读操作不会阻塞

t2 (假设此时该线程没有得到CPU时间片)

开启事务

新录入一个员工

提交

t3 计算在职员工的总人数
t4 计算员工的平均工资

*平均工资不正确,t3的查询到的

员工会比t1时查询到的多一个。


 

先说说[可重复读]隔离级别,在这种级别下,所有对行进行查询操作,都会给查询到的行上增加一下"共享读锁",锁这种锁锁到的行可以被其它事务查询,不会阻塞,但是如果是写操作的话,就必须要等到该锁释放后才能进行写入。如果将这种级别代入到之前的例子中,就不会出来不可重复读的问题。已锁数据无法修改。

但是[可重复读]隔离级别也有问题,他只会锁他所读到的行,不能控制新行的插入。上面的例子中,假设第一次算总工资时,人员是100人,还在计算员工总数时,就会是101人。这也会导致数据出现问题。一般我们叫这是幻像读。



串行读  这个没有什么可讲,所有的操作都会在一个队列中,一个一个的执行。是最严格的隔离级别。但是会造成极大的阻塞,所以在企业级系统中都不会用到,单用户的系统倒可以试试。



ps:上面说的是标准的四种隔离级别,不同数据库在实现的时候都会有些自己独特的地方,比如oracle就用的多版本控制,还额外提供了read-only隔离级别。

   发表时间:2013-01-13  
写的蛮清晰的,楼主,还有例子好理解
0 请登录后投票
论坛首页 综合技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics