先說樂觀鎖version 表t1 字段v值為1
一、並發更新操作
先select當前版本號1
同時更新操作update set v=v+1 where v=1
第一個執行的將v值更新為2,更新結果為1條記錄
其他並發重復操作因數據庫v值已更新為2,where 2=1更新0條記錄,判斷為過期無效操作
timestamp方式同理(建議用timestamp,方便大數據或跨庫進行同步處理)
刪除操作可不考慮並發,因為多刪幾次,結果是一樣的,這條記錄不存在
二、並發插入操作(單用戶重復提交)
0、因為在新增數據時,沒有舊值進行樂觀鎖的控制,所以在這里借用redis(單線程並且緩存操作),大概思路就是來自同一表單數據通過校驗tokenid防止重復提交
1、新增表單時,在action的add()方法中生成uuid,並同時保存在redis中(有效期可為1小時),跳轉到新增表單頁面,在表單隱藏域中通過tokenid保存uuid,在數據並發保存時,首次進入線程的先通過tokenid拿到redis的key並進行key移除操作,進行數據的操作保存,其他重復點擊的線程從redis中因得不到key,不進行任何數據處理,提示數據已經提交,勿重復操作。
三、並發校驗唯一操作
在表單數據新增或修改完后,保存時往往要檢查唯一性,通常是select count(1) from table where x1=? and x2=? and x3=?,如果返回值大於0,則說明有重復,這種方式 因在保存數據時,通過sql檢查數據唯一性是有延遲的,如果因校驗邏輯復雜或數據量大,檢驗時間比如是1s,那么在0.5s中另一個用戶也保存數據,那么他是在數據庫中查不到剛才新增的數據的,如果這兩人用同時保存一個張三客戶資料,此時因上述問題保存數據,都檢測不到張三存在,就存在了重復記錄
也有人說用唯一索引不就OK了,但這種方式一方面會降低數據庫性能,一方面多個參數復合檢驗相對不靈活(比如我要查詢非停用的手機號為133的張三是否重復,而此時停用的張三可能有多個),所以不推薦使用。
多個用戶同時操作保存這個資源時(比如張三客戶資料),第一個進入的先將md5(校驗唯一的多個參數)形成一個lockkey ,通過redis的setnx(lockkey, 當前時間+過期超時時間) 方式,如果返回值1,說明獲取到redis鎖,進行數據的保存操作,如果返回值為0說明該資源已經有用戶進行保存操作,可提示用戶:”系統資源正忙,請稍侯重試“,這樣就避免了唯一約束的資源重復錄入。(這里注意如果有效時間不設置的話,可能lockkey會因網絡原因,沒有釋放掉,造成死鎖)
那么這和冪等性有什么關系?
冪等性指的是無論操作多少次結果都一樣,即f(f(x))=f(x) ,而我們做並發操作時其實也就是在實現冪等性操作,也就是說不論做多少次操作,都能保證結果都是一樣的,換言之,也就是說在並發開發時,一定要找到可控的數據永久不變的那個點即鎖,那么在這個切入點之后的操作都為無效操作,從而保證結果一致。