为什么Redis 6.0引入了多线程?

2020年5月份,Redis正式推出了6.0版本,这个版本中有很多重要的新特性,其中多线程特性引起了广泛关注。 但是,需要提醒大家的是,Redis 6.0中的多线程,也只是针对处理网络请求过程采用了多线程,而数据的读写命令,仍然是单线程处理的。 但是,不知道会不会有人有这样的疑问: Redis不是号称单线程也有很高的性能么? 不是说多路复用技术已经大大的提升了IO利用率了么,为啥还需要多线程? 主要是因为我们对Redis有着更高的要求。 根据测算,Redis 将所有数据放在内存中,内存的响应时长大约为 100 纳秒,对于小数据包,Redis 服务器可以处理 80,000 到 100,000 QPS,这么高的对于 80% 的公司来说,单线程的 Redis 已经足够使用了。 但随着越来越复杂的业务场景,有些公司动不动就上亿的交易量,因此需要更大的 QPS。 为了提升QPS,很多公司的做法是部署Redis集群,并且尽可能提升Redis机器数。但是这种做法的资源消耗是巨大的。 而经过分析,限制Redis的性能的主要瓶颈出现在网络IO的处理上,虽然之前采用了多路复用技术。但是我们前面也提到过,多路复用的IO模型本质上仍然是同步阻塞型IO模型。 下面是多路复用IO中select函数的处理过程: 从上图我们可以看到,在多路复用的IO模型中,在处理网络请求时,调用 select (其他函数同理)的过程是阻塞的,也就是说这个过程会阻塞线程,如果并发量很高,此处可能会成为瓶颈。 虽然现在很多服务器都是多个CPU核的,但是对于Redis来说,因为使用了单线程,在一次数据操作的过程中,有大量的CPU时间片是耗费在了网络IO的同步处理上的,并没有充分的发挥出多核的优势。 如果能采用多线程,使得网络处理的请求并发进行,就可以大大的提升性能。多线程除了可以减少由于网络 I/O 等待造成的影响,还可以充分利用 CPU 的多核优势。 所以,Redis 6.0采用多个IO线程来处理网络请求,网络请求的解析可以由其他线程完成,然后把解析后的请求交由主线程进行实际的内存读写。提升网络请求处理的并行度,进而提升整体性能。 但是,Redis 的多 IO 线程只是用来处理网络请求的,对于读写命令,Redis 仍然使用单线程来处理。 那么,在引入多线程之后,如何解决并发带来的线程安全问题呢? 这就是为什么我们前面多次提到的"Redis 6.0的多线程只用来处理网络请求,而数据的读写还是单线程"的原因。 Redis 6.0 只有在网络请求的接收和解析,以及请求后的数据通过网络返回给时,使用了多线程。而数据读写操作还是由单线程来完成的,所以,这样就不会出现并发问题了。

March 22, 2026 · 1 min · santu

为什么Redis不支持回滚?

典型回答 我们都知道,Redis是不支持回滚的,即使是Redis的事务和Lua脚本,在执行的过程中,如果出现了错误,也是无法回滚的,可是,为什么呢? 在Redis的官网文档中明确的提到过,不支持回滚:https://redis.io/docs/interact/transactions/ 不支持回滚主要的原因是支持回滚将对 Redis 的简洁性和性能产生重大影响。 然后,Redis官方博客中还有一篇文章,You Don’t Need Transaction Rollbacks in Redis 这里介绍了更多的内容。 我这里试着帮大家总结一下,主要有以下几个原因: 1、使用场景:Redis 通常用作缓存和,而不是作为需要复杂事务处理的关系型数据库。因此,它的目标用户通常不需要复杂的事务支持。如果需要的话,直接用数据库就行了。 2、性能优先:Redis 是一个高性能的K-V存储系统,它优化了速度和效率。引入回滚机制会增加复杂性和开销,从而影响其性能。 3、简化设计:Redis 的设计哲学倾向于简单和高效。回滚机制会使系统变得复杂,增加了错误处理和状态管理的难度。 4、数据类型和操作:Redis 支持的数据类型和操作通常不需要复杂的事务支持。大多数命令都是原子性的。 5、单线程模型:Redis事务是提交后一次性在单线程中执行的,而关系型数据库如MySQL是交互式的多线程模型执行的,所以MySQL需要事务的回滚来确保并发更新结果不出现异常。而Redis不太需要。 6、出错情况:在Redis中,命令失败的原因比较有限:语法错误、操作的数据的类型不一致、Redis资源不足等。而这几种问题,是应该在编码阶段就发现的,而不应该在Redis执行过程中出现。 总结一下,因为Redis的设计就是简单、高效等,所以引入事务的回滚机制会让系统更加的复杂,并且影响性能。从使用场景上来说,Redis一般都是被用作缓存的,不太需要很复杂的事务支持,当人们需要复杂的事务时会考虑持久化的关系型数据库。相比于关系型数据库,Redis是通过单线程执行的,在执行过程中,出现错误的概率比较低,并且这些问题一般来编译阶段都应该被发现,所以就不太需要引入回滚机制。 扩展知识 不支持回滚如何保证原子性? ✅并发编程中的原子性和数据库ACID的原子性一样吗? ✅为什么Lua脚本可以保证原子性?

