1. 程式人生 > >【資料一致性】多執行緒寫資料庫,如何保持資料一致性?

【資料一致性】多執行緒寫資料庫,如何保持資料一致性?

如題,這種情況一般在數字類資料更新時需要保證萬無一失,尤其是金額類的數字

比如小明的銀行號有1000塊錢

他做了一筆交易20元,很簡單,我們要做一次更新

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

一次一次的來沒關係,隨便怎麼更新

加入併發量高了,N人都在花小明賬號的錢,

每個人都在背後執行UPDATE XXX SET MONEY=NOWMONEY-Y WHERE ID=小明

這時候就極易出現賬號金額不一致的情況

第一筆 20,剩80才對

第二筆30,有可能剩70,但是應該剩50才對啊

兩個請求一起來的

這裡有個簡易的解決方案

我們在做數字類資料更新時,首先應該將該資料查出來再去更新,這樣保證後續的更新是在這個查出來的基數上面更新的

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值,否則,什麼都不做。