一、Redis 基础概念

Redis 是什么?有哪些特点?

Redis 是一个开源的、基于内存的数据结构存储系统,可用于数据库、缓存和消息中间件。

特点:高性能(10万+ QPS)、单线程模型(6.0后支持多线程IO)、持久化、支持事务、发布订阅等。

Redis 与 Memcached 的区别?

Redis 支持更丰富的数据类型,Memcached 仅支持字符串。

Redis 支持持久化,Memcached 纯内存。

Redis 单线程模型(6.0后支持多线程IO),Memcached 多线程。

Redis 支持集群模式,Memcached 需客户端分片。

Redis 单线程为什么性能高?

纯内存操作,无上下文切换开销。

非阻塞 IO 多路复用(epoll/kqueue)。

单线程避免锁竞争问题(6.0 后引入多线程处理网络 IO,但核心逻辑仍单线程)。

二、Redis 数据结构与使用场景

Redis 支持哪些数据结构?举例说明应用场景

String:缓存、计数器(INCR)、分布式锁(SETNX)。

List:消息队列(LPUSH/RPOP)、最新消息排行。

Hash:存储对象(用户信息、商品详情)。

Set:标签系统、共同好友(SINTER)。

ZSet:排行榜(ZADD)、延迟队列(按时间戳排序)。

BitMap:用户签到、活跃统计。

HyperLogLog:UV 统计(去重计数)。

Stream:消息队列(支持消费者组,类似 Kafka)。

ZSet 底层实现原理?

使用 跳跃表(SkipList) + 哈希表 实现,支持 O(logN) 复杂度的范围查询和 O(1) 的单点查询。

Redis 如何实现分布式锁?

使用 SET key value NX EX timeout 命令(原子操作)。

需解决锁续期问题(看门狗机制)和锁误删问题(Lua 脚本验证值)。

推荐 Redlock 算法(多实例部署,半数以上成功才算获取锁)。

Redis 分布式锁原理

基于setNX命令,防止死锁需要设置过期时间
(早版本setNX和过期时间是两条指令,不是原子操作,需要引入lua,变成一条指令后就不用了),
防止过期时程序未完成,需要自动续期。自动续期可以使用redision的watchdog实现。
使用流程:先上锁,如果上锁成功则执行业务,最后释放锁。如果上锁失败则返回或重试等,
根据业务情况而定。

Redis 如何实现延时队列?

使用 sortedset,拿时间戳作为score,消息内容作为 key 调用 zadd 来生产消息,
消费者用 zrangebyscore 指令获取 N 秒之前的数据轮询进行处理。

三、Redis 持久化

RDB 和 AOF 的区别与优缺点?

RDB:

生成数据快照,文件小,恢复快。

缺点:可能丢失最后一次快照后的数据,大数据量时 fork 可能阻塞主线程。

AOF:

记录写操作命令,支持秒级持久化(appendfsync everysec)。

缺点:文件较大,恢复速度慢。

生产环境通常结合使用:AOF 保证数据安全,RDB 用于快速恢复。

AOF 重写(Rewrite)过程?

根据当前内存数据生成新的 AOF 文件,替换旧文件以缩小体积。

通过 bgrewriteaof 命令触发,fork 子进程完成,不影响主线程。

四、Redis 高可用与集群

主从复制原理?

全量同步:从节点发送 SYNC 命令,主节点生成 RDB 并传输,之后传输缓冲区的命令。

增量同步:主节点维护复制积压缓冲区,从节点断线重连后发送 PSYNC 命令同步偏移量。

哨兵(Sentinel)模式的作用?

监控主从节点状态,自动故障转移(主节点宕机时选举新主)。

提供配置中心功能,客户端通过哨兵获取主节点地址。

Redis Cluster 如何实现数据分片?

采用 哈希槽(Hash Slot) 分片(共 16384 个槽)。

每个节点负责部分槽位,客户端通过 CRC16(key) % 16384 计算槽位,直接路由到对应节点。

Redis Cluster 如何应对节点故障?

每个节点定期向其他节点发送 PING 命令,半数以上节点认为某主节点不可达时触发故障转移。

从节点升级为主节点,原主节点恢复后变为从节点。

五、Redis 应用场景与问题解决

缓存穿透、缓存击穿、缓存雪崩的区别与解决方案?

穿透:查询不存在的数据(恶意攻击)。
方案:布隆过滤器拦截、缓存空值。