March 22, 2026 · 1 min · santu

为什么Redis设计成单线程也能这么快?

Redis的性能很好,除了因为他基于内存、有高效的数据结构等等原因以外,还有一个重要的原因那就是他在单线程中使用多路复用 I/O技术也能提升Redis的I/O利用率。 Redis为什么这么快? Redis的多路复用 多路复用这个词,相信很多人都不陌生。那么,Redis的多路复用技术有什么特别的呢? 这里先讲讲Linux多路复用技术,就是多个进程的IO可以注册到同一个管道上,这个管道会统一和内核进行交互。当管道中的某一个请求需要的数据准备好之后,进程再把对应的数据拷贝到用户空间中。 多看一遍上面这张图和上面那句话,后面可能还会用得到。 也就是说,通过一个线程来处理多个IO流。 IO多路复用在Linux下包括了三种,select、poll、epoll,抽象来看,他们功能是类似的,但具体细节各有不同。 其实,Redis的IO多路复用程序的所有功能都是通过包装操作系统的IO多路复用函数库来实现的。每个IO多路复用函数库在Redis源码中都有对应的一个单独的文件。  在Redis 中,每当一个套接字准备好执行连接应答、写入、读取、关闭等操作时,就会产生一个文件事件。因为一个服务器通常会连接多个套接字,所以多个文件事件有可能会并发地出现。  一旦有请求到达,就会交给 Redis 线程处理,这就实现了一个 Redis 线程处理多个 IO 流的效果。 所以,Redis选择使用多路复用IO技术来提升I/O利用率。 而之所以Redis能够有这么高的性能,不仅仅和采用多路复用技术和单线程有关,此外还有以下几个原因: 1、完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。 2、数据结构简单,对数据操作也简单,如哈希表、跳表都有很高的性能。 3、采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU 4、使用多路I/O复用模型

March 22, 2026 · 1 min · santu

为什么需要延迟双删,两次删除的原因是什么?

