Redis实战宝典:基础知识、实战技巧、应用场景及最佳实践全攻略

背景

在Java系统实现过程中,我们不可避免地会借助大量开源功能组件。然而,这些组件往往功能丰富且体系庞大,官方文档常常详尽至数百页。而在实际项目中,我们可能仅需使用其中的一小部分功能,这就造成了一个挑战:如何在有限的时间和精力下,高效地掌握并使用这些组件的核心功能,以实现投入产出最大化?

针对这一问题,我基于二八原则,整理编写本文。

首先,我会聚焦于组件的常见和核心功能,这些功能通常是我们在日常开发中频繁使用到的,也是构建稳定、高效系统的基石。通过深入了解这些核心功能的使用方法和最佳实践,我们可以确保在关键点上投入足够的精力,从而避免在实际使用中掉入陷阱。

其次,我会以问题为导向,将实用性作为第一要素,对组件的功能进行筛选和整理。这意味着我会优先关注那些在项目中实际需要用到的功能,而对于那些特定场景下才会用到的功能,我会在文中提及但不做详细展开。这样做的好处是,我们可以在保证核心功能得到充分理解的同时,减少不必要的阅读负担,提高学习效率,降低投入成本。

最后,我会注重内容的精炼和易读性。通过简明扼要的文字描述和直观的示例代码,帮助读者快速理解并掌握组件的核心用法。
同时,我也会结合经验指出常见的问题和注意事项,以便读者在使用过程中能够规避一些常见的错误和陷阱。

综上所述,通过这个系列的内容整理,我希望能够帮助读者在有限的时间和精力下,高效地掌握并使用这些开源功能组件的核心功能,满足系统实现的需要。

注:部分内容章节由AI辅助生成草稿,我对其进行了复核和修订,修复了有问题和有错误的部分。

理论

Redis是什么?

Redis(Remote Dictionary Server),即远程字典服务,是一个开源的、使用C语言编写的、支持网络的、可基于内存亦可持久化的日志型Key-Value数据库,提供多种开发语言的API,能够很好地补充关系数据库在某些场合的不足,并为开发者提供了丰富的数据操作选项和高效的数据处理性能。

Redis与关系型数据库的区别是什么?

Redis与关系型数据库在多个方面存在显著的区别,主要涉及以下几个方面:
数据模型与存储方式
Redis采用键值存储(Key-Value Store)模型,支持多种数据结构,包括字符串、哈希、列表、集合、有序集合等,使得数据存储和操作更加灵活。这种模型能够很好地适应各种数据存储需求,且更易于扩展和修改。
关系型数据库则采用表格的形式来组织数据,数据被组织成行和列的形式,并通过外键关系进行关联,以支持复杂的关系模型。这种结构化的数据模型有助于数据的清晰表示和查询,但也可能导致数据结构相对固定,不易于灵活修改。

存储机制与性能
Redis主要将数据存储在内存中,通过持久化机制可选地将数据写入磁盘。这种设计使得Redis能够实现高速的读写操作,特别适用于需要快速响应的场景。
关系型数据库通常将数据存储在磁盘上,通过缓存等机制提高读取速度。由于磁盘I/O操作通常比内存操作慢得多,因此关系型数据库的读写速度相对较慢。

事务处理
Redis支持事务,其事务是基于队列实现的,即创建一个事务队列,然后将事务操作都放入到队列中,最后依次执行,但没有提供回滚机制
关系型数据库则支持基于ACID的事务,提供更严格的一致性和隔离性。这使得关系型数据库在处理复杂的事务逻辑时更加可靠。

查询语言与操作能力
Redis提供简单的键值查询和一些特定的数据结构操作命令。客观地说,其职责定位也没有复杂查询的需求。
关系型数据库使用SQL作为查询语言,支持复杂的查询和连接操作,能够处理各种复杂的业务逻辑和数据关系。

应用场景
Redis适用于需要快速读写、对数据结构操作要求较高、需要缓存或实时分析等场景。例如,它可以作为缓存层,减少数据库压力,提高系统性能;也可以用于实现分布式锁、消息队列等功能。
关系型数据库则更适用于需要严格事务控制、支持复杂查询和关联操作的应用场景。它们在企业级应用、数据仓库等领域有着广泛的应用。

