合理的使用缓存策略对开发同学来讲,就好像孙悟空习得自在极意功一般~
redis如何批量设置过期时间呢?
不要说在foreach中通过set()函数批量设置过期时间
我们引入redis的pipline,来解决批量设置过期时间的问题。
pipline的原理是什么?
未使用pipline执行n条命令
使用pipline执行n条命令
通过图例可以很明显的看出来pipline的原理:
客户端通过pipline拼接子命令,只需要发送一次请求,在redis收到pipline命令后,处理pipline组成的命令块,减少了网络请求响应次数。
网络延迟越大pipline的优势越能体现出来
拼接的子命令条数越多使用pipline的优势越能体现出来
注意:并不是拼接的子命令越多越好,n值也有是上限的,当拼接命令过长时会导致客户端等待很长时间,造成网络堵塞;我们可以根据实际情况,把大批量命令拆分成几个pipline执行。
代码封装
//批量设置过期时间 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(); }
项目实战
需求描述
- 打开app,给喜欢我的人发送我的上线通知(为了避免打扰,8小时内重复登录不触发通知)
- 每个人每半小时只会收到一次这类上线通知(即半小时内就算我喜欢的1万人都上线了,我也只收到一次喜欢的人上线通知)
要点分析
- 合理使用缓存,减少db读写次数
- 不仅要减少db读写次数,也要减少redis的读写次数,使用pipline
代码实现解析
- canrecall() 写的比较优雅,先判断是否已发送的标记,再判断houseopen::getcurrentopen(),因为houseopen::getcurrentopen()是要查询db计算的,这种代码要尽可能少的被执行到,减少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小时重复登录不触发 //初始化 传入setrecalled 的过期时间 public function __construct($userid) { $this->_userid = $userid; //登录后给喜欢我的人推送校验:同一场次重复登录不重复发送 $this->_recallflagkey = cachekey::getcachekey(cachekey::type_house_like_recall_flag, $this->_userid); } //设置当前用户推送标示 public function setrecalled() { cache::put($this->_recallflagkey, 1, self::type_ttl_house_like_recall_flag); } //获取当前用户是否触发推送 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); } //获得最近喜欢我的且已经推送过的用户 $cachedata = []; if (!empty($batchkey)) { $cachedata = redis::connection('cache')->mget($batchkey); } //计算最近喜欢我的用户 和 已经推送过的用户 的差集:就是需要推送的用户 $this->_availableuser = array_diff($recentlikemeuser, $cachedata); return $this->_availableuser; } //更新已经推送的用户 public function updaterecalleduser() { //批量更新差集用户 $recalleduser = []; foreach ($this->_availableuser as $userid) { $cachekey = cachekey::getcachekey(cachekey::type_house_like_recall, $userid); $recalleduser[$cachekey] = $userid; } //批量更新 设置过期时间 self::myput($recalleduser, self::type_ttl_house_like_recall); } //批量设置过期时间 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(); } }
调用工具类
public function handle() { $userid = $this->_userid; $houselikerecalluser = new houselikerecalluser($userid); if ($houselikerecalluser->canrecall()) { $recalluserids = $houselikerecalluser->getavailableuser(); $houselikerecalluser->setrecalled(); $houselikerecalluser->updaterecalleduser(); //群发推送消息 . . . } }
总结
不同量级的数据需要不同的处理办法,减少网络请求次数,合理使用缓存,是性能优化的必经之路。
进一步思考
如果我喜欢的1万人同时上线(秒级并发),我只收到一个消息推送,要避免被通知轰炸,怎么解决这类并发问题呢?
到此这篇关于redis 如何批量设置过期时间(pipline的使用)的文章就介绍到这了,更多相关redis 批量设置过期时间内容请搜索www.887551.com以前的文章或继续浏览下面的相关文章希望大家以后多多支持www.887551.com!