JDBC事务隔离级别和db2中几个隔离级别行锁等

星期六, 2009-08-01 | Author: Lee | Database, JAVA-and-J2EE | 10,749 views

聊记下对应的事务处理问题 ———工作中头疼的事务拆分,降低事务的–文字简介–

JDBC的数据隔离级别设置:
JDBC 数据库隔离级别 数据访问情况
TRANSACTION_READ_UNCOMMITTED ur 就是俗称“脏读”(dirty read),在没有提交数据时能够读到已经更新的数据
TRANSACTION_READ_COMMITTED cs 在一个事务中进行查询时,允许读取提交前的数据,数据提交后,当前查询就可以读取到数据。update数据时候并不锁住表
TRANSACTION_REPEATABLE_READ rs 在一个事务中进行查询时,不允许读取其他事务update的数据,允许读取到其他事务提交的新增数据
TRANSACTION_SERIALIZABLE rr 在一个事务中进行查询时,不允许任何对这个查询表的数据修改。

JDBC事务隔离级别
为了解决与“多个线程请求相同数据”相关的问题,事务之间用锁相互隔开。多数主流的数据库支持不同类型的锁;因此,JDBC API 支持不同类型的事务,它们由 Connection 对象指派或确定。在 JDBC API 中可以获得下列事务级别:

TRANSACTION_NONE 说明不支持事务。
TRANSACTION_READ_UNCOMMITTED 说明在提交前一个事务可以看到另一个事务的变化。这样脏读、不可重复的读和虚读都是允许的。
TRANSACTION_READ_COMMITTED 说明读取未提交的数据是不允许的。这个级别仍然允许不可重复的读和虚读产生。
TRANSACTION_REPEATABLE_READ 说明事务保证能够再次读取相同的数据而不会失败,但虚读仍然会出现。
TRANSACTION_SERIALIZABLE 是最高的事务级别,它防止脏读、不可重复的读和虚读。

为了在性能与一致性之间寻求平衡才出现了上面的几种级别。事务保护的级别越高,性能损失就越大。
假定您的数据库和 JDBC 驱动程序支持这个特性,则给定一个 Connection 对象,您可以明确地设置想要的事务级别:

1
        conn.setTransactionLevel(TRANSACTION_SERIALIZABLE) ;

可以通过下面的方法确定当前事务的级别:

1
2
3
4
5
6
7
8
9
10
11
            int level = conn.getTransactionIsolation();
            if(level == Connection.TRANSACTION_NONE)
             System.out.println("TRANSACTION_NONE");
            else if(level == Connection.TRANSACTION_READ_UNCOMMITTED)
             System.out.println("TRANSACTION_READ_UNCOMMITTED");
            else if(level == Connection.TRANSACTION_READ_COMMITTED)
             System.out.println("TRANSACTION_READ_COMMITTED");
            else if(level == Connection.TRANSACTION_REPEATABLE_READ)
             System.out.println("TRANSACTION_REPEATABLE_READ");
            else if(level == Connection.TRANSACTION_SERIALIZABLE)
             System.out.println("TRANSACTION_SERIALIZABLE");

DB2中隔离级别和锁的各种用法和机制
基于db2 9 中做了以下的试验

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
CREATE TABLE RRTest (pkID VARCHAR(20) NOT NULL ,unID1 VARCHAR(20) NOT NULL,UnID2 VARCHAR(20) ,"CUSTOMER_ID" VARCHAR(6) ,
                  "ORDER_TYPE" DECIMAL(2,0) , 
                  "EXECUTION_TYPE" DECIMAL(2,0) , 
                  "ORDER_DATE" VARCHAR(8) , 
                  "ORDER_TIME" VARCHAR(6) , 
                  "ORDER_DATETIME" TIMESTAMP , 
                  "SIDE" DECIMAL(1,0) , 
                  "TRADE_TYPE" DECIMAL(1,0) , 
                  "ORDER_AMOUNT" DECIMAL(15,2) , 
                  "ORDER_PRICE" DECIMAL(8,4),
                   TSID VARCHAR(20) ) 
 
INSERT INTO RRTest 
SELECT Order_ID, Order_ID, Order_ID, CUSTOMER_ID, ORDER_TYPE, EXECUTION_TYPE, ORDER_DATE, ORDER_TIME, ORDER_DATETIME, SIDE, TRADE_TYPE, ORDER_AMOUNT, ORDER_PRICE ,ORDER_ID
    FROM DB2INST1.Fx_Order WHERE ORDER_DATE >'20070401'
GO
SELECT COUNT(*) FROM RRTEST
72239
 
ALTER TABLE "DB2INST1".RRTest
        ADD PRIMARY KEY
                (pkID);
 
CREATE UNIQUE INDEX UNIQINDX ON RRTest(unID1)
CREATE INDEX INDX002 ON RRTest(unID2)
db2 "RUNSTATS ON TABLE DB2INST1.RRTest ON ALL COLUMNS AND INDEXES ALL ALLOW WRITE ACCESS"
 
 
db2 CONNECT TO db2TT
db2 +c
 
SELECT * FROM RRTEST WHERE TSID='20070223ORD01267732' FOR UPDATE WITH RR
SELECT * FROM RRTEST WHERE TSID='20070222ORD01266302' FOR UPDATE WITH RR
 
SELECT * FROM RRTEST WHERE TSID='20070223ORD01267732' FOR UPDATE WITH RS
SELECT * FROM RRTEST WHERE TSID='20070222ORD01266302' FOR UPDATE WITH RS
 
