【数据一致性】多线程写数据库,如何保持数据一致性?

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://xxlcube.blog.csdn.net/article/details/49683193

如题,这种情况一般在数字类数据更新时需要保证万无一失,尤其是金额类的数字

比如小明的银行号有1000块钱

他做了一笔交易20元,很简单,我们要做一次更新

UPDATE XXX SET MONEY=NOWMONEY-20 WHERE ID=小明


一次一次的来没关系,随便怎么更新

加入并发量高了,N人都在花小明账号的钱,

每个人都在背后执行UPDATE XXX SET MONEY=NOWMONEY-Y WHERE ID=小明


这时候就极易出现账号金额不一致的情况

第一笔 20,剩80才对

第二笔30,有可能剩70,但是应该剩50才对啊

两个请求一起来的


这里有个简易的解决方案

我们在做数字类数据更新时,首先应该将该数据查出来再去更新,这样保证后续的更新是在这个查出来的基数上面更新的

参照:http://blog.csdn.net/simonchi/article/details/43735883

CAS  compare and set/swap

只有原值没有变化的情况下才会更新

按照这个思想,我们再回到上面的问题看一下



小明账号1000元

第一次 交易20

UPDATE XXX SET MONEY=NOWMONEY-20 WHERE ID=小明 AND MONEY = NOWMONEY,这里的NOWMONEY就是查出来的数字,更新成功

第二次 交易30 这时候基础数据已经变了,仍然用上面的条件更新就会失败了

更新成功与否通过executeUpdate返回的int值来判断就行了。


代码展示

String url = "jdbc:mysql://XXXXXXXXXX";
		String username = "XXX";
		String passwd = "XXX";
		Connection conn = DriverManager.getConnection(url, username, passwd);
		String sql = "update test set idinfo = idinfo- ? where id = 5 and idinfo=1000";
		String sql2 = "update test set idinfo = idinfo- ? where id = 5 and idinfo=1000";
		final PreparedStatement ps = conn.prepareStatement(sql);
		int temp = 1;
		ps.setInt(1, temp);
		final PreparedStatement ps2 = conn.prepareStatement(sql2);
		int temp2 = 5;
		ps2.setInt(1, temp2);
		int count = 10;
		ThreadPoolExecutor poolExe = new ThreadPoolExecutor(10, 100, 1, TimeUnit.SECONDS,
				new LinkedBlockingDeque<Runnable>(count));
		for (int i = 0; i < count; i++) {
			poolExe.submit(new Runnable() {
				public void run() {
					// TODO Auto-generated method stub
					try {
						if (ps.executeUpdate()>0) {
							System.out.println("-1执行成功");
						} else {
							System.out.println("-1执行失败");
						}
						if (ps2.executeUpdate()>0) {
							System.out.println("-5执行成功");
						} else {
							System.out.println("-5执行失败");
						}
					} catch (SQLException e) {
						e.printStackTrace();
					}
				}
			});
		}
		poolExe.shutdown();
		try {
			poolExe.awaitTermination(1, TimeUnit.DAYS);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}


compareAndSet方法调用native方法去实现CAS操作,current是在该方法之前获取到的一个当前值,通过get拿到的,是一个volatile定义的变量,也就是从内存中读到的值,那么在做update之前当前值与刚才从内存中读到的值再次比对一下,相等则update到next值,否则,什么都不做。



展开阅读全文

没有更多推荐了,返回首页