- 浏览: 268567 次
- 性别:
- 来自: 上海
文章分类
- 全部博客 (187)
- spring (10)
- jquery (14)
- js (18)
- java (44)
- freemarker (7)
- 框架介绍 (1)
- tomcat (4)
- oracle (7)
- 喜欢的诗文 (4)
- linux (19)
- nokia (1)
- 那些事 (1)
- apache (3)
- dom4j (1)
- SOA (1)
- 企业架构 (2)
- baidu (1)
- svn (1)
- 测试 (4)
- liunx (0)
- memcahce (1)
- mysql (25)
- maven (4)
- 推广 (1)
- 网络 (1)
- vpn (1)
- drools (1)
- mongodb (1)
- redis (3)
- xlightweb (1)
- wifi (1)
- 学习资料 (1)
- git (1)
- 系统设计 (1)
- springboot (1)
- 自考 (0)
最新评论
-
zuo_qin_bo:
if(df==null){ //这个地方并发多的情况 ...
ThreadLocal解决dateFormat多线程错误 -
kongnan93:
好文!赞一个!!!!
jquery blockUI 居中显示 -
cxc_110928:
如果加上不能出现连续的数字或者字母 如 1234 abcd ...
用java 匹配一个数字和字母密码的正则表达式 -
wzwahl36:
http://www.atool.org/json2javab ...
JSON和JAVA的POJO的相互转换
测试表脚本:
SQL SERVER
CREATE TABLE [ Customer ](
[ CustID ] [ int ] NOT NULL ,
[ Fname ] [ nvarchar ]( 20 ),
[ Lname ] [ nvarchar ]( 20 ),
[ Address ] [ nvarchar ]( 50 ),
[ City ] [ nvarchar ]( 20 ),
[ State ] [ nchar ]( 2 ) DEFAULT ( 'CA' ),
[ Zip ] [ nchar ]( 5 ) NOT NULL ,
[ Phone ] [ nchar ]( 10 )
)
insert into customer values ( 1 , 'Gary' , 'Mckee' , '111 Main' , 'Palm Springs' , 'CA' , 94312 , 7605551212 )
insert into customer values ( 2 , 'Tom' , 'Smith' , '609 Geogia' , 'Fresno' 'JP' , 33045 , 5105551212 )
insert into customer values ( 3 , 'Jams' , 'bond' , 'ST Geogie 21' , 'Washington' , 'NY' , 20331 , 4405551864 )
ORACLE
CREATE TABLE Customer (
CustID int NOT NULL ,
Fname nvarchar2 ( 20 ),
Lname nvarchar2 ( 20 ),
Address nvarchar2 ( 50 ),
City nvarchar2 ( 20 ),
State nchar ( 2 ) DEFAULT 'CA' ,
Zip nchar ( 5 ) NOT NULL ,
Phone nchar ( 10 )
);
insert into customer values ( 1 , 'Gary' , 'Mckee' , '111 Main' , 'Palm Springs' , 'CA' , 94312 , 7605551212 );
insert into customer values ( 2 , 'Tom' , 'Smith' , '609 Geogia' , 'Fresno' , 'JP' , 33045 , 5105551212 );
insert into customer values ( 3 , 'Jams' , 'bond' , 'ST Geogie 21' , 'Washington' , 'NY' , 20331 , 4405551864 );
1。Sqlserver与oracle单条语句处理对比
SQL SERVER单条语句默认自动提交,即单条语句自动作为一个事务处理;而oracle的原则是尽量延后提交,除非遇到显式提交命令或者DDL语句。
SQL SERVER
打开事务1 :
运行:select * from customer
可以看到表有3条记录
运行:insert into customer values ( 4 , 'Hello' , 'world' , 'paradise road 01' , 'heaven' , 'XY' , 00001 , 1234564321 )
转到事务2:
运行:select * from customer
可以看到事务1中刚插入的custid为4的记录。
ORACLE
打开事务1,运行:
select * from customer ;
可以看到表有3条记录,运行:
insert into customer values ( 4 , 'Hello' , 'world' , 'paradise road 01' , 'heaven' , 'XY' , 00001 , 1234564321 );
转到事务2,运行:
select * from customer ;
能看到的还是3条记录,事务1中刚插入的一条记录未自动提交,看不到。
转到事务1,运行:
commit ;
转到事务2,运行:
select * from customer ;
现在能看到4条记录了。
2. 丢失更新
Sqlserver完全兼容ANSI 92标准定义的4个隔离级别。它的默认隔离级别是提交读(read committed),在该级别下,可能会有丢失更新的问题。Oracle的默认情形也一样。故不再重复。
SQL SERVER
打开事务1运行:
set transaction isolation level read committed
begin tran
select * from customer -- 看到3条记录
现在切换到事务2,此时事务1还未结束。在事务2中运行:
set transaction isolation level read committed
begin tran
select * from customer -- 看到3条记录,和事务1中相同
现在假设事务1事务继续运行,修改数据并提交:
update customer set state = 'TK' where CustID = 3
commit
回到事务2,事务2根据先前查询到的结果修改数据:
update customer set Zip = 99999 where state = 'NY'
commit
结 果因为事务1已经修改了事务2的where条件数据,事务2未成功修改数据(其实准确的说应该算是幻象读引起的更新失败。不过若满足条件的记录数多的话, 事务2的update可能更新比预期的数量少的记录数,也可算“丢失”了部分本应完成的更新。个人认为只要明白实际上发生了什么即可,不必过分追究字 眼)。丢失更新还可能有别的情形,比如事务2也是
update customer set state = 'KO' where CustID = 3
两个事务都结束后,事务2的结果反映到数据库中,但事务1的更新丢失了,事务2也不知道自己覆盖了事务1的更新。
3.脏读演示
sqlserver的默认隔离级别是提交读(read committed),当手工将其改为未提交读时,事务可以读取其它事务没提交的数据;oracle由于自身特殊实现机制,可以理解为自身基础的隔离级别就是可重复读(与ANSI标准还是有区别的,后面例子会说明)。
SQL SERVER
打开事务1,运行:
begin tran
select * from customer
update customer set state = 'TN' where CustID = 3
转到事务2,运行:
set transaction isolation level read uncommitted
begin tran
select * from customer
此时看到的数据是事务1已经更新但还未提交的(3号记录state值TN)。而如果事务1发觉数据处理有误,转到事务1,进行回滚:
Rollback
此时事务2如根据刚读取的数据进一步处理,会造成错误。它读取的数据并未更新到数据库,是“脏”的。
ORACLE
ANSI 定义未提交读(read uncommitted)级别本意不是为了故意引入错误,而是提供一种可能的最大并发程度级别,即一个事务的数据更新不影响其它 事务的读取。Oracle从内核层面实现了更新数据不阻塞读。可以说它提供未提交读级别的兼容,但没有脏读问题。(详情参考对应PPT文档)故 oracle没有手工设置read uncommitted级别的语句。
4.不可重复读
Sql server 的默认级别没有脏读问题,但存在不可重复读问题。Oracle默认级别也是提交读,不过它因为自身特殊机制,在语句一级不存在不可重复读问题。也就是说当 运行时间较长的查询时,查询结果是与查询开始时刻一致的(即使查询过程中其它事务修改了要查询的数据),而SQL SERVER就存在问题 (sql server 2005新特性提供了可选的语句一级一致性支持,叫做行版本机制,实际上可以说是照着oracle的多版本来的,大体原理差不 多)。
由于语句一级的事务一致性难以演示,下面例子是事务一级,提交读隔离级别下发生的不可重复读现象:
SQL SERVER
打开事务1,运行:
set transaction isolation level read committed
begin tran
select * from customer where State = 'CA'
可以得到1条记录,这个时候事务2中运行:
set transaction isolation level read committed
begin tran
update Customer set state = 'JP' where state = 'CA'
commit
事务2插入一条记录并提交。回到事务1,事务1继续运行,此时它再次相同的查询,并借此作进一步修改,却发现读取到的数据发生了变化。
select * from customer where State = 'CA'
-- 2次读取不一致,之后的数据处理应该取消。否则不正确
update Customer set city = 'garden' where state = 'CA'
commit
读取未能获得记录。也就是说在同一事务中两次相同的查询获得了不同的结果,产生读取不可重复现象。
ORACLE
尽管oracle在默认隔离级别下提供了语句级的事务读一致性,但在事务级仍然是会出现不可重复读现象。和sql server一样,故不再重复。
5.幻像读
当sqlserver的隔离级别设置为可重复读(repeatable read),可以解决上面例子出现的问题。其内部是通过事务期间保持读锁来实现的。
SQL SERVER
开始事务1,修改事务级别为可重复读,执行:
set transaction isolation level repeatable read
begin tran
select * from customer where State = 'CA'
和上例一样得到1条记录,这个时候事务2中运行:
set transaction isolation level repeatable read
begin tran
update Customer set state = 'JP' where state = 'CA'
commit
会发现事务2一直等待,并不结束。返回事务1,运行:
select * from customer where State = 'CA' -- 2次读取结果一致
update Customer set city = 'garden' where state = 'CA'
commit
事务2成功结束后,再返回事务1,发现事务1也完成了。通过锁机制阻塞其它事务的修改,保持了事务期间读取的一致性。然而,如果是插入数据,则还是会出现问题:
开始事务1,修改事务级别为可重复读,执行:
set transaction isolation level repeatable read
begin tran
select * from customer where State = 'CA'
得到1条记录,这个时候事务2中运行:
set transaction isolation level repeatable read
begin tran
insert into customer values ( 4 , 'hellow' , 'world' , 'paradise 001' , 'garden' , 'CA' , 00000 , 1119995555 )
commit
发现事务2立刻提交并正常结束了。返回事务1,运行:
select * from customer where State = 'CA'
会发现得到了2条记录。这种现象就叫做幻像读。
ORACLE
由于自身特殊的机制,oracle没有提供一致读隔离级别的选项,想要获得一致读的效果,实际上需要将事务提升到串行化等级,即serializable。
6.串行化级别不同数据库实现
在这个级别,可以认为事务中的数据无论何时都是一致的,此级别使它显得好像没有其它用户在修改数据,数据库在事务开始时候被“冻结”(至少,对于本事务涉及的数据如此)。然而在不同数据库中,其实现机制也不同。
SQL SERVER
开始事务1,运行:
set transaction isolation level serializable
begin tran
select * from customer where State = 'CA'
会得到1条记录,这时事务2开始运行:
set transaction isolation level serializable
begin tran
insert into customer values ( 4 , 'hellow' , 'world' , 'paradise 001' , 'garden' , 'CA' , 00000 , 1119995555 )
commit
会发现事务2挂起,它在等待事务1结束。回到事务1,继续:
select * from customer where State = 'CA'
update Customer set city = 'garden' where state = 'CA'
commit
在片刻的等待以后,事务1得到类似以以下格式消息:
消息1205,级别13,状态56,第1 行
事务 ( 进程ID 51 ) 与另一个进程被死锁在锁资源上,并且已被选作死锁牺牲品。请重新运行该事务。
而 事务2更新了数据并正常结束。这是因为两个事务都设置成了串行化级别,当遇到冲突时候,sql server根据一定的规则选择牺牲掉其中一个事务,来保 证事务的串行性。上面的例子,如果将事务2的隔离级别改为提交读,那么事务2会等待事务1完成,之后自己正常完成(因为事务2没有串行需求,不会有死 锁)。
ORACLE
在oracle中,通过多版本,可以在一定程度上避免死锁。
开始事务1,运行:
set transaction isolation level serializable ;
select * from customer where State = 'CA' ; -- set tran语句隐式开始事务
得到1条记录,然后事务2开始运行:
set transaction isolation level serializable ;
insert into customer values ( 4 , 'hellow' , 'world' , 'paradise 001' , 'garden' , 'CA' , 00000 , 1119995555 );
commit ;
可以发现事务2立刻完成,没有阻塞。回到事务1继续:
select * from customer where State = 'CA' ;
update Customer set city = 'garden' where state = 'CA' ;
commit ;
事务1中的第二次查询和事务开始时刻一致,就好像事务2已经完成的提交不存在。事务最终正常更新完毕,并保持了“事务开始”时刻的数据一致性。
然而,如果事务1,2修改同样的数据行,也会有错误,
开始事务1,运行:
set transaction isolation level serializable ;
select * from customer where State = 'CA' ; -- set tran语句隐式开始事务
得到1条记录,然后事务2开始运行:
set transaction isolation level serializable ;
update customer set state = 'KO' where state = 'CA' ;
commit ;
可以发现事务2立刻完成,没有阻塞。回到事务1继续:
select * from customer where State = 'CA' ;
update Customer set city = 'garden' where state = 'CA' ;
commit ;
出现错误信息:
第 1 行出现错误 :
ORA - 08177 : 无法连续访问此事务处理
总的来说,oracle利用多版本的方式实现串行化级别更少造成死锁,除非两个事务修改了相同的数据行,一般也不会造成冲突。
7.不同隔离级别的相互影响
前 面的例子基本都是两个相同隔离级别事务的情况。如果不同隔离级别的事务发生冲突,会有什么不同吗?实际上,对于一个事务来说,其它事务的隔离级别对它来说 是未知的,更进一步,甚至数据库中有没有其它事务,有多少事务也不知道。影响事务访问数据就两方面因素:该事务自身的隔离级别,该事务要访问的数据上面锁 的状态。
SQL SERVER
开始事务1,运行:
set transaction isolation level serializable
begin tran
select * from customer where State = 'CA'
事务1的查询获得1条记录,转到事务2,运行:
set transaction isolation level read uncommitted
begin tran
select * from customer
事务2查询获得3条记录,回到事务1,运行:
update Customer set city = 'garden' where state = 'CA'
切换到事务2,运行:
select * from customer
update customer set state = 'KO' where state = 'CA'
commit ;
因为事务2隔离级别为未提交读,因此事务1中刚作的修改可以立刻从查询看到,即使事务1还未结束。进一步的update因为事务1对记录加了独占锁,因此事务2挂起。回到事务1将其提交:
Commit
事务1正常结束,独占锁释放,从而让事务2得以继续修改数据,并最终完成。
ORACLE
Oracle数据库的隔离级别设置语句只有read committed和serializable(read only暂不讨论),加上其特殊锁机制,不同隔离级别事务间的影响除了上例(例6)中两个都为serializable的情况,其它都可视为互不阻塞。
8.页锁与行锁(限sql server)
Sql server的锁可以到行一级。然而它又存在自动的锁定扩大,锁定转换。因此存在一些意想不到的情况。下面是演示:
开始事务1,运行:
set transaction isolation level read committed
begin tran
select * from customer where State = 'CA'
update Customer set city = 'garden' where state = 'CA'
理论上来说,在提交读级别下,上面的update语句只是在state值为CA的数据行上加了独占锁,表中其它数据应该可以被其它事务更新,然而,如下事务2开始:
set transaction isolation level read committed
begin tran
select * from customer
update customer set state = 'KO' where state = 'JP'
commit
发现事务2陷入阻塞状态。尽管它们更新的不是同一条记录。回到事务1,运行:
Commit
事务1结束后事务2才继续运行至结束。
如果我们在表上加入索引,如下:
CREATE NONCLUSTERED INDEX [ idx_state ] ON [ dbo ].[ Customer ] ( [ State ])
再重复上面的步骤,会发现阻塞不再存在。
PS:这种现象应该和数据库默认加锁参数 / 机制有关,应该可以调整,但目前手中没有进一步资料。故仅罗列了现象。
ORACLE
Oracle在数据一级只有一种数据行上的锁,因此不存在sql server中这些问题。
9.Set transaction语句的作用周期
前面所有的例子,都是在会话窗口中进行的演示。一旦使用了set transaction语句,会影响整个会话。除非再显式改变隔离级别,否则将保持到会话结束。例如:
开始事务1,假设会话一开始处于SQL SERVER的默认隔离级别(read committed):
begin tran
select * from customer where State = 'CA'
select * from sys . dm_tran_locks
系统视图sys . dm_tran_locks可以查看当前的加锁情况,到目前位置,只有数据库级的锁。继续运行:
set transaction isolation level repeatable read
select * from customer where State = 'CA'
select * from sys . dm_tran_locks
commit
接下来的语句改变了隔离级别到可重复读,接下来的查询,会看到行级锁的记录。在上面事务提交后,运行:
begin tran
select * from customer where State = 'CA'
select * from sys . dm_tran_locks
commit
仍然会从视图sys . dm_tran_locks看到行级锁记录。整个会话期间都受到影响。
但是,如果调用存储过程,函数,则过程 / 函数中事务隔离级别的改变并不会对调用环境造成影响。可以通过以下例子说明,首先,创建一个存储过程脚本:
CREATE PROCEDURE [ dbo ].[ test_tran_level ]
AS
BEGIN
BEGIN TRAN
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ
SELECT * FROM CUSTOMER
UPDATE CUSTOMER SET STATE = 'SS' WHERE CustID = 3
SELECT * FROM sys . dm_tran_locks
COMMIT
END
然后,在会话窗口调用该过程,会话窗口当前隔离级别为默认的提交读:
Exec test_tran_level
运行的结果可以看到读取锁信息,再在会话中运行:
begin tran
select * from customer where State = 'CA'
select * from sys . dm_tran_locks
commit
视图sys . dm_tran_locks并未有读锁的记录,说明事务隔离级
SQL SERVER
CREATE TABLE [ Customer ](
[ CustID ] [ int ] NOT NULL ,
[ Fname ] [ nvarchar ]( 20 ),
[ Lname ] [ nvarchar ]( 20 ),
[ Address ] [ nvarchar ]( 50 ),
[ City ] [ nvarchar ]( 20 ),
[ State ] [ nchar ]( 2 ) DEFAULT ( 'CA' ),
[ Zip ] [ nchar ]( 5 ) NOT NULL ,
[ Phone ] [ nchar ]( 10 )
)
insert into customer values ( 1 , 'Gary' , 'Mckee' , '111 Main' , 'Palm Springs' , 'CA' , 94312 , 7605551212 )
insert into customer values ( 2 , 'Tom' , 'Smith' , '609 Geogia' , 'Fresno' 'JP' , 33045 , 5105551212 )
insert into customer values ( 3 , 'Jams' , 'bond' , 'ST Geogie 21' , 'Washington' , 'NY' , 20331 , 4405551864 )
ORACLE
CREATE TABLE Customer (
CustID int NOT NULL ,
Fname nvarchar2 ( 20 ),
Lname nvarchar2 ( 20 ),
Address nvarchar2 ( 50 ),
City nvarchar2 ( 20 ),
State nchar ( 2 ) DEFAULT 'CA' ,
Zip nchar ( 5 ) NOT NULL ,
Phone nchar ( 10 )
);
insert into customer values ( 1 , 'Gary' , 'Mckee' , '111 Main' , 'Palm Springs' , 'CA' , 94312 , 7605551212 );
insert into customer values ( 2 , 'Tom' , 'Smith' , '609 Geogia' , 'Fresno' , 'JP' , 33045 , 5105551212 );
insert into customer values ( 3 , 'Jams' , 'bond' , 'ST Geogie 21' , 'Washington' , 'NY' , 20331 , 4405551864 );
1。Sqlserver与oracle单条语句处理对比
SQL SERVER单条语句默认自动提交,即单条语句自动作为一个事务处理;而oracle的原则是尽量延后提交,除非遇到显式提交命令或者DDL语句。
SQL SERVER
打开事务1 :
运行:select * from customer
可以看到表有3条记录
运行:insert into customer values ( 4 , 'Hello' , 'world' , 'paradise road 01' , 'heaven' , 'XY' , 00001 , 1234564321 )
转到事务2:
运行:select * from customer
可以看到事务1中刚插入的custid为4的记录。
ORACLE
打开事务1,运行:
select * from customer ;
可以看到表有3条记录,运行:
insert into customer values ( 4 , 'Hello' , 'world' , 'paradise road 01' , 'heaven' , 'XY' , 00001 , 1234564321 );
转到事务2,运行:
select * from customer ;
能看到的还是3条记录,事务1中刚插入的一条记录未自动提交,看不到。
转到事务1,运行:
commit ;
转到事务2,运行:
select * from customer ;
现在能看到4条记录了。
2. 丢失更新
Sqlserver完全兼容ANSI 92标准定义的4个隔离级别。它的默认隔离级别是提交读(read committed),在该级别下,可能会有丢失更新的问题。Oracle的默认情形也一样。故不再重复。
SQL SERVER
打开事务1运行:
set transaction isolation level read committed
begin tran
select * from customer -- 看到3条记录
现在切换到事务2,此时事务1还未结束。在事务2中运行:
set transaction isolation level read committed
begin tran
select * from customer -- 看到3条记录,和事务1中相同
现在假设事务1事务继续运行,修改数据并提交:
update customer set state = 'TK' where CustID = 3
commit
回到事务2,事务2根据先前查询到的结果修改数据:
update customer set Zip = 99999 where state = 'NY'
commit
结 果因为事务1已经修改了事务2的where条件数据,事务2未成功修改数据(其实准确的说应该算是幻象读引起的更新失败。不过若满足条件的记录数多的话, 事务2的update可能更新比预期的数量少的记录数,也可算“丢失”了部分本应完成的更新。个人认为只要明白实际上发生了什么即可,不必过分追究字 眼)。丢失更新还可能有别的情形,比如事务2也是
update customer set state = 'KO' where CustID = 3
两个事务都结束后,事务2的结果反映到数据库中,但事务1的更新丢失了,事务2也不知道自己覆盖了事务1的更新。
3.脏读演示
sqlserver的默认隔离级别是提交读(read committed),当手工将其改为未提交读时,事务可以读取其它事务没提交的数据;oracle由于自身特殊实现机制,可以理解为自身基础的隔离级别就是可重复读(与ANSI标准还是有区别的,后面例子会说明)。
SQL SERVER
打开事务1,运行:
begin tran
select * from customer
update customer set state = 'TN' where CustID = 3
转到事务2,运行:
set transaction isolation level read uncommitted
begin tran
select * from customer
此时看到的数据是事务1已经更新但还未提交的(3号记录state值TN)。而如果事务1发觉数据处理有误,转到事务1,进行回滚:
Rollback
此时事务2如根据刚读取的数据进一步处理,会造成错误。它读取的数据并未更新到数据库,是“脏”的。
ORACLE
ANSI 定义未提交读(read uncommitted)级别本意不是为了故意引入错误,而是提供一种可能的最大并发程度级别,即一个事务的数据更新不影响其它 事务的读取。Oracle从内核层面实现了更新数据不阻塞读。可以说它提供未提交读级别的兼容,但没有脏读问题。(详情参考对应PPT文档)故 oracle没有手工设置read uncommitted级别的语句。
4.不可重复读
Sql server 的默认级别没有脏读问题,但存在不可重复读问题。Oracle默认级别也是提交读,不过它因为自身特殊机制,在语句一级不存在不可重复读问题。也就是说当 运行时间较长的查询时,查询结果是与查询开始时刻一致的(即使查询过程中其它事务修改了要查询的数据),而SQL SERVER就存在问题 (sql server 2005新特性提供了可选的语句一级一致性支持,叫做行版本机制,实际上可以说是照着oracle的多版本来的,大体原理差不 多)。
由于语句一级的事务一致性难以演示,下面例子是事务一级,提交读隔离级别下发生的不可重复读现象:
SQL SERVER
打开事务1,运行:
set transaction isolation level read committed
begin tran
select * from customer where State = 'CA'
可以得到1条记录,这个时候事务2中运行:
set transaction isolation level read committed
begin tran
update Customer set state = 'JP' where state = 'CA'
commit
事务2插入一条记录并提交。回到事务1,事务1继续运行,此时它再次相同的查询,并借此作进一步修改,却发现读取到的数据发生了变化。
select * from customer where State = 'CA'
-- 2次读取不一致,之后的数据处理应该取消。否则不正确
update Customer set city = 'garden' where state = 'CA'
commit
读取未能获得记录。也就是说在同一事务中两次相同的查询获得了不同的结果,产生读取不可重复现象。
ORACLE
尽管oracle在默认隔离级别下提供了语句级的事务读一致性,但在事务级仍然是会出现不可重复读现象。和sql server一样,故不再重复。
5.幻像读
当sqlserver的隔离级别设置为可重复读(repeatable read),可以解决上面例子出现的问题。其内部是通过事务期间保持读锁来实现的。
SQL SERVER
开始事务1,修改事务级别为可重复读,执行:
set transaction isolation level repeatable read
begin tran
select * from customer where State = 'CA'
和上例一样得到1条记录,这个时候事务2中运行:
set transaction isolation level repeatable read
begin tran
update Customer set state = 'JP' where state = 'CA'
commit
会发现事务2一直等待,并不结束。返回事务1,运行:
select * from customer where State = 'CA' -- 2次读取结果一致
update Customer set city = 'garden' where state = 'CA'
commit
事务2成功结束后,再返回事务1,发现事务1也完成了。通过锁机制阻塞其它事务的修改,保持了事务期间读取的一致性。然而,如果是插入数据,则还是会出现问题:
开始事务1,修改事务级别为可重复读,执行:
set transaction isolation level repeatable read
begin tran
select * from customer where State = 'CA'
得到1条记录,这个时候事务2中运行:
set transaction isolation level repeatable read
begin tran
insert into customer values ( 4 , 'hellow' , 'world' , 'paradise 001' , 'garden' , 'CA' , 00000 , 1119995555 )
commit
发现事务2立刻提交并正常结束了。返回事务1,运行:
select * from customer where State = 'CA'
会发现得到了2条记录。这种现象就叫做幻像读。
ORACLE
由于自身特殊的机制,oracle没有提供一致读隔离级别的选项,想要获得一致读的效果,实际上需要将事务提升到串行化等级,即serializable。
6.串行化级别不同数据库实现
在这个级别,可以认为事务中的数据无论何时都是一致的,此级别使它显得好像没有其它用户在修改数据,数据库在事务开始时候被“冻结”(至少,对于本事务涉及的数据如此)。然而在不同数据库中,其实现机制也不同。
SQL SERVER
开始事务1,运行:
set transaction isolation level serializable
begin tran
select * from customer where State = 'CA'
会得到1条记录,这时事务2开始运行:
set transaction isolation level serializable
begin tran
insert into customer values ( 4 , 'hellow' , 'world' , 'paradise 001' , 'garden' , 'CA' , 00000 , 1119995555 )
commit
会发现事务2挂起,它在等待事务1结束。回到事务1,继续:
select * from customer where State = 'CA'
update Customer set city = 'garden' where state = 'CA'
commit
在片刻的等待以后,事务1得到类似以以下格式消息:
消息1205,级别13,状态56,第1 行
事务 ( 进程ID 51 ) 与另一个进程被死锁在锁资源上,并且已被选作死锁牺牲品。请重新运行该事务。
而 事务2更新了数据并正常结束。这是因为两个事务都设置成了串行化级别,当遇到冲突时候,sql server根据一定的规则选择牺牲掉其中一个事务,来保 证事务的串行性。上面的例子,如果将事务2的隔离级别改为提交读,那么事务2会等待事务1完成,之后自己正常完成(因为事务2没有串行需求,不会有死 锁)。
ORACLE
在oracle中,通过多版本,可以在一定程度上避免死锁。
开始事务1,运行:
set transaction isolation level serializable ;
select * from customer where State = 'CA' ; -- set tran语句隐式开始事务
得到1条记录,然后事务2开始运行:
set transaction isolation level serializable ;
insert into customer values ( 4 , 'hellow' , 'world' , 'paradise 001' , 'garden' , 'CA' , 00000 , 1119995555 );
commit ;
可以发现事务2立刻完成,没有阻塞。回到事务1继续:
select * from customer where State = 'CA' ;
update Customer set city = 'garden' where state = 'CA' ;
commit ;
事务1中的第二次查询和事务开始时刻一致,就好像事务2已经完成的提交不存在。事务最终正常更新完毕,并保持了“事务开始”时刻的数据一致性。
然而,如果事务1,2修改同样的数据行,也会有错误,
开始事务1,运行:
set transaction isolation level serializable ;
select * from customer where State = 'CA' ; -- set tran语句隐式开始事务
得到1条记录,然后事务2开始运行:
set transaction isolation level serializable ;
update customer set state = 'KO' where state = 'CA' ;
commit ;
可以发现事务2立刻完成,没有阻塞。回到事务1继续:
select * from customer where State = 'CA' ;
update Customer set city = 'garden' where state = 'CA' ;
commit ;
出现错误信息:
第 1 行出现错误 :
ORA - 08177 : 无法连续访问此事务处理
总的来说,oracle利用多版本的方式实现串行化级别更少造成死锁,除非两个事务修改了相同的数据行,一般也不会造成冲突。
7.不同隔离级别的相互影响
前 面的例子基本都是两个相同隔离级别事务的情况。如果不同隔离级别的事务发生冲突,会有什么不同吗?实际上,对于一个事务来说,其它事务的隔离级别对它来说 是未知的,更进一步,甚至数据库中有没有其它事务,有多少事务也不知道。影响事务访问数据就两方面因素:该事务自身的隔离级别,该事务要访问的数据上面锁 的状态。
SQL SERVER
开始事务1,运行:
set transaction isolation level serializable
begin tran
select * from customer where State = 'CA'
事务1的查询获得1条记录,转到事务2,运行:
set transaction isolation level read uncommitted
begin tran
select * from customer
事务2查询获得3条记录,回到事务1,运行:
update Customer set city = 'garden' where state = 'CA'
切换到事务2,运行:
select * from customer
update customer set state = 'KO' where state = 'CA'
commit ;
因为事务2隔离级别为未提交读,因此事务1中刚作的修改可以立刻从查询看到,即使事务1还未结束。进一步的update因为事务1对记录加了独占锁,因此事务2挂起。回到事务1将其提交:
Commit
事务1正常结束,独占锁释放,从而让事务2得以继续修改数据,并最终完成。
ORACLE
Oracle数据库的隔离级别设置语句只有read committed和serializable(read only暂不讨论),加上其特殊锁机制,不同隔离级别事务间的影响除了上例(例6)中两个都为serializable的情况,其它都可视为互不阻塞。
8.页锁与行锁(限sql server)
Sql server的锁可以到行一级。然而它又存在自动的锁定扩大,锁定转换。因此存在一些意想不到的情况。下面是演示:
开始事务1,运行:
set transaction isolation level read committed
begin tran
select * from customer where State = 'CA'
update Customer set city = 'garden' where state = 'CA'
理论上来说,在提交读级别下,上面的update语句只是在state值为CA的数据行上加了独占锁,表中其它数据应该可以被其它事务更新,然而,如下事务2开始:
set transaction isolation level read committed
begin tran
select * from customer
update customer set state = 'KO' where state = 'JP'
commit
发现事务2陷入阻塞状态。尽管它们更新的不是同一条记录。回到事务1,运行:
Commit
事务1结束后事务2才继续运行至结束。
如果我们在表上加入索引,如下:
CREATE NONCLUSTERED INDEX [ idx_state ] ON [ dbo ].[ Customer ] ( [ State ])
再重复上面的步骤,会发现阻塞不再存在。
PS:这种现象应该和数据库默认加锁参数 / 机制有关,应该可以调整,但目前手中没有进一步资料。故仅罗列了现象。
ORACLE
Oracle在数据一级只有一种数据行上的锁,因此不存在sql server中这些问题。
9.Set transaction语句的作用周期
前面所有的例子,都是在会话窗口中进行的演示。一旦使用了set transaction语句,会影响整个会话。除非再显式改变隔离级别,否则将保持到会话结束。例如:
开始事务1,假设会话一开始处于SQL SERVER的默认隔离级别(read committed):
begin tran
select * from customer where State = 'CA'
select * from sys . dm_tran_locks
系统视图sys . dm_tran_locks可以查看当前的加锁情况,到目前位置,只有数据库级的锁。继续运行:
set transaction isolation level repeatable read
select * from customer where State = 'CA'
select * from sys . dm_tran_locks
commit
接下来的语句改变了隔离级别到可重复读,接下来的查询,会看到行级锁的记录。在上面事务提交后,运行:
begin tran
select * from customer where State = 'CA'
select * from sys . dm_tran_locks
commit
仍然会从视图sys . dm_tran_locks看到行级锁记录。整个会话期间都受到影响。
但是,如果调用存储过程,函数,则过程 / 函数中事务隔离级别的改变并不会对调用环境造成影响。可以通过以下例子说明,首先,创建一个存储过程脚本:
CREATE PROCEDURE [ dbo ].[ test_tran_level ]
AS
BEGIN
BEGIN TRAN
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ
SELECT * FROM CUSTOMER
UPDATE CUSTOMER SET STATE = 'SS' WHERE CustID = 3
SELECT * FROM sys . dm_tran_locks
COMMIT
END
然后,在会话窗口调用该过程,会话窗口当前隔离级别为默认的提交读:
Exec test_tran_level
运行的结果可以看到读取锁信息,再在会话中运行:
begin tran
select * from customer where State = 'CA'
select * from sys . dm_tran_locks
commit
视图sys . dm_tran_locks并未有读锁的记录,说明事务隔离级
发表评论
-
数据库连接池的工作原理
2014-09-10 11:34 654对于共享资源,有一个 ... -
DBCP连接池配置参数说明
2014-09-10 10:59 1012DBCP连接池配置 <bean i ... -
触发器
2014-08-11 09:20 615--账户表 DROP TRIGGER IF E ... -
Oracle 创建存储过程例子
2012-10-12 18:09 795--创建存储过程 CREATE OR REPLACE PRO ... -
精妙Sql语句
2012-05-25 13:17 771DDL—数据定义语言(CREAT ... -
数据库事务与并发
2012-05-25 13:17 892概述:事务通过隔离级别控制锁的形式来处理数据的并发问题。 为 ...
相关推荐
查询:默认事务隔离级别 mysql> select @@tx_isolation;当前会话的默认事务隔离级别 mysql> select @@session.tx_isolation;当前会话的默认事务隔离级别 mysql> select @@global.tx_isolation;全局的事务隔离级别
MySQL事务隔离级别是数据库管理系统中一个非常重要的概念,它关系到数据的一致性和并发性能。在MySQL中,事务被用于确保数据库操作的原子性、一致性、隔离性和持久性(ACID特性)。事务隔离级别主要涉及四个方面:读...
MySQL数据库管理系统提供了四种事务隔离级别来处理事务并发中可能遇到的问题。事务并发问题通常包括脏读、不可重复读和幻读。 一、事务的基本要素(ACID) 事务具有四个基本要素,即原子性(Atomicity)、一致性...
Spring 框架提供了一套完善的事务管理机制,其中包含了多种事务传播属性和事务隔离级别。这些特性使得在处理数据库操作时,能够更好地控制事务的边界和行为,从而确保数据的一致性和完整性。 首先,我们来看一下...
### Spring 事务传播特性和事务隔离级别详解 #### 一、Spring 事务传播特性 在进行多层服务架构设计时,事务的管理尤其重要。为了确保数据的一致性,Spring 提供了一种灵活的方式来控制事务的传播行为。下面详细...
SQL Server的锁机制和事务隔离级别是数据库管理系统中确保数据一致性、避免并发问题的重要机制。在SQL Server中,锁主要用于控制多个用户同时访问同一数据时的并发操作,而事务隔离级别则是确定在事务中如何处理这些...
深入理解Mysql事务隔离级别与锁机制 Mysql事务隔离级别与锁机制是数据库系统中非常重要的概念,它们都是为了解决多事务并发问题而设计的。下面我们将深入讲解这些机制,让大家彻底理解数据库内部的执行原理。 事务...
事务传播特性&事务隔离级别 事务传播特性是指在 Java 中,事务的传播行为,即在多个事务之间如何交互和传播。Java 中有七种事务传播特性,分别是: 1. PROPAGATION_REQUIRED:如果存在一个事务,则支持当前事务。...
事务隔离级别 课程目标 了解 —— 事务隔离级别的概念; 理解 —— 事务隔离的四种级别; 掌握 —— 事务隔离级别的设置; 事务隔离级别 事务隔离:每一个事务都有一个所谓的隔离级,它定义了用户彼此之间隔离和交互...
介绍数据库事务的四种隔离级别,比较不同隔离级别的区别和影响
在多用户并发访问数据库系统时,事务隔离级别的设置对于保障数据一致性与系统并发性能之间至关重要。本文档详细探讨了SQL Server中不同事务隔离级别的实验研究,揭示了各隔离级别与锁管理之间的关系,并通过实验验证...
### Spring事务的传播特性和事务隔离级别 #### 一、Spring事务的传播特性(Propagation) 在Spring框架中,事务管理不仅提供了ACID属性的支持,还引入了事务的传播特性,这些特性决定了当一个方法调用另一个方法时,...
**事务隔离级别** 在计算机科学,尤其是数据库管理系统(DBMS)的设计中,事务隔离级别是确保数据一致性、避免并发操作导致的问题的关键概念。事务是一系列数据库操作的逻辑单元,它们要么全部成功,要么全部失败。...
### 数据库事务隔离级别详解 #### 一、引言 在多用户共享的数据库系统中,为了保证数据的一致性和准确性,必须对并发事务进行控制。数据库事务隔离级别是用来管理多个事务之间相互影响的程度,主要解决的问题包括脏...
【事务隔离级别】 在计算机科学,特别是在数据库管理系统(DBMS)中,事务隔离级别是确保数据库并发操作一致性的重要概念。事务是一系列数据库操作的逻辑单元,必须作为一个整体执行,要么全部成功,要么全部失败。...
Hibernate 事务隔离级别 深入探究 在 Hibernate 中,事务隔离级别是指数据库系统提供的一种机制,以解决并发事务带来的问题。为了确保数据库的可靠性和一致性,Hibernate 提供了四种事务隔离级别,分别是 ...
理解SQL Server中事务隔离级别及相应封锁机制 SQL Server中事务隔离级别是数据库管理系统中一个重要的概念,它决定了事务之间的并发执行和数据的一致性。本文将详细介绍SQL Server中事务隔离级别的概念、分类、特点...
MySQL 支持四种事务隔离级别:READ UNCOMMITTED(读未提交)、READ COMMITTED(读已提交)、REPEATABLE READ(可重复读)和 SERIALIZABLE(串行化)。每种隔离级别对应不同程度的事务并发问题的防范,其中,MySQL ...