成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

解決"并發(fā)下查詢(xún)并更新帶來(lái)的問(wèn)題"

roadtogeek / 2196人閱讀

摘要:場(chǎng)景在日常開(kāi)發(fā)中經(jīng)常遇到先根據(jù)條件判斷某條數(shù)據(jù)是否存在,如果不存在的話(huà)就插入,如果存在的話(huà)就更新或提示異常。查詢(xún)名字叫的用戶(hù)是否存在如果不存在就插入數(shù)據(jù)使用鎖其實(shí)和代碼塊是相同的作用,但是要注意必須在中釋放鎖,避免出現(xiàn)異常死鎖了。

場(chǎng)景:

在日常開(kāi)發(fā)中經(jīng)常遇到先根據(jù)條件判斷某條數(shù)據(jù)是否存在,如果不存在的話(huà)就插入,如果存在的話(huà)就更新或提示異常。一般代碼的模式都寫(xiě)成下面這個(gè)樣子,是一種很常見(jiàn)的寫(xiě)法,但是在并發(fā)情況下很容易會(huì)重復(fù)插入兩條數(shù)據(jù),大概的情況就是第一個(gè)請(qǐng)求進(jìn)來(lái),沒(méi)有查詢(xún)到該用戶(hù)通過(guò)了if判斷,但是if中有比較耗時(shí)的邏輯,在第一個(gè)請(qǐng)求還沒(méi)執(zhí)行insert的時(shí)候第二個(gè)請(qǐng)求也進(jìn)來(lái)了,因?yàn)檫@個(gè)時(shí)候第一個(gè)請(qǐng)求還沒(méi)執(zhí)行insert操作,所以第二個(gè)請(qǐng)求也沒(méi)有查詢(xún)到該用戶(hù)也通過(guò)了if判斷,這個(gè)樣子就造成了兩條重復(fù)的數(shù)據(jù)。

// 查詢(xún)名字叫user1的用戶(hù)是否存在
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同步代碼塊

直接將查詢(xún)校驗(yàn)邏輯和插入邏輯都進(jìn)行同步,也就是說(shuō)第一個(gè)請(qǐng)求的邏輯沒(méi)結(jié)束,第二個(gè)請(qǐng)求就會(huì)一直等待著,只有當(dāng)?shù)谝粋€(gè)請(qǐng)求執(zhí)行完同步代碼塊中的邏輯釋放鎖后第二個(gè)請(qǐng)求才能獲取到鎖執(zhí)行這段邏輯。

private Object obj = new Object();

synchronized (object){
    // 查詢(xún)名字叫user1的用戶(hù)是否存在
    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

其實(shí)和synchronized代碼塊是相同的作用,但是要注意必須在finally中釋放鎖,避免出現(xiàn)異常死鎖了。

private Lock lock = new ReentrantLock();
try {
    lock.lock();
    // 查詢(xún)名字叫user1的用戶(hù)是否存在
    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ù)庫(kù)索引

既然是要根據(jù)用戶(hù)名字判斷是否有重復(fù)數(shù)據(jù),所以可以直接在數(shù)據(jù)庫(kù)上給userName字段添加UNIQUE索引,這樣在第二次重復(fù)插入的時(shí)候就會(huì)提示異常。如果不想重復(fù)插入的時(shí)候有報(bào)錯(cuò)提示可以使用INSERT IGNORE INTO語(yǔ)句。而代碼則不必做任何邏輯操作。

// 查詢(xún)名字叫user1的用戶(hù)是否存在
UserVo userVo= userMapper.selectUserByName("user1");
// 如果不存在就插入數(shù)據(jù)
if (userVo==null) {
    Thread.sleep(10000);
    UserVo userVo = new UserVo();
    userVo.setUserName("user1");
    userMapper.insert(userVo);
   }
}
4.使用redissetnx來(lái)作為鎖

redissetnx命令是只有當(dāng)你存入的key不存在時(shí)才會(huì)成功存入,并返回1,而如果key已經(jīng)存在的時(shí)候則存入失敗并返回0,我們可以拿這個(gè)特性來(lái)當(dāng)做鎖。首先這個(gè)方法進(jìn)來(lái)第一步就是執(zhí)行setnx操作,把查詢(xún)的用戶(hù)名存入redis,然后查詢(xún)?cè)撚脩?hù)是否存在,第一個(gè)請(qǐng)求進(jìn)到if判斷中但是沒(méi)執(zhí)行插入邏輯,第二個(gè)請(qǐng)求雖然也沒(méi)有查詢(xún)到該用戶(hù),但是它的setnx會(huì)失敗,因?yàn)榈谝粋€(gè)請(qǐng)求存的key還沒(méi)刪除,所以這樣就避免了并發(fā)重新插入的問(wèn)題,而且最大的優(yōu)點(diǎn)就是它不像synchronizedLock無(wú)論所有請(qǐng)求進(jìn)來(lái)都只能一個(gè)一個(gè)通過(guò),使用這種方法是只有當(dāng)操作同一個(gè)用戶(hù)有并發(fā)請(qǐng)求的時(shí)候才會(huì)阻塞,而如果是請(qǐng)求兩個(gè)不同的用戶(hù)時(shí)是不會(huì)阻塞的,都可以順利通過(guò),因?yàn)榇嫒氲?b>key是不同的。

