事务的4个特性——ACID(原子性、一致性、隔离性和持久性)
事务是一个操作序列。这些操作要么都做,要么都不做,是一个不可分割的工作单位。事务通常以BEGIN TRANSACTION开始,以COMMIT或ROLLBACK操作结束,COMMIT即提交,提交事务中所有的操作、事务正常结束。ROLLBACK即回滚,撤消已做的所有操作,回滚到事务开始时的状态。
对于事务可以举一个简单的例子:转账,有A和B两个用户,A用户转100到B用户,如下所示:
A:---->支出100,则 A-100
B:---->收到100,则 B+100
A--->B转账,对应如下SQL语句:
UPDATE ACCOUNT SET MONEY=MONEY - 100 WHERE NAME='A';
UPDATE ACCOUNT SET MONEY=MONEY + 100 WHERE NAME='B';
事务有4个特性,一般都称之为ACID特性,简单记为原一隔持(谐音:愿意各吃,即愿意各吃各的),如下表所示:
事务是指对系统进行的一组操作,为了保证系统的完整性,事务需要具有ACID特性,具体如下:
1. 原子性(
Atomicity
)
一个事务包含多个操作,这些操作要么全部执行,要么全都不执行。实现事务的原子性,要支持回滚操作,在某个操作失败后,回滚到事务执行之前的状态。
回滚实际上是一个比较高层抽象的概念,大多数DB在实现事务时,是在事务操作的数据快照上进行的(比如,MVCC),并不修改实际的数据,如果有错并不会提交,所以很自然的支持回滚。
而在其他支持简单事务的系统中,不会在快照上更新,而直接操作实际数据。可以先预演一边所有要执行的操作,如果失败则这些操作不会被执行,通过这种方式很简单的实现了原子性。
2. 一致性(Consistency)
一致性是指事务使得系统从一个一致的状态转换到另一个一致状态。事务的一致性决定了一个系统设计和实现的复杂度。事务可以不同程度的一致性:
强一致性
:读操作可以立即读到提交的更新操作。
弱一致性
:提交的更新操作,不一定立即会被读操作读到,此种情况会存在一个不一致窗口,指的是读操作可以读到最新值的一段时间。
最终一致性
:是弱一致性的特例。事务更新一份数据,最终一致性保证在没有其他事务更新同样的值的话,最终所有的事务都会读到之前事务更新的最新值。如果没有错误发生,不一致窗口的大小依赖于:通信延迟,系统负载等。
其他一致性变体还有:
单调一致性
:如果一个进程已经读到一个值,那么后续不会读到更早的值。
会话一致性
:保证客户端和服务器交互的会话过程中,读操作可以读到更新操作后的最新值。
3. 隔离性(Isolation)
并发事务之间互相影响的程度,比如一个事务会不会读取到另一个未提交的事务修改的数据。在事务并发操作时,可能出现的问题有:
脏读
:事务A修改了一个数据,但未提交,事务B读到了事务A未提交的更新结果,如果事务A提交失败,事务B读到的就是脏数据。
不可重复读
:在同一个事务中,对于同一份数据读取到的结果不一致。比如,事务B在事务A提交前读到的结果,和提交后读到的结果可能不同。
不可重复读出现的原因就是事务并发修改记录
,要避免这种情况,最简单的方法就是对要修改的记录加锁,这回导致锁竞争加剧,影响性能。另一种方法是通过MVCC可以在无锁的情况下,避免不可重复读。
幻读
:在同一个事务中,同一个查询多次返回的结果不一致。事务A新增了一条记录,事务B在事务A提交前后各执行了一次查询操作,发现后一次比前一次多了一条记录。
幻读是由于并发事务增加记录导致的
,这个不能像不可重复读通过记录加锁解决,因为对于新增的记录根本无法加锁。需要将事务串行化,才能避免幻读。
事务的隔离级别从低到高有:
Read Uncommitted
:最低的隔离级别,什么都不需要做,一个事务可以读到另一个事务未提交的结果。所有的并发事务问题都会发生。
Read Committed
:只有在事务提交后,其更新结果才会被其他事务看见。
可以解决脏读问题
。
Repeated Read
:在一个事务中,对于同一份数据的读取结果总是相同的,无论是否有其他事务对这份数据进行操作,以及这个事务是否提交。
可以解决脏读、不可重复读
。
Serialization
:事务串行化执行,隔离级别最高,牺牲了系统的并发性。
可以解决并发事务的所有问题
。
通常,在工程实践中,为了性能的考虑会对隔离性进行折中。
4. 持久性(Durability)
事务提交后,对系统的影响是永久的。
更新丢失问题
更新丢失是指多个用户通过应用程序访问数据库时,由于查询数据并返回到页面和用户修改完毕点击保存按钮将修改后的结果保存到数据库这个时间段(即修改数据在页面上停留的时间)在不同用户之间可能存在偏差,从而最先查询数据并且最后提交数据的用户会把其他用户所作的修改覆盖掉。当两个或多个事务选择同一行数据,然后基于最初选定的值更新该行时,会发生丢失更新问题。每个事务都不知道其它事务的存在。最后的更新将重写由其它事务所做的更新,这将导致数据丢失。解决更新丢失的方法如下表所示 :
悲观锁 |
试图在更新之前把行锁住,使用SELECT … FOR UPDATE然后更新数据。 |
|||
乐观锁 |
认为数据不会被其他用户修改,修改屏幕上的信息而不要锁 |
1.使用版本列的乐观锁定 |
增加NUMBER或TIMESTAMP或DATE列。每次修改行时,检查数据库中这一列的值与最初读出的值是否匹配,匹配的话修改数据且通过触发器要负责递增NUMBER、DATE、TIMESTAMP。 |
增加一个时间戳列,可以知道最后修改时间。 |
2.使用校验和的乐观锁定 |
用基数据本身来计算一个“虚拟的”版本列,生成散列值进行比较。 |
数据库独立性好,从CPU使用和网络传输方面来看,资源开销量大。 |
||
3.使用 ORA_ROWSCN的乐观锁定 |
建立在Oracle SCN的基础上,建表时,启用ROWDEPENDENCIES,防止整个数据块的ORA_ROWSCN向前推进。可以用SCN_TO_TIMESTAMP(ORA_ROWSCN)将SCN转换为时间格式。 |
将原先的悲观锁机制修改为乐观锁来控制并发,可以使用ORA_ROWSCN,这样可以无需增加新列。也可以通过SCN_TO_TIMESTAMP来获取最后修改时间。 |
第一类丢失更新 (通过设置隔离级别可以防止 Repeatable Read)
A事务撤销时,把已经提交的B事务的更新数据覆盖了。这种错误可能造成很严重的问题,通过下面的账户取款转账就可以看出来:
时间 |
取款事务A |
转账事务B |
T1 |
开始事务 |
|
T2 |
|
开始事务 |
T3 |
查询账户余额为1000元 |
|
T4 |
|
查询账户余额为1000元 |
T5 |
|
汇入100元把余额改为1100元 |
T6 |
|
提交事务 |
T7 |
取出100元把余额改为900元 |
|
T8 |
撤销事务 |
|
T9 |
余额恢复为1000 元(丢失更新) |
|
A事务在撤销时,“不小心”将B事务已经转入账户的金额给抹去了。
第二类丢失更新 (需要应用程序控制,乐观锁)
A事务覆盖B事务已经提交的数据,造成B事务所做操作丢失:
时间 |
转账事务A |
取款事务B |
T1 |
|
开始事务 |
T2 |
开始事务 |
|
T3 |
|
查询账户余额为1000元 |
T4 |
查询账户余额为1000元 |
|
T5 |
|
取出100元把余额改为900元 |
T6 |
|
提交事务 |
T7 |
汇入100元 |
|
T8 |
提交事务 |
|
T9 |
把余额改为1100 元(丢失更新) |
|
上面的例子里由于支票转账事务覆盖了取款事务对存款余额所做的更新,导致银行最后损失了100元,相反如果转账事务先提交,那么用户账户将损失100元。
如果多个线程操作,基于同一个查询结构对表中的记录进行修改,那么后修改的记录将会覆盖前面修改的记录,前面的修改就丢失掉了,这就叫做更新丢失。
Serializable可以防止更新丢失问题的发生。其他的三个隔离级别都有可能发生更新丢失问题。
Serializable虽然可以防止更新丢失,但是效率太低,通常数据库不会用这个隔离级别,所以我们需要其他的机制来防止更新丢失:
乐观锁和悲观锁不是数据库中真正存在的锁,只是人们在解决更新丢失时的不同的解决方案,体现的是人们看待事务的态度。
悲观锁:认为每一条sql语句都会出现更新丢失的情况
隔离级别不设置为Serializable,防止效率过低。
在查询时手动加上排他锁。
如果数据库中的数据查询比较多而更新比较少的话,悲观锁将会导致效率低下。
乐观锁:认为每一条sql语句都不会出现更新丢失的情况
在表中增加一个version字段,在更新数据库记录是将version加一,从而在修改数据时通过检查版本号是否改变判断出当前更新基于的查询是否已经是过时的版本。
如果数据库中数据的修改比较多,更新失败的次数会比较多,程序需要多次重复执行更新操作。
Oracle数据库支持READ COMMITTED 和 SERIALIZABLE这两种事务隔离级别。所以Oracle不支持脏读。
SQL标准所定义的默认事务隔离级别是SERIALIZABLE,但是Oracle 默认使用的是READ COMMITTED
设置隔离级别使用 SET TRANSACTION ISOLATION LEVEL [READ UNCOMMITTED|READ COMMITTED|REPEATABLE READ|SERIALIZABLE]
下面是oracle 设置SERIALIZABLE隔离级别一个示例:
左面是事务T1,右面是事务T2,因为T2级别为SERIALIZABLE,所以即使事务T1在提交了数据之后,事务T2还是看不到T1提交的数据,幻想读和不可重复读都不允许了。
那如何能查看到T1新增的记录呢? 上面T1和T2是并发执行,在T1执行insert的时候事务T2已经开始了,因为T2级别是SERIALIZABLE,所以T2所查询的数据集是T2事务开始前数据库的数据。即事务T1在事务T2开始之后的insert和update操作的影响都不会影响事务T2。现在重新开启一个事务T3 就可以看到T1新增的记录了。
当下列事件发生时,事务就开始了:
1、连接到数据库,并执行第一条DML语句
2、前一个事务结束后,又输入了另一条DML语句
Oracle事务隔离级别
脏读(dirty read):当一个事务读取另一个事务尚未提交的修改时,产生脏读。
不可重复读(nonrepeatable read):同一查询在同一事务中多次进行,在此期间,由于其他事务提交了对数据的修改或删除,每次返回不同的结果。
幻读(phantom read):同一查询在同一事务中多次进行,由于其他提交事务所做的插入操作,虽然查询条件相同,每次返回的结果集却不同。
将T2的事务级别设置为 可串行化后:
事务级别:
Oracle 事务隔离级别
Oracle 支持以下三种事务隔离级别(transaction isolation level)。
隔离级别 | 描述 |
|
|
已提交读取 | Oracle 默认使用的事务隔离级别。事务内执行的查询只能看到查询执行前(而非事务开始前)就已经提交的数据。Oracle 的查询永远不会读取脏数据(未提交的数据)。 Oracle 不会阻止一个事务修改另一事务中的查询正在访问的数据,因此在一个事务内的两个查询的执行间歇期间,数据有可能被其他事务修改。举例来说,如果一个事务内同一查询执行两次,可能会遇到不可重复读取或不存在读取的现象。 |
串行化 | 串行化隔离的事务只能看到事务执行前就已经提交的数据,以及事务内 INSERT , UPDATE ,及 DELETE 语句对数据的修改。串行化隔离的事务不会出现不可重复读取或不存在读取的现象。 |
只读模式 | 只读事务只能看到事务执行前就已经提交的数据,且事务中不能执行 INSERT , UPDATE ,及 DELETE 语句。 |
应用程序的设计开发者及数据库管理员可以依据应用程序的需求及系统负载(workload)而为不同的事务选择不同的隔离级别(isolation level)。用户可以在事务开始时使用以下语句设定事务的隔离级别:
已提交读模式:SET TRANSACTION ISOLATION LEVEL=READ COMMITTED;
串行模式:SET TRANSACTION ISOLATION LEVEL= SERIALIZABLE;
只读模式:SET TRANSACTION= READ ONLY;
mysql,oracle,sql server中的默认事务隔离级别查看,更改
未提交读(隔离事务的最低级别,只能保证不读取物理上损坏的数据)
已提交读(数据库引擎的默认级别)
可重复读
可序列化(隔离事务的*别,事务之间完全隔离)
可串行化比较严谨,级别高;
MySQL
mysql默认的事务处理级别是'REPEATABLE-READ',也就是可重复读
1.查看当前会话隔离级别
select @@tx_isolation;
2.查看系统当前隔离级别
select @@global.tx_isolation;
3.设置当前会话隔离级别
set session transaction isolation level repeatable read;
4.设置系统当前隔离级别
set global transaction isolation level repeatable read;
Oracle
oracle数据库支持READ COMMITTED 和 SERIALIZABLE这两种事务隔离级别。
默认系统事务隔离级别是READ COMMITTED,也就是读已提交
1.查看系统默认事务隔离级别,也是当前会话隔离级别
--首先创建一个事务
declare
trans_id Varchar2(100);
begin
trans_id := dbms_transaction.local_transaction_id( TRUE );
end;
--查看事务隔离级别
SELECT s.sid, s.serial#,
CASE BITAND(t.flag, POWER(2, 28))
WHEN 0 THEN 'READ COMMITTED'
ELSE 'SERIALIZABLE'
END AS isolation_level
FROM v$transaction t
JOIN v$session s ON t.addr = s.taddr AND s.sid = sys_context('USERENV', 'SID');
SQL Server
默认系统事务隔离级别是read committed,也就是读已提交
1.查看系统当前隔离级别
DBCC USEROPTIONS
isolation level 这一项的 Value 既是当前的隔离级别设置值
2.设置系统当前隔离级别
SET TRANSACTION ISOLATION LEVEL Read UnCommitted;
其中Read UnCommitted为需要设置的值
数据库事务隔离级ORACLE数据库事务隔离级别介绍
本文系转载,原文地址:http://singo107.iteye.com/blog/1175084
数据库事务的隔离级别有4个,由低到高依次为Read uncommitted、Read committed、Repeatable read、Serializable,这四个级别可以逐个解决脏读、不可重复读、幻读这几类问题。
√: 可能出现 ×: 不会出现
脏读 | 不可重复读 | 幻读 | |
Read uncommitted | √ | √ | √ |
Read committed | × | √ | √ |
Repeatable read | × | × | √ |
Serializable | × | × | × |
注意:我们讨论隔离级别的场景,主要是在多个事务并发的情况下,因此,接下来的讲解都围绕事务并发。
Read uncommitted 读未提交
公司发工资了,领导把5000元打到singo的账号上,但是该事务并未提交,而singo正好去查看账户,发现工资已经到账,是5000元整,非常高兴。可是不幸的是,领导发现发给singo的工资金额不对,是2000元,于是迅速回滚了事务,修改金额后,将事务提交,最后singo实际的工资只有2000元,singo空欢喜一场。
出现上述情况,即我们所说的脏读,两个并发的事务,“事务A:领导给singo发工资”、“事务B:singo查询工资账户”,事务B读取了事务A尚未提交的数据。
当隔离级别设置为Read uncommitted时,就可能出现脏读,如何避免脏读,请看下一个隔离级别。
Read committed 读提交
singo拿着工资卡去消费,系统读取到卡里确实有2000元,而此时她的老婆也正好在网上转账,把singo工资卡的2000元转到另一账户,并在singo之前提交了事务,当singo扣款时,系统检查到singo的工资卡已经没有钱,扣款失败,singo十分纳闷,明明卡里有钱,为何......
出现上述情况,即我们所说的不可重复读,两个并发的事务,“事务A:singo消费”、“事务B:singo的老婆网上转账”,事务A事先读取了数据,事务B紧接了更新了数据,并提交了事务,而事务A再次读取该数据时,数据已经发生了改变。
当隔离级别设置为Read committed时,避免了脏读,但是可能会造成不可重复读。
大多数数据库的默认级别就是Read committed,比如Sql Server , Oracle。如何解决不可重复读这一问题,请看下一个隔离级别。
Repeatable read 重复读
当隔离级别设置为Repeatable read时,可以避免不可重复读。当singo拿着工资卡去消费时,一旦系统开始读取工资卡信息(即事务开始),singo的老婆就不可能对该记录进行修改,也就是singo的老婆不能在此时转账。
虽然Repeatable read避免了不可重复读,但还有可能出现幻读。
singo的老婆工作在银行部门,她时常通过银行内部系统查看singo的信用卡消费记录。有一天,她正在查询到singo当月信用卡的总消费金额(select sum(amount) from transaction where month = 本月)为80元,而singo此时正好在外面胡吃海塞后在收银台买单,消费1000元,即新增了一条1000元的消费记录(insert transaction ... ),并提交了事务,随后singo的老婆将singo当月信用卡消费的明细打印到A4纸上,却发现消费总额为1080元,singo的老婆很诧异,以为出现了幻觉,幻读就这样产生了。
注:MySQL的默认隔离级别就是Repeatable read。
Serializable 序列化
Serializable是最高的事务隔离级别,同时代价也花费最高,性能很低,一般很少使用,在该级别下,事务顺序执行,不仅可以避免脏读、不可重复读,还避免了幻像读。
本文系转载,原文地址:http://blog.csdn.NET/w_l_j/article/details/7354530
[sql] view plain copy
- 对于同时运行的多个事务, 当这些事务访问数据库中相同的数据时, 如果没有采取必要的隔离机制, 就会导致各种并发问题:
- ? 脏读: 对于两个事物 T1, T2, T1 读取了已经被 T2 更新但还没有被提交的字段. 之后, 若 T2 回滚, T1读取的内容就是临时且无效的.
- ? 不可重复读: 对于两个事物 T1, T2, T1 读取了一个字段, 然后 T2 更新了该字段. 之后, T1再次读取同一个字段, 值就不同了.
- ? 幻读: 对于两个事物 T1, T2, T1 从一个表中读取了一个字段, 然后 T2 在该表中插入了一些新的行. 之后, 如果 T1 再次读取同一个表, 就会多出几行.
- 数据库事务的隔离性: 数据库系统必须具有隔离并发运行各个事务的能力, 使它们不会相互影响, 避免各种并发问题.
- 一个事务与其他事务隔离的程度称为隔离级别. 数据库规定了多种事务隔离级别, 不同隔离级别对应不同的干扰程度, 隔离级别越高, 数据一致性就越好, 但并发性越弱
- 数据库提供了4中隔离级别:
- 隔离级别 描述
- READ UNCOMMITTED(读未提交数据) 允许事务读取未被其他事务提交的变更,脏读、不可重复读和幻读的问题都会出现
- READ COMMITED(读已提交数据) 只允许事务读取已经被其他事务提交的变更,可以避免脏读,但不可重复读和幻读问题仍然会出现
- REPEATABLE READ(可重复读) 确保事务可以多次从一个字段中读取相同的值,在这个事务持续期间,禁止其他事务对这个字段进行更新,可以避免脏读和不可重复读,但幻读的问题依然存在
- SERIALIZABLE(串行化) 确保事务可以从一个表中读取相同的行,在这个事务持续期间,禁止其他事务对该表执行插入、更新和删除操作,所有并发问题都可以避免,但性能十分低
- Oracle 支持的 2 种事务隔离级别:READ COMMITED, SERIALIZABLE. Oracle 默认的事务隔离级别为: READ COMMITED
- Mysql 支持 4 中事务隔离级别. Mysql 默认的事务隔离级别为: REPEATABLE READ
本文系转载,原文地址:http://baike.baidu.com/link?url=ECqk10IwUnE8JxHPn-qyzNsqT1XgkD6P3td71Wj3HUT-lWoJ76uU-9hSKtSuCNwp7w56IbIcQ0J00__Qn0z9ra
事务隔离级别编辑
本词条缺少 概述、 名片图,补充相关内容使词条更完整,还能快速升级,赶紧来 编辑吧!
中文名
事务隔离级别
性 质
隔离级别
属 性
事务
不可重复读
Non-repeatable Reads
目录
1定义
2问题的提出
? 更新丢失 ? 脏读 ? 不可重复读
3解决方案
? 未授权读取 ? 授权读取 ? 可重复读取(Repeatable Read) ? 序列化(Serializable)
1定义编辑
在数据库操作中,为了有效保证并发读取数据的正确性,提出的 事务隔离级别。
2问题的提出编辑
数据库是要被广大客户所共享访问的,那么在数据库操作过程中很可能出现以下几种不确定情况。
更新丢失
两个事务都同时更新一行数据,一个事务对数据的更新把另一个事务对数据的更新覆盖了。这是因为系统没有执行任何的锁操作,因此并发事务并没有被隔离开来。
脏读
一个事务读取到了另一个事务未提交的数据操作结果。这是相当危险的,因为很可能所有的操作都被 回滚。
不可重复读
不可重复读(Non-repeatable Reads):一个事务对同一行数据重复读取两次,但是却得到了不同的结果。
包括以下情况:
(1) 虚读:事务T1读取某一数据后,事务T2对其做了修改,当事务T1再次读该数据时得到与前一次不同的值。
(2) 幻读(Phantom Reads):事务在操作过程中进行两次查询,第二次查询的结果包含了第一次查询中未出现的数据或者缺少了第一次查询中出现的数据(这里并不要求两次查询的 SQL语句相同)。这是因为在两次查询过程中有另外一个事务插入数据造成的。
3解决方案编辑
为了避免上面出现的几种情况,在标准 SQL规范中,定义了4个事务隔离级别,不同的隔离级别对事务的处理不同。
未授权读取
也称为读未提交(Read Uncommitted):允许脏读取,但不允许更新丢失。如果一个事务已经开始写数据,则另外一个事务则不允许同时进行写操作,但允许其他事务读此行数据。该隔离级别可以通过“排他写锁”实现。
授权读取
也称为读提交(Read Committed):允许 不可重复读取,但不允许脏读取。这可以通过“瞬间共享读锁”和“排他写锁”实现。读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。
可重复读取(Repeatable Read)
可重复读取(Repeatable Read):禁止 不可重复读取和脏读取,但是有时可能出现幻影数据。这可以通过“共享读锁”和“排他写锁”实现。读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。
序列化(Serializable)
序列化(Serializable):提供严格的事务隔离。它要求事务 序列化执行,事务只能一个接着一个地执行,但不能并发执行。如果仅仅通过“行级锁”是无法实现事务序列化的,必须通过其他机制保证新插入的数据不会被刚执行查询操作的事务访问到。
隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。对于多数应用程序,可以优先考虑把 数据库系统的隔离级别设为 Read Committed。它能够避免脏读取,而且具有较好的并发性能。尽管它会导致 不可重复读、幻读和第二类丢失更新这些并发问题,在可能出现这类问题的个别场合,可以由应用程序采用 悲观锁或 乐观锁来控制。
ORACLE数据库事务隔离级别介绍
两个并发事务同时访问数据库表相同的行时,可能存在以下三个问题:
1、幻想读:事务T1读取一条指定where条件的语句,返回结果集。此时事务T2插入一行新记录,恰好满足T1的where条件。然后T1使用相同的条件再次查询,结果集中可以看到T2插入的记录,这条新纪录就是幻想。
2、不可重复读取:事务T1读取一行记录,紧接着事务T2修改了T1刚刚读取的记录,然后T1再次查询,发现与第一次读取的记录不同,这称为不可重复读。
3、脏读:事务T1更新了一行记录,还未提交所做的修改,这个T2读取了更新后的数据,然后T1执行回滚操作,取消刚才的修改,所以T2所读取的行就无效,也就是脏数据。
一、为了处理这些问题,SQL标准定义了以下几种事务隔离级别:
READ UNCOMMITTED 幻想读、不可重复读和脏读都允许。一个会话可以读取其他事务未提交的更新结果,如果这个事务最后以回滚结束,这时的读取结果就可能是不正确的,所以多数的数据库都不会运用这种隔离级别。
READ COMMITTED 允许幻想读、不可重复读,不允许脏读。一个会话只能读取其他事务已提交的更新结果,否则,发生等待,但是其他会话可以修改这个事务中被读取的记录,而不必等待事务结束,显然,在这种隔离级别下,一个事务中的两个相同的读取操作,其结果可能不同。
REPEATABLE READ 允许幻想读,不允许不可重复读和脏读。在一个事务中,如果在两次相同条件的读取操作之间没有添加记录的操作,也没有其他更新操作导致在这个查询条件下记录数增多,则两次读取结果相同。换句话说,就是在一个事务中第一次读取的记录保证不会在这个事务期间发生改动。SQL Server是通过在整个事务期间给读取的记录加锁实现这种隔离级别的,这样,在这个事务结束前,其他会话不能修改事务中读取的记录,而只能等待事务结束,但是SQL Server不会阻碍其他会话向表中添加记录,也不阻碍其他会话修改其他记录。
SERIALIZABLE 幻想读、不可重复读和脏读都不允许。在一个事务中,读取操作的结果是在这个事务开始之前其他事务就已经提交的记录,SQL Server通过在整个事务期间给表加锁实现这种隔离级别。在这种隔离级别下,对这个表的所有DML操作都是不允许的,即要等待事务结束,这样就保证了在一个事务中的两次读取操作的结果肯定是相同的。SQL标准所定义的默认事务隔离级别是SERIALIZABLE。
二、Oracle中的隔离级别及实现机制:
Oracle数据库支持READ COMMITTED 和 SERIALIZABLE这两种事务隔离级别。所以Oracle不支持脏读,即Oracle中不允许一个会话读取其他事务未提交的数据修改结果,从而防止了由于事务回滚发生的读取不正确。
Oracle回滚段,在修改数据记录时,会把这些记录被修改之前的结果存入回滚段或撤销段中。Oracle读取操作不会阻碍更新操作,更新操作也不会阻碍读取操作,这样在Oracle中的各种隔离级别下,读取操作都不会等待更新事务结束,更新操作也不会因为另一个事务中的读取操作而发生等待,这也是Oracle事务处理的一个优势所在。
Oracle缺省的配置是Read Committed隔离级别(也称为语句级别的隔离),在这种隔离级别下,如果一个事务正在对某个表执行 DML操作,而这时另外一个会话对这个表的记录执行读取操作,则Oracle会去读取回滚段或撤销段中存放的更新之前的记录,而不会象SQL Server一样等待更新事务的结束。
Oracle的Serializable隔离级别(也称为事务级别的隔离),事务中的读取操作只能读取这个事务开始之前已经提交的数据结果。如果在读取时,其他事务正在对记录执行修改,则Oracle就会在回滚段或撤销段中去寻找对应的原来未经修改的记录(而且是在读取操作所在的事务开始之前存放于回滚段或撤销段的记录),这时读取操作也不会因为相应记录被更新而等待。
设置隔离级别使用 SET TRANSACTION ISOLATION LEVEL [READ UNCOMMITTED|READ COMMITTED|REPEATABLE READ|SERIALIZABLE]
下面是oracle 设置SERIALIZABLE隔离级别一个示例:
左面是事务T1,右面是事务T2,因为T2级别为SERIALIZABLE,所以即使事务T1在提交了数据之后,事务T2还是看不到T1提交的数据,幻想读和不可重复读都不允许了。
那如何能查看到T1新增的记录呢? 上面T1和T2是并发执行,在T1执行insert的时候事务T2已经开始了,因为T2级别是SERIALIZABLE,所以T2所查询的数据集是T2事务开始前数据库的数据。即事务T1在事务T2开始之后的insert和update操作的影响都不会影响事务T2。现在重新开启一个事务T3 就可以看到T1新增的记录了。
当下列事件发生时,事务就开始了:
1、连接到数据库,并执行第一条DML语句
2、前一个事务结束后,又输入了另一条DML语句
两个并发事务同时访问数据库表相同的行时,可能存在以下三个问题:
1、幻想读:事务T1读取一条指定where条件的语句,返回结果集。此时事务T2插入一行新记录,恰好满足T1的where条件。然后T1使用相同的条件再次查询,结果集中可以看到T2插入的记录,这条新纪录就是幻想。
2、不可重复读取:事务T1读取一行记录,紧接着事务T2修改了T1刚刚读取的记录,然后T1再次查询,发现与第一次读取的记录不同,这称为不可重复读。
3、脏读:事务T1更新了一行记录,还未提交所做的修改,这个T2读取了更新后的数据,然后T1执行回滚操作,取消刚才的修改,所以T2所读取的行就无效,也就是脏数据。
一、为了处理这些问题,SQL标准定义了以下几种事务隔离级别:
READ UNCOMMITTED 幻想读、不可重复读和脏读都允许。一个会话可以读取其他事务未提交的更新结果,如果这个事务最后以回滚结束,这时的读取结果就可能是不正确的,所以多数的数据库都不会运用这种隔离级别。
READ COMMITTED 允许幻想读、不可重复读,不允许脏读。一个会话只能读取其他事务已提交的更新结果,否则,发生等待,但是其他会话可以修改这个事务中被读取的记录,而不必等待事务结束,显然,在这种隔离级别下,一个事务中的两个相同的读取操作,其结果可能不同。
REPEATABLE READ 允许幻想读,不允许不可重复读和脏读。在一个事务中,如果在两次相同条件的读取操作之间没有添加记录的操作,也没有其他更新操作导致在这个查询条件下记录数增多,则两次读取结果相同。换句话说,就是在一个事务中第一次读取的记录保证不会在这个事务期间发生改动。SQL Server是通过在整个事务期间给读取的记录加锁实现这种隔离级别的,这样,在这个事务结束前,其他会话不能修改事务中读取的记录,而只能等待事务结束,但是SQL Server不会阻碍其他会话向表中添加记录,也不阻碍其他会话修改其他记录。
SERIALIZABLE 幻想读、不可重复读和脏读都不允许。在一个事务中,读取操作的结果是在这个事务开始之前其他事务就已经提交的记录,SQL Server通过在整个事务期间给表加锁实现这种隔离级别。在这种隔离级别下,对这个表的所有DML操作都是不允许的,即要等待事务结束,这样就保证了在一个事务中的两次读取操作的结果肯定是相同的。SQL标准所定义的默认事务隔离级别是SERIALIZABLE。
二、Oracle中的隔离级别及实现机制:
Oracle数据库支持READ COMMITTED 和 SERIALIZABLE这两种事务隔离级别。所以Oracle不支持脏读,即Oracle中不允许一个会话读取其他事务未提交的数据修改结果,从而防止了由于事务回滚发生的读取不正确。
Oracle回滚段,在修改数据记录时,会把这些记录被修改之前的结果存入回滚段或撤销段中。Oracle读取操作不会阻碍更新操作,更新操作也不会阻碍读取操作,这样在Oracle中的各种隔离级别下,读取操作都不会等待更新事务结束,更新操作也不会因为另一个事务中的读取操作而发生等待,这也是Oracle事务处理的一个优势所在。
Oracle缺省的配置是Read Committed隔离级别(也称为语句级别的隔离),在这种隔离级别下,如果一个事务正在对某个表执行 DML操作,而这时另外一个会话对这个表的记录执行读取操作,则Oracle会去读取回滚段或撤销段中存放的更新之前的记录,而不会象SQL Server一样等待更新事务的结束。
Oracle的Serializable隔离级别(也称为事务级别的隔离),事务中的读取操作只能读取这个事务开始之前已经提交的数据结果。如果在读取时,其他事务正在对记录执行修改,则Oracle就会在回滚段或撤销段中去寻找对应的原来未经修改的记录(而且是在读取操作所在的事务开始之前存放于回滚段或撤销段的记录),这时读取操作也不会因为相应记录被更新而等待。
设置隔离级别使用 SET TRANSACTION ISOLATION LEVEL [READ UNCOMMITTED|READ COMMITTED|REPEATABLE READ|SERIALIZABLE]
下面是oracle 设置SERIALIZABLE隔离级别一个示例:
左面是事务T1,右面是事务T2,因为T2级别为SERIALIZABLE,所以即使事务T1在提交了数据之后,事务T2还是看不到T1提交的数据,幻想读和不可重复读都不允许了。
那如何能查看到T1新增的记录呢? 上面T1和T2是并发执行,在T1执行insert的时候事务T2已经开始了,因为T2级别是SERIALIZABLE,所以T2所查询的数据集是T2事务开始前数据库的数据。即事务T1在事务T2开始之后的insert和update操作的影响都不会影响事务T2。现在重新开启一个事务T3 就可以看到T1新增的记录了。
当下列事件发生时,事务就开始了:
1、连接到数据库,并执行第一条DML语句
2、前一个事务结束后,又输入了另一条DML语句
数据库事务与隔离级别
http://blog.csdn.Net/jialinqiang/article/details/8723044
1.数据库事务的概念:
?事务是指一组相互依赖的操作行为,如银行交易、股票交易或网上购物。事务的成功取决于这些相互依赖的操作行为是否都能执行成功,只要有一个操作行为失败,就意味着整个事务失败。例如,Tom到银行办理转账事务,把100元钱转到Jack的账号上,这个事务包含以下操作行为:
–(1)从Tom的账户上减去100元。
–(2)往Jack的账户上增加100元。
?显然,以上两个操作必须作为一个不可分割的工作单元。假如仅仅第一步操作执行成功,使得Tom的账户上扣除了100元,但是第二步操作执行失败,Jack的账户上没有增加100元,那么整个事务失败。
?数据库事务是对现实生活中事务的模拟,它由一组在业务逻辑上相互依赖的SQL语句组成。
2.数据库事务的生命周期:
3.声明事务的边界:
?事务的开始边界。
?事务的正常结束边界(COMMIT):提交事务,永久保存被事务更新后的数据库状态。
?事务的异常结束边界(ROLLBACK):撤销事务,使数据库退回到执行事务前的初始状态。
(1).在mysql.exe中声明事务:
?每启动一个mysql.exe程序,就会得到一个单独的数据库连接。每个数据库连接都有个全局变量@@autocommit,表示当前的事务模式,它有两个可选值:
–0:表示手工提交模式。
–1:默认值,表示自动提交模式。
?如果要察看当前的事务模式,可使用如下SQL命令:
–mysql> select @@autocommit
?如果要把当前的事务模式改为手工提交模式,可使用如下SQL命令:
–mysql> set autocommit=0;
——在自动提交模式下提交事务:
?在自动提交模式下,每个SQL语句都是一个独立的事务。如果在一个mysql.exe程序中执行SQL语句:
–mysql>insert into ACCOUNTS values(1,'Tom',1000);
?MySQL会自动提交这个事务,这意味着向ACCOUNTS表中新插入的记录会永久保存在数据库中。此时在另一个mysql.exe程序中执行SQL语句:
–mysql>select * from ACCOUNTS;
?这条select语句会查询到ID为1的ACCOUNTS记录。这表明在第一个mysql.exe程序中插入的ACCOUNTS记录被永久保存,这体现了事务的ACID特性中的持久性。
——在手工模式下提交事务:
?在手工提交模式下,必须显式指定事务开始边界和结束边界:
–事务的开始边界:begin
–提交事务:commit
–撤销事务:rollback
例:
–mysql>begin;
–mysql>select * from ACCOUNTS;
–mysql>commit;
(2).通过JDBC API声明事务边界:
? Connection提供了以下用于控制事务的方法:
–setAutoCommit(boolean autoCommit):设置是否自动提交事务
–commit():提交事务
–rollback():撤销事务
例:
try {
con = Java.sql.DriverManager.getConnection(dbUrl,dbUser,dbPwd);
//设置手工提交事务模式
con.setAutoCommit(false);
stmt = con.createStatement();
//数据库更新操作1
stmt.executeUpdate("update ACCOUNTS set BALANCE=900 where ID=1 ");
//数据库更新操作2
stmt.executeUpdate("update ACCOUNTS set BALANCE=1000 where ID=2 ");
con.commit(); //提交事务
}catch(Exception e) {
try{
con.rollback(); //操作不成功则撤销事务
}catch(Exception ex){
//处理异常
……
}
//处理异常
……
}finally{…}
(3).通过hibernate API声明事务边界:
?声明事务的开始边界:Transaction tx=session.beginTransaction();
?提交事务: tx.commit();
?撤销事务: tx.rollback();
4.多个事务并发时的并发问题:
?第一类丢失更新:撤销一个事务时,把其他事务已提交的更新数据覆盖。
?脏读:一个事务读到另一事务未提交的更新数据。
?虚读:一个事务读到另一事务已提交的新插入的数据。
?不可重复读:一个事务读到另一事务已提交的更新数据。
?第二类丢失更新:这是不可重复读中的特例,一个事务覆盖另一事务已提交的更新数据。
以取款事务和支票转账事务例:
?取款事务包含以下步骤:
–(1)某银行客户在银行前台请求取款100元,出纳员先查询账户信息,得知存款余额为1000元。
–(2)出纳员判断出存款额超过了取款额,就支付给客户100元,并将账户上的存款余额改为900元。
?支票转账事务包含以下步骤:
–(1)某出纳员处理一转帐支票,该支票向一帐户汇入100元。出纳员先查询账户信息,得知存款余额为900元。
–(2)出纳员将存款余额改为1000元。
并发运行的两个事务导致脏读:
取款事务在T5时刻把存款余额改为900元,支票转账事务在T6时刻查询账户的存款余额为900元,取款事务在T7时刻被撤销,支票转账事务在T8时刻把存款余额改为1000元。
由于支票转账事务查询到了取款事务未提交的更新数据,并且在这个查询结果的基础上进行更新操作,如果取款事务最后被撤销,会导致银行客户损失100元。
并发运行的两个事务导致第二类更新丢失:
取款事务在T5时刻根据在T3时刻的查询结果,把存款余额改为1000-100元,在T6时刻提交事务。支票转账事务在T7时刻根据在T4时刻的查询结果,把存款余额改为1000+100
元。由于支票转账事务覆盖了取款事务对存款余额所做的更新,导致银行最后损失100元。
5.数据库的隔离级别:
(1).隔离级别与并发性能的关系:
(2).设置隔离级别的原则:
?隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。
?对于多数应用程序,可以优先考虑把数据库系统的隔离级别设为Read Committed,它能够避免脏读,而且具有较好的并发性能。尽管它会导致不可重复读、虚读
和第二类丢失更新这些并发问题,在可能出现这类问题的个别场合,可以由应用程序采用悲观锁或乐观锁来控制。
(3)在mysql.exe程序中中设置隔离级别:
?每启动一个mysql.exe程序,就会获得一个单独的数据库连接。每个数据库连接都有个全局变量@@tx_isolation,表示当前的事务隔离级别。MySQL默认的隔离
级别为Repeatable Read。如果要察看当前的隔离级别,可使用如下SQL命令:
–mysql> select @@tx_isolation;
?如果要把当前mysql.exe程序的隔离级别改为Read Committed,可使用如下SQL命令:
–mysql> set transaction isolation level read committed;
(4)在Hibernate中设置隔离级别:
?在Hibernate的配置文件中可以显式的设置隔离级别。每一种隔离级别都对应一个整数:
–1:Read Uncommitted
–2:Read Committed
–4:Repeatable Read
–8:Serializable
?例如,以下代码把hibernate.cfg.xml文件中的隔离级别设为Read Committed:
hibernate.connection.isolation=2
对于从数据库连接池中获得的每个连接,Hibernate都会把它改为使用Read Committed隔离级别。
文章可以转载,必须以链接形式标明出处。
事务隔离级别和脏读的快速入门
PHP爱好者 2017-06-28 19:16
关键要点
仅从ACID或非ACID角度考虑问题是不够的,你应知道你的数据库支持何种事务隔离级别。
一些数据库宣称自己具有“最终一致性”,但却可能对重复查询返回不一致的结果。
相比于你所寻求的数据库,一些数据库提供更高的事务隔离级别。
脏读可导致同一记录得到两个版本,或是完全地丢失一条记录。
在同一事务中多次重新运行同一查询后,可能会出现幻读。
最近MongoDB登上了Reddit的头条,因为MongoDB的核心开发者David Glasser痛苦地认识到MongoDB默认会执行脏读。
在本文中,我们将解释什么是事务隔离级别和脏读,并给出一些广受欢迎的数据库是如何实现它们的。
ANSI SQL给出了四种标准的事务隔离级别:可序列化(Serializable)、可重复读(Repeatable reads)、提交读(Read committed)和未提交读(Read uncommitted)。
许多数据库缺省是提交读的,这保证了在事务运行期间用户看不到转变中的数据。提交读的实现通过在读取时暂时性地获取锁,并持有写入锁直至事务提交。
如果在一个事务中需要多次重复同一读取,并想要“合理地确定”所有的读取总是会得到同样的结果,这要在整个过程期间持有读取锁。在使用可重复读事务隔离级别时,上述操作是自动完成的。
我们这里所说的“合理地确定”可重复读,是因为存在“幻读”(phantom reads)的可能性。当执行使用了WHERE语句的查询时,类似于“WHERE Status=1”,就有可能发生幻读。虽然所涉及的行将被锁上,但是这并不能阻止匹配WHERE条件的新行被添加进来。“幻”(phantom)一词指在查询第二次执行时所出现的行。
为确保在同一事务中的两次读取会返回同样的数据,可使用可序列化事务隔离级别。可序列化使用了“范围锁”,避免了匹配WHERE条件的新行添加到一个开放的事务中。
一般情况下,由于锁竞争的存在,事务隔离级别越高,性能越差。因此为了改进读取性能,一些数据库还支持未提交读。该事务隔离级别将无视锁的存在(事实上其在SQL Server中被称为“NOLOCK”),因此该级别下可执行脏读。
脏读所存在的问题
在探讨脏读问题之前,你必须要理解表并非是真实存在于数据库中的,表只是一个逻辑结构。事实上你的数据是按一个或多个索引进行存储的。主索引在大多数数据库中被称为“聚束索引”或“堆”(该术语在各NoSQL数据库中各不相同)。因而当执行插入操作时,需要在每个索引中插入一行。当执行更新操作时,数据库引擎仅需访问指到被改变列的索引。但更新操作常常必须要在每个索引上执行两个操作,即从旧的位置删除并在新的位置插入。
在下图中,你可看见一个普通的表,还有表中IX_Customer_State和PK_Customer对象更新操作的执行计划。鉴于表的FullName列并未改变,所以可以跳过IX_Customer_FullName索引。
注意在SQL Server中,PK前缀指代主键,通常也是用于聚束索引的键。IX用于指代非聚束索引。其它的数据具有它们自己的命名规范。
解决了上述问题,让我们看一下脏读导致不一致数据的多种途径。
未提交读问题易于理解。在事务被完全提交之前,如果无视写入锁的存在,使用“未提交读”的SELECT语句就可以就看到新插入或更新的行。如果这些转变操作这时被回滚,从逻辑上说,SELECT操作将返回并不存在的数据。
如果数据在更新操作过程中被移动了,这就产生了双重读取。例如,你正在读取所有的客户记录的状态。如果在你读取“California”记录和读取“Texas”记录之间,上面所说的更新语句被执行了,你就能看见“客户1253”记录两次。一次是旧值,一次是新值。
记录丢失发生的方式相同。如果我们提取“客户1253”记录并将其从“Texas”记录移动到“Alaska”记录,并再次使用状态去选择数据,你可能会完全地丢失该记录。这就是发生在David Glasser的MongoDB数据库中的事情。由于在更新操作期间读取了索引,查询丢失了记录。
脏读也会妨碍到排序操作,该问题的出现取决于数据库的设计方式及特定的执行计划。例如,脏读可能发生于执行计划对所有候选数据行采集指针信息时,如果在其后一行数据被更新了,但实际上执行引擎还是会使用已被采集的指针信息从原始位置拷贝数据。
快照隔离,或被称为“行级版本控制”
为在避免脏读问题的同时提供好的性能,许多数据库支持快照隔离语义。运行于快照隔离状态下,当前的事务不能看到任何先于其启动的其它事务的结果。
快照隔离的实现是通过做被改变行的临时拷贝,而非仅依靠于锁机制,因此它也常被称为“行级版本控制”。
很多支持快照隔离语义的数据库在被请求使用“提交读”事务隔离时,会自动使用快照隔离。
SQL Server中的事务隔离级别
SQL Server支持所有四种ANSI SQL事务隔离级别,外加一种显式的快照隔离级别。提交读可能也使用快照语义,这取决于数据库中READ_COMMITTED_SNAPSHOT选项的配置方式。
在开关该选项前,你的数据库需要做充分的测试。虽然提交读可以提升读取性能,但它也同时降低了写入性能。尤其是tempdb被部署在慢速磁盘上时,因为这存储了行的旧版本。
在SELECT语句中可以使用臭名昭著的NOLOCK指示符。NOLOCK的作用等同于将事务运行设置为未提交读。这在SQL Server 2000及更早期的版本中被大量地使用,因为那时并没有提供行级版本控制。尽管现在不再必要或不建议这样做,但是该习惯仍然保留着。
更多信息参见“设置事务隔离级别 (Transact-SQL)”.
PostgreSQL中的事务隔离级别
虽然官方宣称PostgreSQL支持所有四种ANSI事务隔离级别,但事实上PostgreSQL中只有三种事务隔离级别。每当查询请求“未提交读”时,PostgreSQL就默默地将其升级为“提交读”。因此PostgreSQL不允许脏读。
当你选取“未提交读”级别时,事实上你得到了“提交读”,在PostgreSQL对可重复读的实现中,脏读是不可能发生的,因此实际的事务隔离级别可能比你所选取的要更加严格。这是被SQL标准所允许的,因为四种事务隔离级别仅定义了事务中一定不能发生的现象,它们并未定义应该发生哪种现象。
PostgreSQL并未显式地提供快照隔离。当然快照隔离是在使用提交读时自动发生的。这是因为PostgreSQL的设计从一开始就考虑了多版本并发控制。
在9.1版本之前,PostgreSQL不提供可序列化事务,会将它们静默降级为可重复读。但当前所有仍在支持的PostgreSQL版本中都不再有这个限制了。
更多的信息参见PostgreSQL官方文档的13.2节,“ 事务隔离”.
MySQL中的事务隔离级别
InnoDB默认为可重复读,但是提供所有四种ANSI SQL事务隔离级别。提交读使用快照隔离语义。
更多InnoDB相关的信息,参见MySQL官方文档的15.3.2.1节“ 事务隔离等级”
事务在使用MyISAM存储引擎时是完全不被支持的,这里使用了表一级的单一读写锁(虽然在某些情况下,插入操作是可以绕过锁的。)
Oracle中的事务隔离等级
Oracle只支持三种事务隔离级别,即提交读、可序列化和只读。在Oracle中,提交读是默认的,它使用快照语义。
类似于PostgreSQL,Oracle并不提供未提交读,永不允许脏读。
可重复读并不在Oracle的支持列表中。如果你需要在Oracle中具有该行为,你的事务隔离级别需要被设置为可序列化。
只读是Oracle所独有的事务隔离级别。但是对此并没有很好的文档,手册中只有如下描述:
只读事务只能看见那些在事务开始阶段就被提交的改变,不允许INSERT、UPDATE和DELETE语言。
对其它两种事务隔离级别的更多信息,参见Oracle官方文档第13章“数据并发和一致性”。
DB2中的事务隔离级别
DB2具有四种隔离级别,分别称为可重复读、读稳定性、游标稳定性和未提交读。这四种级别并不与上述四种ANSI术语一一对应。
可重复读对应于ANSI SQL中的可序列化,意味着不可能存在脏读。
读稳定性对应于ANSI SQL中的可重复读。
游标稳定性用于提交读,是DB2的默认设置配置。对于9.7版快照语义生效。而在9.7的前期版本中,DB2使用类似于SQL Server的锁机制。
未提交读在很大程度上类似于SQL Server中的未提交读,也允许脏读。手册中推荐仅在只读表上使用未提交读,或是用在“可以看到未被其它应用提交的数据时”。
更多信息参见“事务隔离级别”。
MongoDB中的事务隔离级别
正如前文所提到的,MongoDB不支持事务。在其手册中对此是这样描述的:
因为在MongoDB中对单一文档的操作是原子的,两阶段提交只能提供类事务语义。在两阶段提交或回滚期间,应用可在中间点返回中间数据。
事实上这意味着MongoDB使用脏读语义,具有双倍或丢失记录的可能性。
CouchDB中的事务隔离等级
CouchDB也不支持事务。但是不同于MongoDB的是,它使用了多版本并发控制去避免脏读。
读取请求将总是在请求开始时就能看到数据库的最新快照。
这所给予CouchDB的事务隔离等级,等价于具有快照语义的提交读。
更多的信息参见“最终一致性”。
Couchbase Server的事务隔离级别
Couchbase Server常被混淆为CouchDB,但它是一种完全不同的产品。就索引而言,它并未提供任何形式的隔离。
当执行更新操作时,Couchbase Server仅更新主索引,或称其为“真实的表”。所有的二级索引将被延迟更新。
虽然在Couchbase Server文档并没有明确说明,看上去它在构建索引时使用了快照,如果确是如此,脏读应该不成为问题。但是由于索引的延迟更新,在Couchbase Server中仍不能获得真正的提交读事务隔离级别。
和许多的NoSQL数据库一样,Couchbase Server并不直接支持事务。但是你确实可以使用显式锁,但锁只能在被自动丢弃前维持30秒的时间。
更多的信息参见“对条目上锁”、“你所应知道的关于Couchbase架构的所有事情”和“Couchbase视图引擎的内幕”。
Cassandra中的事务隔离级别
Cassandra 1.0隔离了甚至是对一行的写入操作。因为字段是被逐一更新的,所以可以终止对旧值和新值混合在一起的记录的读取。
从1.1版本开始,Cassandra提供了“行级隔离”。这让Cassandra具有等同于其它的数据库中被称为“未提交读”的隔离级别。Cassandra并未提供更高级别的隔离。
更多的信息参见“关于事务和并发控制”。
了解你的数据库的事务隔离级别
正如从上述实例中可看到的,仅从ACID和非ACID角度考虑你的数据库是不够的。你的确需要去知道你的数据库应在何种情况下支持何种的事务隔离级别。
About Me
...............................................................................................................................
● 本文整理自网络
● 本文在itpub(http://blog.itpub.net/26736162)、博客园(http://www.cnblogs.com/lhrbest)和个人微信公众号(xiaomaimiaolhr)上有同步更新
● 本文itpub地址:http://blog.itpub.net/26736162/abstract/1/
● 本文博客园地址:http://www.cnblogs.com/lhrbest
● 本文pdf版及小麦苗云盘地址:http://blog.itpub.net/26736162/viewspace-1624453/
● 数据库笔试面试题库及解答:http://blog.itpub.net/26736162/viewspace-2134706/
● QQ群:230161599 微信群:私聊
● 联系我请加QQ好友(646634621),注明添加缘由
● 于 2017-07-01 09:00 ~ 2017-07-31 22:00 在魔都完成
● 文章内容来源于小麦苗的学习笔记,部分整理自网络,若有侵权或不当之处还请谅解
● 版权所有,欢迎分享本文,转载请保留出处
...............................................................................................................................
拿起手机使用微信客户端扫描下边的左边图片来关注小麦苗的微信公众号:xiaomaimiaolhr,扫描右边的二维码加入小麦苗的QQ群,学习最实用的数据库技术。
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/26736162/viewspace-2141490/,如需转载,请注明出处,否则将追究法律责任。
第一类丢失更新 (通过设置隔离级别可以防止 Repeatable Read)
A事务撤销时,把已经提交的B事务的更新数据覆盖了。这种错误可能造成很严重的问题,通过下面的账户取款转账就可以看出来:
时间 |
取款事务A |
转账事务B |
T1 |
开始事务 |
|
T2 |
|
开始事务 |
T3 |
查询账户余额为1000元 |
|
T4 |
|
查询账户余额为1000元 |
T5 |
|
汇入100元把余额改为1100元 |
T6 |
|
提交事务 |
T7 |
取出100元把余额改为900元 |
|
T8 |
撤销事务 |
|
T9 |
余额恢复为1000 元(丢失更新) |
|
A事务在撤销时,“不小心”将B事务已经转入账户的金额给抹去了。
第二类丢失更新 (需要应用程序控制,乐观锁)
A事务覆盖B事务已经提交的数据,造成B事务所做操作丢失:
时间 |
转账事务A |
取款事务B |
T1 |
|
开始事务 |
T2 |
开始事务 |
|
T3 |
|
查询账户余额为1000元 |
T4 |
查询账户余额为1000元 |
|
T5 |
|
取出100元把余额改为900元 |
T6 |
|
提交事务 |
T7 |
汇入100元 |
|
T8 |
提交事务 |
|
T9 |
把余额改为1100 元(丢失更新) |
|
上面的例子里由于支票转账事务覆盖了取款事务对存款余额所做的更新,导致银行最后损失了100元,相反如果转账事务先提交,那么用户账户将损失100元。