综上所述,Redis与关系型数据库在数据模型、存储方式、性能、事务处理、查询语言以及应用场景等方面都存在显著差异。
关系型数据库和Redis在应用系统中往往是同时存在,配合使用,用于实现不同的需求,应对不同的场景。

Redis有哪些特点?

Redis的特点主要体现在其速度快简单稳定等方面。由于Redis数据都是缓存在内存中,并且采用单线程避免了不必要的上下文切换和竞争条件,因此其读写性能非常出色,官方给出的数字显示,Redis的读速度可以达到110000次/s,写速度可以达到81000次/s。此外,Redis还支持主从同步,数据可以从主服务器向任意数量的从服务器上同步,从而实现了数据的备份以及读写分离。

Redis应用场景有哪些?

Redis在多个场合都有广泛的应用,包括但不限于用作缓存来存储热点数据、提升数据访问速度、降低数据库压力;实现各种复杂的排行榜应用;作为计数器记录如电商网站商品的浏览量等;实现分布式锁;在社交网络应用中实现点赞、关注等功能;以及在多个应用之间共享数据等。

Redis支持哪些数据类型?

Redis支持以下五种主要的数据类型:

  1. 字符串(String)
    • 是Redis最基本的数据类型,一个key对应一个value。
    • 字符串类型是二进制安全的,这意味着你可以把Redis的字符串类型理解成字节数组,你可以对字符串类型进行很多操作,例如追加、获取子串等。
  2. 哈希(Hash)
    • Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。
  3. 列表(List)
    • Redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。
  4. 集合(Set)
    • Redis的集合是无序的字符串集合,并且集合成员是唯一的,不存在重复的元素。
    • Redis集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。
  5. 有序集合(Sorted Set)
    • Redis有序集合和集合一样也是string类型元素的集合,并且集合成员是唯一的。不同的是每个元素都会关联一个double类型的分数(score)。Redis正是通过分数来为集合中的元素进行从小到大的排序。
    • 有序集合的成员是唯一的,但分数(score)可以重复。
    • 集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。

这些数据类型为Redis提供了丰富的功能集,使其可以灵活地处理各种类型的数据和应用场景。

实际上,使用最多的类型还是字符串。

哪些数据需要放到Redis缓存?

一般来说,放到缓存中的往往是更新频率低,大量读取的数据。
对于应用系统而言,主要分为两类:
一是基础数据,如系统的配置参数,数据字典(性别、证件号码等)。
二是业务数据,如某城市的天气预报数据,电商系统中的秒杀商品。

使用缓存常见误区是什么?

缓存数据没有及时刷新。
将数据放入缓存,意味着内存中多了一份数据库中的数据副本。应用系统会优先从缓存中读取。若数据库中的数据被更新,则需要主动将其更新到缓存,否则业务上读取到的就是陈旧的错误数据,在大多数情况下是不可接受的。
比如秒杀商品变更了价格,用户看到的是缓存中的尚未改变的价格,下单支付时才发现价格变了。

实战

Redis如何安装?

服务器端往往是Linux或Docker环境,如何安装部署根据环境操作即可。
开发环境大多都是windows中,建议使用exe安装,会自动将其注册为windows服务。
image.png
通过操作系统的服务管理面板可进行灵活控制启停及开机自动运行。
Redis的日志目录和配置文件参见安装路径,在不清楚各参数含义的情况下,保持默认即可,如下图:image.png

Redis的读写库应该选哪个?

java中进行redis读写操作的库主要有两个,一个是Jedis,另外一个是lettuce。
我们应该选哪个呢?
先来看下简介。
Jedis:是老牌的Redis的Java实现客户端,提供了比较全面的Redis命令的支持。
Lettuce:高级Redis客户端,用于线程安全同步,异步和响应使用,支持集群,Sentinel,管道和编码器。
好像从简介中也看不出谁优谁劣,再进一步看下技术实现。
Jedis使用阻塞的I/O,且其方法调用都是同步的,程序流需要等到sockets处理完I/O才能执行,不支持异步。Jedis客户端实例不是线程安全的,所以需要通过连接池来使用Jedis。
Lettuce基于Netty框架的事件驱动的通信层,其方法调用是异步的。Lettuce的API是线程安全的,所以可以操作单个Lettuce连接来完成各种操作。
这时候就看出来差别来了,从技术实现上,明显Lettuce更胜一筹。

