精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

基于數據庫鎖實現防重復提交

數據庫
今天介紹如何結合數據庫的悲觀鎖或樂觀鎖來實現對請求的有效驗證,確保同一操作不會被重復執行。

在 Web 應用開發中,重復提交問題是一個常見的挑戰。當用戶由于網絡延遲、誤操作等原因,多次點擊提交按鈕時,可能會導致相同的數據被多次插入到數據庫中,從而引發數據一致性問題。

為了解決這個問題,我們可以采用 token 機制,大多數實現是基于Redis實現,今天介紹如何結合數據庫的悲觀鎖或樂觀鎖來實現對請求的有效驗證,確保同一操作不會被重復執行。

一、Token 機制原理

Token 機制的核心思想是在用戶訪問頁面時,后端服務生成一個唯一的 token,并返回給前端。前端在用戶提交請求時,將這個 token 一并發送到后端。后端接收到請求后,驗證該 token 是否有效,即是否已經被使用過。如果 token 未被使用過,則處理此次請求,并將該 token 標記為已使用;如果 token 已被使用過,則判定為重復提交,拒絕處理此次請求。

二、實現

1. 使用數據庫悲觀鎖驗證 Token

悲觀鎖認為數據在被訪問時很可能被其他事務修改,因此在獲取數據時就對其加鎖,防止其他事務對其進行修改。在驗證 token 時,我們可以利用悲觀鎖來確保在同一時刻只有一個事務能夠處理帶有特定 token 的請求。

假設我們有一個token_info表,用于存儲 token 信息,表結構如下:

CREATE TABLE token_info (
    id INT AUTO_INCREMENT PRIMARY KEY,
    token VARCHAR(255) NOT NULL UNIQUE,
    create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_token (token)
);

驗證 token 的 SQL 語句及相關代碼如下:

public interface TokenMapper {
    // 插入Token到數據庫
    @Insert("INSERT INTO token_info (token) VALUES (#{token})")
    int insertToken(String token);

    // 使用悲觀鎖查詢Token
    @Select("SELECT * FROM token_info WHERE token = #{token} FOR UPDATE")
    Token selectTokenForUpdate(String token);

    // 刪除Token
    @Delete("DELETE FROM token_info WHERE token = #{token}")
    int deleteToken(String token);
}

Token的Service層,處理業務邏輯:

@Service
public class TokenService {
    @Autowired
    private TokenMapper tokenMapper;

    // 生成Token并存儲到數據庫
    public String generateToken() {
        String token = UUID.randomUUID().toString();
        tokenMapper.insertToken(token);
        return token;
    }

    // 驗證Token的有效性
    @Transactional
    public boolean validateToken(String token) {
        Token dbToken = tokenMapper.selectTokenForUpdate(token);
        if (dbToken != null) {
            tokenMapper.deleteToken(token);
            return true;
        }
        return false;
    }
}

在控制器中使用服務層方法進行驗證:

@RestController
public class SubmissionController {

    private final TokenService tokenService;

    public SubmissionController(TokenService tokenService) {
        this.tokenService = tokenService;
    }

    @PostMapping("/submit")
    public String submit(@RequestParam String token) {
        if (tokenService.validateToken(token)) {
            // 處理正常的提交邏輯
            return "提交成功";
        } else {
            return "重復提交,請求被拒絕";
        }
    }
}

2. 使用數據庫樂觀鎖驗證 Token

樂觀鎖認為數據在被訪問時很少會被其他事務修改,因此在獲取數據時不會對其加鎖,而是在更新數據時檢查數據是否被其他事務修改過。在驗證 token 時,我們可以通過版本號來實現樂觀鎖機制。

首先,修改token_info表結構,添加一個版本號字段version:

CREATE TABLE token_info (
    id INT AUTO_INCREMENT PRIMARY KEY,
    token VARCHAR(255) NOT NULL UNIQUE,
    version INT DEFAULT 0,
    create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_token (token)
);

驗證 token 的 SQL 語句及相關代碼如下:

// Token 的 Mapper 接口,定義與數據庫交互的方法
public interface TokenMapper {
    // 插入 Token 到數據庫
    @Insert("INSERT INTO token_info (token, version) VALUES (#{token}, 0)")
    int insertToken(String token);

    // 查詢 Token 及其版本號
    @Select("SELECT * FROM token_info WHERE token = #{token}")
    Token selectToken(String token);