典型回答 其实这个问题在以下两篇文章中都讲过,但是还是有的读者不太理解,那么单独把这个高频问题拿出来说一下,请先看以下两篇文章,否则会看不懂本文: ✅什么情况下会出现数据库和缓存不一致的问题? ✅如何解决Redis和数据库的一致性问题? 所谓延迟双删,其实是: 1、先删除缓存 2、更新数据库 3、再删除缓存 1 2 3 4 5 6 7 8 9 10 11 @startuml autonumber 外部服务 -> 服务端应用:更新数据 服务端应用-> Redis: 第一次删除缓存 服务端应用-> 数据库: 更新数据库 服务端应用 -> 外部服务:更新成功 服务端应用--> Redis: 第二次删除缓存 @enduml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @Service public class ProductService { @Autowired private StringRedisTemplate redisTemplate; // 创建单线程的定时任务调度器 private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); public void updateProduct(Product product) { // 第一次删除缓存 deleteCache(product.getId()); // 更新数据库 updateProductInDB(product); // 2秒后执行第二次删除 scheduler.schedule(() -> deleteCache(product.getId()), 2, TimeUnit.SECONDS); } } 第一次删除缓存的原因: ...

March 22, 2026 · 1 min · santu

什么是Redis的Pipeline,和事务有什么区别?

典型回答 Redis 的 Pipeline 机制是一种用于优化网络延迟的技术,主要用于在单个请求/响应周期内执行多个命令。在没有 Pipeline 的情况下,每执行一个 Redis 命令,客户端都需要等待服务器响应之后才能发送下一个命令。这种往返通信尤其在网络延迟较高的环境中会显著影响性能。 在 Pipeline 模式下,客户端可以一次性发送多个命令到 Redis 服务器,而无需等待每个命令的响应。Redis 服务器接收到这批命令后,会依次执行它们并返回响应。 所以,Pipeline通过减少客户端与服务器之间的往返通信次数,可以显著提高性能,特别是在执行大量命令的场景中。 但是,需要注意的是,Pipeline是不保证原子性的,他的多个命令都是独立执行的,Redis并不保证这些命令可以以不可分割的原子操作进行执行。这是Pipeline和Redis的事务的最大的区别。 虽然都是执行一些相关命令,但是Redis的事务提供了原子性保障,保证命令执行以不可分割、不可中断的原子性操作进行,而Pipeline则没有原子性保证。 但是他们在命令执行上有一个相同点,那就是如果执行多个命令过程中,有一个命令失败了,其他命令还是会被执行,而不会回滚的。 ✅Redis 的事务机制是怎样的? 扩展知识 如何使用Pipeline 在 Java 中,可以用Jedis来使用pipeline: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 import redis.clients.jedis.Jedis; import redis.clients.jedis.Pipeline; public class RedisPipelineExample { public static void main(String[] args) { // 连接到 Redis 服务器 try (Jedis jedis = new Jedis("localhost", 6379)) { // 创建 Pipeline Pipeline pipeline = jedis.pipelined(); // 向 Pipeline 添加命令 pipeline.set("foo", "bar"); pipeline.get("foo"); pipeline.incr("counter"); // 执行 Pipeline 中的所有命令,并获取响应 List<Object> responses = pipeline.syncAndReturnAll(); // 输出响应 for (Object response : responses) { System.out.println(response); } } catch (Exception e) { e.printStackTrace(); } } }

March 22, 2026 · 1 min · santu

什么是Redis的数据分片?

典型回答 Redis的数据分片(sharding)是一种将一个Redis数据集分割成多个部分,分别存储在不同的Redis节点上的技术。它可以用于将一个单独的Redis数据库扩展到多个物理机器上,从而提高Redis集群的性能和可扩展性。 Redis数据分片的实现方式通常是将数据按照某种规则(例如,key的hash值)分配到不同的节点上。当客户端想要访问某个key时,它会先计算出这个key应该存储在哪个节点上,然后直接连接到该节点进行操作。因此,对于客户端而言,Redis集群就像是一个大型的、统一的数据库,而不需要关心数据的实际分布情况。 在Redis的Cluster 集群模式中,使用哈希槽(hash slot)的方式来进行数据分片,将整个数据集划分为多个槽,每个槽分配给一个节点。客户端访问数据时,先计算出数据对应的槽,然后直接连接到该槽所在的节点进行操作。Redis Cluster还提供了自动故障转移、数据迁移和扩缩容等功能,能够比较方便地管理一个大规模的Redis集群。 Redis Cluster将整个数据集划分为16384个槽,每个槽都有一个编号(0~16383),集群的每个节点可以负责多个hash槽,客户端访问数据时,先根据key计算出对应的槽编号,然后根据槽编号找到负责该槽的节点,向该节点发送请求。 在 Redis 的每一个节点上,都有这么两个东西,一个是槽(slot),它的的取值范围是:0-16383。还有一个就是 cluster,可以理解为是一个集群管理的插件。当我们在存取的 Key 的时候,Redis 会根据 CRC16 算法得出一个结果,然后把结果对 16384 求余数,这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,通过这个值,去找到对应的插槽所对应的节点,然后直接自动跳转到这个对应的节点上进行存取操作。 Redis Cluster中的数据分片具有以下特点: 提升性能和吞吐量:通过在多个节点上分散数据,可以并行处理更多的操作,从而提升整体的性能和吞吐量。这在高流量场景下尤其重要,因为单个节点可能无法处理所有请求。 提高可扩展性:分片使得Redis可以水平扩展。可以通过添加更多节点扩展数据库的容量和处理能力。 更好的资源利用:分片允许更有效地利用服务器资源。每个节点只处理数据的一部分,这降低了单个节点的内存和计算需求。 避免单点故障:在没有分片的情况下,如果唯一的Redis服务器发生故障,整个服务可能会停止。在分片的环境中,即使一个节点出现问题,其他节点仍然可以继续运行。 数据冗余和高可用性:在某些分片策略中,如Redis集群,每个分片的数据都可以在集群内的其他节点上进行复制。这意味着即使一个节点失败,数据也不会丢失,从而提高了系统的可用性。 扩展知识 16384 Redis Cluster将整个数据集划分为16384个槽,为什么是16384呢,这个数字有什么特别的呢? 这个问题在Github上有所讨论,Redis的作者也下场做过回复:https://github.com/redis/redis/issues/2576 The reason is: 1、Normal heartbeat packets carry the full configuration of a node, that can be replaced in an idempotent way with the old in order to update an old config. This means they contain the slots configuration for a node, in raw form, that uses 2k of space with16k slots, but would use a prohibitive 8k of space using 65k slots. ...

March 22, 2026 · 2 min · santu

什么是Redis的渐进式rehash

典型回答 在 Redis 中,他的hash表结构随着数据量的增大可能会导致扩容,通常是将数组大小扩大为原来的两倍,而在扩容过程中,因为容量变化了,所以元素在新的hash表中所处的位置也会随之变化,这个变化过程就是通过rehash实现的。 可以参考下hashmap的rehash过程: ✅HashMap是如何扩容的? 而随着Redis的hash表越来越大,rehash的成本也会越来越高。Redis中实现了一种渐进式rehash的方案,他可以在哈希表rehash操作时,分多个步骤逐渐完成的方式,这样不会因为要一次性把所有元素都完成迁移而导致IO升高,线程阻塞。这个特性使得Redis可以在继续提供读写服务的同时,逐步迁移数据到新的哈希表,而不会对性能造成明显的影响。 在 Redis 中,他的hash结构其实底层是使用了两个全局哈希表的。我们把他们称之为哈希表 1 和哈希表 2。并且会维护一个rehashindex ,初始值为-1,来记录当前rehash的下标位置。 当我们开始向hash表中插入数据时,只使用哈希表 1,不断向其中添加数据。 而随着数据逐渐增多,当元素个数和hash表中的数组长度一致时,就会触发rehash动作,这时候,会把哈希表2的容量扩大一倍。然后就开始进入rehash流程。 在进入rehash过程中,不会立刻把哈希表1中的数据全部rehash到哈希表2中,而是在后续有新的增删改查操作时,会从头开始进行rehash动作。 假如,我们现在要新增一个元素: 那么就会从当前的hashindex开始,把这个哈希表1的hashindex这个位置的桶中的数据全部rehash到哈希表2中,然后rehashindex +1 。 然后再在哈希表2中进行添加操作: 在后续的其他操作中也一样,会沿着hashindex一直往后开始进行逐个桶的rehash,一直到哈希表1中的元素全部完成rehash。 然后再把哈希表1和哈希表2的指针互换一下(后续会再把哈希表2给直接置为NULL),后续的增删改查继续在新的哈希表1中操作,直到下一次rehash开始。 扩展知识 查询怎么办 在Rehash开始时,Redis会创建一个新的哈希表(称为哈希表2),而旧的哈希表(称为哈希表1)仍然保留。这时,Redis同时维护这两个哈希表。 当执行查询操作时,Redis首先会在哈希表1中查找键。如果在哈希表1中没找到,Redis会接着在哈希表2中查找。这确保了即使在Rehash过程中,所有的键都是可查询的。 当哈希表1中的所有数据都迁移到哈希表2后,Rehash操作完成。此时,哈希表1会被释放,哈希表2成为当前使用的哈希表。 查询就直接查询哈希表2即可

March 22, 2026 · 1 min · santu

什么是RedLock,他解决了什么问题?

典型回答 RedLock是Redis的作者提出的一个多节点分布式锁算法,旨在解决使用单节点Redis分布式锁可能存在的单点故障问题。(https://redis.io/docs/manual/patterns/distributed-locks/ ) Redis的单点故障问题: 1、在使用单节点Redis实现分布式锁时,如果这个Redis实例挂掉,那么所有使用这个实例的客户端都会出现无法获取锁的情况。 2、当使用集群模式部署的时候,如果master一个客户端在master节点加锁成功了,然后没来得及同步数据到其他节点上,他就挂了, 那么这时候如果选出一个新的节点,再有客户端来加锁的时候,就也能加锁成功,因为数据没来得及同步,新的master会认为这个key是不存在的。 如上图,就是我们前面说的第二个问题。 RedLock通过使用多个Redis节点,来提供一个更加健壮的分布式锁解决方案,能够在某些Redis节点故障的情况下,仍然能够保证分布式锁的可用性。 RedLock是通过引入多个Redis节点来解决单点故障的问题。 在进行加锁操作时,RedLock会向每个Redis节点发送相同的命令请求,每个节点都会去竞争锁,如果至少在大多数节点上成功获取了锁,那么就认为加锁成功。反之,如果大多数节点上没有成功获取锁,则加锁失败。这样就可以避免因为某个Redis节点故障导致加锁失败的情况发生。 在redis集群中有3个节点的情况下: 1、客户端想要获取锁时,会生成一个全局唯一的ID(官方文档建议使用系统时间来生成这个ID) 2、客户端尝试使用这个ID获取所有redis节点的同意,这一步通过使用SETNX命令实现。 3、如果有2个以上的节点同意,那么锁就被成功设置了。 4、获取锁之后,用户可以执行想要的操作。 5、最后,不想用这把锁的时候,再尝试依次解锁,无论锁是否成功获取。 这样,当超过半数以上的节点都写入成功之后,即使master挂了,新选出来的master也能保证刚刚的那个key一定存在(否则这个节点就不会被选为master)。 需要注意的是,RedLock并不能完全解决分布式锁的问题。例如,在脑裂的情况下,RedLock可能会产生两个客户端同时持有锁的情况。 扩展知识 如何使用 了解了RedLock的机制之后,我们在Java中如何使用呢?可以直接使用RedissonRedLock,他是支持RedLock算法的: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 Config config1 = new Config(); config1.useSingleServer() .setAddress("redis://127.0.0.1:6379"); Config config2 = new Config(); config2.useSingleServer() .setAddress("redis://127.0.0.1:6380"); Config config3 = new Config(); config3.useSingleServer() .setAddress("redis://127.0.0.1:6381"); RedissonClient redissonClient1 = Redisson.create(config1); RedissonClient redissonClient2 = Redisson.create(config2); RedissonClient redissonClient3 = Redisson.create(config3); RLock lock1 = redissonClient1.getLock("lockKey"); RLock lock2 = redissonClient2.getLock("lockKey"); RLock lock3 = redissonClient3.getLock("lockKey"); RedissonRedLock redLock = new RedissonRedLock(lock1, lock2, lock3); boolean lockResult = redLock.tryLock(waitTime, leaseTime, TimeUnit.MILLISECONDS); // 业务逻辑 if (lockResult) { try { // 业务逻辑 } finally { redLock.unlock(); } } else { // 获取锁失败的处理逻辑 } redissonClient1.shutdown(); redissonClient2.shutdown(); redissonClient3.shutdown(); 除了Redisson,还有其他的一些工具可以用于实现RedLock,比如Java的Redlock-java库、Go的Redsync库等。这些工具的使用方式类似,都是创建多个Redis实例,然后使用RedLock算法获取分布式锁。 ...

March 22, 2026 · 1 min · santu

什么是缓存击穿、缓存穿透、缓存雪崩?

典型回答 缓存击穿:是指当某一key的缓存过期时大并发量的请求同时访问此key,瞬间击穿缓存服务器直接访问数据库,让数据库处于负载的情况。 缓存穿透:是指缓存服务器中没有缓存数据,数据库中也没有符合条件的数据,导致业务系统每次都绕过缓存服务器查询下游的数据库,缓存服务器完全失去了其应有的作用。 缓存雪崩:是指当大量缓存同时过期或缓存服务宕机,所有请求的都直接访问数据库,造成数据库高负载,影响性能,甚至数据库宕机。 在记忆这几个概念的时候,不要死记硬背,你可以尝试这样记忆: 首先,雪崩的概念比较容易记,就是很多个Key同时过期才会雪崩,“缓存雪崩的时候没有一个Key是无辜的”。至于穿透和击穿,区别在于穿透是"透",什么叫透呢,那就是不仅缓存被击穿了,数据库也被击穿了,这种才叫透。所以,这种缓存和数据库中都没有的情况叫做"缓存穿透"。 扩展知识 缓存穿透 举个例子,一个女孩子去门店买口红,到了门店之后被告知她想要的那个色号已经没有了。于是她要求店员去问总部还有没有货。总部发现这个色号也没有了,于是女孩子就离开了。 过了一会另一个女孩子又来了,也想要购买同一个色号,店员就又总部问了一次。如此反复。 女孩子买口红不仅需要门店帮忙查询,还需要总部也进行盘货。类似这种情况,在缓存领域有一个类似的概念叫做缓存穿透。 缓存穿透是指缓存服务器中没有缓存数据,数据库中也没有符合条件的数据,导致业务系统每次都绕过缓存服务器查询下游的数据库,缓存服务器完全失去了其应用的作用。 缓存空值 解决多次询问总部的方法比较简单,如果口红门店在帮第一个女孩子查询之后,就记录下来这个色号已经没有了,下次其他女孩再来问这个色号的时候,直接告诉她没货了。 这样就可以避免每次都惊动总部了。 在缓存中,之所以会发生穿透,就是因为缓存没有对那些不存在的值得Key缓存下来,从而导致每次查询都要请求到数据库。 那么我们就可以为这些key对应的值设置为null并放到缓存中,这样再出现查询这个key 的请求的时候,直接返回null即可 。 但是还需要注意的就是需要有一个失效时间,因为如果不设置失效的话,如果哪天总部有货了,门店还是当做没货的话,就会影响销量了。 BloomFilter 很多时候,缓存穿透是因为有很多恶意流量的请求,这些请求可能随机生成很多Key来请求查询,这些肯定在缓存和数据库中都没有,那就很容易导致缓存穿透。 针对类似的情况,可以使用一个过滤器。 比如如果有一群人经常来门店问一些根本不存在的色号,比如五彩斑斓的黑,这些色号该品牌根本没生产过的话,店员就可以直接告诉顾客不存在就行了,也不需要惊动总部。 在缓存穿透防治上常用的技术是布隆过滤器(Bloom Filter)。 ✅什么是布隆过滤器,实现原理是什么? 布隆过滤器是一种比较巧妙的概率性数据结构,它可以告诉你数据一定不存在或可能存在,相比Map、Set、List等传统数据结构它占用内存少、结构更高效。 对于缓存穿透,我们可以将查询的数据条件都哈希到一个足够大的布隆过滤器中,用户发送的请求会先被布隆过滤器拦截,一定不存在的数据就直接拦截返回了,从而避免下一步对数据库的压力。 缓存击穿 有一种比较特殊的情况,那就是如果某一个热门色号的口红刚好卖完了,这时候有很多顾客同时来咨询要购买这个色号,那么门店内的多个售货员可能分别给总部打电话咨询是否有存货。 或者如果有多家门店同时卖完了,那么总部接收到的咨询量就会剧增。类似这种情况,在缓存领域有一个类似的概念叫做缓存击穿。 缓存击穿是指当某一key的缓存过期时大并发量的请求同时访问此key,瞬间击穿缓存服务器直接访问数据库,让数据库处于负载的情况。 异步定时更新 如果提前知道某一个色号比较畅销的话,那就可以定时的咨询总部是否还有存货,定时的更新库存情况就可以避免上面这种情况了。 在缓存处理上,同理,比如某一个热点数据的过期时间是1小时,那么每59分钟,通过定时任务去更新这个热点key,并重新设置其过期时间。 互斥锁 还有一种解决办法,那就是如果很多顾客咨询的是同一个色号的口红,那么就先处理第一个用户的咨询,其他同样请求的顾客先排队等待。一直到店员从总部那里获取到最新的库存信息后,就可以安排其他人继续购买了。 在缓存处理上,通常使用一个互斥锁来解决缓存击穿的问题。简单来说就是当Redis中根据key获得的value值为空时,先锁上,然后从数据库加载,加载完毕,释放锁。若其他线程也在请求该key时,发现获取锁失败,则先阻塞。 缓存雪崩 如果门店内的多个色号的口红同时售罄了,并且门店在这个时间点刚好也不知道总部有没有库存了,这时候如果有大量顾客来到门店购物的话,就会有更多的咨询电话打到总部那里。 或者是门店突然出现问题了,不能提供服务了,很多顾客就可能自己打电话到总部咨询库存情况。类似这种情况,在缓存领域有一个类似的概念叫做缓存雪崩。 缓存雪崩是指当大量缓存同时过期或缓存服务宕机,所有请求的都直接访问数据库,造成数据库高负载,影响性能,甚至数据库宕机。 不同的过期时间 为了避免缓存雪崩,门店可以考虑给不同的色号的口红预留不同的库存,并且采用不同的频率咨询总部库存情况,更新到门店中。这样就可以避免突然同一个时间点所有色号都售罄。 为了避免大量的缓存在同一时间过期,可以把不同的key过期时间设置成不同的, 并且通过定时刷新的方式更新过期时间。 集群 为了避免门店出问题导致大量顾客直接打电话到总部,可以考虑开更多的门店,将用户分流到多个店铺中。 类似的,在缓存雪崩问题防治上面,一个比较典型的技术就是采用集群方式部署,使用集群可以避免服务单点故障。

March 22, 2026 · 1 min · santu

介绍一下Redis的集群模式?

Redis有三种主要的集群模式,用于在分布式环境中实现高可用性和数据复制。这些集群模式分别是:主从复制(Master-Slave Replication)、哨兵模式(Sentinel)和Redis Cluster模式。 主从模式 主从复制是Redis最简单的集群模式。这个模式主要是为了解决单点故障的问题,所以将数据复制多个副本中,这样即使有一台服务器出现故障,其他服务器依然可以继续提供服务。 主从模式中,包括一个主节点(Master)和一个或多个从节点(Slave)。**主节点负责处理写操作和读操作,而从节点则复制主节点的数据,并且只能处理读操作。**当主节点发生故障时,可以将一个从节点升级为主节点,实现故障转移(需要手动实现)。 **主从复制的优势在于简单易用,适用于读多写少的场景。**它提供了数据备份功能,并且可以有很好的扩展性,只要增加更多的从节点,就能让整个集群的读的能力不断提升。 但是主从模式最大的缺点,就是不具备故障自动转移的能力,没有办法做容错和恢复。 主节点和从节点的宕机都会导致客户端部分读写请求失败,需要人工介入让节点恢复或者手动切换一台从节点服务器变成主节点服务器才可以。并且在主节点宕机时,如果数据没有及时复制到从节点,也会导致数据不一致。 哨兵模式 为了解决主从模式的无法自动容错及恢复的问题,Redis引入了一种哨兵模式的集群架构。 哨兵模式是在主从复制的基础上加入了哨兵节点。哨兵节点是一种特殊的Redis节点,用于监控主节点和从节点的状态。当主节点发生故障时,哨兵节点可以自动进行故障转移,选择一个合适的从节点升级为主节点,并通知其他从节点和应用程序进行更新。 在原来的主从架构中,引入哨兵节点,其作用是监控Redis主节点和从节点的状态。通常需要部署多个哨兵节点,以确保故障转移的可靠性。 哨兵节点定期向所有主节点和从节点发送PING命令,如果在指定的时间内未收到PONG响应,哨兵节点会将该节点标记为主观下线。如果一个主节点被多数哨兵节点标记为主观下线,那么它将被标记为客观下线。 当主节点被标记为客观下线时,哨兵节点会触发故障转移过程。它会从所有健康的从节点中选举一个新的主节点,并将所有从节点切换到新的主节点,实现自动故障转移。同时,哨兵节点会更新所有客户端的配置,指向新的主节点。 哨兵节点通过发布订阅功能来通知客户端有关主节点状态变化的消息。客户端收到消息后,会更新配置,将新的主节点信息应用于连接池,从而使客户端可以继续与新的主节点进行交互。 这个哨兵模式的优点就是为整个集群提供了一种故障转移和恢复的能力。 Cluster模式 Redis Cluster是Redis中推荐的分布式集群解决方案。它将数据自动分片到多个节点上,每个节点负责一部分数据。 Redis Cluster采用主从复制模式来提高可用性。每个分片都有一个主节点和多个从节点。主节点负责处理写操作,而从节点负责复制主节点的数据并处理读请求。 Redis Cluster能够自动检测节点的故障。当一个主节点失去连接或不可达时,Redis Cluster会尝试将该节点标记为不可用,并从可用的从节点中提升一个新的主节点。 Redis Cluster是适用于大规模应用的解决方案,它提供了更好的横向扩展和容错能力。它自动管理数据分片和故障转移,减少了运维的负担。 Cluster模式的特点是数据分片存储在不同的节点上,每个节点都可以单独对外提供读写服务。不存在单点故障的问题。 关于分片的规则和细节,参考: ✅什么是Redis的数据分片? 关于 Cluster 中存在对事务和 lua 的限制,参考: ✅Redis Cluster 中使用事务和 lua 有什么限制?

March 22, 2026 · 1 min · santu

留言给博主