文章目录
- 1. Redis 有哪些数据结构?
- 2. String 的底层实现是什么?为什么实现了 SDS?
- 3. Redis 中跳表的实现原理是什么?
- 4. Redis 为什么这么快?
- 5. Redis 为什么用跳表,而不用平衡树、红黑树或者 B+树?
- 6. Redis 中有哪些内存淘汰策略?
- 7. Redis中的BigKey问题是什么?如何解决?
- 8. 如何解决Redis中的热点key问题?
- 9. Redis中的缓存击穿、缓存穿透和缓存雪崩是什么?
- 10. Redis数据过期后的删除策略是什么?
- 11. Redis主从复制的实现原理是什么?
- 12. Redis集群的实现原理是什么?
- 13. Redis通常应用于哪些场景?
- 14. 为什么Redis设计为单线程?6.0版本为何引入多线程?/ Redis6.0 之前为什么不使用多线程?Redis6.0 之后为何引入了多线程?
- 15. Redis支持事务吗?如何买现?
- 16. Redis的Pipeline功能是什么?
- 17. Redis的持久化机制有哪些?
- 18. Redis的哨兵机制是什么?
- 19. Redis的订阅发布功能是什么?你了解吗?
- 20. Redisson分布式锁的原理?
- 21. Redisson看门狗(watchdog)机制了解吗?
- 22. Redis和Memcached有哪些区别?
- 23. Redis的hash是什么?
- 24. RedisCluster模式与Sentinel模式的区别是什么?
- 25. Redis 是单线程,那怎么监听大量的客户端连接呢?
- 26. Redis 给缓存数据设置过期时间有啥用? Redis 是如何判断数据是否过期的呢?过期数据是如何被删除的?
- 27. Redis 事务支持原子性吗?
- 28. 如何保证缓存和数据库的一致性?
内容参考:
https://www.mianshiya.com、
https://javaguide.cn
1. Redis 有哪些数据结构?
Redis常见的数据结构主要有五种,这五种类型分别为:String(字符串)、List(列表)、Hash、Set(集合)、Zset(有序集合,也叫sorted set)。

2. String 的底层实现是什么?为什么实现了 SDS?
Redis 的 String 类型底层使用 SDS (Simple Dynamic String) 实现。SDS 是一种动态、二进制安全的字符串抽象,它通过存储长度信息(O(1) 获取长度)、避免缓冲区溢出、支持二进制安全(通过 len 结束,可包含 ‘\0’)以及通过空间预分配和惰性空间释放减少内存重分配次数等特性,完美克服了 C 语言原生字符串在作为高性能数据库数据结构时的诸多缺陷,是 Redis 高效、可靠和安全运行的重要基石之一。其末尾保留 ‘\0’ 是为了兼容部分 C 库函数,但这 不影响 其核心的二进制安全性。
Redis 为什么要实现 SDS? (解决 C 字符串的缺陷)
SDS 的设计目标是为了解决 C 语言原生字符串在用作 Redis 核心数据结构时所暴露出的主要缺陷:
1) 获取长度时间复杂度高 (O(n) vs O(1)):
- C 字符串: 必须遍历整个字符串直到遇到 ‘\0’ 才能确定长度,时间复杂度为 O(n)。
- SDS: 长度直接存储在 len 字段中,直接读取,时间复杂度为 O(1)。这对于 Redis 频繁执行 STRLEN 命令和高性能要求至关重要。
2)缓冲区溢出 (Buffer Overflow):
- C 字符串: strcat(dest, src) 等操作假设 dest 有足够的空间,否则会导致 dest 之后的数据被覆盖(缓冲区溢出),严重的安全隐患。
- SDS: SDS 的 API(如 sdscat(sds s, const char *t))在执行拼接等修改操作前,会先检查 alloc - len 是否足够。如果不足,API 内部会先自动扩展 SDS 的空间,然后再进行修改,从源头上避免了缓冲区溢出。
3)二进制不安全 (Binary Unsafe):
- C 字符串: 依赖 ‘\0’ 作为结束标识,因此字符串内部不能包含 ‘\0’。这使得 C 字符串只能用于存储文本数据,无法安全存储图片、音视频、压缩数据等包含 ‘\0’ 的二进制数据。
- SDS: 使用 len 属性来判断字符串结束,而不是 ‘\0’。buf 里的数据是二进制安全的(Binary Safe),可以存储任何形式的二进制数据,包括 ‘\0’。这是 Redis 能够作为通用数据存储的关键特性。
4)内存重分配开销大:
- C 字符串: 每次增长(strcat)或缩短(truncate)字符串,都需要显式调用 realloc 来重新分配内存。
- SDS: 通过两个策略大大减少了内存重分配的次数:
- 空间预分配 (Preallocation): 当对 SDS 进行增长操作且需要扩展空间时:
- 如果增长后的新长度 len < 1MB,则额外分配和 新len 相同大小的未使用空间 (alloc = 新len * 2)。
- 如果增长后的新长度 len >= 1MB,则额外分配 1MB 的未使用空间 (alloc = 新len + 1MB)。
- 这样下次再增长时,很可能就不需要再次分配内存了。
- 惰性空间释放 (Lazy Free): 当对 SDS 进行缩短操作时:
- SDS API 不会立即回收缩短后释放的未使用空间(只需更新 len)。
- 这些空间被保留在 alloc 中,留给未来可能的增长操作使用。
- SDS 也提供了 API (sdsRemoveFreeSpace) 来真正释放未使用空间。
- 空间预分配 (Preallocation): 当对 SDS 进行增长操作且需要扩展空间时:
3. Redis 中跳表的实现原理是什么?
跳表主要是通过多层链表来实现,底层链表保存所有元素,而每一层链表都是下一层的子集。
- 插入时,首先从最高层开始查找插入位置,然后随机决定新节点的层数,最后在相应的层中插入节点并更新指针。
- 删除时,同样从最高层开始查找要删除的节点,并在各层中更新指针,以保持跳表的结构。
- 查找时,从最高层开始,逐层向下,直到找到目标元素或确定元素不存在。查找效率高,时间复杂度为O(logn)