    // 使用樂觀鎖更新 Token 的版本號
    @Update("UPDATE token_info SET version = version + 1 WHERE token = #{token} AND version = #{version}")
    int updateTokenVersion(@Param("token") String token, @Param("version") Integer version);

    // 刪除 Token
    @Delete("DELETE FROM token_info WHERE token = #{token}")
    int deleteToken(String token);
}

Token的Service層,處理業務邏輯:

@Service
public class TokenService {
    @Autowired
    private TokenMapper tokenMapper;

    // 生成 Token 并存儲到數據庫
    public String generateToken() {
        String token = UUID.randomUUID().toString();
        tokenMapper.insertToken(token);
        return token;
    }

    // 驗證 Token 的有效性
    //token用完即刪除,新的token版本號永遠為0,也可以不查詢庫默認0
    @Transactional
    public boolean validateToken(String token) {
        Token dbToken = tokenMapper.selectToken(token);
        if (dbToken != null) {
            int rowsAffected = tokenMapper.updateTokenVersion(dbToken.getToken(), dbToken.getVersion());
            if (rowsAffected > 0) {
                // 驗證成功,刪除 Token
                tokenMapper.deleteToken(token);
                return true;
            }
        }
        return false;
    }
}

在控制器中使用服務層方法進行驗證:

@RestController
public class SubmissionController {

    private final TokenService tokenService;

    public SubmissionController(TokenService tokenService) {
        this.tokenService = tokenService;
    }

    @PostMapping("/submit")
    public String submit(@RequestParam String token) {
        if (tokenService.validateToken(token)) {
            // 處理正常的提交邏輯
            return "提交成功";
        } else {
            return "重復提交,請求被拒絕";
        }
    }
}

3. Redis

@Slf4j
@Service
public class TokenUtilService {

    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 存入 Redis 的 Token 鍵的前綴
     */
    private static final String IDEMPOTENT_TOKEN_PREFIX = "idempotent_token:";

    /**
     * 創建 Token 存入 Redis,并返回該 Token
     *
     * @param value 用于輔助驗證的 value 值
     * @return 生成的 Token 串
     */
    public String generateToken(String value) {
        // 實例化生成 ID 工具對象
        String token = UUID.randomUUID().toString();
        // 設置存入 Redis 的 Key
        String key = IDEMPOTENT_TOKEN_PREFIX + token;
        // 存儲 Token 到 Redis,且設置過期時間為5分鐘
        redisTemplate.opsForValue().set(key, value, 5, TimeUnit.MINUTES);
        // 返回 Token
        return token;
    }

    /**
     * 驗證 Token 正確性
     *
     * @param token token 字符串
     * @param value value 存儲在Redis中的輔助驗證信息
     * @return 驗證結果
     */
    public boolean validToken(String token, String value) {
        // 設置 Lua 腳本,其中 KEYS[1] 是 key,KEYS[2] 是 value
        String script = "if redis.call('get', KEYS[1]) == KEYS[2] then return redis.call('del', KEYS[1]) else return 0 end";
        RedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
        // 根據 Key 前綴拼接 Key
        String key = IDEMPOTENT_TOKEN_PREFIX + token;
        // 執行 Lua 腳本
        Long result = redisTemplate.execute(redisScript, Arrays.asList(key, value));
        // 根據返回結果判斷是否成功成功匹配并刪除 Redis 鍵值對,若果結果不為空和0,則驗證通過
        if (result != null && result != 0L) {
            log.info("驗證 token={},key={},value={} 成功", token, key, value);
            return true;
        }
        log.info("驗證 token={},key={},value={} 失敗", token, key, value);
        return false;
    }
}

三、最后

通過使用 token 機制結合數據庫的悲觀鎖或樂觀鎖,我們可以有效地避免用戶重復提交請求,保證數據的一致性和系統的穩定性。悲觀鎖適用于數據競爭較為激烈的場景,能夠確保數據的完整性,但可能會影響系統的并發性能;樂觀鎖則適用于數據沖突較少的場景,能夠提高系統的并發處理能力,但在數據沖突較多時可能會導致多次重試。在實際應用中,我們需要根據具體的業務場景和數據特點選擇合適的鎖機制來實現 token 驗證邏輯。

責任編輯:趙寧寧 來源: 一安未來
相關推薦

2024-05-28 09:26:46

2024-07-26 07:59:25

2025-02-21 12:00:00

SpringBoot防重復提交緩存機制

2013-04-26 11:39:40

2010-09-30 09:11:01

2010-09-30 08:27:48

2021-03-11 09:53:07

SpringBoot數據庫分布式鎖

2024-05-06 00:00:00

