Redis 如何批量設(shè)置過期時間(PIPLINE的使用)
合理的使用緩存策略對開發(fā)同學來講,就好像孫悟空習得自在極意功一般~
Redis如何批量設(shè)置過期時間呢?
不要說在foreach中通過set()函數(shù)批量設(shè)置過期時間
我們引入redis的PIPLINE,來解決批量設(shè)置過期時間的問題。
PIPLINE的原理是什么?
未使用pipline執(zhí)行N條命令

使用pipline執(zhí)行N條命令

通過圖例可以很明顯的看出來PIPLINE的原理:
客戶端通過PIPLINE拼接子命令,只需要發(fā)送一次請求,在redis收到PIPLINE命令后,處理PIPLINE組成的命令塊,減少了網(wǎng)絡(luò)請求響應次數(shù)。
網(wǎng)絡(luò)延遲越大PIPLINE的優(yōu)勢越能體現(xiàn)出來
拼接的子命令條數(shù)越多使用PIPLINE的優(yōu)勢越能體現(xiàn)出來
注意:并不是拼接的子命令越多越好,N值也有是上限的,當拼接命令過長時會導致客戶端等待很長時間,造成網(wǎng)絡(luò)堵塞;我們可以根據(jù)實際情況,把大批量命令拆分成幾個PIPLINE執(zhí)行。
代碼封裝
//批量設(shè)置過期時間
public static function myPut(array $data, $ttl = 0)
{
if (empty($data)) {
return false;
}
$pipeline = Redis::connection('cache')
->multi(\Redis::PIPELINE);
foreach ($data as $key => $value) {
if (empty($value)) {
continue;
}
if ($ttl == 0) {
$pipeline->set(trim($key), $value);
} else {
$pipeline->set(trim($key), $value, $ttl);
}
}
$pipeline->exec();
}
項目實戰(zhàn)
需求描述
- 打開APP,給喜歡我的人發(fā)送我的上線通知(為了避免打擾,8小時內(nèi)重復登錄不觸發(fā)通知)
- 每個人每半小時只會收到一次這類上線通知(即半小時內(nèi)就算我喜歡的1萬人都上線了,我也只收到一次喜歡的人上線通知)
要點分析
- 合理使用緩存,減少DB讀寫次數(shù)
- 不僅要減少DB讀寫次數(shù),也要減少Redis的讀寫次數(shù),使用PIPLINE
代碼實現(xiàn)解析
- canRecall() 寫的比較優(yōu)雅,先判斷是否已發(fā)送的標記,再判斷HouseOpen::getCurrentOpen(),因為HouseOpen::getCurrentOpen()是要查詢DB計算的,這種代碼要盡可能少的被執(zhí)行到,減少DB查詢。
- array_diff() 取差集的思路,獲得需要推送的人
封裝工具類
<?php
namespace App\Model\House;
.
.
.
class HouseLikeRecallUser
{
protected $_userid = '';
protected $_availableUser = [];
protected $_recallFlagKey = '';
const TYPE_TTL_HOUSE_LIKE_RECALL = 60 * 30; //半小時后可以再次接收到喜歡的xxx進入通知
const TYPE_TTL_HOUSE_LIKE_RECALL_FLAG = 60 * 60 * 8; //8小時重復登錄不觸發(fā)
//初始化 傳入setRecalled 的過期時間
public function __construct($userid)
{
$this->_userid = $userid;
//登錄后給喜歡我的人推送校驗:同一場次重復登錄不重復發(fā)送
$this->_recallFlagKey = CacheKey::getCacheKey(CacheKey::TYPE_HOUSE_LIKE_RECALL_FLAG, $this->_userid);
}
//設(shè)置當前用戶推送標示
public function setRecalled()
{
Cache::put($this->_recallFlagKey, 1, self::TYPE_TTL_HOUSE_LIKE_RECALL_FLAG);
}
//獲取當前用戶是否觸發(fā)推送
public function canRecall()
{
$res = false;
if (empty(Cache::get($this->_recallFlagKey))) {
$houseOpen = HouseOpen::getCurrentOpen();
if ($houseOpen['status'] == HouseOpen::HOUSE_STATUS_OPEN) {
$res = true;
}
}
return $res;
}
//獲取需要推送用戶
public function getAvailableUser()
{
//獲得最近喜歡我的用戶
$recentLikeMeUser = UserRelationSingle::getLikeMeUserIds($this->_userid, 100, Utility::getBeforeNDayTimestamp(7));
//獲得最近喜歡我的用戶的 RECALL緩存標記
foreach ($recentLikeMeUser as $userid) {
$batchKey[] = CacheKey::getCacheKey(CacheKey::TYPE_HOUSE_LIKE_RECALL, $userid);
}
//獲得最近喜歡我的且已經(jīng)推送過的用戶
$cacheData = [];
if (!empty($batchKey)) {
$cacheData = Redis::connection('cache')->mget($batchKey);
}
//計算最近喜歡我的用戶 和 已經(jīng)推送過的用戶 的差集:就是需要推送的用戶
$this->_availableUser = array_diff($recentLikeMeUser, $cacheData);
return $this->_availableUser;
}
//更新已經(jīng)推送的用戶
public function updateRecalledUser()
{
//批量更新差集用戶
$recalledUser = [];
foreach ($this->_availableUser as $userid) {
$cacheKey = CacheKey::getCacheKey(CacheKey::TYPE_HOUSE_LIKE_RECALL, $userid);
$recalledUser[$cacheKey] = $userid;
}
//批量更新 設(shè)置過期時間
self::myPut($recalledUser, self::TYPE_TTL_HOUSE_LIKE_RECALL);
}
//批量設(shè)置過期時間
public static function myPut(array $data, $ttl = 0)
{
if (empty($data)) {
return false;
}
$pipeline = Redis::connection('cache')
->multi(\Redis::PIPELINE);
foreach ($data as $key => $value) {
if (empty($value)) {
continue;
}
if ($ttl == 0) {
$pipeline->set(trim($key), $value);
} else {
$pipeline->set(trim($key), $value, $ttl);
}
}
$pipeline->exec();
}
}
調(diào)用工具類
public function handle()
{
$userid = $this->_userid;
$houseLikeRecallUser = new HouseLikeRecallUser($userid);
if ($houseLikeRecallUser->canRecall()) {
$recallUserIds = $houseLikeRecallUser->getAvailableUser();
$houseLikeRecallUser->setRecalled();
$houseLikeRecallUser->updateRecalledUser();
//群發(fā)推送消息
.
.
.
}
}
總結(jié)
不同量級的數(shù)據(jù)需要不同的處理辦法,減少網(wǎng)絡(luò)請求次數(shù),合理使用緩存,是性能優(yōu)化的必經(jīng)之路。
進一步思考
如果我喜歡的1萬人同時上線(秒級并發(fā)),我只收到一個消息推送,要避免被通知轟炸,怎么解決這類并發(fā)問題呢?
到此這篇關(guān)于Redis 如何批量設(shè)置過期時間(PIPLINE的使用)的文章就介紹到這了,更多相關(guān)Redis 批量設(shè)置過期時間內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
RediSearch加RedisJSON大于Elasticsearch的搜索存儲引擎
這篇文章主要為大家介紹了RediSearch加RedisJSON大于Elasticsearch的王炸使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-07-07
RedisTemplate 實現(xiàn)基于Value 操作的簡易鎖機制(示例代碼)
本文將介紹如何使用 RedisTemplate 的 opsForValue().setIfAbsent() 方法來實現(xiàn)一種簡單的鎖機制,并提供一個示例代碼,展示如何在 Java 應用中利用這一機制來保護共享資源的訪問,感興趣的朋友跟隨小編一起看看吧2024-05-05
Redis報錯UnrecognizedPropertyException: Unrecognized 
在使用SpringBoot訪問Redis時,報錯提示識別不了屬性headPart,經(jīng)過排查,發(fā)現(xiàn)并非Serializable或getset方法問題,而是存在一個方法getHeadPart,但無headPart屬性,解決方案是將getHeadPart改為makeHeadPart2024-10-10
Redis動態(tài)熱點數(shù)據(jù)緩存策略設(shè)計
本文主要介紹了Redis動態(tài)熱點數(shù)據(jù)緩存策略設(shè)計,包括熱點數(shù)據(jù)識別、動態(tài)緩存、多級緩存、預加載機制、更新策略以及監(jiān)控告警等,具有一定的參考價值,感興趣的可以了解一下2025-01-01

