很久之前写的一篇文章,只是简单的叙述

先回顾一下事务的属性(ACID原则)

原子性 一个事务应该是一个原子操作,要么成功,要么失败,不能存在部分成功部分失败的中间状态。
一致性 事务无论成败不能破坏业务规则,必须是从一个一致性状态到另一个一致性状态。(如转帐:钱的总量不能变化/一致)
隔离性 每个事务必须独立于其它事务执行。
持久性 执行结果的值/状态持久。

 

数据并发操作的三种误读

脏读(Dirty Read)

打开一个”别人”正读写的资源的数据。既能读到未提交的数据。
不可重复读(Nonrepeatable Read)
在一个原子操作中,时间点t1去读取一个值与时间点t2再次读取这个值发生了变化,可以是更新,删除。
幻象读(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人。这也会导致数据出现问题。一般我们叫这是幻像读。

串行读

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