.NET分布式鎖技術

2018-09-11 17:13:23

MySQ數據庫重復記錄

2022-03-29 10:52:08

MySQL數據庫

2019-12-09 10:03:40

MySQLSQL數據庫

2011-08-18 13:44:42

Oracle悲觀鎖樂觀鎖

2024-09-06 11:52:47

2011-05-24 14:13:20

Oracle數據庫

2021-01-26 13:40:44

mysql數據庫

2018-10-16 16:00:39

數據庫鎖舞MySQL

2018-02-27 15:48:31

數據庫SQL鎖死

2025-06-23 08:55:00

架構Vue重復提交

2018-04-27 13:00:00

數據庫MySQL刪除重復行

2010-08-18 09:00:38

數據庫
點贊
收藏

51CTO技術棧公眾號

老司机aⅴ在线精品导航| 黄色在线免费看| 老色鬼久久亚洲一区二区| 色狠狠av一区二区三区香蕉蜜桃| 韩国成人av| 免费一级a毛片| 操欧美老女人| 欧美videossexotv100| 国产真实乱子伦| 好操啊在线观看免费视频| 99精品视频在线播放观看| 国产精品自拍偷拍| 国产真人真事毛片| 99热精品久久| 亚洲久久久久久久久久久| 五月花丁香婷婷| 亚洲精品国产精品国产| 亚洲免费视频中文字幕| 欧美日韩在线一区二区三区| 精品久久久久中文慕人妻| 日韩二区在线观看| 98精品在线视频| 国模无码国产精品视频| 欧美日韩国产免费观看视频| 亚洲аv电影天堂网| 91国内在线播放| 欧美7777| 欧美日韩在线影院| 国产欧美精品aaaaaa片| 二区三区在线观看| 国产欧美日韩不卡| 久久精品国产美女| 黄色www视频| 国产一二三精品| 91精品久久久久久久久| 天天操天天干天天摸| 在线亚洲精品| 午夜精品一区二区三区在线视| 国产十八熟妇av成人一区| 高清一区二区中文字幕| 欧美日韩午夜在线视频| 91视频免费版污| 欧美电影h版| 大荫蒂欧美视频另类xxxx| 日韩国产成人无码av毛片| 免费av在线网址| 中文字幕亚洲欧美在线不卡| 欧美三级网色| 青青草视频免费在线观看| 粉嫩av亚洲一区二区图片| 亚洲jizzjizz日本少妇| 国产视频手机在线观看| 韩国精品免费视频| 91久久爱成人| 高清国产mv在线观看| 国产1区2区3区精品美女| 不卡一卡2卡3卡4卡精品在| 国产日本精品视频| 国产精品系列在线观看| 97在线中文字幕| 超碰在线播放97| 大尺度一区二区| 国产色综合一区二区三区| 日韩一级片免费观看| 成人的网站免费观看| 国产日韩欧美一区二区三区四区| 国产精品成人久久久| 丝瓜av网站精品一区二区 | 亚洲成人av福利| 久操手机在线视频| segui88久久综合9999| 欧美性色视频在线| 国产真人无码作爱视频免费| 另类一区二区三区| 日韩限制级电影在线观看| 黑人玩弄人妻一区二区三区| 欧美天堂影院| 伊人一区二区三区久久精品| 很污很黄的网站| 欧美日本一区| 欧亚精品中文字幕| 夜夜躁很很躁日日躁麻豆| 国产麻豆成人精品| 国产亚洲欧美一区二区三区| 国产一级免费在线观看| 国产精品乱人伦中文| 黑人巨茎大战欧美白妇| 无遮挡在线观看| 欧美精品乱码久久久久久按摩| 成人在线观看a| 日韩经典一区| 精品美女一区二区| 午夜精产品一区二区在线观看的| 天天躁日日躁狠狠躁欧美巨大小说| 欧美裸体一区二区三区| 免费啪视频在线观看| 亚洲人成网77777色在线播放 | 一区二区三区波多野结衣在线观看| 秋霞久久久久久一区二区| 日本中文字幕伦在线观看| 亚洲一区二区三区国产| 欧美精品成人网| 哺乳挤奶一区二区三区免费看| 日韩一区和二区| 丰满少妇一区二区| 欧美日韩精品| 国产日韩精品在线观看| 日本美女一级视频| 一区精品在线播放| 日本老熟妇毛茸茸| 高清精品视频| 久久久999精品视频| 一级做a爰片久久毛片| 国产寡妇亲子伦一区二区| 热re99久久精品国产99热| 污视频免费在线观看| 欧美区在线观看| 永久免费看mv网站入口78| 国产精品av久久久久久麻豆网| 欧美区二区三区| 中文在线免费观看| 91丨九色丨国产丨porny| 九九久久九九久久| 高清在线一区| 亚洲一区二区久久久| 国产一卡二卡在线| 国产一区二区三区观看| 手机看片福利永久国产日韩| 亚洲人成在线网站| 亚洲第一视频网站| 亚洲熟女www一区二区三区| 免费成人在线视频观看| 欧美日韩亚洲综合一区二区三区激情在线| 免费播放片a高清在线观看| 一区二区三区精品| 久久精品久久99| 久久激情电影| 国产精品亚洲一区二区三区| 久久精品国产亚洲a∨麻豆| 亚洲国产aⅴ成人精品无吗| 亚洲天堂小视频| 在线成人激情| 91久久偷偷做嫩草影院| 日韩av官网| 日韩精品一区二| 久久久精品91| 懂色中文一区二区在线播放| 少妇一晚三次一区二区三区| 一区二区三区四区高清视频 | 天堂va在线| 欧美一级高清片| 久久久久久久久久久久国产| 粉嫩一区二区三区在线看| 热久久最新地址| 中文字幕一区二区三区中文字幕| 国产婷婷色综合av蜜臀av | 内射无码专区久久亚洲| 一二三四区精品视频| 深夜视频在线观看| 欧美日韩伊人| 国内视频一区| 美女日韩欧美| 在线观看免费高清视频97| 中文字幕资源网| 成人免费小视频| 亚洲av毛片在线观看| 国产综合激情| 精品欧美国产一区二区三区不卡| 欧美三级电影一区二区三区| 777精品伊人久久久久大香线蕉| 亚洲精品乱码久久久久久蜜桃图片| 国产精品午夜一区二区三区| 国产精品欧美日韩久久| 一广人看www在线观看免费视频| 精品毛片网大全| 精品人妻一区二区三区视频| 日本视频一区二区| 好色先生视频污| 伦理一区二区三区| 国产精品久久久久久久久久免费| 午夜视频在线播放| 在线精品视频一区二区| 欧美人禽zoz0强交| 2022国产精品视频| 九九热免费精品视频| 91精品福利| 精品无人区一区二区三区竹菊| 日本在线观看大片免费视频| 日韩激情av在线免费观看| 国产偷人爽久久久久久老妇app| 91在线观看地址| 亚洲综合欧美在线| 亚洲精品人人| 一区二区在线不卡| 欧美综合自拍| 91手机视频在线观看| 亚洲十八**毛片| 久热国产精品视频| 视频二区在线| 7878成人国产在线观看| 国产精品21p| 伊人夜夜躁av伊人久久| 国产美女精品久久| 国产99一区视频免费| 丁香婷婷激情网| 99re国产精品| 日本高清xxxx| 精品大片一区二区| 国产精品亚洲不卡a| 欧美美女被草| 欧美在线激情网| 1024在线播放| 最近2019中文字幕mv免费看| 飘雪影视在线观看免费观看| 欧美成人伊人久久综合网| 中文字幕a级片| 欧美日韩精品在线播放| 九九视频免费看| 国产精品久久久久久久久晋中| 色婷婷一区二区三区在线观看| 在线国产一区二区| 亚洲日本无吗高清不卡| 国产成人黄色| 免费一区二区三区在在线视频| 奇米777日韩| 57pao精品| tube8在线hd| 欧美大片在线看免费观看| av电影在线网| 伊人伊成久久人综合网小说| 四虎影视2018在线播放alocalhost| 日本精品一级二级| 天堂网一区二区三区| 亚洲精品成人在线| 午夜免费激情视频| 亚洲欧美经典视频| 性生交大片免费全黄| 国产精品成人免费在线| 婷婷丁香综合网| 国产精品美日韩| 亚洲色图日韩精品| 国产精品色婷婷| 日韩av片在线| 国产精品福利电影一区二区三区四区| 日本亚洲一区二区三区| 国产在线播精品第三| 亚洲一二三av| 国产精品一二三四| 夜夜爽久久精品91| 国产91丝袜在线播放| 亚洲麻豆一区二区三区| zzijzzij亚洲日本少妇熟睡| 中国男女全黄大片| 波多野结衣中文字幕一区二区三区| 性欧美极品xxxx欧美一区二区| 91精品国产自产拍在线观看蜜| 国内视频一区二区| 美女久久99| 色视频一区二区三区| 欧美亚洲激情| 中文字幕一区二区三区乱码| 中文字幕免费一区二区| 国产av熟女一区二区三区| 一区二区国产精品| 国产一级特黄a大片免费| 蜜臀av在线播放一区二区三区| 大j8黑人w巨大888a片| 亚洲麻豆av| 精品久久久久久中文字幕2017| 日韩网站在线| 在线免费视频a| 国产主播一区二区三区| av在线天堂网| 久久久av毛片精品| 天天色影综合网| 亚洲一二三级电影| 一二三区免费视频| 7777精品伊人久久久大香线蕉| 激情视频网站在线观看| 精品视频免费在线| 亚洲精品久久久久久久久久| 亚洲第一中文字幕| 97超碰人人在线| 久久男人资源视频| 国模一区二区| 成人欧美一区二区| 国产一区二区三区91| 欧美一级中文字幕| 亚欧美中日韩视频| 久久久久久综合网| 99视频精品在线| 2014亚洲天堂| 日韩欧美在线视频观看| 99精品久久久久久中文字幕 | 日韩暖暖在线视频| 91精品麻豆| 欧美成人dvd在线视频| 亚洲一区色图| 国产天堂在线播放| 成人永久免费视频| 五月天精品视频| 亚洲国产日韩在线一区模特| 精品国产青草久久久久96| 亚洲大尺度美女在线| 国产秀色在线www免费观看| 欧美中文字幕在线视频| 一起草在线视频| 最新成人av网站| 国内外成人免费在线视频| av电影一区二区| 国产精品 欧美激情| 欧美性videosxxxxx| 熟妇人妻一区二区三区四区 | 日韩精品系列| 久久国产精彩视频| 欧美一级做一级爱a做片性| 精品欧美一区二区在线观看视频| 欧美激情极品| 51xx午夜影福利| 美国毛片一区二区| 三上悠亚ssⅰn939无码播放| 亚洲一区成人在线| a天堂视频在线| 最近2019中文字幕mv免费看| 婷婷激情一区| 蜜桃久久精品乱码一区二区| 亚洲福利电影| 一级黄色大片免费看| 亚洲男同性视频| 亚洲最大成人av| 夜夜嗨av一区二区三区四区 | 午夜精品久久久久久久99热 | 国产在线精品一区免费香蕉| 国产成人1区| 九色porny91| 国产欧美日韩亚州综合| 久久黄色精品视频| 日韩激情av在线播放| 五月天国产在线| 欧美精品七区| 午夜一区不卡| 欧洲女同同性吃奶| 色素色在线综合| 国内av一区二区三区| 国产第一区电影| 欧美另类69xxxxx| 中文字幕第100页| 久久一区中文字幕| 91九色单男在线观看| 日本在线电影一区二区三区| 国产aaaaa毛片| 国产精品网友自拍| 国产精品福利电影| 欧美成人免费va影院高清| 久久天堂久久| 日本欧美视频在线观看| 91玉足脚交白嫩脚丫在线播放| 欧美日韩国产一二三区| 欧美日韩dvd在线观看| 黄色av电影在线观看| 国产99视频精品免费视频36| 亚洲精选91| 久久久视频6r| 777奇米成人网| ****av在线网毛片| 久久久亚洲综合网站| 日本中文一区二区三区| 中日韩一级黄色片| 精品国产91乱码一区二区三区 | 国产美女三级无套内谢| 精品国产一区二区三区四区在线观看| 韩日成人影院| 亚洲欧美一区二区原创| 国产精一区二区三区| 天天操天天干视频| 在线精品视频视频中文字幕| 亚洲精品第一| 少妇av一区二区三区无码| 久久久精品免费观看| 国产99久久九九精品无码免费| 中文字幕久热精品视频在线| 亚洲伊人精品酒店| 亚洲熟妇无码一区二区三区导航| 国产精品一区2区| 狠狠躁夜夜躁人人爽天天高潮| 日韩精品一区二区三区中文不卡 | 妖精视频一区二区三区| 一区二区成人网| 亚洲丶国产丶欧美一区二区三区| 性生交大片免费看女人按摩| 欧美在线视频一区二区| 久久久久国产精品| 国产精品无码专区| 91麻豆精品91久久久久久清纯| 国产区视频在线| 97人人模人人爽视频一区二区| 一区二区三区在线观看免费| 国产亚洲无码精品| 欧美成人精品福利| 亚洲a∨精品一区二区三区导航|