4. Redis 为什么这么快?
主要有3个方面的原因,分别是存储方式、优秀的线程模型以及IO模型、高效的数据结构。
- Redis将数据存储在内存中,提供快速的读写速度,相比于传统的磁盘数据库,内存访问速度快得多。
- Redis使用单线程事件驱动模型结合I/O多路复用,避免了多线程上下文切换和竞争条件,提高了并发处理效率。
- Redis提供多种高效的数据结构(如字符串、哈希、列表、集合等),这些结构经过优化,能够快速完成各种操作。
5. Redis 为什么用跳表,而不用平衡树、红黑树或者 B+树?
为什么不用红黑树?
1)相比红黑树而言实现简单
- 跳表基于多层链表实现,通过概率算法动态生成索引层级,没有左旋右旋等操作,逻辑理解上更为简单。而红黑树需要复杂的平衡操作(旋转)来维护结构,代码实现复杂度较高,理解门槛更高。
2)范围查询更高效
- 范围查询跳表可以通过O(logn)的时间复杂度定位起点,然后在原始的链表中往后遍历即可。
- 红黑树从结构上不支持范围查询。
3)结构更灵活
- 跳表的层数和节点结构是动态的,可以基于概率分布调整层数,灵活的适应不同的数据量(数据量大层级可以多一些,小的话层级少一些)。
- 红黑树则无法调整。
为什么不用B+树?
- B+树节点更新比较复杂,涉及页合并和分裂,会导致额外的计算。
- B+树节点理论上占用内存也比跳表节点大。因为控制层级的情况下,大部分跳表节点仅需维护自身的值和一个指针(可能还有一个回退指针,redis的实现有回退指针),而B+树是多叉树,一个节点需要多指针,且节点内部还有若干指针。每个元素在叶子节点有一份完
整数据内容,在非叶子节点还需要存储键的数据,所以内存开销相比跳表大。 - B+树其实更适合磁盘存储,特别是大规模存储数据。因为B+树完整数据都存储在叶子节点中,而非叶子节点只起到索引作用,这样内存中就能存放更多的索引,便于海量数据的快速检索。
6. Redis 中有哪些内存淘汰策略?
Redis的内存淘汰策略一共有8种。8种里面可以细分为两大类,即开启数据淘汰和不开启数据淘汰两大类,然后开启数据的那一类又可以进行细分,分为基于过期时间的淘汰策略以及全部数据的淘汰策略。
不淘汰数据(默认):
- noeviction:当运行内存超过最大设置内存的时候,不会淘汰数据,而是直接返回报错禁止写入
设置了过期时间的数据淘汰:
- volatile-random:随机淘汰掉设置了过期时间的key
- volatile-ttl:优先淘汰掉较早过期的key
- volatile-lru(redis3.0之前默认策略):淘汰掉所有设置了过期时间的,然后最久未使用的key
- volatile-Ifu(redis4.0后新增):与上面类似,不过是淘汰掉最少使用的key
所有数据的数据淘汰:
- allkeys-random:随机淘汰掉任意的key
- allkeys-lru:淘汰掉缓存中最久没有使用的key
- allkeys-Ifu(redis4.0后新增):淘汰掉缓存中最少使用的key