SELECT * FROM RRTEST WHERE unID1='20070223ORD01267732' FOR UPDATE WITH RR
SELECT * FROM RRTEST WHERE unID1='20070222ORD01266302' FOR UPDATE WITH RR
SELECT * FROM RRTEST WHERE unID1='20070223ORD01267732' FOR UPDATE WITH RS
SELECT * FROM RRTEST WHERE unID1='20070222ORD01266302' FOR UPDATE WITH RS
 
SELECT * FROM RRTEST WHERE unID2='20070223ORD01267732' FOR UPDATE WITH RR
SELECT * FROM RRTEST WHERE unID2='20070222ORD01266302' FOR UPDATE WITH RR
SELECT * FROM RRTEST WHERE unID2='20070223ORD01267732' FOR UPDATE WITH RS
SELECT * FROM RRTEST WHERE unID2='20070222ORD01266302' FOR UPDATE WITH RS
 
SELECT * FROM RRTEST WHERE pkID='20070223ORD01267732' FOR UPDATE WITH RR
SELECT * FROM RRTEST WHERE pkID='20070222ORD01266302' FOR UPDATE WITH RR
SELECT * FROM RRTEST WHERE pkID='20070223ORD01267732' FOR UPDATE WITH RS
SELECT * FROM RRTEST WHERE pkID='20070222ORD01266302' FOR UPDATE WITH RS

按照以上字段 pkID 是主键,unID1 是唯一健索引,unID2 是普通健索引,TSID 是普通字段,没有在上建立索引。

试验结论:

 

     

 PK_INDEX    UNIQ_INDEX   NormalINDEX   NO_INDEX
 WITH RR 锁行,不锁表  锁行,不锁表  锁行,不锁表(1)  锁行,锁表
 WITH RS

锁行,不锁表

 锁行,不锁表  锁行,不锁表  锁行,锁表(2)
         

锁行是指在一个事务中用某种方式读取并更改了改行数据并显示得指明要修改后,这个事务将锁住改行,直到它提交或者回滚了事务后,才释放该锁。

锁表是指在用以上各种SQL在读取并更改一行的同时锁住了整个表。

对以上红字部分(1)可能有不能理解的是:为什么对普通索引和主键或者唯一健索引的不同结论? 

 对 PK和UNIQ的解释是因为RR 是可重复的读的级别,对这次检索扫描到的有可能成为自己的潜在检索对象的内容都会锁住,而因为是主键或者唯一健,别的行不可能成为这次这个检索的潜在读的范围,就是对别的数据此事务根本就没有必要锁,任何情况的更改都不可能出现幻读的情况(此表上的约束限制),所以只锁这一行。这么理解对PK,UNIQ没有问题。

但是NormalINDEX 我认为应该是锁住这个表而不是不锁。这点一直没想明白。留待以后再加强理解。

对 RS隔离级别是“锁定检索到的数据行”,是通过SQL检索到的结果进行锁定, PK,UNIQ,INDEX的结论完全都可以理解。 对 tableScan的检索而出现的锁表有些象RR隔离级别的所为。

遇到此类并发控制程序中注意一下,select * From TTT where ****= ? for update with RR(RS),这里的 *** 可不是随便定义的。

隔离级别分为RR/RS/CS/UR这四个级别。 下面让我们来逐一论述:

1. RR隔离级别: 在此隔离级别下, DB2会锁住所有相关的纪录。 在一个SQL语句执行期间, 所有执行此语句扫描过的纪录都会被加上相应的锁。 具体的锁的类型还是由操作的类型来决定, 如果是读取,则加共享锁; 如果是更新, 则加独占锁。 由于会锁定所有为获得SQL语句的结果而扫描的纪录, 所以锁的数量可能会很庞大, 这个时候, 索引的增加可能会对SQL语句的执行有很大的影响,因为索引会影响SQL语句扫描的纪录数量。

2. RS隔离级别: 此隔离级别的要求比RR隔离级别稍弱,此隔离级别下会锁定所有符合条件的纪录。 不论是读取, 还是更新, 如果SQL语句中包含查询条件, 则会对所有符合条件的纪录加相应的锁。 如果没有条件语句, 也就是对表中的所有记录进行处理,则会对所有的纪录加锁。

3. CS隔离级别: 此隔离级别仅锁住当前处理的纪录。

4. UR隔离级别:此隔离级别下,如果是读取操作,不会出现任何的行级锁。对于非只读的操作,它的锁处理和CS相同。

Tags: , , , ,

文章作者: Lee

本文地址: https://www.pomelolee.com/476.html

除非注明,Pomelo Lee文章均为原创,转载请以链接形式标明本文地址

7 Comments to JDBC事务隔离级别和db2中几个隔离级别行锁等

Diamond Bracelets
2009 年 08 月 02 日

Best article, lots of intersting things to digest. Very informative

John
2009 年 08 月 03 日

Interesting and informative. But will you write about this one more?

Floost
2009 年 08 月 03 日

Thank you! You often write very interesting articles. You improved my mood.

babafisa
2009 年 08 月 03 日

Best article, lots of intersting things to digest. Very informative

hotspot shield
2009 年 08 月 03 日

Great article . Will definitely copy it to my website.

alswl
2009 年 08 月 05 日

我想说的是,你Rss源里面的content好像没有设置html格式,导致在reader上阅读有点吃力。
另外,上面那些是不是span啊。。。

短歌行
2009 年 08 月 05 日

路过。

Leave a comment

Search

文章分类

Links

Meta