高并發(fā)場(chǎng)景分析之redis+lua防重校驗(yàn)
高并發(fā)場(chǎng)景:redis+lua防重校驗(yàn)
大家平時(shí)在做有并發(fā)量下單的項(xiàng)目時(shí),代碼層面基本上就分為這么幾個(gè)步驟:參數(shù)校驗(yàn)--->防重校驗(yàn)--->庫(kù)存校驗(yàn)扣減--->下單成功--->支付。
最近公司有個(gè)商城項(xiàng)目說(shuō)要30分鐘達(dá)到1億的并發(fā)量。當(dāng)時(shí)聽(tīng)到突然猛了一下。真是牛逼克拉斯呀。
不過(guò)該說(shuō)不說(shuō)還是得開(kāi)搞,所謂的并發(fā)無(wú)非就是要想辦法減少io的操作,盡量少查表,其余配置方面的問(wèn)題就看公司舍不舍得花錢(qián)了。
其中防重校驗(yàn)這一部分選用了redis集合lua來(lái)做,來(lái)防止多次提交。
注意:真實(shí)下單操作是分為了訂單前置操作 (前置校驗(yàn)和生成防重令牌)和提交訂單兩個(gè)接口來(lái)寫(xiě)的,這里為了測(cè)試簡(jiǎn)單合并了
@RestController @RequestMapping("/lua") public class TestLuaController { @Autowired private StringRedisTemplate redisTemplate; private static final String ORDER_CHECK_TOKEN = "order_check_token"; private static final String ORDER_TOKEN_DEFAULT_VALUE = "value"; /** * 場(chǎng)景:最近公司有高并發(fā)場(chǎng)景,涉及到用令牌防重(防止有人多次提交訂單) * 可以使用redis執(zhí)行l(wèi)ua腳本 比較key值令牌是否存在,如果存在即刪除 */ @RequestMapping("/checkTempToken") public Long luaExecute(String checkToken){ //模擬提交訂單生成的防重令牌 redisTemplate.opsForValue().set(ORDER_CHECK_TOKEN + checkToken,ORDER_TOKEN_DEFAULT_VALUE,60, TimeUnit.SECONDS); String script = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" + "then\n" + " return redis.call(\"del\",KEYS[1])\n" + "else\n" + " return 0\n" + "end"; //執(zhí)行redis腳本操作的函數(shù) Long execute = redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Arrays.asList(ORDER_CHECK_TOKEN + checkToken), ORDER_TOKEN_DEFAULT_VALUE); System.out.println(execute); return execute; } }
結(jié)果:
redis+lua語(yǔ)言去重
1)redis
redis是一種鍵值對(duì)的單線程架構(gòu)模型,所以它是線程安全的,也是分布式緩存常用的解決方案。
2)lua
lua是基于c語(yǔ)言的一種腳本語(yǔ)言,它可以很輕便地被使用在嵌入式方面。我們不會(huì)去重寫(xiě)redis,但是我們可以去使用lua來(lái)擴(kuò)展redis的功能。而redis也內(nèi)置了對(duì)lua支持的模塊。
3)redis+lua是我司基于分布式告警去重的一種解決方案,要達(dá)到的目的:堵住10是s內(nèi)的重復(fù)告警,主要應(yīng)用的場(chǎng)景是同一個(gè)告警,在不通的客戶(hù)端同時(shí)推送了一條告警數(shù)據(jù),此時(shí)要堵住
一條數(shù)據(jù)。
直接上代碼吧
@Component public class RedisLua { @Autowired private RedisTemplate<String,Object> redisTemplate; private DefaultRedisScript<Boolean> script; private static final String KEY_PREX = "alertInfo_"; @PostConstruct private void init(){ script = new DefaultRedisScript<Boolean>(); script.setResultType(Boolean.class); script.setScriptSource(new ResourceScriptSource(new ClassPathResource("lua/alert.lua")));//lua腳本文件 }public Boolean blockRepetition(String ruleId,String status,String ip,Object...args){ List<String> keys = new ArrayList<>(); keys.add(KEY_PREX + ip+ruleId+status);//key包括前綴+客戶(hù)端ip+告警規(guī)則id+狀態(tài) return redisTemplate.execute(script, keys, args); } }
下面是lua腳本
--[[ ARGV1表示redis生效時(shí)間,key表示存儲(chǔ)redis的key值]] local alert = redis.call('exists',KEYS[1]); if alert then return false else redis.call('set',KEYS[1],ngx.time()) redis.call('expire',KEYS[1],ARGV[1]) return true end
該方案主要用于微服務(wù)集群中,采用分布式鎖防和redis集群確保數(shù)據(jù)唯一。
使用腳本的好處如下:
1.減少網(wǎng)絡(luò)開(kāi)銷(xiāo):本來(lái)5次網(wǎng)絡(luò)請(qǐng)求的操作,可以用一個(gè)請(qǐng)求完成,原先5次請(qǐng)求的邏輯放在redis服務(wù)器上完成。使用腳本,減少了網(wǎng)絡(luò)往返時(shí)延。
2.原子操作:Redis會(huì)將整個(gè)腳本作為一個(gè)整體執(zhí)行,中間不會(huì)被其他命令插入。
3.復(fù)用:客戶(hù)端發(fā)送的腳本會(huì)永久存儲(chǔ)在Redis中,意味著其他客戶(hù)端可以復(fù)用這一腳本而不需要使用代碼完成同樣的邏輯。
到此這篇關(guān)于高并發(fā)場(chǎng)景分析之redis+lua防重校驗(yàn)的文章就介紹到這了,更多相關(guān)redis+lua防重內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
nestjs使用redis實(shí)現(xiàn)ip限流的步驟詳解
如果使用nestjs開(kāi)發(fā)接口并部署之后,我們通常需要考慮到接口是否會(huì)被惡意盜刷消耗過(guò)多的資源,一個(gè)簡(jiǎn)單的方式就是限制在單位時(shí)間內(nèi)的訪問(wèn)次數(shù),所以本文給大家介紹了nestjs使用redis實(shí)現(xiàn)ip限流的步驟,需要的朋友可以參考下2025-01-01Redis實(shí)現(xiàn)分布式鎖的幾種方法總結(jié)
這篇文章主要介紹了Redis實(shí)現(xiàn)分布式鎖的幾種方法總結(jié)的相關(guān)資料, Redis實(shí)現(xiàn)與Zookeeper實(shí)現(xiàn)和數(shù)據(jù)庫(kù)實(shí)現(xiàn),需要的朋友可以參考下2017-07-07Windows中Redis安裝配置流程并實(shí)現(xiàn)遠(yuǎn)程訪問(wèn)功能
很多在windows環(huán)境中安裝Redis總是出錯(cuò),今天小編抽空給大家分享在Windows中Redis安裝配置流程并實(shí)現(xiàn)遠(yuǎn)程訪問(wèn)功能,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2021-06-06Redis延遲隊(duì)列的實(shí)現(xiàn)示例
Redis 延遲隊(duì)列是一種使用 Redis 實(shí)現(xiàn)的消息隊(duì)列,本文主要介紹了Redis延遲隊(duì)列的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2025-01-01Redis實(shí)現(xiàn)布隆過(guò)濾器的代碼詳解
布隆過(guò)濾器(Bloom?Filter)是Redis?4.0版本提供的新功能,它被作為插件加載到Redis服務(wù)器中,給Redis提供強(qiáng)大的去重功能,本文將給大家詳細(xì)介紹一下Redis布隆過(guò)濾器,文中有相關(guān)的代碼示例,需要的朋友可以參考下2023-07-07redis的hGetAll函數(shù)的性能問(wèn)題(記Redis那坑人的HGETALL)
這篇文章主要介紹了redis的hGetAll函數(shù)的性能問(wèn)題,需要的朋友可以參考下2016-02-02redis cluster集群模式下實(shí)現(xiàn)批量可重入鎖
本文主要介紹了使用redis cluster集群版所遇到的問(wèn)題解決方案及redis可重入鎖是否會(huì)有死鎖的問(wèn)題等,具有一定的參考價(jià)值,感興趣的可以了解一下2024-02-02Redis 通過(guò) RDB 方式進(jìn)行數(shù)據(jù)備份與還原的方法
這篇文章主要介紹了Redis 通過(guò) RDB 方式進(jìn)行數(shù)據(jù)備份與還原,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-03-03Redis基本數(shù)據(jù)類(lèi)型String常用操作命令
這篇文章主要為大家介紹了Redis基本數(shù)據(jù)類(lèi)型String常用操作命令,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05