日期:2014-05-16  浏览次数:20427 次

数据库-丢失更新测试

?1.前言 ? ??

?

? ? ? “丢失更新”是一个很经典的问题。具体什么是“丢失更新”大家百度一下就知道了。

?

? ? ? 预测我们的程序可能会出现?“丢失更新”问题。据我所知解决方法有两种(在隔离等级不为“可串行”的情况下):悲观锁、乐观锁。但同事认为update tablename set 字段1=字段1+1;这种类似的sql可以阻止“丢失更新”问题。我认为绝对不行(从隔离事物等级推断而出)。讨论无果,测试说话。

?

? ? ? 经测试我的结论是正确的,呵呵。运行TestUpdateLost即可查看效果。

? ? ? 此时数据的隔离等级,经SELECT @@tx_isolation;或者SELECT @@global.tx_isolation;查询都是REPEATABLE-READ

?

?

?

2.实验1

?

? ?目的:证明uppdate tablename set 字段1=字段1+1;这种类似的sql无法避免“丢失更新”。

? ?思路:启动N个多线程调用如下sql语句

?

?

update  ACCOUNT set VERSION = VERSION+1 where ACC_ID = 1 

?

? ?如果最终VERSION中的值与线程个数N不相同,则任务无法避免。

?

? ?代码实现说明:这事情本和语言,框架无关,但我们总需要用具体代码实现一下。我用了java、ibatis。

?

2.1 主要代码

?

2.1.1 主要java代码

?

? ?TestUpdateLost片段1:

?

//经测试size=!VERSION(实际情况是VERSION=6096,size=10000),故而:update tablename set a=a+1;是不能阻止“丢失更新”的。
		int size=10000;
		for (int i = 1; i <=size; i++) {
			TestUpdateLost t = new TestUpdateLost();
			new Thread(t).start();
		}// end of for
?

? ?TestUpdateLost片段2:

?

public void run() {
		try {
			Thread.sleep((int) (Math.random() * 1000));
			SimpleExample.updateVersionForUpdateLost(id);//执行sql语句
			System.out.println("-----------------"
					+ Thread.currentThread().getName()
					+ "-----------------------------------------" + count++);
		} catch (SQLException e) {
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
??

?

2.2 实验结果

?

? ? ?实验结果是数据库中VERSION=6096,此时线程个数为1w。故而:update tablename set a=a+1;这种类似的sql是不能阻止“丢失更新”的。



2.实验2-乐观锁的一种实现

? ?目的:提供一种通过版本号递增的方式实现乐观锁

? ?思路:启动N个多线程调用如下sql语句

?

?

update ACCOUNT set VERSION = VERSION+1 where ACC_ID = #id# AND VERSION=#version#
?

? ?并通过如下java代码乱判断是否即将出现“丢失更新”:

?

?

/**
	 * 更新版本,如果版本不一致则抛出乐观锁异常
	 * 
	 * @param id
	 * @throws SQLException
	 * @throws SQLException
	 */
	public static void updateVersionForOptimisticLock(int id,int version)
			throws OptimisticLockException, SQLException {
		Map<String, Integer> map=new HashMap<String, Integer>();
		map.put("id", id);
		map.put("version", version);
		final int count = sqlMapper
				.update("updateVersionForOptimisticLock", map);
		if (count <= 0) {
			// throw new SQLException("乐观锁异常");
			throw new OptimisticLockException("乐观锁异常");//这里把javaee.jar包导入了
		}

	}

?

? 下载代码后,初始化sql,运行TestOptimisticLock即可看到效果。