Redis 如何批量设置过期时间?PIPLINE的使用

简介: 不要说在foreach中通过set()函数批量设置过期时间我们引入redis的PIPLINE,来解决批量设置过期时间的问题。

Redis如何批量设置过期时间呢?


不要说在foreach中通过set()函数批量设置过期时间

我们引入redis的PIPLINE,来解决批量设置过期时间的问题。


PIPLINE的原理是什么?


  1. 未使用pipline执行N条命令


微信图片_20221111233240.jpg


  1. 使用pipline执行N条命令


微信图片_20221111233244.jpg

通过图例可以很明显的看出来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();
}


项目实战


需求描述


  1. 打开APP,给喜欢我的人发送我的上线通知(为了避免打扰,8小时内重复登录不触发通知)
  2. 每个人每半小时只会收到一次这类上线通知(即半小时内就算我喜欢的1万人都上线了,我也只收到一次喜欢的人上线通知)


要点分析


  1. 合理使用缓存,减少DB读写次数
  2. 不仅要减少DB读写次数,也要减少Redis的读写次数,使用PIPLINE


代码实现解析


  1. canRecall() 写的比较优雅,先判断是否已发送的标记,再判断HouseOpen::getCurrentOpen(),因为HouseOpen::getCurrentOpen()是要查询DB计算的,这种代码要尽可能少的被执行到,减少DB查询。
  2. 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();
        //群发推送消息
        .
        .
        .
    }
}


总结


不同量级的数据需要不同的处理办法,减少网络请求次数,合理使用缓存,是性能优化的必经之路。


相关文章
|
canal 缓存 NoSQL
Redis常见面试题(一):Redis使用场景,缓存、分布式锁;缓存穿透、缓存击穿、缓存雪崩;双写一致,Canal,Redis持久化,数据过期策略,数据淘汰策略
Redis使用场景,缓存、分布式锁;缓存穿透、缓存击穿、缓存雪崩;先删除缓存还是先修改数据库,双写一致,Canal,Redis持久化,数据过期策略,数据淘汰策略
3061 6
Redis常见面试题(一):Redis使用场景,缓存、分布式锁;缓存穿透、缓存击穿、缓存雪崩;双写一致,Canal,Redis持久化,数据过期策略,数据淘汰策略
|
NoSQL Linux Redis
Linux Redis 服务设置开机自启动
【9月更文挑战第2天】在 Linux 系统中,可使用两种方法设置 Redis 开机自启动:一是通过创建 `redis.service` 文件并利用 systemd 进行管理,包括定义服务参数和启动脚本;二是编辑 `/etc/rc.local` 文件,在其中添加启动命令。推荐使用 systemd 方法,因为它更符合现代 Linux 系统的设计理念。设置完成后,可通过 `sudo systemctl status redis.service` 检查服务状态。
2757 4
|
存储 缓存 NoSQL
【Azure Redis 缓存 Azure Cache For Redis】如何设置让Azure Redis中的RDB文件暂留更久(如7天)
【Azure Redis 缓存 Azure Cache For Redis】如何设置让Azure Redis中的RDB文件暂留更久(如7天)
236 1
|
NoSQL Redis
Redis——批量设置key的过期时间
Redis——批量设置key的过期时间
478 1
|
NoSQL 网络安全 Redis
Redis 密码设置和查看密码
【7月更文挑战第28天】
4466 3
|
NoSQL Redis 数据安全/隐私保护
macos系统中redis如何设置密码
以上步骤应该可以帮助你在macOS系统的Redis服务中设置密码,确保你的数据存储更加安全。此外,确保你定期检查Redis安全性相关的最佳实践和更新,以保持你的服务安全可靠。
1309 3
|
NoSQL 网络安全 Redis
用python安装redis并设置服务自启
用python安装redis并设置服务自启
410 0
|
NoSQL Redis
Redis——设置最大内存 | key淘汰机制
Redis——设置最大内存 | key淘汰机制
405 0
|
监控 NoSQL Redis
Redis性能优化问题之配置 Redis 的自动碎片整理功能,如何解决
Redis性能优化问题之配置 Redis 的自动碎片整理功能,如何解决
|
NoSQL Redis Windows
Windows设置Redis服务使其开机自启
Windows设置Redis服务使其开机自启
2340 0