使用redis的increment()方法實(shí)現(xiàn)計(jì)數(shù)器功能案例
一直知道redis可以用來實(shí)現(xiàn)計(jì)數(shù)器功能,但是之前沒有實(shí)際使用過,昨天碰到一個(gè)需求:用戶掃碼當(dāng)天達(dá)到20次即提示:當(dāng)日掃碼次數(shù)達(dá)到上限!
當(dāng)時(shí)就想到使用redis的遞增方法increment()來實(shí)現(xiàn)計(jì)數(shù)器功能,一定要注意redisTemplate和stringRedisTemplate的使用
首先設(shè)置key:
該key我使用了用戶id和當(dāng)天日期作為key的一部分,date:xxxx-xx-xx格式,這樣一來該用戶在第二天掃碼的時(shí)候又是一個(gè)新key,因?yàn)槿掌诓煌?/p>
設(shè)置key的過期時(shí)間:
實(shí)現(xiàn)計(jì)數(shù)器功能:
通過使用上面的方法,redis的計(jì)數(shù)器功能就可以實(shí)現(xiàn)了。
在使用過程過遇到的問題:
在使用的過程中,老是會(huì)拋錯(cuò):ERR value is not an integer or out of range
后來發(fā)現(xiàn)當(dāng)時(shí)我使用的方法底層用的redisTemplate和stringRedisTemplate串了,當(dāng)時(shí)setKey的時(shí)候用的方法底層是
stringRedisTemplate,后面我想get(key)的時(shí)候方法底層的模板使用的是redisTemplate,后面統(tǒng)一了一下模板的使用,然后計(jì)數(shù)
器功能正常運(yùn)行不再拋錯(cuò)。
看過很多文章說是序列化器的鍋,increment方法必須是stringRedisTemplate模板才能使用,但是我在實(shí)際使用的時(shí)候也確實(shí)是
用了redisTemplate,這個(gè)具體原因我還在看,此次使用中最主要的問題是setKey的時(shí)候使用的模板和取key的時(shí)候使用的模板不
一致導(dǎo)致的。寫個(gè)筆記記錄一下,一個(gè)坑不踩第二遍。大家如果遇到一樣的問題可以一起討論學(xué)習(xí)一下。
補(bǔ)充知識:認(rèn)識redis:redis計(jì)數(shù)器與數(shù)量控制
這篇文章是我個(gè)人對redis的一些理解,可以幫助大家系統(tǒng)的認(rèn)識redis。本文的目標(biāo)讀者是使用過redis,但對redis了解不深的朋友。文章內(nèi)容以redis為主,也會(huì)少量提到memcached。文章從redis的設(shè)計(jì)目的、工作模式、應(yīng)用場景等方面闡述,最后會(huì)講解一些具體的應(yīng)用場景,還會(huì)夾帶一些代碼作為“干貨”。
鑒于本人水平有限,文中如有不準(zhǔn)確的內(nèi)容,敬請斧正。
redis是什么
redis是一種內(nèi)存型的數(shù)據(jù)存儲器,使用場景
數(shù)據(jù)庫
緩存
消息代理(message broker)
同memcached對比
memcached是分布式的內(nèi)存對象緩存系統(tǒng),設(shè)計(jì)意圖為通過緩解數(shù)據(jù)庫壓力來加快web應(yīng)用的響應(yīng)速度。
上面的描述分別被放在了redis和memcached的官網(wǎng)首頁最顯眼的位置,這也回答了redis和memcached的本質(zhì)區(qū)別。
redis的工作模式
redis的工作模式為單進(jìn)程,這意味著redis只能利用到一個(gè)cpu內(nèi)核。 redis對請求的處理是串行的,對于同時(shí)涌進(jìn)來的多個(gè)請求,redis首先把請求存入隊(duì)列,按請求到達(dá)的先后順序串行處理。
了解單進(jìn)程和串行這兩個(gè)特點(diǎn),有助于我們使用redis時(shí)“揚(yáng)長避短”。
之前有同事提到過,為何redis不適合存儲大塊的數(shù)據(jù)?從redis的工作模式我們可以窺知一二:大塊的數(shù)據(jù)意味著需要較長的io時(shí)間,包括內(nèi)存io和網(wǎng)絡(luò)的io,cpu資源在io過程中是一直被占用的,這會(huì)阻塞其它請求,從而影響redis的整體性能。
數(shù)據(jù)類型
大家對redis的數(shù)據(jù)類型已經(jīng)比較熟悉了,主要有以下5種。
string
list
hash
set
sorted set
各種數(shù)據(jù)類型的常用操作
string
set,get,setnx,setex,psetex
拼接
增加、減少(整數(shù)型字符串)
位操作
list
入列,出列(這兩個(gè)命令都有阻塞模式)
按排位獲取部分元素
hash
設(shè)置某個(gè)索引
獲取某個(gè)索引
增加/減少某個(gè)索引的值(整數(shù)型字符串)
獲取所有索引的值
set
集合是一個(gè)數(shù)學(xué)概念,啰嗦提一下:集合中的元素都是唯一的
加入元素
刪除元素
檢查元素是否在集合
獲取集合中的元素?cái)?shù)量
取差集/并集/交集
元素轉(zhuǎn)移(從集合a移至集合b)
zset
有序集合,每個(gè)元素都有一個(gè)分值,用于對元素進(jìn)行排序
取交集/并集
獲取一個(gè)元素的rank
獲取分值在某個(gè)范圍內(nèi)的元素?cái)?shù)量
獲取分值在某個(gè)范圍內(nèi)的元素
按rank范圍獲取元素
redis事務(wù)
redis事務(wù)和我們熟悉的mysql事務(wù)有所區(qū)別,它們的相同在于都是對一個(gè)或一組命令的打包執(zhí)行,不同的地方在于redis事務(wù)不可回滾。
redis的事務(wù)具有原子性,一個(gè)事務(wù)的執(zhí)行有兩種結(jié)果
完全執(zhí)行
完全不執(zhí)行
一些不可抗力因素除外,如服務(wù)掛掉,服務(wù)器斷電。redis事務(wù)是一個(gè)獨(dú)立的請求,執(zhí)行過程中會(huì)阻塞其它請求。
實(shí)現(xiàn)redis事務(wù)有以下兩種方式
multi-exec
lua腳本
multi-exec
127.0.0.1:6380[1]> set counter1 1 OK 127.0.0.1:6380[1]> set counter2 2 OK 127.0.0.1:6380[1]> set counter3 3 OK 127.0.0.1:6380[1]> 127.0.0.1:6380[1]> 127.0.0.1:6380[1]> multi OK 127.0.0.1:6380[1]> incr counter1 QUEUED 127.0.0.1:6380[1]> sadd counter2 1 QUEUED 127.0.0.1:6380[1]> incr counter3 QUEUED 127.0.0.1:6380[1]> exec 1) (integer) 2 2) (error) WRONGTYPE Operation against a key holding the wrong kind of value 3) (integer) 4
從上面的事務(wù)執(zhí)行輸出可以看到,我們的sadd執(zhí)行出錯(cuò)了,原因是操作的數(shù)據(jù)類型不正確,但redis沒有中止整個(gè)事務(wù),而是繼續(xù)往下執(zhí)行。redis這么做是有道理的,見參考文獻(xiàn)1。
multi-exec和watch的組合可以實(shí)現(xiàn)類似于mysql事務(wù)的功能,如果被watch的key在事務(wù)執(zhí)行前被修改了,redis會(huì)放棄執(zhí)行事務(wù)。
lua腳本
和multi-exec相比,lua腳本的優(yōu)勢在于靈活性,也可以減少一定的網(wǎng)絡(luò)時(shí)間消耗(為什么?)。
鑒于redis的工作模式,不建議用lua腳本實(shí)現(xiàn)耗時(shí)較長的事務(wù)。
下面模擬了lua腳本的執(zhí)行阻塞其它請求的場景,大家可以親自試一下。
client 1
eval "local i=0; while(i<1000000) do redis.call('keys', '*'); i=i+1; end" 0
client 2
incr counter
redis的實(shí)際應(yīng)用
鎖
類型:string
命令:setnx name alice |set name alice NX
返回true則鎖定成功,否則鎖定失敗
了解了redis的工作模式,就知道為什么用redis實(shí)現(xiàn)鎖是如此容易了。用memcache也可以實(shí)現(xiàn)鎖,但代碼層面要復(fù)雜一些,參見memcached.cas。
事件隊(duì)列
類型:zset
命令:zadd,zrangebyscore,zrem
適合存儲一些需要順序處理的事件,將事件的score值設(shè)為時(shí)間戳或自增id即可。為什么用list不可以?
計(jì)數(shù)器
類型:string
命令:incr
抽獎(jiǎng)限額
類型:string
命令:incrby
某活動(dòng)的現(xiàn)金紅包每天最多只能發(fā)送10000元。下面是這段邏輯的偽代碼,如果能夠舉一反三,這段代碼將大有用武之地。大家可以用并發(fā)測試工具測試這段代碼,如果發(fā)現(xiàn)了bug,或者能有更好的實(shí)現(xiàn)方式,請不要告訴我 -_-
$key = 'max_amount'; $amountLimit = 10000; if (!$currAmount = Redis::get($key)) { $currAmount = 9990; // 從持久化數(shù)據(jù)庫獲取當(dāng)前已發(fā)放金額 // 初始化 Redis::setnx($key, $currAmount); } if ($currAmount >= $amountLimit) { // 超出限額退出 } // 抽獎(jiǎng)金額 $rewardAmount = 10; if ($rewardAmount > $amountLimit - $currAmount) { $rewardAmount = $amountLimit - $currAmount; } if (Redis::incrby($key, $rewardAmount) > $amountLimit) { // 超出限額退出 } else { // 成功抽獎(jiǎng) }
初始化為何用setnx,用set可以嗎?
最后為何使用incrby,不用可以嗎?
總結(jié)
文章內(nèi)容不多,所謂的干貨更少,這和作者的水平有關(guān),也和文章的定位有關(guān)。細(xì)心的朋友可能發(fā)現(xiàn)了,文中有一些內(nèi)容是帶有問號的,有興趣的朋友可以加以思考。希望能給大家一個(gè)參考,也希望大家多多支持腳本之家
相關(guān)文章
SpringBoot去除內(nèi)嵌tomcat的實(shí)現(xiàn)
這篇文章主要介紹了SpringBoot去除內(nèi)嵌tomcat的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09Java實(shí)現(xiàn)微信網(wǎng)頁授權(quán)的示例代碼
這篇文章主要介紹了Java實(shí)現(xiàn)微信網(wǎng)頁授權(quán)的示例代碼,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-07-07Java編程實(shí)現(xiàn)統(tǒng)計(jì)一個(gè)字符串中各個(gè)字符出現(xiàn)次數(shù)的方法
這篇文章主要介紹了Java編程實(shí)現(xiàn)統(tǒng)計(jì)一個(gè)字符串中各個(gè)字符出現(xiàn)次數(shù)的方法,涉及java針對字符串的遍歷、判斷、運(yùn)算等相關(guān)操作技巧,需要的朋友可以參考下2017-12-12sharding-jdbc 兼容 MybatisPlus動(dòng)態(tài)數(shù)據(jù)源的配置方法
這篇文章主要介紹了sharding-jdbc 兼容 MybatisPlus動(dòng)態(tài)數(shù)據(jù)源的配置方法,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2024-07-07教你怎么用java實(shí)現(xiàn)客戶端與服務(wù)器一問一答
這篇文章主要介紹了教你怎么用java實(shí)現(xiàn)客戶端與服務(wù)器一問一答,文中有非常詳細(xì)的代碼示例,對正在學(xué)習(xí)java的小伙伴們有非常好的幫助,需要的朋友可以參考下2021-04-04JDBC如何訪問MySQL數(shù)據(jù)庫,并增刪查改
這篇文章主要介紹了JDBC如何訪問MySQL數(shù)據(jù)庫,幫助大家更好的理解和學(xué)習(xí)java與MySQL,感興趣的朋友可以了解下2020-08-08JavaFX如何獲取ListView(列表視圖)的選項(xiàng)
這篇文章主要介紹了JavaFX如何獲取ListView(列表視圖)的選項(xiàng),具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01Java Druid連接池與Apache的DBUtils使用教程
這篇文章主要介紹了Java Druid連接池與Apache的DBUtils使用方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-12-12SpringBoot之Helloword 快速搭建一個(gè)web項(xiàng)目(圖文)
這篇文章主要介紹了SpringBoot之Helloword 快速搭建一個(gè)web項(xiàng)目(圖文),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-12-12