击穿:热点 key 过期后高并发查询直接压到数据库。
缓存并发的问题通常发生在高并发的场景下,当一个缓存key过期时,因为访问这个缓存key 的请求量较大,
多个请求同时发现缓存过期,因此多个请求会同时访问数据库来查询最新数据,并且回写缓存,
这样会造成应用和数据库的负载增加,性能降低,由于并发较高,甚至会导致数据库被压死。

方案:互斥锁(Redis 或分布式锁)、永不过期(逻辑过期时间)。

雪崩:大量 key 同时过期或 Redis 宕机。
方案:随机过期时间、集群高可用、熔断降级。

如何保证缓存与数据库双写一致性?

读操作:先读缓存,未命中则读数据库并回写缓存。

写操作:先更新数据库,再删除缓存(延迟双删策略)。

最终一致性可通过消息队列或监听 binlog 异步同步。

大 Key 和热 Key 问题如何处理?

大 Key:拆分(Hash 分 field)、压缩、异步删除(UNLINK)。

热 Key:多级缓存(本地缓存)、打散到多个 key(添加随机后缀)。

六、Redis 性能优化

Redis 内存淘汰策略有哪些?

  noeviction:当内存使用达到限制时,不执行任何回收操作。

  volatile-lru:尝试回收最少使用的键(LRU),但仅限于在过期集合的键。

  volatile-random:随机回收键,但仅限于在过期集合的键。

  volatile-ttl:回收在过期集合的键,并且优先回收存活时间(TTL)较短的键。

  allkeys-lru:尝试回收最少使用的键(LRU)。

  allkeys-random:随机回收键。

Pipeline 和 Lua 脚本的作用?


Pipeline:批量发送命令,减少网络往返时间(RTT)。

Lua 脚本:原子执行多个命令,避免竞态条件(如库存扣减)。

七、其他高频问题

Redis 事务(MULTI/EXEC)的 ACID 特性?

满足原子性(全部执行或全部不执行。

无回滚(仅语法错误会取消事务)。

隔离性(单线程无并发问题)。

不保证持久性(依赖持久化配置)。

Redis 6.0 多线程模型原理?

多线程仅用于处理网络 IO(读写 socket),命令执行仍为单线程,保证原子性。

Redis 的慢查询如何监控?

通过 slowlog get 命令查看,需配置阈值(slowlog-log-slower-than)。

什么是脑裂?

在 Redis 高可用架构(如主从复制 + 哨兵模式)中,当网络分区(Network Partition)导致主节点与哨兵、
从节点之间的通信中断时,可能会出现多个主节点同时存在的现象,称为脑裂。例如:    
原主节点因网络问题被哨兵判定为宕机,触发故障转移选举出新主节点。    
但原主节点实际上仍在运行(可能仍在接受客户端写入),导致集群中存在两个主节点,数据不一致。

脑裂的危害

数据丢失:客户端可能同时向两个主节点写入数据,当原主节点恢复后,会被哨兵降级为从节点,
并清空自身数据以同步新主节点数据,导致原主节点上的写入数据丢失。 
数据不一致:两个主节点独立接受写入,数据无法自动合并。

Redis 如何避免脑裂?

1、配置 min-slaves-to-write 和 min-slaves-max-lag

主节点需满足以下条件才允许写入:
    min-slaves-to-write 1    # 至少存在 1 个从节点
    min-slaves-max-lag 10    # 从节点复制延迟不超过 10 秒

当主节点发现从节点数量不足或复制延迟过高时,拒绝写入请求,避免与原主节点同时写入数据。

2、合理设置哨兵参数

down-after-milliseconds:适当增大主节点判定不可用的超时时间(如30秒),避免因网络抖动误判主节点宕机。

quorum:设置合理的哨兵投票数(半数以上确认才触发故障转移),防止少数哨兵误操作。

3、客户端重试机制

客户端在写入失败后,应检查当前主节点是否已切换,避免持续向旧主节点写入。

4、在 Redis Cluster 中,通过 多数派原则 和 Gossip 协议 避免脑裂:


脑裂后的数据恢复

若已发生脑裂,需人工介入:

保留新主节点的数据(因其被多数哨兵认可)。

将原主节点恢复为从节点前,手动备份其数据并对比差异,选择性合并(需业务逻辑支持)。

八、实战经验与扩展

如何选择 Redis 集群方案?

小规模用哨兵+主从,大规模数据用 Redis Cluster。

Redis 的常见监控指标:内存使用率、QPS、连接数、命中率、慢查询。

Redis 7.0 新特性:Function(服务端脚本)、Multi-part AOF 等。

高旭
40 声望3 粉丝