// 自動(dòng)注入spring的redis操作類(lèi)
@Autowired
private RedisTemplate redisTemplate;

public String addUser (String userName) {
    // 執(zhí)行setnx命令,存入當(dāng)前拿來(lái)判斷的用戶(hù)名
    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 {
        // 無(wú)論更新成功和失敗都去刪除setnx添加的key
        operations.getOperations().delete(userName);
    }
    return result;
}
總結(jié):

上述四種方法,給數(shù)據(jù)庫(kù)加索引、Lockredis都有使用過(guò),synchronizedLock也差不多,個(gè)人感覺(jué)給數(shù)據(jù)庫(kù)加索引來(lái)控制這種并發(fā)太死板了,萬(wàn)一系統(tǒng)中有其他地方的邏輯是需要重復(fù)添加這個(gè)字段的數(shù)據(jù),這個(gè)時(shí)候就沒(méi)辦法使用索引了,synchronizedLock效率太低了,如果是并發(fā)量太大的這種方式肯定是不可缺的,而redis的這種方法則效率高很多,比較適合并發(fā)量高的操作。

結(jié)尾:

因?yàn)楸救私佑|的系統(tǒng)的并發(fā)量也不是很大,所以對(duì)這方面的技術(shù)也是自己在鉆研摸索,可能會(huì)有很多地方有遺漏和錯(cuò)誤,如果大家有更好的方法歡迎一起留言討論,也歡迎指出錯(cuò)誤。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/77799.html

相關(guān)文章

  • 支付寶防發(fā)方案之"一鎖二判三更新"

    摘要:每年支付寶在雙和雙的活動(dòng)中,都展示了絕佳的技術(shù)能力。對(duì)于異步并發(fā)重復(fù)消息的處理亦是如此,加深對(duì)狀態(tài)機(jī)的判斷后還可以處理消息亂序問(wèn)題。 每年支付寶在雙11和雙12的活動(dòng)中,都展示了絕佳的技術(shù)能力。這個(gè)能力不但體現(xiàn)在處理高TPS量的訪(fǎng)問(wèn),更體現(xiàn)在幾乎不會(huì)出錯(cuò),不會(huì)出現(xiàn)重復(fù)支付的情況,那這個(gè)是怎么做到的呢? 誠(chéng)然,為了實(shí)現(xiàn)在高并發(fā)下仍不會(huì)出錯(cuò)的技術(shù)目標(biāo),支付寶下了很多功夫,比如冪等性的處理,...

    imingyu 評(píng)論0 收藏0
  • 支付寶防發(fā)方案之"一鎖二判三更新"

    摘要:每年支付寶在雙和雙的活動(dòng)中,都展示了絕佳的技術(shù)能力。對(duì)于異步并發(fā)重復(fù)消息的處理亦是如此,加深對(duì)狀態(tài)機(jī)的判斷后還可以處理消息亂序問(wèn)題。 每年支付寶在雙11和雙12的活動(dòng)中,都展示了絕佳的技術(shù)能力。這個(gè)能力不但體現(xiàn)在處理高TPS量的訪(fǎng)問(wèn),更體現(xiàn)在幾乎不會(huì)出錯(cuò),不會(huì)出現(xiàn)重復(fù)支付的情況,那這個(gè)是怎么做到的呢? 誠(chéng)然,為了實(shí)現(xiàn)在高并發(fā)下仍不會(huì)出錯(cuò)的技術(shù)目標(biāo),支付寶下了很多功夫,比如冪等性的處理,...

    yibinnn 評(píng)論0 收藏0
  • python查詢(xún)本身拼裝所有庫(kù)導(dǎo)出命令

      文章內(nèi)容通常是闡述了python查詢(xún)本身拼裝所有庫(kù)并導(dǎo)出,主要包括查詢(xún)拼裝庫(kù)依據(jù)命令查詢(xún),導(dǎo)出庫(kù)安裝文件運(yùn)行指令,原文中給大家介紹得相當(dāng)詳細(xì),對(duì)于大家學(xué)習(xí)與工作具有極強(qiáng)的參考文獻(xiàn)參照實(shí)際意義,務(wù)必的朋友可以參考一下  一、查詢(xún)拼裝庫(kù)  1.命令查詢(xún)  piplist  2.從安裝路徑site-packages查詢(xún)  二、導(dǎo)出庫(kù)安裝文件  1.導(dǎo)出  在我們要導(dǎo)出的庫(kù)文件夾內(nèi)運(yùn)行指令:  pip...

    89542767 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<