網站首頁 編程語言 正文
前言
項目中經常會遇到這種場景,我們需要先將Redis數據讀取到本地,然后進行修改,修改完成后在將數據寫回Redis,這種讀取-修改-寫回操作,我們稱之為RMW操作。當有多個客戶端對同一份數據執行RMW操作的話,Redis如何保證RMW操作涉及的代碼以原子性方式執行?
原子性操作
Redis的原子性操作是一種無鎖操作,即可以保證并發控制,還能減少系統對并發性能的影響,
單命令模式
把Redis多個操作實現成一個操作,即為單命令模式。
Redis提供了INCR/DECR命令,可以對數據進行增值/減值操作,而且它們本身就是單個命令操作,Redis單線程模式,執行命令時具有互斥性。
示例說明
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則設置過期時間,如果key存在則進行遞增操作返回。所以如果我們執行RMW操作進行相關的遞增或者遞減操作時,Redis提供的INCR和DECY命令可以保證并發控制。
多命令模式
當我們不是執行簡單的加加減減操作,而是更加復雜的邏輯判斷或者其他操作時,Redis是無法保證原子性,所以需要將多個操作寫到一個Lua腳本中,Redis會把Lua腳本作為一個整體執行,在執行過程中不會被其他命令打斷,從而保證了操作的原子性。
lua簡介
Lua是一種輕量小巧的腳本語言,用標準C語言編寫并以源代碼形式開放, 其設計目的是為了嵌入應用程序中,從而為應用程序提供靈活的擴展和定制功能。
示例說明
接口進行限流操作,同一用戶3秒內不能重復訪問,我們可以通過lua腳本來實現。
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為三個傳入參數,分別代表Redis的key、次數和過期時間。通過get獲取key對應的值,獲取的值為時間內訪問接口的次數,如果為第一次訪問則返回的為null,此時需要對當前key進行自增1操作,如果返回為數字,則需要判斷返回的數字是否已經超過了cout值,如果超過說明已經超過限流了,直接返回。
建議
- 1.編寫lua腳本不要進行復雜耗時的計算邏輯,否則執行lua時間過長,會導致Redis主線程阻塞。
- 2.lua腳本盡量簡單,不要把所有的操作都放入到lua腳本中執行,這樣會導致Redis執行腳本的時間增加,同時也會降低Redis的并發性能。
事務
關于事務保證原子性,采用的watch命令其原理和樂觀鎖的實現原理類似,詳情可以參考juejin.cn/post/712582… 文章,本文就不在具體闡述。
加鎖
關于Redis的分布式鎖的實現,后續的章節進行詳情說明。高并發環境下加鎖雖然能夠保證正確性,但是也會帶來其他的問題:
- 加鎖操作過多會降低系統的并發訪問性能
- Redis客戶端要加鎖,需要使用分布式鎖,而分布式鎖實現復雜,增加復雜度。
總結
原文鏈接:https://juejin.cn/post/7126430659173679134
相關推薦
- 2023-01-18 你不知道的C++中namespace和using的用法實例_C 語言
- 2022-09-25 Linux系統CentOS的本地host查詢
- 2022-07-20 初識C語言習題以及知識點
- 2022-08-25 Python面向對象的三大特性封裝、繼承、多態_python
- 2022-07-02 C語言由淺入深講解線程的定義_C 語言
- 2022-10-01 Python利用Bokeh進行數據可視化的教程分享_python
- 2022-04-08 記一次go語言使用time.Duration類型踩過的坑_Golang
- 2022-06-12 C#集合之可觀察集合的用法_C#教程
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支