7. Redis中的BigKey问题是什么?如何解决?
Redis 中的“bigKey”是指一个内存空间占用比较大的键(Key),它有什么危害呢?
- 内存分布不均。在集群模式下,不同 slot 分配到不同实例中,如果大key都映射到一个实例,则分布不均,查询效率也会受到影响。
- 由于Redis 单线程执行命令,操作大Key时耗时较长,从而导致Redis 出现其它命令阻塞的问题。
- 大Key对资源的占用巨大,在你进行网络I/O传输的时候,导致你获取过程中产生的网络流量较大,从而产生网络传输时间延长甚至网络传输发现阻塞的现象,例如一个key2MB,请求个1000次2000MB。
- 客户端超时。因为操作大Key时耗时较长,可能导致客户端等待超时。
如何解决bigkey问题?
1)开发方面
- 对要存储的数据进行压缩,压缩之后再进行存储
- 大化小,即把大对象拆分成小对象,即将一个大Key拆分成若干个小Key,降低单个Key的内存大小
- 使用合适的数据结构进行存储,比如一些用String 存储的场景,可以考虑使用Hash、Set等结构进行优化
2)业务层面
- 可以根据实际情况,调整存储策略,只存一些必要的数据。比如用户的不常用信息(地址等)不存储,仅存储用户ID、姓名、头像等
- 优化业务逻辑,从根源上避免大Key的产生。比如一些可以不展示的信息,直接移除等
3)数据分布方面
- 采用Redis集群方式进行Redis的部署,然后将大Key拆分散落到不同的服务器上面,加快响应速度
怎么样算大key?

