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支持以下五种主要的数据类型:
- 字符串(String):
- 是Redis最基本的数据类型,一个key对应一个value。
- 字符串类型是二进制安全的,这意味着你可以把Redis的字符串类型理解成字节数组,你可以对字符串类型进行很多操作,例如追加、获取子串等。
- 哈希(Hash):
- Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。
- 列表(List):
- Redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。
- 集合(Set):
- Redis的集合是无序的字符串集合,并且集合成员是唯一的,不存在重复的元素。
- Redis集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。
- 有序集合(Sorted Set):
- Redis有序集合和集合一样也是string类型元素的集合,并且集合成员是唯一的。不同的是每个元素都会关联一个double类型的分数(score)。Redis正是通过分数来为集合中的元素进行从小到大的排序。
- 有序集合的成员是唯一的,但分数(score)可以重复。
- 集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。
这些数据类型为Redis提供了丰富的功能集,使其可以灵活地处理各种类型的数据和应用场景。
实际上,使用最多的类型还是字符串。
哪些数据需要放到Redis缓存?
一般来说,放到缓存中的往往是更新频率低,大量读取的数据。
对于应用系统而言,主要分为两类:
一是基础数据,如系统的配置参数,数据字典(性别、证件号码等)。
二是业务数据,如某城市的天气预报数据,电商系统中的秒杀商品。
使用缓存常见误区是什么?
缓存数据没有及时刷新。
将数据放入缓存,意味着内存中多了一份数据库中的数据副本。应用系统会优先从缓存中读取。若数据库中的数据被更新,则需要主动将其更新到缓存,否则业务上读取到的就是陈旧的错误数据,在大多数情况下是不可接受的。
比如秒杀商品变更了价格,用户看到的是缓存中的尚未改变的价格,下单支付时才发现价格变了。
实战
Redis如何安装?
服务器端往往是Linux或Docker环境,如何安装部署根据环境操作即可。
开发环境大多都是windows中,建议使用exe安装,会自动将其注册为windows服务。
通过操作系统的服务管理面板可进行灵活控制启停及开机自动运行。
Redis的日志目录和配置文件参见安装路径,在不清楚各参数含义的情况下,保持默认即可,如下图:
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 {@Autowiredprivate 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: localhostport: 6379password:#新版本Redis的timeout是一个duration,需使用如下写法timeout: 10sdatabase: 0lettuce: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 {@Autowiredpublic 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 {@Beanpublic 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 分钟随机,这样每一个缓存的过期时间的集中程度就会降低,很难引发集体失效的事件。
综上所述,缓存雪崩是一个需要认真对待的问题,合理的缓存策略和管理机制对于维护系统的稳定性和性能至关重要。
相关文章:
Redis实战宝典:基础知识、实战技巧、应用场景及最佳实践全攻略
背景 在Java系统实现过程中,我们不可避免地会借助大量开源功能组件。然而,这些组件往往功能丰富且体系庞大,官方文档常常详尽至数百页。而在实际项目中,我们可能仅需使用其中的一小部分功能,这就造成了一个挑战&#…...
[FFmpeg学习]初级的SDL播放mp4测试
在之前的学习中,通过AVFrame来保存为图片来认识了AVFrame, [FFmpeg学习]从视频中获取图片_ffmpeg 获取图片-CSDN博客 在获取到AVFrame时,还可以调用SDL方法来进行展现,实现播放效果。 参考资料 SDL,ffmpeg实现简单…...
情景题之小明的Linux实习之旅:linux实战练习1(下)【基础命令,权限修改,日志查询,进程管理...】
小明的Linux实习之旅:基础指令练习情景练习题下 前景提要小明是怎么做的场景1:初识Linux,创建目录和文件场景2:权限管理,小明的权限困惑场景3:打包与解压,小明的备份操作场景4:使用G…...
k8s 证书更新
如何使用脚本更新Kubernetes集群证书 引言 Kubernetes集群中,由kubeadm初始化的证书有效期默认为一年。当这些证书接近或已经超过有效期时,它们必须被更新以保证集群的正常运作。本文将介绍如何使用特定脚本来更新这些证书,将它们的有效期延…...
Linux操作系统学习:day01
内容来自:Linux介绍 视频推荐:Linux基础入门教程-linux命令-vim-gcc/g -动态库/静态库 -makefile-gdb调试 day01 基础概念 Linux 是 UNIX 操作系统的一个克隆系统, 但是Linux是开源的。 Linux是一个基于文件的操作系统。操作系统需要和硬件进行交互…...
【Oracle生产运维】数据库服务器负载过高异常排查处理
说明 在Oracle数据库运维工作中,经常会遇到Oracle数据库服务器平均负载(load average)突然异常升高,如果放任不管,严重的情况下会出现数据库宕机、服务器重启等重大故障。因此,当发现数据库服务器平均负载…...
IIR和FIR两种滤波器有什么区别?
概念的区分 IIR(Infinite Impulse Response,无限脉冲响应)和FIR(Finite Impulse Response,有限脉冲响应)滤波器是两种常见的数字信号处理滤波器类型,它们在结构、性能和用途上有显著区别&#…...
让GNSSRTK不再难【第二天-第4部分】
第12讲 GNSS授时与PPS 12.1 为什么需要高精度时间 授时的传统理解就是时间传递或者对时。比如以前手机没这么方便时,大家还都使用石英钟手表看时间时,大家都习惯晚上七点准时对着中央一套的报时,来校准你家的机械钟或者挂钟,这就…...
「OC」UI练习(一)—— 登陆界面
「OC」登陆界面 明确要求 一个登陆界面的组成,用户名提示以及输入框,密码提示提示以及输入框,登陆按钮,以及注册按钮,根据以上要求我们将我们的组件设置为成员变量。 //viewControl.h #import <UIKit/UIKit.h>…...
基于机器学习和深度学习的NASA涡扇发动机剩余使用寿命预测(C-MAPSS数据集,Python代码,ipynb 文件)
以美国航空航天局提供的航空涡扇发动机退化数据集为研究对象,该数据集包含多台发动机从启动到失效期间多个运行周期的多源传感器时序状态监测数据,它们共同表征了发动机的性能退化情况。为减小计算成本,需要对原始多源传感器监测数据进行数据…...
计算机组成原理-常见计算题含IEE754
一、补码加减运算 二、溢出判断 采用一位符号位 采用双符号位 三、定点数的移位运算 算术右移 算数左移 反码的算术移位 补码的算术移位 四、浮点数的表示 一个右规的例子 五、IEEE754 移码...
InnoDB存储引擎非常重要的一个机制--MVCC(多版本并发控制)
Mysql是如何实现隔离性的?(锁MVCC) 隔离性是指一个事务内部的操作以及操作的数据对正在进行的其他事务是隔离的,并发执行的各个事务之间不能相互干扰。隔离性可以防止多个事务并发执行时,可能存在交叉执行导致数据的不…...
【DevOps】服务器硬件基础知识
目录 前言 1、处理器(CPU):服务器的“大脑” 2、内存(RAM):服务器的“工作台” 3、存储(Storage):服务器的“仓库” 4、 网络接口(NIC)&…...
6.10 c语言
7.1 if-else语句 简化形式 if(表达式)语句块 阶梯形式 if(表达式1)语句块1 else if(表达式2)语句块2 嵌套形式 if() if() 语句1 else 语句2 else if() 语句3 else 语句4 表达式一般情况下为逻辑表达式或关系表达式 #include <stdio.h>//从小到大排序,输出顺…...
jenkins插件之Jdepend
JDepend插件是一个为构建生成JDepend报告的插件。 安装插件 JDepend Dashboard -->> 系统管理 -->> 插件管理 -->> Available plugins 搜索 Jdepend, 点击安装构建步骤新增执行shell #执行pdepend if docker exec phpfpm82 /tmp/composer/vendor/bin/pdepe…...
vue3之基于el-image实现图片预览
实现的功能: 图片可放大预览,支持放大、缩小、向左向右旋转查看可点击任意一张图后进行左右切换查看大图 主要使用的方法:splice和concat 主要代码 // template中 <div><el-imagev-for"(item, index) in imgsData":src&q…...
wooyun_2015_110216-Elasticsearch-vulfocus
1.原理 ElasticSearch具有备份数据的功能,用户可以传入一个路径,让其将数据备份到该路径下,且文件名和后缀都可控。 所以,如果同文件系统下还跑着其他服务,如Tomcat、PHP等,我们可以利用ElasticSearch的备…...
Fedora的远程桌面
要在 Fedora 40 上开启远程桌面功能。 首先,要确保已安装 gnome-remote-desktop 和 vino 包。 这些软件包通常默认安装在 Fedora 的 GNOME 桌面环境中。 可以按照以下步骤操作: 1、判断电脑是否安装了 gnome-remote-desktop 和 vino 包: tomfedora:…...
CSS id选择器
目录 任务描述 相关知识 id选择器 id选择器语法 类选择器与id选择器的区别 编程要求 任务描述 在本关中,你将通过id选择器的方式完成页面菜单栏样式布局,栏目导航等任务。 完成任务之后,基本页面效果如下: 动态效果如下&am…...
22.搭积木
上海市计算机学会竞赛平台 | YACSYACS 是由上海市计算机学会于2019年发起的活动,旨在激发青少年对学习人工智能与算法设计的热情与兴趣,提升青少年科学素养,引导青少年投身创新发现和科研实践活动。https://www.iai.sh.cn/problem/717 题目描述 小爱用积木搭起一座金字塔。为…...
手机投屏到电脑时,手机提示连接失败
前言 注意,本方法建立在你已经通过其他帖子等解决了前置条件的情况下,手机提示连接失败情况下,包括但不限于关闭防火墙、安装无线投屏工具、手机和电脑连接在同一个WiFi频段下、关闭杀毒软件等。 具体操作方法 1、请进入设置 > 系统和…...
软件测试--Mysql快速入门
文章目录 软件测试-mysql快速入门sql主要划分mysql常用的数据类型sql基本操作常用字段的约束:连接查询mysql内置函数存储过程视图事务索引 软件测试-mysql快速入门 sql主要划分 sql语言主要分为: DQL:数据查询语言,用于对数据进…...
什么是PV操作
PV操作是一种在操作系统中用于同步和互斥的机制,它基于信号量(Semaphore)的概念。在并发编程中,多个进程或线程可能会同时访问共享资源,PV操作可以用来确保这些访问是同步的,以防止竞态条件和数据不一致的问题。 PV操作包括两个原子操作: P操作(Proberen,测试):这…...
差动放大器
差动器的出现是为了解决直接耦合电路存在的零点漂移问题,另外,差动放大器还有灵活的输入,输出方式。 一,基本差动放大器 差动放大器在电路结构上具有对称性,三极管VT1,VT2同型号,R1R2,R3R4,R5…...
【数据结构与算法 经典例题】括号匹配问题
💓 博客主页:倔强的石头的CSDN主页 📝Gitee主页:倔强的石头的gitee主页 ⏩ 文章专栏:《数据结构与算法 经典例题》C语言 期待您的关注 目录 一、问题描述 二、解题思路 🍃破解之道 🍃…...
2024年6月最新开源电视影视TVAPP原生源码和后台管理平台源码及完整教程
本套源码为本人维护更新完善半年左右的还在使用开发的源码,与市面上倒卖的残次品不一样,没有可比性,向下兼容安卓4.0,向上兼容安卓13以上TV电视系统, 完全无闪退,弹窗报错,卡死、异常死循环残次…...
[大模型]GLM4-9B-chat Lora 微调
本节我们简要介绍如何基于 transformers、peft 等框架,对 LLaMA3-8B-Instruct 模型进行 Lora 微调。Lora 是一种高效微调方法,深入了解其原理可参见博客:知乎|深入浅出 Lora。 这个教程会在同目录下给大家提供一个 nodebook 文件,…...
目标检测算法YOLOv9简介
YOLOv9由Chien-Yao Wang等人于2024年提出,论文名为:《YOLOv9: Learning What You Want to Learn Using Programmable Gradient Information》,论文见:https://arxiv.org/pdf/2402.13616 ;源码见: https://github.com/W…...
达梦数据库搭建守护集群
前言 DM 数据守护(Data Watch)是一种集成化的高可用、高性能数据库解决方案,是数据库异地容灾的首选方案。通过部署 DM 数据守护,可以在硬件故障(如磁盘损坏)、自然灾害(地震、火灾)…...
OpenGL-ES 学习(6)---- Ubuntu OES 环境搭建
OpenGL-ES Ubuntu 环境搭建 此的方法在 ubuntu 和 deepin 上验证都可以成功搭建 目录 OpenGL-ES Ubuntu 环境搭建软件包安装第一个三角形基于 glfw 实现基于 X11 实现 软件包安装 sudo apt install libx11-dev sudo apt install libglfw3 libglfw3-dev sudo apt-get install…...
wordpress用户中心授权码/营业推广策略
阅读本文前,请您先点击上面的蓝色字体,再点击“关注”,这样您就可以继续免费收到最新文章了。每天都有分享。完全是免费订阅,请放心关注。 …...
东莞万江今天最新通知/搜索引擎优化中的步骤包括
JUC中提供了Condition对象, 用于让指定线程等待与唤醒, 按照预期顺序执行, 它必须和ReentrantLock重入锁配合使用 Conddition用于替代wait() / notify()方法 notify 只能随机唤醒等待的线程, 而Condition可以唤醒指定的线程, 这样有利于更好的控制并发程序 Condition核心方法…...
浙江自己如何做网站/百度首页优化
P1554 梦中的统计 题目背景 Bessie 处于半梦半醒的状态。过了一会儿,她意识到她在数数,不能入睡。 题目描述 Bessie的大脑反应灵敏,仿佛真实地看到了她数过的一个又一个数。她开始注意每一个数码(0..9):每一个数码在计数的过程中出…...
vs2010网站制作教程/排行榜哪个网站最好
这里写目录标题1. 相关符号2. 基本知识 【同时行动博弈,或标准博弈】2.1 定义2.2 策略博弈表示2.3 最优反应策略2.3.1 最优反应策略定义3. 策略型博弈案例3.1 案例一3.2 案例二3.3 案例三3.4 案例四3.5 案例五3.6 案例六3.7 案例七3.8 案例八3.9 案例九参考1. 相关符…...
做西点网站/社群运营的经典案例
1 制定目的按照项目组所有成员的贡献度,为“项目阶段奖金”的合理分配提供依据。2 奖金池金额及其运作方法奖金总额按月度发放,月奖金总额为:;根据项目运作的进度及客户反馈进行评估,奖罚并施;细则如下。2.…...
创新的中小型网站建设/百度新闻app
面试例题1:对象与实例有什么区别? 解析:在Java的内存分配中,对象和实例是不同的,前者分配在内存堆里,后者分配在堆栈里,至于为什么要这样分,参考一下其他的资料。对象是不变的东西&a…...