使用jedis库去操作redis,其流程跟访问关系型数据库非常像,即创建一个连接池,然后从连接池中获取一个连接,进行读写操作,最后再关闭连接(将连接归还给连接池)。

@Slf4j
public class JedisUtil {
    @Autowired
    private JedisPool jedisPool;

    /**
     * 存入Redis缓存
     *
     * @param key
     * @param value
     */
    public void set(String key, String value) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            jedis.set(key, value);
        } catch (Exception ex) {
            log.error("存储Redis出错" + ex);
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }

    }
}

因此,相比Jedis,更推荐使用Lettuce。

如何在SpringBoot项目中使用?

可以在SpringBoot项目中直接使用Lettuce,但是更佳的方式,是使用spring-data-redis。
spring-data-redis对redis底层开发包(Jedis、lettuce等 )进行了高度封装,统一由RedisTemplate提供了redis各种操作、异常处理及序列化工作。
也就是说,可以将spring-data-redis视作抽象的接口,对redis的读写,可以灵活更换为具体的客户端,如jedis或lettuce。这种设计方式挺常见的,比如slf4j提供日志门面,接入logback、log4j2等实现日志功能。
使用spring-data-redis,而不是直接使用lettuce等客户端,一方面,封装后的组件,往往比未封装的组件更易用;另一方面,当需要更换组件时,易于实现,应用系统的代码无需调整。

添加哪些依赖?

在SpringBoot项目的pom文件中,添加如下依赖:

<!-- redis缓存 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>

SpringBoot从2.0版本开始,将spring-boot-starter-data-redis内置的jedis,更换为lettuce,也间接说明了后者更优。
注意这里面有个坑点,lettuce内部使用了apache的连接池,但并没有强依赖,因此需要单独引入commons-pool2。

如何配置?

Spring Boot为Redis提供了自动配置的功能,使得我们可以很容易地通过配置文件(通常是application.yml或application.properties)来设置Redis的相关参数。
在yml中配置redis及lettuce参数,如下所示:

spring: 
   redis:
    host: localhost
    port: 6379
    password:
    #新版本Redis的timeout是一个duration,需使用如下写法
    timeout: 10s
    database: 0
    lettuce:
      pool:
        # 连接池中的最小空闲连接
        min-idle: 2
        # 连接池中的最大空闲连接
        max-idle: 2
        # 连接池的最大连接数
        max-active: 16
        #连接池最大阻塞等待时间
        max-wait: 30s

host
说明:指定Redis服务器的地址。
值:localhost 表示Redis服务器运行在本机上。
port
说明:指定Redis服务器的端口号。
值:6379 是Redis的默认端口号。
password
说明:指定连接Redis服务器所需的密码。
值:此处为空,表示没有设置密码。如果Redis服务器设置了密码,需要在这里填写。
timeout
说明:指定连接Redis服务器的超时时间。
值:10s 表示超时时间为10秒。在新版本的Redis中,timeout已经是一个duration,所以使用“s”来表示秒。
database
说明:指定使用的Redis数据库索引。
值:0 表示使用Redis的第一个数据库。Redis默认提供了16个数据库,索引从0到15。

以下是Lettuce连接池的配置部分
min-idle
说明:连接池中的最小空闲连接数。
值:2 表示连接池中至少保持2个空闲连接。
max-idle
说明:连接池中的最大空闲连接数。
值:2 表示连接池中最多可以保持2个空闲连接。
max-active
说明:连接池的最大连接数。
值:16 表示连接池中最多可以有16个活跃连接。
max-wait
说明:当连接池中没有可用连接时,获取连接的最大阻塞等待时间。
值:30s 表示如果连接池中没有可用连接,获取连接的线程会最多等待30秒。

这些配置为Spring Boot应用程序提供了连接Redis服务器的所有基本信息,以及关于连接池的行为参数。通过调整这些参数,可以优化Redis的使用性能,满足应用程序的不同需求。