8. 如何解决Redis中的热点key问题?
Redis中的热点Key问题是指某些Key被频繁访问,导致Redis的压力过大,进而影响整体性能甚至导致集群节点故障。
解决热点Key问题的主要方法包括:
- 热点key拆分:将热点数据分散到多个Key中,例如通过引入随机前缀,使不同用户请求分散到多个Key,多个key分布在多实例中,避免集中访问单一Key。
- 多级缓存:在Redis前增加其他缓存层(如CDN、本地缓存),以分担Redis的访问压力。
- 读写分离:通过Redis主从复制,将读请求分发到多个从节点,从而减轻单节点压力。
- 限流和降级:在热点Key访问过高时,应用限流策略,减少对Redis的请求,或者在必要时返回降级的数据或空值。
9. Redis中的缓存击穿、缓存穿透和缓存雪崩是什么?
缓存击穿:指某个热点数据在缓存中失效,导致大量请求直接访问数据库。此时,由于瞬间的高并发,可能导致数据库崩溃。
缓存穿透:指查询一个不存在的数据,缓存中没有相应的记录,每次请求都会去数据库查询,造成数据库负担加重。
缓存雪崩:指多个缓存数据在同一时间过期,导致大量请求同时访问数据库,从而造成数据库瞬间负载激增。
解决方案
缓存击穿:
- 使用互斥锁,确保同一时间只有一个请求可以去数据库查询并更新缓存。
- 热点数据永不过期。
缓存穿透:
- 使用布隆过滤器,过滤掉不存在的请求,避免直接访问数据库。
- 对查询结果进行缓存,即使是不存在的数据,也可以缓存一个标识,以减少对数据库的请求。
缓存雪崩:
- 采用随机过期时间策略,避免多个数据同时过期。
- 使用双缓存策略减少数据库直接请求。
10. Redis数据过期后的删除策略是什么?
Redis数据过期主要有两种删除策略,分别为定期删除、惰性删除两种:
- 定期删除:Redis每隔一定时间(默认是100毫秒)会随机检查一定数量的键,如果发现过期键,则将其删除。这种方式能够在后台持续清理过期数据,防止内存膨胀。
- 情性删除:在每次访问键时,Redis检查该键是否已过期,如果已过期,则将其删除。这种策略保证了在使用过程中只删除不再需要的数据,但在不访问过期键时不会立即清除。
11. Redis主从复制的实现原理是什么?
Redis的主从复制是指一个Redis实例(主节点)可以将数据复制到一个或多个从节点(从节点),从节点从主节点获取数据并保持同步。
复制流程:
- 开始同步:从节点通过向主节点发送PSYNC命令发起同步。
- 全量复制:如果是第一次连接或之前的连接失效,从节点会请求全量复制,主节点将当前数据快照(RDB文件)发送给从节点。
- 增量复制:全量复制完毕后,主从之间会保持一个长连接,主节点会通过这个连接将后续的写操作传递给从节点执行,来保证数据的一致。
12. Redis集群的实现原理是什么?
Redis 集群(Redis cluster)是通过多个Redis实例组成的,每个实例存储部分的数据(即每个实例之间的数据是不重复的)。
具体是采用哈希槽(Hash Slot)机制来分配数据,将整个键空间划分为16384个槽(slots)。每个Redis实例负责一定范围的哈希槽,数据的key经过哈希函数计算后对16384取余即可定位到对应的节点。
客户端在发送请求时,会通过集群的任意节点进行连接,如果该节点存储了对应的数据则直接返回,反之该节点会根据请求的键值计算哈希槽并路由到正确的节点。
13. Redis通常应用于哪些场景?
1)缓存:
- Redis最常用的场景是作为缓存层,以减少数据库的负载,提高数据读取速度。例如,常用的用户会话数据和页面渲染结果可以存储在Redis中。
2)实时系统:
- Redis支持快速的数据写入和读取,非常适合用于实时分析,如网站点击统计、实时排行榜等。
3)消息队列:
- 利用Redis的List和Pub/Sub功能,可以实现轻量级的消息队列,适用于任务处理和异步消息传递。
4)分布式锁:
- Redis可以用作分布式锁的实现,确保在分布式系统中资源的安全访问,避免竞态条件。
5)计数器:
- Redis的原子性操作非常适合用作计数器。例如,可以使用Redis来统计页面访问量、点赞数、评论数等。通过INCR命令可以轻松实现高效的计数。
14. 为什么Redis设计为单线程?6.0版本为何引入多线程?/ Redis6.0 之前为什么不使用多线程?Redis6.0 之后为何引入了多线程?
单线程设计原因:
- Redis的操作是基于内存的,其大多数操作的性能瓶颈主要不是CPU导致的
- 使用单线程模型,代码简便的同时也减少了线程上下文切换带来的性能开销
- Redis在单线程的情况下,使用I/O多路复用模型就可以提高Redis的I/O利用率了
6.0版本引入多线程的原因:
随着数据规模的增长、请求量的增多,Redis的执行瓶颈主要在于网络I/O。引I入多线程处理可以提高网络I/O处理速度。
15. Redis支持事务吗?如何买现?
Redis支持事务,但它的事务与MySQL中的事务有所不同。MySQL中的事务主要支持ACID的特性,而Redis中的事务主要保证的是多个命令执行的原子性,即所有的命令在一个原子操作中执行,不会被打断。
MySQL中的事务是支持回滚的,而Redis中的事务是不支持回滚的。
Redis的事务
Redis支持事务,通过使用MULTI、EXEC、WATCH 和 DISCARD等命令来实现。事务中的命令会被排队并在调用EXEC时一次性执行,保证了事务的原子性。
具体实现流程如下:
- 开始事务:使用MULTI命令开始一个事务,之后的所有命令都会被排队。
- 添加命令:在事务中添加命令,这些命令不会立即执行,而是存储在队列中。
- 执行事务:使用EXEC命令执行队列中的所有命令,确保原子性。
- 取消事务:使用DISCARD命令可以放弃事务,清空命令队列。
- 监视键:使用WATCH命令可以监视一个或多个键,如果在事务执行前这些键被修改,则EXEC将不会执行,确保数据一致性。
Redis事务不支持回滚功能
Redis事务一旦执行,所有命令都会被应用到数据中,即使其中某个命令失败。因此,在设计时需充分考虑可能的错误。
16. Redis的Pipeline功能是什么?
Redis的Pipeline功能允许客户端在一次网络请求中批量发送多个命令,以减少网络延迟并提高性能。通过将多个命令打包发送,客户端可以在不等待每个命令响应的情况下继续发送其他命令,从而显著提高吞吐量。
好处:
- 节省了网络传输时间 减
- 少了Redis服务端上下文切换带来的开销
17. Redis的持久化机制有哪些?
Redis提供两种主要的持久化机制:
RDB(RedisDatabase)快照:
- RDB是通过生成某一时刻的数据快照来实现持久化的,可以在特定时间间隔内保存数据的快照。
- 适合灾难恢复和备份,能生成紧凑的二进制文件,但可能会在崩溃时丢失最后一次快照之后的数据。
AOF(Append OnlyFile)日志:
- AOF通过将每个写操作追加到日志文件中实现持久化,支持将所有写操作记录下来以便恢复。
- 数据恢复更为精确,但文件体积较大,重写时可能会消耗更多资源。
18. Redis的哨兵机制是什么?
Redis的哨兵机制(Sentinel)是一种高可用性解决方案,用于监控Redis主从集群,自动完成主从切换,以实现故障自动恢复和通知。
主要功能包括:
- 监控:哨兵不断监控Redis主节点和从节点的运行状态,定期发送PING请求检查节点是否正常。
- 自动故障转移:当主节点发生故障时,哨兵会选举一个从节点提升为新的主节点,并通知客户端更新主节点的地址,从而实现高可用。
- 通知:哨兵可以向系统管理员或其他服务发送通知,以便快速处理Redis实例的状态变化。
主从架构中,如果采用读写分离的模式,即主节点负责写请求,从节点负责读请求。假设这个时候主节点宕机了,没有新的主节点顶替上来的话,就会出现很长一段时间写请求没响应的情况。
针对这个情况,便出现了哨兵这个机制。它主要进行监控作用,如果主节点挂了,将从节点切换成主节点,从而最大限度地减少停机时间和数据丢失。
19. Redis的订阅发布功能是什么?你了解吗?
Redis的订阅发布功能(Publish/Subscribe,简称Pub/Sub),是一种消息通信机制,用于在不同客户端之间实现消息的实时传递和广播。使用Pub/Sub 模型,客户端可以订阅一个或多个频道,当有其他客户端向这些频道发布消息时,所有订阅了该频道的客户端都会立即收到消息。
主要功能包括:
- 发布(Publish):某个客户端向指定的频道发布消息。
- 订阅(Subscribe):一个或多个客户端订阅指定的频道,并在频道接收到消息时立刻获取该消息。
20. Redisson分布式锁的原理?
Redisson是基于 Redis 实现的分布式锁,实际上是使用Redis的原子操作来确保多线程、多进程或多节点系统中,只有一个线程能获得锁,避免并发操作导致的数据不一致问题。
1)锁的获取:
- Redisson 使用Lua脚本,利用exists+hexists+hincrby 命令来保证只有一个线程能成功设置键(表示获得锁)。
- 同时,Redisson 会通过pexpire命令为锁设置过期时间,防止因宕机等原因导致锁无法释放(即死锁问题)。
2)锁的续期:
- 为了防止锁在持有过程中过期导致其他线程抢占锁,Redisson实现了锁自动续期的功能。持有锁的线程会定期续期,即更新锁的过期时间,确保任务没有完成时锁不会失效。
3)锁的释放:
- 锁释放时,Redisson也是通过Lua脚本保证释放操作的原子性。利用hexists+del 确保只有持有锁的线程才能释放锁,防止误释放锁的情况。
- Lua 脚本同时利用publish 命令,广播唤醒其它等待的线程。
4)可重入锁:
- Redisson支持可重入锁,持有锁的线程可以多次获取同一把锁而不会被阻塞。具体是利用Redis中的哈希结构,哈希中的key为线程ID,如果重入则value+1,如果释放则value-1,减到0说明锁被释放了,则de1锁。
21. Redisson看门狗(watchdog)机制了解吗?
Redisson的看门狗(watchdog)主要用来避免Redis中的锁在超时后业务逻辑还未执行完毕,锁却被自动释放的情况。它通过定期刷新锁的过期时间来实现自动续期。
主要原理:
- 定时刷新:如果当前分布式锁未设置过期时间,Redisson基于Netty时间轮启动一个定时任务,定期向Redis发送命令更新锁的过期时间,默认每10s发送一次请求,每次续期30s。
- 释放锁:当客户端主动释放锁时,Redisson会取消看门狗刷新操作。如果客户端宕机了,定时任务自然也就无法执行了,此时等超时时间到了,锁也会自动释放。
22. Redis和Memcached有哪些区别?
从数据结构上:
- Redis:支持多种数据结构,如字符串、列表、集合、有序集合和哈希表,适合存储复杂数据类型。
- Memcached:仅支持简单的键值对存储,数据结构较为单一。
从持久化上:
- Redis:支持持久化功能,可以将数据保存在磁盘上,通过RDB和AOF两种方式实现数据的持久化。
- Memcached:不支持数据持久化,重启后数据会丢失,适合临时数据存储。
从分布式架构上:
- Redis:内置支持主从复制和集群分片(RedisCluster),能在分布式环境中提供高可用性和扩展性。
- Memcached:没有内置分片支持,需要在客户端代码中实现分布式逻辑。
从功能上:
- Redis:支持发布订阅、Lua脚本等特性。
- Memcached:特性较少。
23. Redis的hash是什么?
Redis的hash是一种键值对集合,可以将多个字段和值存储在同一个键中,便于管理一些关联数据。
特点如下:
- 适合存储小数据,使用哈希表实现,能够在内存中高效存储和操作。
- 支持快速的字段操作(如增、删、改、查),非常适合存储对象的属性。
24. RedisCluster模式与Sentinel模式的区别是什么?
1)RedisCluster是Redis集群,提供自动分片功能,将数据自动分布在多个节点上,支持自动故障转移。如果一个节点失败,集群会自动重新配置和平衡,不需要外部介入,因为它内置了哨兵逻辑。
2)Sentinel是哨兵,主要用于管理多个Redis服务器实例来提高数据的高可用性。当主节点宕机,哨兵会将从节点提升为主节点,它并不提供数据分片功能。
如果需要处理大量数据并进行数据分片,应选择RedisCluster,它支持水平扩展,适用于大规模数据、高吞吐量场景。
如果只是为了提高Redis实例的可用性,并不需要数据分片,应选择主从+Sentinel,它主要关注故障转移和实例高可用,适用于高可用性、读写分离场景。
25. Redis 是单线程,那怎么监听大量的客户端连接呢?
Redis的核心操作虽然是单线程的,但是Redis 使用像 epoll(Linux 系统)这样的 I/O 多路复用技术,检测所有客户端连接上的可读/可写事件,命令解析、执行、响应内容生成 等核心逻辑 依然由主线程单线程串行执行,但是会将处理好的结果 分发给 I/O 多线程进行具体的读写操作,所以能够在同一时间高效处理大量客户端的并发请求,避免了多线程环境下的上下文切换开销和锁竞争问题,从而实现对大量客户端连接的监听与处理。
26. Redis 给缓存数据设置过期时间有啥用? Redis 是如何判断数据是否过期的呢?过期数据是如何被删除的?
1)Redis 给缓存数据设置过期时间有啥用?
- 释放内存资源: 缓存空间是有限的(由物理内存大小限制)。通过设置过期时间(TTL - Time To Live),Redis 可以自动删除那些不再需要或可能已失效的数据,为新数据腾出空间,避免内存溢出OOM。
- 保证数据时效性: 许多业务场景中的数据只在特定时间内有效(如验证码、会话信息、限时优惠商品价格、临时锁等)。设置过期时间后,一旦数据过期,用户就不会再访问到过时的信息,有助于保持数据的实时性和正确性。
- 简化应用逻辑: 如果没有自动过期,应用需要自行实现复杂的逻辑来判断何时删除旧数据,增加了开发和维护成本。
2)Redis 是如何判断数据是否过期的呢?
- 内部存储过期时间戳: 当你为一个键设置过期时间(无论是通过 EXPIRE/PEXPIRE、SETEX/PSETEX 还是 SET 的 EX/PX 选项),Redis 会在键对应的 Redis 对象 (Redis Object) 的内部结构中,存储一个绝对过期时间戳(Unix Timestamp,精确到毫秒)。
- 惰性检查: 每次访问一个键时(GET, HGET, LRANGE, EXISTS, DEL, 甚至 SCAN 等命令),Redis 在执行命令操作之前,会检查该键对应的过期时间戳。
3)过期数据是如何被删除的?
- 惰性删除 (Lazy Expiration):在访问处理前,检查发现键已过期 -> 立即删除该键 -> 向客户端返回一个表示键不存在的响应。
- Redis 的周期性后台任务 (serverCron) 会定期(默认每秒执行 10 次,可通过 hz 配置)地、分批次地、随机地扫描一定数量的键。
27. Redis 事务支持原子性吗?
Redis 事务在某种程度上提供原子性,使用 MULTI 开启事务后,所有后续命令会被序列化并存入队列(而非立即执行)。当执行 EXEC 时,队列中的所有命令会按顺序一次性执行。不会穿插执行其他客户端的命令。
但是运行时错误不会导致回滚,如果一个命令执行失败,之前已经在该事务中执行成功的命令结果会被保留,后续命令仍会继续执行。而且也没有回滚机制。
28. 如何保证缓存和数据库的一致性?
1)Cache Aside Pattern 旁路缓存模式
Cache Aside Pattern 是平时使用比较多的一个缓存读写模式,比较适合读请求比较多的场景。
读:
- 从 cache 中读取数据,读取到就直接返回
- cache 中读取不到的话,就从 db 中读取数据返回
- 再把数据放到 cache 中。
写:
- 先更新 db
- 然后直接删除 cache 。
在写数据的过程中,可以先删除 cache ,后更新 db 吗?


