工作中 Redis 常用的 10 種場景
大家好,我是蘇三,又跟大家見面了。
前言
Redis 作爲一種優秀的基於 key/value 的緩存,有非常不錯的性能和穩定性,無論是在工作中,還是面試中,都經常會出現。
今天這篇文章就跟大家一起聊聊,我在實際工作中使用 Redis 的 10 種場景,希望對你會有所幫助。
- 統計訪問次數
對於很多官方網站的首頁,經常會有一些統計首頁訪問次數的需求。
訪問次數只有一個字段,如果保存到數據庫中,再最後做彙總顯然有些麻煩。
該業務場景可以使用 Redis,定義一個 key,比如:OFFICIAL_INDEX_VISIT_COUNT。
在 Redis 中有 incr 命令,可以實現給 value 值加 1 操作:
incr OFFICIAL_INDEX_VISIT_COUNT
當然如果你想一次加的值大於 1,可以用 incrby 命令,例如:
incrby OFFICIAL_INDEX_VISIT_COUNT 5
這樣可以一次性加 5。
- 獲取分類樹
在很多網站都有分類樹的功能,如果沒有生成靜態的 html 頁面,想通過調用接口的方式獲取分類樹的數據。
我們一般爲了性能考慮,會將分類樹的 json 數據緩存到 Redis 當中,爲了後面在網站當中能夠快速獲取數據。
不然在接口中需要使用遞歸查詢數據庫,然後拼接成分類樹的數據結構。
這個過程非常麻煩,而且需要多次查詢數據庫,性能很差。
因此,可以考慮用一個定時任務,異步將分類樹的數據,直接緩存到 Redis 當中,定義一個 key,比如:MALL_CATEGORY_TREE。
然後接口中直接使用 MALL_CATEGORY_TREE 這個 key 從緩存中獲取數據即可。
可以直接用 key/value 字符串保存數據。
不過需要注意的是,如果分類樹的數據非常多可能會出現大 key 的問題,優化方案可以參考我的另外一篇文章《分類樹,我從 2s 優化到 0.1s》。
- 做分佈式鎖
分佈式鎖可能是使用 Redis 最常見的場景之一,相對於其他的分佈式鎖,比如:數據庫分佈式鎖或者 Zookeeper 分佈式鎖,基於 Redis 的分佈式鎖,有更好的性能,被廣泛使用於實際工作中。
我們使用下面這段代碼可以加鎖:
try{
String result = jedis.set(lockKey, requestId, "NX", "PX", expireTime);
if ("OK".equals(result)) {
return true;
}
return false;
} finally {
unlock(lockKey);
}
但上面這段代碼在有些場景下,會有一些問題,釋放鎖可能會釋放了別人的鎖。
說實話 Redis 分佈式鎖雖說很常用,但坑也挺多的,如果用不好的話,很容易踩坑。
如果大家對 Redis 分佈式鎖的一些坑比較感興趣,可以看看我的另一篇文章《聊聊 redis 分佈式鎖的 8 大坑》,文章中有非常詳細的介紹。
- 做排行榜
很多網站有排行榜的功能,比如:商城中有商品銷量的排行榜,遊戲網站有玩家獲得積分的排行榜。
通常情況下,我們可以使用Sorted Set
保存排行榜的數據。
使用ZADD
可以添加排行榜的數據,使用ZRANGE
可以獲取排行榜的數據。
例如:
ZADD rank:score 100 "周星馳"
ZADD rank:score 90 "周杰倫"
ZADD rank:score 80 "周潤發"
ZRANGE rank:score 0 -1 WITHSCORES
返回數據:
1) "周星馳"
2) "100"
3) "周杰倫"
4) "90"
5) "周潤發"
6) "80"
- 記錄用戶登錄狀態
通常下,用戶登錄成功之後,用戶登錄之後的狀態信息,會保存到 Redis 中。
這樣後面該用戶訪問其他接口的時候,會直接從 Redis 中查詢用戶登錄狀態,如果可以查到數據,說明用戶已登錄,則允許做後續的操作。
如果從 Redis 中沒有查到用戶登錄狀態,說明該用戶沒有登錄,或者登錄狀態失效了,則直接跳轉到用戶登錄頁面。
使用 Redis 保存用戶登錄狀態,有個好處是它可以設置一個過期時間,比如:該時間可以設置成 30 分鐘。
jedis.set(userId, userInfo, 1800);
在 Redis 內部有專門的 job,會將過期的數據刪除,也有獲取數據時實時刪除的邏輯。
- 限流
使用 Redis 還有一個非常常用的的業務場景是做限流
。
當然還有其他的限流方式,比如:使用 nginx,但使用 Redis 控制可以更精細。
比如:限制同一個 ip,1 分鐘之內只能訪問 10 次接口,10 分鐘之內只能訪問 50 次接口,1 天之內只能訪問 100 次接口。
如果超過次數,則接口直接返回:請求太頻繁了,請稍後重試。
跟上面保存用戶登錄狀態類似,需要在 Redis 中保存用戶的請求記錄。
比如:key 是用戶 ip,value 是訪問的次數從 1 開始,後面每訪問一次則加 1。
如果 value 超過一定的次數,則直接攔截這種異常的 ip。
當然也需要設置一個過期時間,異常 ip 如果超過這個過期時間,比如:1 天,則恢復正常了,該 ip 可以再發起請求了。
或者限制同一個用戶 id。
- 位統計
比如現在有個需求:有個網站需要統計一週內連續登陸的用戶,以及一個月內登陸過的用戶。
這個需求使用傳統的數據庫,實現起來比較麻煩,但使用 Redis 的bitmap
讓我們可以實時的進行類似的統計。
bitmap 是二進制的 byte 數組,也可以簡單理解成是一個普通字符串。它將二進制數據存儲在 byte 數組中以達到存儲數據的目的。
保存數據命令使用 setbit,語法:
setbit key offset value
具體示例:
setbit user:view:2024-01-17 123456 1
往 bitmap 數組中設置了用戶 id=123456 的登錄狀態爲 1,標記 2024-01-17 已登錄。
然後通過命令 getbit 獲取數據,語法:
getbit key offset
具體示例:
getbit user:view:2024-01-17 123456
如果獲取的值是 1,說明這一天登錄了。
如果我們想統計一週內連續登錄的用戶,只需要遍歷用戶 id,根據日期中數組中去查詢狀態即可。
- 緩存加速
我們在工作中使用 Redis 作爲緩存加速,這種用法也是非常常見的。
如果查詢訂單數據,先從 Redis 緩存中查詢,如果緩存中存在,則直接將數據返回給用戶。
如果緩存中不存在,則再從數據庫中查詢數據,如果數據存在,則將數據保存到緩存中,然後再返回給用戶。
如果緩存和數據庫都不存在,則直接給用戶返回數據不存在。
流程圖如下:
- 做消息隊列
我們說起隊列經常想到是:kafka、rabbitMQ、RocketMQ 等這些分佈式消息隊列。
其實 Redis 也有消息隊列的功能,我們之前有個支付系統,就是用的 Redis 隊列功能。
PubSub(發佈訂閱) 是 Redis2.0 版本引入的消息傳遞模型。
顧名思義,消費者可以訂閱一個或多個 channel,生產者向對應 channel 發送消息後,所有訂閱者都能收到相關消息。對應 channel 發送消息後,所有訂閱者都能收到相關消息。
在 java 代碼中可以實現 MessageListener 接口,來消費隊列中的消息。
@Slf4j
@Component
public class RedisMessageListenerListener implements MessageListener {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Override
public void onMessage(Message message, byte[] pattern) {
String channel = new String(pattern);
RedisSerializer<?> valueSerializer = redisTemplate.getValueSerializer();
Object deserialize = valueSerializer.deserialize(message.getBody());
if (deserialize == null) return;
String md5DigestAsHex = DigestUtils.md5DigestAsHex(deserialize.toString().getBytes(StandardCharsets.UTF_8));
Boolean result = redisTemplate.opsForValue().setIfAbsent(md5DigestAsHex, "1", 20, TimeUnit.SECONDS);
if (Boolean.TRUE.equals(result)) {
log.info("接收的結果:{}", deserialize.toString());
} else {
log.info("其他服務處理中");
}
}
}
- 生成全局 ID
在有些需要生成全局 ID 的業務場景,其實也可以使用 Redis。
可以使用 incrby 命令,利用原子性操作,可以執行下面這個命令:
incrby userid 10000
在分庫分表的場景,對於有些批量操作,我們可以從 Redis 中,一次性拿一批 id 出來,然後給業務系統使用。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/Y7zPwX4_AsPDIg8vcdzAcA