如何读写Redis?

spring-data-redis组件使用RedisTemplate来进行操作,自行封装的一个工具类,如下所示:

package tech.abc.platform.common.utils;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;


/**
 * 缓存工具类
 *
 * @author wqliu
 * @date 2023-03-06
 */
@Component
@Slf4j
public class CacheUtil {


    @Autowired
    public RedisTemplate redisTemplate;


    /**
     * 设置缓存对象
     *
     * @param key   缓存的键
     * @param value 缓存的值
     */
    public <T> void set(String key, T value) {

        redisTemplate.opsForValue().set(key, value);

    }

    /**
     * 设置缓存对象,附带设定有效期
     *
     * @param key      缓存的键值
     * @param value    缓存的值
     * @param timeout  时间
     * @param timeUnit 时间单位
     */
    public <T> void set(String key, T value, long timeout, TimeUnit timeUnit) {
        redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
    }


    /**
     * 获取缓存对象
     *
     * @param key 缓存键值
     * @return 缓存键值对应的数据
     */
    public <T> T get(String key) {
        ValueOperations<String, T> operation = redisTemplate.opsForValue();
        return operation.get(key);
    }


    /**
     * 删除缓存对象
     *
     * @param key 缓存的键
     */
    public boolean remove(String key) {
        return redisTemplate.delete(key);
    }


    /**
     * 从redis缓存中移除指定前缀的所有值
     */
    public void removePrefix(String prefix) {

        Set keys = redisTemplate.keys(prefix + "*");
        redisTemplate.delete(keys);
    }

    /**
     * 设置缓存对象的有效期
     *
     * @param key      缓存的键值
     * @param timeout  时间
     * @param timeUnit 时间单位
     */
    public <T> void expire(String key, Integer timeout, TimeUnit timeUnit) {
        redisTemplate.expire(key, timeout, timeUnit);
    }


    /**
     * 批量存入缓存
     *
     * @param cachedMap
     */
    public void setBatch(Map<String, String> cachedMap) {
        for (String key : cachedMap.keySet()) {
            set(key, cachedMap.get(key));
        }
    }

}

可以看出来,使用封装后的RedisTemplate,要比原生的jedis方便得多,一句代码就能实现读或写,而jedis更像是访问关系型数据库的模式,需要先从连接池中获取1个连接,然后执行读或写操作,最后再关闭连接。

Redis工具查看数据异常怎么解决?

经过上述步骤,通过系统读写是没问题,但是使用redis客户端工具,直连redis服务器,查看数据时,则会显示多了一些不可读的前缀\xac\xed\x00\x05t\x00\,这是另外一个坑点。
这是怎么出现的呢?原来lettuce默认使用JdkSerializationRedisSerializer作为序列化与反序列化的工具,将字符串转换为字节数组搞出来的幺蛾子。

怎么解决呢?知道原因了,解决思路也有了,搞一个配置类,将默认的序列化与反序列的类替换掉。

@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<Object, Object> redisStringTemplate(RedisTemplate<Object, Object> redisTemplate) {
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(stringRedisSerializer);
        redisTemplate.setValueSerializer(stringRedisSerializer);
        return redisTemplate;
    }

}

StringRedisSerializer是spring-data提供的,同时还提供了负责json数据的处理类,可用于键值为json的场景。

场景

缓存数据字典

将应用系统中的下拉列表中的字典数据,如性别、证件类型,在系统启动时从数据库写入到Redis缓存。
在使用这些字典作为属性的业务实体时,如查询用户,读取Redis,将编码值转换为文本,返回给前端,从而避免了大量数据库读操作。

自助重置密码

用户忘记密码时,提供自助方式,让用户输入注册时的邮箱地址,然后生成一个uuid,一方面,将该uuid作为键值,存入到redis,并设置24小时有效;另一方面,将uuid作为参数附加到自助重置密码的地址上,作为邮件内容发送到用户邮箱。
用户点击邮件中的链接地址时,系统获取到uuid,去Redis中查询键是否存在,若不存在,则认为链接已超出时效。
这里是利用了Redis自身支持时效的特点来简化开发工作(相比自己在关系型数据库中建表来保存失效时间,验证环节自行比较)。
同理,用户登录环节,验证码5分钟有效,也可以采用这种方案。

