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

資訊專欄INFORMATION COLUMN

解決方案:如何防止數(shù)據(jù)重復(fù)插入?

wenshi11019 / 1992人閱讀

摘要:問題是,重復(fù)請求導(dǎo)致的數(shù)據(jù)重復(fù)插入。這問題造成的后果很明顯數(shù)據(jù)冗余,可能不單單多一條有些業(yè)務(wù)需求不能有多余數(shù)據(jù),造成服務(wù)問題問題如圖所示解決方式如何將同請求,不執(zhí)行插入,而是讀取前一個請求插入的數(shù)據(jù)并返回。那么使用分布式鎖的解決方案。

摘要: 原創(chuàng)出處 https://www.bysocket.com 「公眾號:泥瓦匠BYSocket 」歡迎關(guān)注和轉(zhuǎn)載,保留摘要,謝謝!
目錄

為啥要解決數(shù)據(jù)重復(fù)插入?

解決方案實戰(zhàn)

可落地小總結(jié)

一、為啥要解決數(shù)據(jù)重復(fù)插入?

問題起源,微信小程序抽風(fēng) wx.request() 重復(fù)請求服務(wù)器提交數(shù)據(jù)。后端服務(wù)也很簡單,偽代碼如下:

class SignLogService {
    public void saveSignLog(SignLogDO log) {
        // 簡單插入做記錄
        SignLogDAO.insert(log);
    }
}

發(fā)現(xiàn)數(shù)據(jù)庫會存在重復(fù)數(shù)據(jù)行,提交時間一模一樣。但業(yè)務(wù)需求是不能有多余的 log 出現(xiàn),這明顯是個問題。

問題是,重復(fù)請求導(dǎo)致的數(shù)據(jù)重復(fù)插入。這問題造成的后果很明顯:

數(shù)據(jù)冗余,可能不單單多一條

有些業(yè)務(wù)需求不能有多余數(shù)據(jù),造成服務(wù)問題

問題如圖所示:

解決方式:如何將 同請求 A,不執(zhí)行插入,而是讀取前一個請求插入的數(shù)據(jù)并返回。解決后流程應(yīng)該如下:

二、解決方案實戰(zhàn) 1.單庫單表解決方案

唯一索引 + 唯一字段

冪等

上面說的那種業(yè)務(wù)場景:sign_log 表會有 user_id、sign_id、sign_time 等。那么每次簽到,每個人每天只有一條簽到記錄。

數(shù)據(jù)庫層采取唯一索引的形式,保證數(shù)據(jù)記錄唯一性。即 UNIQUE 約束,UNIQUE 約束唯一標(biāo)識數(shù)據(jù)庫表中的每條記錄。另外,user_id,sign_id,sign_time 三個組合適唯一字段。創(chuàng)表的偽代碼如下:

CREATE TABLE sign_log
(
id int NOT NULL,
user_id int NOT NULL,
sign_id int,
sign_time int,
CONSTRAINT unique_sign_log UNIQUE (user_id,sign_id,sign_time)
)

重點(diǎn)是 CONSTRAINT unique_sign_log UNIQUE (user_id,sign_id,sign_time)。有個小問題,數(shù)據(jù)量大的時候,每條記錄都會有對應(yīng)的唯一索引,比較耗資源。那么這樣就行了嗎?

答案是不行,服務(wù)不夠健壯。第一個請求插入成功,第二個請求直接報錯,Java 服務(wù)會拋出 DuplicateKeyException

簡單的冪等寫法操作即可,偽代碼如下:

class SignLogService {
    public SingLogDO saveSignLog(SignLogDO log) {
        // 冪等處理
        SignLogDO insertLog = null;
        try {
            insertLog = signLogDAO.insert(log);
        } catch (DuplicateKeyException e) {
            insertLog = selectByUniqueKeys(userId,signId,signTime);
        }
        
        return insertLog;
    }
}

的確,流量不是很大,也不算很高并發(fā)。重復(fù)寫問題,這樣處理即可。那大流量、高并發(fā)場景咋搞

2.分庫分表解決方案

流量大了后,單庫單表會演變成分庫分表。那么基于單表的唯一索引形式,在碰到分表就無法保證呢,插入的地方可能是兩個分表 A1 和 A2。

解決思路:將數(shù)據(jù)的唯一性條件放到其他存儲,并進(jìn)行鎖控制

還是上面的例子,每天,每次簽到,每個人只有一條簽到記錄。那么使用分布式鎖 Redis 的解決方案。大致偽代碼如下:

a.加鎖
// 加鎖
jedis.set(lockKey, requestId, "NX", "PX", expireTime);

lockKey 最簡單的是 user_id + sign_id + sign_time

expireTime 設(shè)置為一天

b.解鎖
// 解鎖
jedis.eval(script, lockKey,requestId);
c.冪等代碼加強(qiáng)
class SignLogService {
    public SingLogDO saveSignLog(SignLogDO log) {
        
        // 冪等校驗
        SignLogDO existLog = selectByUniqueKeys(userId,signId,signTime);
        if(Objects.nonNull(existLog)) {
            return existLog;
        }
    
        // 加鎖
        jedis.set
        
        SignLogDO insertLog = signLogDAO.insert(log);
        
        // 解鎖
        jedis.eval

        return insertLog;
    }
}

這個方案還是不是很成熟,大家參考下即可。

三、可落地小總結(jié)

解決方案實戰(zhàn)中,了解具體術(shù)。歸納如下:

冪等:保證多次同意請求后結(jié)果一致

并發(fā)控制:單表唯一索引、分布式多表分布式鎖

降級兜底方案:分布式鎖鎖失效 - 考慮樂觀鎖兜底

參考資料

重復(fù)插入方案: http://www.bysocket.com/archi...

《阿里巴巴 Java 開發(fā)手冊》

以下專題教程也許您會有興趣

《Spring Boot 2.x 系列教程》

《Java 核心系列教程》

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

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

相關(guān)文章

  • 為什么開發(fā)人員必須要了解數(shù)據(jù)庫鎖?

    摘要:小明馬上開發(fā)完畢,成功上線。下班過后,小明回想大紅說的話,什么是間隙鎖,什么是插入意向鎖,看來作為開發(fā)者對數(shù)據(jù)庫不應(yīng)該只會寫啊,不然遇到一些疑難雜癥完全沒法解決啊。破壞了數(shù)據(jù)庫中的隔離性。 1.鎖? 1.1何為鎖 鎖在現(xiàn)實中的意義為:封閉的器物,以鑰匙或暗碼開啟。在計算機(jī)中的鎖一般用來管理對共享資源的并發(fā)訪問,比如我們java同學(xué)熟悉的Lock,synchronized等都是我們常見的...

    AbnerMing 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<