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

合理的使用缓存策略对开发同学来讲,就好像孙悟空习得自在极意功一般~

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 批量设置过期时间内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Redis 事务与过期时间详细介绍

    Redis 事务与过期时间详细介绍 一.Redis事务: Redis中支持事务,事务即为当我们需要执行几条命令时,要么这几条命令都不执行,要么都执行: 1.开始事务写入: multi 2.然后写入命令,注意写完事务要执行的每条命令之后回车即可,命令会自动入队: lpush art:1 hello lpush art:1 nihao 3.执行事务: exec Redis则会保证事务中的所有命令要么都执行,要么都不执行. 二.Redis过期时间: 实际开发中经常会遇到一些有时效性的数据,比如缓存,过

  • java操作Redis缓存设置过期时间的方法

    关于Redis的概念和应用本文就不再详解了,说一下怎么在java应用中设置过期时间. 在应用中我们会需要使用redis设置过期时间,比如单点登录中我们需要随机生成一个token作为key,将用户的信息转为json串作为value保存在redis中,通常做法是: //生成token String token = UUID.randomUUID().toString(); //把用户信息写入redis jedisClient.set(REDIS_USER_SESSION_KEY + ":"

  • Redis 如何批量设置过期时间(PIPLINE的使用)

    合理的使用缓存策略对开发同学来讲,就好像孙悟空习得自在极意功一般~ Redis如何批量设置过期时间呢? 不要说在foreach中通过set()函数批量设置过期时间 我们引入redis的PIPLINE,来解决批量设置过期时间的问题. PIPLINE的原理是什么? 未使用pipline执行N条命令 使用pipline执行N条命令 通过图例可以很明显的看出来PIPLINE的原理: 客户端通过PIPLINE拼接子命令,只需要发送一次请求,在redis收到PIPLINE命令后,处理PIPLINE组成的命令

  • python redis 批量设置过期key过程解析

    这篇文章主要介绍了python redis 批量设置过期key过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 在使用 Redis.Codis 时,我们经常需要做一些批量操作,通过连接数据库批量对 key 进行操作: 关于未过期: 1.常有大批量的key未设置过期,导致内存一直暴增 2.rd需求 扫描出这些key,rd自己处理过期(一般dba不介入数据的修改) 3.dba 批量设置过期时间,(一般测试可以直接批量设置,线上谨慎操作) 通过

  • 手写redis@Cacheable注解 支持过期时间设置方式

    目录 原理解释 实现方法 源代码 原理解释 友情链接  手写redis @ Cacheable注解参数java对象作为键值 @Cacheable注解作用,将带有该注解方法的返回值存放到redis的的中; 使用方法在方法上使用@Cacheable(键=“测试+#P0 + P1#...”) 表示键值为测试+方法第一个参数+方法第二个参数,值为该方法的返回值. 以下源代码表示获取人员列表,Redis的中存放的关键值为'领袖'+ leaderGroupId + UUID + yearDetailId @

  • Java如何设置过期时间的map的几种方法

    目录 一.技术背景 二.技术效果 三.ExpiringMap 3.1功能简介 3.2源码 3.3示例 四.LoadingCache 4.1功能简介 4.2示例 4.3移除机制 4.4其他 五.HashMap的封装 一.技术背景 在实际的项目开发中,我们经常会使用到缓存中间件(如redis.MemCache等)来帮助我们提高系统的可用性和健壮性. 但是很多时候如果项目比较简单,就没有必要为了使用缓存而专门引入Redis等等中间件来加重系统的复杂性.那么Java本身有没有好用的轻量级的缓存组件呢.

  • Java缓存Map设置过期时间实现解析

    这篇文章主要介绍了Java缓存Map设置过期时间实现解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 前言 最近项目需求需要一个类似于redis可以设置过期时间的K,V存储方式.项目前期暂时不引进redis,暂时用java内存代替. 解决方案 1. ExpiringMap 功能简介 : 1.可设置Map中的Entry在一段时间后自动过期. 2.可设置Map最大容纳值,当到达Maximum size后,再次插入值会导致Map中的第一个值过期.

  • Asp 操作Cookies(包括设置[赋值]、读取、删除[设置过期时间])

    例子: 复制代码 代码如下: Response.Cookies("letwego")("visiter")="84ww" '赋值 Response.Cookies("letwego").Expires= (now() 7) '设置过期时间(7天) userName=Request.Cookies("letwego")("visiter") '取Cookies Response.Cooki

  • C#实现给PDF文档设置过期时间

    目录 引入dll程序集 添加过期时间 实现代码 C# VB.NET 效果图 我们可以给一些重要文档或者临时文件设置过期时间和过期信息提示来提醒读者或管理者文档的时效性,并及时对文档进行调整.更新等.下面,分享通过C#程序代码来给PDF文档设置过期时间的方法. 引入dll程序集 [方法1]通过 NuGet 安装. 可以在Visual Studio中打开“解决方案资源管理器”,鼠标右键点击“引用”,“管理NuGet包”,然后搜索“Free Spire.PDF”,点击“安装”. 也可以将以下内容复制到

  • 本地存储localStorage设置过期时间示例详解

    目录 思考 实现思路 代码实现 代码测试 思考 在我们使用cookie的时候是可以设置有效期的,但是localStorage本身是没有该机制的,只能人为的手动删除,否则会一直存放在浏览器当中,可不可以跟cookie一样设置一个有效期.如果一直存放在浏览器又感觉有点浪费,那我们可以把localStorage进行二次封装实现该方案. 实现思路 在存储的时候设置一个过期时间,并且存储的数据进行格式化方便统一校验,在读取的时候获取当前时间进行判断是否过期,如果过期进行删除即可. 代码实现 目录结构 en

  • Python操作Redis之设置key的过期时间实例代码

    Expire 命令用于设置 key 的过期时间.key 过期后将不再可用. r.set('2', '4028b2883d3f5a8b013d57228d760a93') #成功就返回True 失败就返回False,下面的20表示是20秒 print r.expire('2',20) #如果时间没事失效我们能得到键为2的值,否者是None print r.get('2') 对于一个已经存在的key,我们可以设置其过期时间,到了那个时间后,当你再去访问时,key就不存在了 有两种方式可以设置过期时间

随机推荐