扩展

集群模式有哪些?

Redis集群模式主要有三种,分别是:主从复制模式、哨兵模式(Sentinel)和Cluster模式。

主从复制模式
主从复制模式是最简单的集群方式。它使用一个Redis实例作为主机(master),其他实例作为备份机(slave)。主机负责数据的写入和读取操作,而从机只支持与主机数据的同步和读取操作。当主机发生故障时,需要人工介入,将某个从机提升为新的主机。
优点:提高了服务器性能,实现了读写分离。
缺点:故障恢复效率较低,需要人工介入。

哨兵模式(Sentinel)
哨兵模式通过一组哨兵实例组成的哨兵系统来监视主从节点的健康状态。一旦主节点故障被侦测到,系统会自动选举出一个从节点,晋升为新的主节点,从而实现故障恢复的自动化。
优点:提高了系统的高可用性,故障恢复更加自动化,提高了系统的稳定性和可靠性。
缺点:内存容量和写入性能仍受限于单个节点。

Cluster模式
Cluster模式通过数据分片(sharding)和多节点水平扩展,有效提高了内存利用率和写入性能,适用于更大规模和更高要求的数据处理场景。Redis Cluster将数据分为16384个槽位,每个节点负责管理一部分槽位。当客户端向Redis Cluster发送请求时,Cluster会根据键的哈希值将请求路由到相应的节点。具体来说,Redis Cluster使用CRC16算法计算键的哈希值,然后对16384取模,得到槽位编号。
优点:为Redis集群的性能和扩展性提供了重要的支撑。
缺点:复杂度高
在实际应用中,可以根据具体的业务需求和场景来选择合适的Redis集群模式。如果需要更高级别的自动化故障恢复和更好的性能,可以考虑使用哨兵模式或Cluster模式。如果只是简单的读写分离和备份需求,主从复制模式可能是一个不错的选择。

如果应用规模有限,用户量比较小,单机模式也足够用了,redis自身的稳定性相当高,没必要集群。

Redis如何实现持久化?

Redis的持久化模式主要有三种:快照方式(RDB, RedisDataBase)、文件追加方式(AOF, AppendOnlyFile)以及混合持久化方式。

