`
b_l_east
  • 浏览: 636629 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

Oracle和Mysql中的数据库事务问题:Mysql Read-Repeatable有问题

 
阅读更多

今天不知不觉想到数据库的乐观锁和悲观锁,遂想写个程序测测,却发现了另一个问题,Mysql InnoDB的Read-Repeatable事务级别使用不当会存在数据一致性问题。

 

如下的测试程序:

 

 

public class OptimisticAndPessimisticLockTest2 {

	public static void main(String[] args) throws Exception {
		//创建测试表和数据
		initDatabase();
		
		//创建两个线程同时操作同一条记录
		OptimisticThread ot1 = new OptimisticThread(newConnection(), "O1");
		OptimisticThread ot2 = new OptimisticThread(newConnection(), "O2");
		
		ot1.start();
		ot2.start();
	}
	
	public static Connection newConnection() throws Exception {
		Class.forName("com.mysql.jdbc.Driver");
		Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "root");
		return con;
	}
	
	public static void initDatabase() throws Exception {
		Connection con = newConnection();
		Statement stmt = con.createStatement();
		stmt.execute("drop table if exists locktest");
		stmt.execute("create table locktest( name varchar(10)) ENGINE=InnoDB");
		stmt.executeUpdate("insert into locktest values('XJD')");
		con.close();
	}
	
	public static class OptimisticThread extends Thread {
		Connection con;
		String name;
		
		public OptimisticThread(Connection con, String name) {
			this.con = con;
			this.name = name;
		}
		
		@Override
		public void run() {
			try {
				//设置事务级别为可重复读
				this.con.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);
				this.con.setAutoCommit(false); //开始事务
				System.out.println("Started Transaction...: " + name);
				
				Statement stmt = this.con.createStatement();
				
				//先查询一下是否有'XJD'的记录
				ResultSet rs = stmt.executeQuery("select * from locktest where name='XJD'");
				if (rs.next()) {
					System.out.println("Got Record: " + name + " value: " + rs.getString(1));
				}
				rs.close();
				
				Thread.sleep(5000);//暂停5s让另一个线程也查询完成
				
				//更新'XJD'的记录
				int i = stmt.executeUpdate("update locktest set name = '" + name + "' where name = 'XJD'");
				System.out.println("Update Record: " + name + " count: " + i);//更新成功后i为1
				
				Thread.sleep(5000);//暂停5s让另一个线程也作更新操作
				
				//查询更新后的记录
				rs = stmt.executeQuery("select * from locktest where name='" + name + "'");
				if (rs.next()) {
					System.out.println("Got Record: " + name + " value: " + rs.getString(1));
				}
				rs.close();
				//查询原来的记录
				rs = stmt.executeQuery("select * from locktest where name='XJD'");
				if (rs.next()) {
					System.out.println("Got Record: " + name + " value: " + rs.getString(1));
				}
				rs.close();
				
				Thread.sleep(10000);
				
				System.out.println("Commiting Transaction...: " + name);
				this.con.commit();
				this.con.close();
				
			} catch (SQLException e) {
				System.out.println("Exception in " + name + ": " + e);
				e.printStackTrace();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			
		}
		
	}

}

 

输出结果:

 

 

Started Transaction...: O1
Got Record: O1 value: XJD
Started Transaction...: O2
Got Record: O2 value: XJD
Update Record: O1 count: 1
Got Record: O1 value: O1
Commiting Transaction...: O1
Update Record: O2 count: 0    //见下一行的说明
Got Record: O2 value: XJD       //此处在O2线程中还可以查询到XJD记录,但前一条的Update结果却是0
Commiting Transaction...: O2
 

从输出结果中注释的两行可以看出,Read-Repeatable事务级别容易出现问题:

  O1和O2两个线程都开启了事务--O1和O2中都可以查询到XJD的记录--O1更新并提交了XJD记录为'O1'--O2中Update语句返回0可知O2没有更新到‘XJD'的记录--但是O2中仍可使用查询语句查询到‘XJD'的记录(因为可重得读事务设置)--但此时问题就来了,我在O2中可查询到记录,为什么更新不到呢????

 

从上面的分析,可以看出,Read-Repeatable事务级别容易出现业务上的问题,比如我们在一个事务中查询到一条记录,而后我们对该记录进行操作,发现这些操作跟本不起作用,如果业务比较复杂,跨度大,很容易使我们“迷惑“,我们错在哪,为什么查询到了,却更新不到!!!

 

难怪Oracle不支持Read-Repeatable事务,在对Oracle执行:

 

 

con.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);

 抛出异常:

 

 

java.sql.SQLException: 仅 READ_COMMITTED 和 SERIALIZABLE 是有效的事务处理级
 

我使用的是Mysql默认安装,发现Mysql Innodb默认使用的就是Read-Repeatable事务,所以只能说要谨慎吧!

 

 

再则对于乐观锁,可能使用版本列、时间戳列、其它列数据对比,无论是何种方式,都要在提交前先查询一次,与之前查询的数据进行某些列的对比,如果使用了Read-Repeatable事务,那么对比永远是相同的,这样乐观锁就出现问题了,不是我们想要的结果,所以在使用乐观锁时不能使用Read-Repeatable事务,而应该使用Read-Commited事务。

 

以上为个人见解,本人对数据库事使用不是很多,有问题之处望高人指点赐教!!!

 

此处附上一处乐观与悲观锁的文章:http://www.zhujiangroad.com/program/Oracle/21775.html

 

 

分享到:
评论

相关推荐

    MySQL数据库:事务隔离级别.pptx

    事务隔离:每一个事务都有一个所谓的隔离级,它定义了用户彼此之间隔离和交互的程度。隔离性强制对事务进行某种程度的隔离,保证应用程序在事务中看到一致的数据。 MySQL提供了下面4种隔离级:序列化(SERIALIZABLE...

    浅析MYSQL REPEATABLE-READ隔离级别

    REPEATABLE-READ 即可重复读,set autocommit= 0或者START TRANSACTION状态下select表的内容不会改变。这种隔离级别可能导致读到的东西是已经修改过的。 比如: 回话一中读取一个字段一行a=1 在回话二里这个字段该行...

    mysql面试题,MySQL是一种关系型数据库管理系统,由Oracle公司开发

    答:MySQL是一种关系型数据库管理系统,由Oracle公司开发。 2. MySQL的安装有哪些方式? 答:本地安装、虚拟机安装、云服务安装(阿里云RDS、腾讯云CDB等) 3. MySQL的数据类型有哪些? 答:数值型(整数、小数)、日期型、...

    MySQL数据库事务隔离级别详解

    数据库事务的隔离级别有4个,由低到高依次为 Read uncommitted:允许脏读。 Read committed: 防止脏读,最常用的隔离级别,并且是大多数数据库的默认隔离级别。 Repeatable read:可以防止脏读和不可重复读。 ...

    MySQL数据库事务隔离级别

    全局修改,修改mysql.ini配置文件,在最后加上#可选参数有:READ-UNCOMMITTED,READ-COMMITTED,REPEATABLE-READ,SERIALIZABLE.2[mysqld]3transaction-isolation=REPEATABLE-READ这里全局默认是REPEATABLE-READ,其实...

    MySQL数据库事务隔离级别介绍(Transaction Isolation Level)

    数据库隔离级别有四种,应用《高性能mysql》一书中的说明: 然后说说修改事务隔离级别的方法: 1.全局修改,修改mysql.ini配置文件,在最后加上 代码如下: #可选参数有:READ-UNCOMMITTED, READ-COMMITTED, ...

    mysql数据库my.cnf配置文件

    # 如没有指定,MySQL默认采用的是REPEATABLE-READ,ORACLE默认的是READ-COMMITTED log_bin = mysql-bin binlog_format = mixed expire_logs_days = 30 #超过30天的binlog删除 slow_query_log = 1 long_query_time = ...

    Oracle 9i&10g编程艺术:深入数据库体系结构(全本)含脚本

    3.4.2 Oracle数据库中的存储层次体系 87 3.4.3 字典管理和本地管理的表空间 91 3.5 临时文件 93 3.6 控制文件 95 3.7 重做日志文件 95 3.7.1 在线重做日志 96 3.7.2 归档重做日志 98 3.8 密码文件 100 3.9 ...

    解读MySQL事务的隔离级别和日志登记模式选择技巧

    MySQL的四种事务隔离级别:Read-uncommitted、Read-committed、Repeatable-read、Seriailizable,相信大家都清楚各自异同,不清楚的朋友可以查看另外一篇技术文章:MySQL_InnoDB之事务与锁详解。但是对于第二类、第...

    MySql自动备份,自动压缩,自动删除历史

    rem 采用--single-transaction保证备份的一致性,实际上设定本次会话的隔离级别为:REPEATABLE READ,以确保本次会话(dump)时,不会看到其他会话已经提交了的数据。 rem 压缩备份的数据库文件,windows自带压缩到...

    MySQL 四种事务隔离级别详解及对比

    MySQL 四种事务隔离级别详解及对比 按照SQL:1992 事务隔离级别,InnoDB默认是可重复读的(REPEATABLE...transaction-isolation = {READ-UNCOMMITTED | READ-COMMITTED | REPEATABLE-READ | SERIALIZABLE} 用户可以用SE

    数据库事务

    MySQL数据库为我们提供的四种隔离级别: ...  Mysql 的默认事务级别是: Repeatable read  Oracle的默认事务级别是:Read committed  查看数据库的事务级别命令是:select @@tx_isolation;  

    mysql数据库的基本操作语法

    MySQL中约束保存在information_schema数据库的table_constraints中,可以通过该表查询约束信息; 约束主要完成对数据的检验,保证数据库数据的完整性;如果有相互依赖数据,保证该数据不被删除。 常用五类约束: ...

    java-数据库系统原理.docx

    并发访问问题 事务隔离级别 丢失修改 MySQL 所有事务隔离级别在数据库层面上均可避免 脏读 READ-COMMITTED 事务隔离级别以上可避免 不可重复读 REPEATABLE-READ 事务隔离级别以上可避免 幻读 SERIALIZABLE 事务隔离...

    Jonny023#Study#事务隔离级别1

    事务:保证数据一致性,要么执行,要么不执行,mysql默认事务隔离级别:REPEATABLE-READ开启事务三个问题不可重复读:指在一个事务中,对同一数据进行

    transaction_isolation:以与数据库无关的方式在ActiveRecord中设置事务隔离级别

    不再维护-请分叉或寻求其他解决方案transaction_isolation 以与数据库无关的方式在ActiveRecord中设置事务隔离级别。 只要您使用的是新适配器mysql2,pg或sqlite3,就可以与MySQL,PostgreSQL和SQLite一起使用。 ...

    Spring.html

    default:使用数据库默认的隔离级别(mysql:可重复读,oracle:读已提交) readuncommited:读未提交,不可以解决任何问题 readcommited:读已提交,可以解决脏读问题 repeatableRead:可重复读,可以解决脏读,不可重复...

Global site tag (gtag.js) - Google Analytics