`

redis 惊群的处理

 
阅读更多

 缓存惊群现象,在各种缓存中都会存在这种现象,这里以Redis为例,提供一种解决思路,留作参考~

 

首先,所谓的缓存过期引起的“惊群”现象是指,在大并发情况下,我们通常会用缓存来给数据库分压,但是会有这么一种情况发生,那就是当一个缓存数据失效之后会导致同时有多个并发线程去向后端数据库发起请求去获取同一个数据,这样如果在一段时间内同时生成了大量的缓存,然后在另外一段时间内又有大量的缓存失效,这样就会导致后端数据库的压力突然增大,这种现象就可以称为“缓存过期产生的惊群现象”!

 

 

代码如下:

 

/**

     * 获取缓存

     * @param $key string $name

     * @return array,object,number,string,boolean

     * @desc 使用了“锁机制”来防止防止缓存过期时所产生的惊群现象,保证只有一个进程拉取数据,可以更新,其他进程仍然获取过期数据

     */

    public function getByLock($key)

    {

        $sth = $this->redis->get($key);   //todo 缓存伪造的时间($ttl)过期了(比数据真实的过期时间多了300s),只能去后台拉取数据

 

        if ($sth === false) {

            return $sth;

        } else {

            $sth = json_decode($sth, TRUE);

            if (intval($sth['expire']) <= time()) {   //todo 伪造时间尚未过期的情况下,真实时间($exp)过期,需要判断是否存在“唯一进程后台拉取新数据”的这个锁,如果存在则表明已经有一个进程在后台拉取新的数据了,其他的进程当前只能使用旧的数据

               

               $lock = $this->redis->incr($key . ".lock");

                if ($lock === 1) {                  // todo 证明该资源有唯一一个进程在拉取新数据从而加锁了

                    return false;

                } else {

                    return $sth['data'];

                }

            } else {                                 //todo 真实的时间未过期,返回当前redis数据即可

                return $sth['data'];

            }

        }

    }

 

    /**

     * 设置缓存

     * @param $key string $name 缓存键

     * @param $value $string ,array,object,number,boolean $value 缓存值

     * @param null $ttl $string ,number $ttl 过期时间,如果不设置,则使用默认时间,如果为 infinity 则为永久保存

     * @return bool

     * @desc 此方法存储的数据会自动加入一些其他数据来避免惊群现象,如需保存原始数据,请使用 set

     */

    public function setByLock($key, $value, $ttl = null)

    {

        if (is_numeric($ttl) && intval($ttl) > 0) {

            $ttl = intval($ttl);

            $exp = time() + $ttl;

        } else {

            $ttl = 300;

            $exp = time() + $ttl;

        }

        empty($ttl) OR $ttl += 300;         //todo 增加redis缓存时间,使程序有足够的时间生成缓存(伪造的过期时间)

        $arg = array("data" => $value, "expire" => $exp);

        $rs = $this->redis->setex($key, $ttl, json_encode($arg, TRUE));

        $this->redis->del($key . ".lock");   //todo 需要删除“唯一进程后台拉取新数据”的这个锁,在获取数据的时候才能进行判断是否允许一个进程进行拉取数据

        return $rs;

    }

 

原理就是:

 

首先,在存储数据的时候,设置数据的过期时间($ttl,伪造的过期时间)比实际设置的过期时间($exp,真实的过期时间)多300秒,然后存储的数据中,通过一个数组来存储数据,数组中一个键用来存放真实的数据,另外一个键用来存放数据的真实过期时间($exp),这个留到后期获取数据的时候做校验,然后把对应这个数据的“锁”删除掉。

 

这里这么做的原因和读取数据的做法相关!

 

然后,在读取数据的时候,依然像平时一样直接读取,如果数据已经超过了有效期($ttl),那么就只能去读后端数据库。如果数据依然有效,则需要去判断,判断数据“在真正的有效期内是否失效”,如果没有失效,则直接返回数据!

 

重点是,假如数据“在伪造的有效期内没有失效,而在真正的有效期内已经失效”,那么这时就需要去判断“数据的锁”!

 

通过代码“$lock = $this->redis->incr($key . ".lock");”可以获取数据的锁,“$lock === 1”表示数据没有锁,那么这一次请求需要发送到后端数据库去读取最新的数据,否则的话表示该数据已经加了锁,也就是已经有一个线程去后端读取数据了,那么后来的线程也就没有权限再去后端取数据,需要等到前面的那个线程执行结束,但是这次读取就只能读取“旧的数据”了!

 

 

通过上面的解释也就明白,为什么在存储数据的时候需要“删除数据的锁”!因为一旦数据被重新存储,那么说明已经有一个线程去后端得到了最新的数据,那么该数据的锁就可以释放,然后下一个线程在获取数据的时候如果有需要就可以得到这个锁,然后才有权限进入到后端去读取新数据!

    

分享到:
评论

相关推荐

    REdis命令处理流程处理分析

     REdis命令处理流程可分解成三个独立的流程(不包括复制和持久化): 接受连接请求流程; 接收请求数据和处理请求流程,在这个过程并不会发送处理结果给Client,而只是将结果数据写入响应缓冲,将由响应请求流程来...

    mqtt+springBoot+redis消息处理,亲测整理上线

    在本文中,我们将深入探讨如何使用`MQTT`(Message Queuing Telemetry Transport)协议与`SpringBoot`框架集成,并利用`Redis`作为缓存来处理消息。`MQTT`是一种轻量级的发布/订阅消息协议,常用于物联网(IoT)设备...

    REDIS_redis的工具包_redisinlabview_labviewredis_labview调用redis_redis

    在LabVIEW(Laboratory Virtual Instrument Engineering Workbench)环境中,可以利用特定的工具包来调用Redis服务,实现数据的存取和处理。本文将深入探讨`REDIS_redis的工具包`,特别是`redisinlabview`和`...

    DelayQueue延迟队列和Redis缓存实现订单自动取消功能

    5. 为了防止Redis中的订单过期但DelayQueue中仍有未处理的订单,可以使用一个单独的线程或服务定期扫描Redis中的已过期键,确保订单的及时取消。 通过结合DelayQueue和Redis,我们可以在保证高并发性能的同时,实现...

    mqtt+springBoot+redis消息处理,

    在MQTT消息处理中,Redis可以作为消息队列来缓存接收到的消息,确保即使在高并发情况下也能有效地处理和分发消息。此外,Redis的发布/订阅功能与MQTT的发布/订阅模式相结合,可以实现实时消息的传递,提升系统的响应...

    redis-windows-redis7.0.5.zip

    它的设计目标是速度和数据持久化,支持多种数据结构,如字符串、哈希表、列表、集合、有序集合等,这使得Redis在处理复杂的数据操作时具有很高的效率。 标题"redis-windows-redis7.0.5.zip"表明这是一个针对Windows...

    redis和redisdesktop

    这些数据结构使得Redis在处理各种应用场景时更加灵活,比如缓存、消息队列、计数器等。 3. **持久化**:为了防止数据丢失,Redis提供了两种持久化方式:RDB(快照)和AOF( Append Only File)。RDB是在特定时间点...

    WPF操作Redis简单实例

    **文件名称列表:** "C#操作Redis" 这个文件名可能包含一个或多个源代码文件,展示了如何在C#环境中实现Redis的连接、数据操作以及可能的异常处理等关键步骤。 **详细知识点:** 1. **C#与Redis的连接**:通常使用...

    redis 和 redis 可视化工具

    Redis 是一个开源的、基于键值对的数据存储系统,它常被用作数据库、缓存和消息中间件。它的高性能和丰富的数据结构使得在处理...通过正确配置和使用 Redis 及其可视化工具,可以有效地管理和优化数据存储和处理流程。

    redis-windows-Redis7.0.0.zip

    在高可用性架构中,Redis Sentinel系统可以监控、提醒并自动处理主从切换,保证服务的连续性。 总的来说,Redis作为一款高性能的键值数据库,因其强大的功能和易用性,在Windows平台上的应用同样广泛。通过理解其...

    redis配置文件redis.conf

    redis配置文件redis.conf

    redis 购物超买高并发处理

    以下将详细讨论如何利用Redis来处理购物超买和高并发场景。 一、Redis简介 Redis是一款开源的、基于内存的数据结构存储系统,它可以用作数据库、缓存和消息中间件。其支持多种数据结构,如字符串、哈希、列表、集合...

    redis 6.0 windows 版本

    虽然Redis的主要处理逻辑仍然是单线程,但在网络I/O方面引入了多个线程,可以显著提高高并发下的性能。 2. **LUA脚本原子执行**:Redis 6.0对LUA脚本的支持更加强大,保证了脚本在整个执行过程中的原子性,确保数据...

    Redis使用教程,详解

    Redis 是一个高性能的 NoSQL 键值存储数据库,广泛应用于缓存、任务列表、网站访问统计数据、过期处理、应用排行榜、分布式集群架构中的 session 分离等领域。下面是 Redis 的详细使用教程。 NoSQL 概述 NoSQL,即...

    RedisStudio Redis监控工具

    Redis支持多种数据类型,如字符串、哈希、列表、集合、有序集合等,使得在处理复杂数据结构时变得简单高效。 **Redis的主要特性** 1. **内存存储**: Redis的所有数据都存储在内存中,读写速度极快,但这也意味着...

    redis客户端连接工具 RedisDesktopManager

    Redis是世界上最受欢迎的内存数据存储系统之一,常用于构建高性能、低延迟的数据缓存和数据库。...在实际工作中,结合Redis的各种特性和RedisDesktopManager的功能,可以有效地利用Redis解决各种数据存储和处理问题。

    redis-5.0.3 redis-5.0.4 redis-5.0.5

    redis-5.0.3 redis-5.0.4 redis-5.0.5

    windows版Redis1

    5. `redis-server.exe`:这是Redis服务器的可执行文件,负责处理客户端请求和管理数据存储。 6. `redis-cli.exe`:Redis命令行界面工具,用于与Redis服务器交互,执行命令如读写数据、查看键空间、执行事务等。 7. `...

    源码:Java+Redis的库存处理(利用lua脚本)

    源码:Java+Redis的库存处理(利用lua脚本) ...最终我们做了一个利用Redis处理库存的模板工具,需要使用直接写一个服务类继承 DdzStockTemplate,并定义自己的redis key 规则即可实现基本的库存加减。

    c++ 操作redis数据库

    在IT行业中,C++与Redis的结合使用是一个常见的实践,特别是在需要高性能数据存储和处理的场景下。Redis是一个开源的、基于内存的数据结构存储系统,它支持多种数据类型,如字符串、哈希、列表、集合、有序集合等,...

Global site tag (gtag.js) - Google Analytics