防删除失败兜底:
- 引入重试机制(消息队列、异步任务等)确保缓存最终被删除。
- 为缓存设置合理TTL(最终兜底)。
如果数据库是主从模式,还会有问题,先更新主库,然后删除缓存,如果这时候主从还没同步,还会读到脏数据写到缓存,这时候需要用到
延迟双删策略
1. 删缓存(清除可能存在的旧值)→
2. 更新数据库 →
3. 休眠几百ms(等待主从同步)→
4. 再次删缓存(清除可能重建的旧值)
订阅数据库Binlog:
通过 Canal等工具监听DB变更,异步删除/更新缓存。
2)Read/Write Through Pattern 读写穿透
Read/Write Through Pattern 中服务端把 cache 视为主要数据存储,从中读取数据并将数据写入其中。cache 服务负责将此数据读取和写入 db,从而减轻了应用程序的职责。这种缓存读写策略小伙伴们应该也发现了在平时在开发过程中非常少见。抛去性能方面的影响,大概率是因为我们经常使用的分布式缓存 Redis 并没有提供 cache 将数据写入 db 的功能。
写(Write Through):
先查 cache,cache 中不存在,直接更新 db。cache 中存在,则先更新 cache,然后 cache 服务自己更新 db(同步更新 cache 和 db)。
读(Read Through):
从 cache 中读取数据,读取到就直接返回 。
读取不到的话,先从 db 加载,写入到 cache 后返回响应。
3)Write Behind Pattern 异步缓存写入
Write Behind Pattern 和 Read/Write Through Pattern 很相似,两者都是由 cache 服务来负责 cache 和 db 的读写。
但是,两个又有很大的不同:Read/Write Through 是同步更新 cache 和 db,而 Write Behind 则是只更新缓存,不直接更新 db,而是改为异步批量的方式来更新 db。

9530

被折叠的 条评论
为什么被折叠?



