摘要:場景在日常開發(fā)中經(jīng)常遇到先根據(jù)條件判斷某條數(shù)據(jù)是否存在,如果不存在的話就插入,如果存在的話就更新或提示異常。查詢名字叫的用戶是否存在如果不存在就插入數(shù)據(jù)使用鎖其實和代碼塊是相同的作用,但是要注意必須在中釋放鎖,避免出現(xiàn)異常死鎖了。
場景:
在日常開發(fā)中經(jīng)常遇到先根據(jù)條件判斷某條數(shù)據(jù)是否存在,如果不存在的話就插入,如果存在的話就更新或提示異常。一般代碼的模式都寫成下面這個樣子,是一種很常見的寫法,但是在并發(fā)情況下很容易會重復(fù)插入兩條數(shù)據(jù),大概的情況就是第一個請求進來,沒有查詢到該用戶通過了if判斷,但是if中有比較耗時的邏輯,在第一個請求還沒執(zhí)行insert的時候第二個請求也進來了,因為這個時候第一個請求還沒執(zhí)行insert操作,所以第二個請求也沒有查詢到該用戶也通過了if判斷,這個樣子就造成了兩條重復(fù)的數(shù)據(jù)。
// 查詢名字叫user1的用戶是否存在 UserVo userVo= userMapper.selectUserByName("user1"); // 如果不存在就插入數(shù)據(jù) if (userVo==null) { Thread.sleep(10000); UserVo userVo = new UserVo(); userVo.setUserName("user1"); userMapper.insert(userVo); } }解決方法: 1.使用synchronized同步代碼塊
直接將查詢校驗邏輯和插入邏輯都進行同步,也就是說第一個請求的邏輯沒結(jié)束,第二個請求就會一直等待著,只有當(dāng)?shù)谝粋€請求執(zhí)行完同步代碼塊中的邏輯釋放鎖后第二個請求才能獲取到鎖執(zhí)行這段邏輯。
private Object obj = new Object(); synchronized (object){ // 查詢名字叫user1的用戶是否存在 UserVo userVo= userMapper.selectUserByName("user1"); // 如果不存在就插入數(shù)據(jù) if (userVo==null) { Thread.sleep(10000); UserVo userVo = new UserVo(); userVo.setUserName("user1"); userMapper.insert(userVo); } }2.使用Lock鎖
其實和synchronized代碼塊是相同的作用,但是要注意必須在finally中釋放鎖,避免出現(xiàn)異常死鎖了。
private Lock lock = new ReentrantLock(); try { lock.lock(); // 查詢名字叫user1的用戶是否存在 UserVo userVo = userMapper.selectUserByName("user1"); // 如果不存在就插入數(shù)據(jù) if (userVo == null) { Thread.sleep(10000); UserVo userVo = new UserVo(); userVo.setUserName("user1"); userMapper.insert(userVo); } } finally { lock.unlock(); }3.給數(shù)據(jù)庫索引
既然是要根據(jù)用戶名字判斷是否有重復(fù)數(shù)據(jù),所以可以直接在數(shù)據(jù)庫上給userName字段添加UNIQUE索引,這樣在第二次重復(fù)插入的時候就會提示異常。如果不想重復(fù)插入的時候有報錯提示可以使用INSERT IGNORE INTO語句。而代碼則不必做任何邏輯操作。
// 查詢名字叫user1的用戶是否存在 UserVo userVo= userMapper.selectUserByName("user1"); // 如果不存在就插入數(shù)據(jù) if (userVo==null) { Thread.sleep(10000); UserVo userVo = new UserVo(); userVo.setUserName("user1"); userMapper.insert(userVo); } }4.使用redis中setnx來作為鎖
redis中setnx命令是只有當(dāng)你存入的key不存在時才會成功存入,并返回1,而如果key已經(jīng)存在的時候則存入失敗并返回0,我們可以拿這個特性來當(dāng)做鎖。首先這個方法進來第一步就是執(zhí)行setnx操作,把查詢的用戶名存入redis,然后查詢該用戶是否存在,第一個請求進到if判斷中但是沒執(zhí)行插入邏輯,第二個請求雖然也沒有查詢到該用戶,但是它的setnx會失敗,因為第一個請求存的key還沒刪除,所以這樣就避免了并發(fā)重新插入的問題,而且最大的優(yōu)點就是它不像synchronized和Lock無論所有請求進來都只能一個一個通過,使用這種方法是只有當(dāng)操作同一個用戶有并發(fā)請求的時候才會阻塞,而如果是請求兩個不同的用戶時是不會阻塞的,都可以順利通過,因為存入的key是不同的。
// 自動注入spring的redis操作類 @Autowired private RedisTemplate redisTemplate; public String addUser (String userName) { // 執(zhí)行setnx命令,存入當(dāng)前拿來判斷的用戶名 BoundValueOperations operations = redisTemplate.boundValueOps(userName); // 執(zhí)行setnx命令的結(jié)果,這里封裝的方法是直接返回true和false boolean addFlag = operations.setIfAbsent(1); // 返回結(jié)果 String result = null; UserVo userVo= userMapper.selectUserByName(userName); try { if (userVo == null && addFlag == true) { Thread.sleep(10000); UserVo userVo = new UserVo(); userVo.setUserName("user1"); userMapper.insert(userVo); result = "更新成功"; } else{ result = "更新失敗"; } } finally { // 無論更新成功和失敗都去刪除setnx添加的key operations.getOperations().delete(userName); } return result; }總結(jié):
上述四種方法,給數(shù)據(jù)庫加索引、Lock和redis都有使用過,synchronized和Lock也差不多,個人感覺給數(shù)據(jù)庫加索引來控制這種并發(fā)太死板了,萬一系統(tǒng)中有其他地方的邏輯是需要重復(fù)添加這個字段的數(shù)據(jù),這個時候就沒辦法使用索引了,synchronized和Lock效率太低了,如果是并發(fā)量太大的這種方式肯定是不可缺的,而redis的這種方法則效率高很多,比較適合并發(fā)量高的操作。
結(jié)尾:因為本人接觸的系統(tǒng)的并發(fā)量也不是很大,所以對這方面的技術(shù)也是自己在鉆研摸索,可能會有很多地方有遺漏和錯誤,如果大家有更好的方法歡迎一起留言討論,也歡迎指出錯誤。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/77799.html
摘要:每年支付寶在雙和雙的活動中,都展示了絕佳的技術(shù)能力。對于異步并發(fā)重復(fù)消息的處理亦是如此,加深對狀態(tài)機的判斷后還可以處理消息亂序問題。 每年支付寶在雙11和雙12的活動中,都展示了絕佳的技術(shù)能力。這個能力不但體現(xiàn)在處理高TPS量的訪問,更體現(xiàn)在幾乎不會出錯,不會出現(xiàn)重復(fù)支付的情況,那這個是怎么做到的呢? 誠然,為了實現(xiàn)在高并發(fā)下仍不會出錯的技術(shù)目標(biāo),支付寶下了很多功夫,比如冪等性的處理,...
摘要:每年支付寶在雙和雙的活動中,都展示了絕佳的技術(shù)能力。對于異步并發(fā)重復(fù)消息的處理亦是如此,加深對狀態(tài)機的判斷后還可以處理消息亂序問題。 每年支付寶在雙11和雙12的活動中,都展示了絕佳的技術(shù)能力。這個能力不但體現(xiàn)在處理高TPS量的訪問,更體現(xiàn)在幾乎不會出錯,不會出現(xiàn)重復(fù)支付的情況,那這個是怎么做到的呢? 誠然,為了實現(xiàn)在高并發(fā)下仍不會出錯的技術(shù)目標(biāo),支付寶下了很多功夫,比如冪等性的處理,...
文章內(nèi)容通常是闡述了python查詢本身拼裝所有庫并導(dǎo)出,主要包括查詢拼裝庫依據(jù)命令查詢,導(dǎo)出庫安裝文件運行指令,原文中給大家介紹得相當(dāng)詳細,對于大家學(xué)習(xí)與工作具有極強的參考文獻參照實際意義,務(wù)必的朋友可以參考一下 一、查詢拼裝庫 1.命令查詢 piplist 2.從安裝路徑site-packages查詢 二、導(dǎo)出庫安裝文件 1.導(dǎo)出 在我們要導(dǎo)出的庫文件夾內(nèi)運行指令: pip...
閱讀 1812·2021-11-11 10:58
閱讀 4363·2021-09-09 09:33
閱讀 1327·2021-08-18 10:23
閱讀 1629·2019-08-30 15:52
閱讀 1721·2019-08-30 11:06
閱讀 1943·2019-08-29 14:03
閱讀 1568·2019-08-26 14:06
閱讀 3052·2019-08-26 10:39