淺談Redis如何應(yīng)對并發(fā)訪問
前言
項(xiàng)目中經(jīng)常會遇到這種場景,我們需要先將Redis數(shù)據(jù)讀取到本地,然后進(jìn)行修改,修改完成后在將數(shù)據(jù)寫回Redis,這種讀取-修改-寫回操作,我們稱之為RMW操作。當(dāng)有多個(gè)客戶端對同一份數(shù)據(jù)執(zhí)行RMW操作的話,Redis如何保證RMW操作涉及的代碼以原子性方式執(zhí)行?

原子性操作
Redis的原子性操作是一種無鎖操作,即可以保證并發(fā)控制,還能減少系統(tǒng)對并發(fā)性能的影響,
單命令模式
把Redis多個(gè)操作實(shí)現(xiàn)成一個(gè)操作,即為單命令模式。
Redis提供了INCR/DECR命令,可以對數(shù)據(jù)進(jìn)行增值/減值操作,而且它們本身就是單個(gè)命令操作,Redis單線程模式,執(zhí)行命令時(shí)具有互斥性。
示例說明
public Long getIncrNumber(String key,long alive)
{
RedisAtomicLong entityIdCounter = new RedisAtomicLong(key, redisTemplate.getConnectionFactory());
Long incrNum = entityIdCounter.getAndIncrement();
if (null == incrNum || incrNum.longValue()==0)
{
entityIdCounter.expire(alive,TimeUnit.MILLISECONDS);
incrNum = entityIdCounter.getAndIncrement();
}
return incrNum;
}說明:采用Reids的INCR命令,如果不存在的key則設(shè)置過期時(shí)間,如果key存在則進(jìn)行遞增操作返回。所以如果我們執(zhí)行RMW操作進(jìn)行相關(guān)的遞增或者遞減操作時(shí),Redis提供的INCR和DECY命令可以保證并發(fā)控制。
多命令模式
當(dāng)我們不是執(zhí)行簡單的加加減減操作,而是更加復(fù)雜的邏輯判斷或者其他操作時(shí),Redis是無法保證原子性,所以需要將多個(gè)操作寫到一個(gè)Lua腳本中,Redis會把Lua腳本作為一個(gè)整體執(zhí)行,在執(zhí)行過程中不會被其他命令打斷,從而保證了操作的原子性。
lua簡介
Lua是一種輕量小巧的腳本語言,用標(biāo)準(zhǔn)C語言編寫并以源代碼形式開放, 其設(shè)計(jì)目的是為了嵌入應(yīng)用程序中,從而為應(yīng)用程序提供靈活的擴(kuò)展和定制功能。
示例說明
接口進(jìn)行限流操作,同一用戶3秒內(nèi)不能重復(fù)訪問,我們可以通過lua腳本來實(shí)現(xiàn)。
local key = KEYS[1]
local count = tonumber(ARGV[1])
local time = tonumber(ARGV[2])
local current = redis.call('get', key)
if current and tonumber(current) > count then
return tonumber(current)
end
current = redis.call('incr', key)
if tonumber(current) == 1 then
redis.call('expire', key, time)
end
return tonumber(current)說明:key、count、time為三個(gè)傳入?yún)?shù),分別代表Redis的key、次數(shù)和過期時(shí)間。通過get獲取key對應(yīng)的值,獲取的值為時(shí)間內(nèi)訪問接口的次數(shù),如果為第一次訪問則返回的為null,此時(shí)需要對當(dāng)前key進(jìn)行自增1操作,如果返回為數(shù)字,則需要判斷返回的數(shù)字是否已經(jīng)超過了cout值,如果超過說明已經(jīng)超過限流了,直接返回。
建議
- 1.編寫lua腳本不要進(jìn)行復(fù)雜耗時(shí)的計(jì)算邏輯,否則執(zhí)行l(wèi)ua時(shí)間過長,會導(dǎo)致Redis主線程阻塞。
- 2.lua腳本盡量簡單,不要把所有的操作都放入到lua腳本中執(zhí)行,這樣會導(dǎo)致Redis執(zhí)行腳本的時(shí)間增加,同時(shí)也會降低Redis的并發(fā)性能。
事務(wù)
關(guān)于事務(wù)保證原子性,采用的watch命令其原理和樂觀鎖的實(shí)現(xiàn)原理類似,詳情可以參考juejin.cn/post/712582… 文章,本文就不在具體闡述。
加鎖
關(guān)于Redis的分布式鎖的實(shí)現(xiàn),后續(xù)的章節(jié)進(jìn)行詳情說明。高并發(fā)環(huán)境下加鎖雖然能夠保證正確性,但是也會帶來其他的問題:
- 加鎖操作過多會降低系統(tǒng)的并發(fā)訪問性能
- Redis客戶端要加鎖,需要使用分布式鎖,而分布式鎖實(shí)現(xiàn)復(fù)雜,增加復(fù)雜度。
總結(jié)
本文講解了針對并發(fā)訪問Redis如何保證原子性操作,針對不同的業(yè)務(wù)場景,選擇合適的方案,更多相關(guān)Redis 并發(fā)訪問內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Redis權(quán)限和訪問控制的實(shí)現(xiàn)示例
Redis提供了一些機(jī)制來保護(hù)敏感數(shù)據(jù)和限制對Redis服務(wù)器的訪問,本文主要介紹了Redis權(quán)限和訪問控制的實(shí)現(xiàn)示例,具有一定的參考價(jià)值,感興趣的可以了解一下2023-12-12
Redis序列化反序列化不一致導(dǎo)致String類型值多了雙引號問題
這篇文章主要介紹了Redis序列化反序列化不一致導(dǎo)致String類型值多了雙引號問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-08-08
windows環(huán)境下Redis+Spring緩存實(shí)例講解
這篇文章主要為大家詳細(xì)介紹了windows環(huán)境下Redis+Spring緩存實(shí)例教程,感興趣的小伙伴們可以參考一下2016-04-04
redis延時(shí)隊(duì)列的項(xiàng)目實(shí)踐
本文主要介紹了redis延時(shí)隊(duì)列的項(xiàng)目實(shí)踐,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-11-11
gem install redis報(bào)錯(cuò)的解決方案
今天小編就為大家分享一篇關(guān)于gem install redis報(bào)錯(cuò)的解決方案,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2019-01-01
如何提高Redis服務(wù)器的最大打開文件數(shù)限制
文章討論了如何提高Redis服務(wù)器的最大打開文件數(shù)限制,以支持高并發(fā)服務(wù),本文給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2025-01-01