快照方式(RDB
原理:该方式将某个时刻的内存数据以二进制的方式写入磁盘。由于是二进制写入,效率较高。
优点:RDB文件为二进制数据,占用内存小且紧凑,适合作为备份文件。同时,RDB持久化方式可以最大化Redis的性能,因为父进程在保存RDB文件时只需要fork出一个子进程,接下来的工作全部由子进程完成,父进程不需要进行其他IO操作。
缺点:当Redis意外终止时,可能会导致数据部分丢失。此外,RDB需要经常fork子进程来保存数据集到硬盘上,当数据集较大时,fork过程可能会非常耗时,导致Redis在一段时间内无法响应客户端请求。
快照方式不会堵塞主进程。

文件追加方式(AOF)
原理:该方式记录所有的操作命令,并以文本的形式追加到文件中。由于是以文本形式写入,效率相对较低,但保证了数据的完整性。
优点:由于AOF记录的是操作指令,因此可以确保数据的完整性。
缺点:由于AOF是以文本形式写入,其效率不如RDB。同时,AOF文件通常会比RDB文件大,需要更多的磁盘空间。
AOF是先写redis,再追加日志,都在主进程中,所以追加日志操作实际会堵塞主进程。此外,若在写redis成功,写日志还没完成,这时候宕机,通过AOF来恢复会丢数据。

混合持久化方式:
原理:Redis 4.0之后新增的方式,结合了RDB和AOF的优点。在写入时,先把当前数据以RDB形式写入文件的开头,再将后续的操作命令以AOF的格式存入文件。
优点:这种方式既能保证Redis重启时的速度,又能降低数据丢失的风险。

在选择持久化策略时,需要根据实际的应用场景和需求来权衡。例如,如果更看重数据的完整性和可恢复性,可以选择AOF或混合持久化方式;如果更看重性能和磁盘空间的使用效率,可以选择RDB方式。

需要注意的是,在使用Redis的持久化功能时,应定期检查和备份持久化文件,以防止数据丢失或损坏。同时,也应对Redis服务器进行监控和维护,确保其正常运行和数据的安全性。

什么是缓存穿透?

缓存穿透是指查询一个根本不存在的数据,由于缓存也没有该数据,每次请求都会直接打到数据库上,而数据库中也没有该数据,相当于进行了两次无效的查询。常见的场景是并发查询不存在的key时,由于缓存未命中,每次请求都会直接查询数据库,造成数据库压力骤增甚至宕机。

为了避免缓存穿透带来的问题,可以采取以下策略:

布隆过滤器:将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。
缓存空对象:当存储层不命中后,即使是一个空对象或默认值,也将其缓存起来,这样后续再查询同样的key时,就直接返回缓存中的空对象或默认值,而不需要再去存储层查询。但这种方式需要特别注意缓存的失效时间和存储空间的问题。
限制访问频率:针对同一个key的访问,如果一段时间内访问频率过高,可以考虑加入访问队列或使用滑动窗口限制访问频率,从而减少对数据库的查询压力。

在实际应用中,需要根据具体的业务场景和需求来选择合适的缓存穿透应对策略。

什么是缓存击穿?

缓存击穿是指当一个热点(即访问非常频繁)的key在缓存中失效(过期)的瞬间,大量的并发请求同时访问这个key时,由于缓存中没有该key的数据,这些请求都会直接穿透到数据库,导致数据库瞬间承受巨大的压力。
简单来说,就是数据库中有值而缓存中没有值。
为了避免缓存击穿问题,通常有以下几种解决策略:
使用互斥锁:当多个线程同时访问失效的key时,可以使用互斥锁(如Redis的分布式锁)来确保只有一个线程去数据库查询并重建缓存,其他线程则等待锁释放后访问新缓存。
设置热点key永不过期:如果某个key的访问非常频繁且数据基本不会更新,可以考虑将其设置为永不过期,避免其过期导致的缓存击穿。

主动更新缓存:对于更新频率较高的数据,可以利用定时线程在缓存过期前主动重新构建缓存,或者在缓存过期时延后一段时间再过期,以确保缓存始终有效。

注意上面说的的互斥锁方案,加锁排队只是为了减轻数据库的压力,并没有提高系统吞吐量。假设在高并发下,缓存重建期间 key 是锁着的,这是过来 1000 个请求 999 个都在阻塞的,同样会导致用户等待超时。

需要注意的是,在处理缓存击穿问题时,应该结合具体的业务场景和需求来选择最合适的解决方案,并在实施后进行充分的测试以确保其有效性和性能。

什么是缓存雪崩?

缓存雪崩是指当大量缓存同时失效或过期后,系统无法从缓存中获取数据,转而请求数据库,导致数据库承受巨大压力,进而引起系统性能急剧下降的情况。
缓存击穿是对于单个key值失效来了大量并发请求,缓存雪崩是大量key值在同一时间点或短暂的时间段失效。
具体来说,当缓存中的数据失效或被清除时,系统需要再次访问数据库,重新计算并生成缓存数据。这个处理过程通常耗时较长,可能达到上百毫秒甚至更久。对于高并发的系统而言,在缓存失效的这段时间内,系统会接收到大量的请求。由于旧的缓存已经失效且新的缓存尚未生成,这些请求都会直接访问数据库,从而导致数据库承受巨大的访问压力。如果这种压力超过了数据库的承受能力,就可能引发数据库宕机,进而使整个系统崩溃。

为了避免缓存雪崩,可以采取以下策略:
保持缓存层的高可用性:使用Redis哨兵模式或集群部署方式,确保即使个别Redis节点故障,整个缓存层依然可用。
优化缓存过期时间:为缓存中的每个key设置合适的过期时间,避免大量key在同一时刻同时失效。有一个简单方案就是将缓存失效时间分散开,比如我们可以在原有的失效时间基础上增加一个随机值,比如 1-5 分钟随机,这样每一个缓存的过期时间的集中程度就会降低,很难引发集体失效的事件。

综上所述,缓存雪崩是一个需要认真对待的问题,合理的缓存策略和管理机制对于维护系统的稳定性和性能至关重要。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/697913.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

重庆地区媒体宣传邀约资源整理

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 重庆地区媒体宣传邀约资源整理 一、主流媒体资源 电视台&#xff1a;重庆电视台&#xff1a;作为重庆地区最具影响力的电视媒体之一&#xff0c;拥有多个频道&#xff0c;涵盖新闻、综艺…

第2章 Rust初体验1/8:prelude:简化代码的自动标准库加载:猜骰子冷热游戏

讲动人的故事,写懂人的代码 在跑过Hello world程序后,三个人觉得这样还不过瘾,于是决定把那本Rust书里的猜数字游戏换个新面孔,变成“猜骰子冷热”游戏,然后一起动手实现一下。这样我们就能更深入地体验到Rust编程的乐趣啦。 (顺便说一句,如果你想找这本书的所有代码,…

Sklearn的安装和用法

安装sklearn相对简单&#xff0c;因为它是一个Python库&#xff0c;可以通过Python的包管理器pip来安装。 Windows、macOS和Linux通用步骤&#xff1a; 确保Python已安装&#xff1a; sklearn是基于Python的&#xff0c;所以首先确保你的计算机上安装了Python。推荐使用Pytho…

【图论应用】使用多路图(multigraph)对上海地铁站点图建模,并解决最短路径问题

文章目录 1 前言2 导包导入数据集3 创建多路图&#xff0c;导入节点和边信息3 绘制线路图4 计算最短路径 1 前言 最近正在学习图神经网络&#xff0c;先pick up了一些最基础的图论知识并学习了一些好玩的应用。 本文启发于B站视频&#xff08;BV1LY411R7HJ&#xff09;&#…

第1期JAVA社招面试经验月报

面经哥专注互联网社招面试经验分享&#xff0c;关注我&#xff0c;每日推送精选面经&#xff0c;面试前&#xff0c;先找面经哥&#xff5c;面经哥整理了上月30篇面试经历&#xff0c;选取了较为热点高频的面试题供大家参考 基础知识类‍‍‍‍‍ 1、说下双亲委派原则以及类加…

HDFS 读写数据流程

优质博文&#xff1a;IT-BLOG-CN 一、HDFS 写数据流程 HDFS 文件写入流程图如下&#xff1a;三个模块&#xff08;客户端、NameNode、DataNode&#xff09; 【1】校验&#xff1a; 客户端通过 DistributedFileSystem 模块向 NameNode 请求上传文件&#xff0c;NameNode 会检…

使用手机做PC机摄像头

准备工作&#xff1a; 带摄像头的安卓手机一部模拟相机软件&#xff1a;Iriun 、DroidCam 、IP摄像头pythonopencv 一、Iriun 1、分别在PC和手机上安装 2、手机和PC在同一个局域网 3、分别打开PC和手机端软件&#xff0c;电脑端就可以使用手机相机 ​ 二、 DroidCam 1、…

5.大模型高效微调(PEFT)未来发展趋势

PEFT 主流技术分类 UniPELT 探索PEFT 大模型的统一框架&#xff08;2022&#xff09; UIUC 和Meta AI 研究人员发表的UniPELT 提出将不同的PEFT 方法模块化。 通过门控机制学习激活最适合当前数据或任务的方法&#xff0c;尤其是最常见的3大类PEFT 技术&#xff1a; Adapters…

【PB案例学习笔记】-18制作一个IP地址编辑框

写在前面 这是PB案例学习笔记系列文章的第18篇&#xff0c;该系列文章适合具有一定PB基础的读者。 通过一个个由浅入深的编程实战案例学习&#xff0c;提高编程技巧&#xff0c;以保证小伙伴们能应付公司的各种开发需求。 文章中设计到的源码&#xff0c;小凡都上传到了gite…

Cocos2dx 编译游戏安装包制作教程

在 Visual Studio 项目中配置图标并使用 Inno Setup 创建安装包 在本教程中&#xff0c;我们将学习如何为 Visual Studio 编译项目配置图标&#xff0c;并使用 Inno Setup 创建安装包。教程包括以下部分&#xff1a; 设置项目图标&#xff1a;在 Visual Studio 中配置 .exe 文…

英语国际音标 - DJ 音标 - KK 音标

英语国际音标 - DJ 音标 - KK 音标 1. 国际音标 (International Phonetic Alphabet&#xff0c;IPA)1.1. 记音类型1.2. 48 个国际音标发音表1.2.1. 元音 (vowel)1.2.1.1. 单元音 (monophthong)1.2.1.2. 双元音 (diphthong) 1.2.2. 辅音 (consonant)1.2.2.1. 清音 (voiceless so…

用人工智能写2024年高考作文

目录 用人工智能写2024年高考作文 引用 一、2024年 新课标I卷 作文真题 AI写作范文 二、2024年 全国甲卷 作文真题 AI写作范文 三、2024年 新课标II卷 作文真题 AI写作范文 四、2024年 北京卷 作文真题一 AI写作范文 作文真题二 AI写作范文 作文真题三 AI写作…

Nginx中location规则与rewrite重写

一、概念介绍 1、location与rewrite的常用正则表达式 符号作用^ 匹配输入字符串的起始位置$ 匹配输入字符串的结束位置* 匹配前面的字符零次或多次。如“ol*”能匹配“o”及“ol”、“oll” 匹配前面的字符一次或多次。如“ol”能匹配“ol”及“oll”、“olll”&#xff0…

keda-P0460. 潜水员

可达信奥 - 登录 - 可达信奥https://kedaoi.cn/p/P0460 代码思路&#xff1a; 01背包DP。 思路也是比较经典的&#xff0c;就是看用这个水缸的最小值小&#xff0c;还是不用这个水缸的最小值小。但是这里涉及到一个初始化的问题&#xff0c;因为要求最小所以初始化理应…

1992-2012年美国西海岸的海面高度异常数据集

Gridded Altimeter Fields with Enhanced Coastal Coverage 具有增强海岸覆盖范围的网格化测高场 简介 具有增强的海岸覆盖范围的网格化高度计场数据产品包含美国西海岸的海面高度异常&#xff08;SSHA 或 SLA&#xff09;以及北纬 35.25 度-48.5 度和东经 227.75 度-248.5 …

【docker】日志

ocker 日志相关的操作主要涉及查看、管理和理解容器的日志输出。以下是一些常用的 Docker 日志命令和选项&#xff1a; 查看日志 docker logs container_id_or_name&#xff1a;获取指定容器的日志。docker logs -f container_id_or_name&#xff1a;跟随&#xff08;实时输出…

ARM32开发--串口库封装(初级)

知不足而奋进望远山而前行 目录 文章目录 前言 目标 内容 开发流程 文件目录创建 分组创建 接口定义 完整代码 总结 前言 在嵌入式软件开发中&#xff0c;封装抽取流程和抽取封装策略是非常重要的技术&#xff0c;能够提高代码的复用性和可维护性。本文将介绍如何在文…

Python 多进程

单例模式 面试中&#xff0c;就被问到了这个问题&#xff0c;你知道用python怎么创建一个单例模式吗&#xff1f; 单例模式是什么&#xff1f; 就是这个对象只能被创建一次。 每次实例化&#xff0c;都是同一个对象。 单例模式是一种常用的软件设计模式。在它的核心结构中只包…

UE5.2打包安卓

目录 简介: 一. 根据官网配置 二. 手动定位SDK路径 三: 设置Android基本信息 四: 设置KeyStore 五: 开始打包 六:其他 七. 总结 简介: UE5.2 打包安卓是指将使用 Unreal Engine 5.2 开发的项目编译为可在安卓设备上运行的安装包。 以下是一般的打包步骤&#xff1a; 安装…

交易中的群体行为特征和决策模型

本文基于人的行为和心理特征&#xff0c;归纳出交易中群体的行为决策模型&#xff0c;并基于这个模型&#xff0c;分析股价波浪运行背后的逻辑&#xff0c;以及投机情绪的周期变化规律&#xff0c;以此指导交易&#xff0c;分析潜在的风险和机会&#xff0c;寻找并等待高性价比…