正当的应用缓存策略对开发同学来讲,就如同孙悟空习得自在极意功个别~
抛出问题
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万人都上线了,我也只收到一次喜爱的人上线告诉)
要点剖析
PIPLINE
代码实现解析
canRecall()HouseOpen::getCurrentOpen()HouseOpen::getCurrentOpen()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万人同时上线(秒级并发),我只收到一个音讯推送,要防止被告诉轰炸,怎么解决这类并发问题呢?
小伙伴们有没有解决思路,能够在评论区探讨哦~
相干浏览举荐
性能优化反思:不要在for循环中操作DB
性能优化反思:不要在for循环中操作DB 进阶版
最初
👍🏻:感觉有播种请点个赞激励一下!
🌟:珍藏文章,不便回看哦!
💬:评论交换,相互提高!
本文由博客一文多发平台 OpenWrite 公布!