跳转到内容

Redis on Kubernetes 面试题

30 道题
分类
中间件
题目数
30 道
已阅读 0 / 30 题
1 Redis 在 Kubernetes 上有哪些部署模式?各自适用场景是什么?

答案:

Redis 在 Kubernetes 上的部署模式分为三种:StandaloneSentinelCluster

部署模式架构特征高可用数据分片适用场景
Standalone单实例 Deployment 或 StatefulSet开发测试、缓存场景(数据可丢失)
Sentinel一主多从 + Sentinel 哨兵集群自动故障转移读多写少、数据量在单机内存范围内
Cluster多主多从、分片架构自动故障转移 + 分片冗余16384 个 Slot数据量超过单机内存、高吞吐写入

Standalone 以 Deployment 部署 Redis 单实例,无持久化,数据丢失可接受时最简单。

Sentinel 架构在 K8s 上通常以三个独立 Deployment 部署 Sentinel 进程,Redis 主从以 StatefulSet 部署,通过 Headless Service 实现 Pod 间互相发现。

Cluster 模式需为每个节点配置 cluster-announce-ip,依赖 StatefulSet + Headless Service 提供稳定网络标识,扩容时需手动执行 Slot 迁移命令。

2 Redis Sentinel 在 Kubernetes 上如何部署?自动故障转移流程是怎样的?

答案:

Sentinel 在 K8s 上的经典部署架构:

# Sentinel Deployment(3 副本)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis-sentinel
spec:
  replicas: 3
  selector:
    matchLabels:
      app: redis-sentinel
  template:
    spec:
      containers:
      - name: sentinel
        image: redis:7.2
        command: ["redis-sentinel"]
        args: ["/etc/redis/sentinel.conf"]
        ports:
        - containerPort: 26379

Redis 主从以 StatefulSet 部署:

# Redis StatefulSet(3 副本:1 主 2 从)
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: redis
spec:
  serviceName: redis-headless
  replicas: 3
  selector:
    matchLabels:
      app: redis
  template:
    spec:
      containers:
      - name: redis
        image: redis:7.2
        command: ["redis-server"]
        args: ["/etc/redis/redis.conf"]
        ports:
        - containerPort: 6379
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: ["ReadWriteOnce"]
      resources:
        requests:
          storage: 50Gi

故障转移流程

  1. Sentinel 通过 Headless Service(redis-headless)发现所有 Redis Pod,持续 PING 检测
  2. 主观下线(SDOWN):单个 Sentinel 判定主节点不可达(down-after-milliseconds 超时)
  3. 客观下线(ODOWN):超过 quorum 数量的 Sentinel 确认主节点下线
  4. Sentinel Leader 选举:通过 Raft 协议选出执行故障转移的 Sentinel
  5. 从节点选举:按 slave-priority、复制偏移量、Run ID 排序选出新主
  6. 故障转移执行:新主执行 SLAVEOF NO ONE,其余从节点重定向到新主
  7. ConfigMap 更新:K8s 场景下 Sentinel 通过 ConfigMap 或 Operator 更新客户端连接信息

关键配置

参数说明
sentinel monitor mymaster <ip> <port> <quorum>监控主节点,quorum 设为 2
sentinel down-after-milliseconds mymaster 5000主观下线判定超时 5 秒
sentinel failover-timeout mymaster 60000故障转移超时 60 秒
sentinel parallel-syncs mymaster 1故障转移后并行同步的从节点数
3 Redis Cluster 的分片机制与 16384 个 Slot 分配原理是什么?

答案:

Redis Cluster 采用 Hash Slot 机制实现数据分片,将键空间划分为 16384 个 Slot

Slot 分配原理

  • 每个键通过 CRC16(key) % 16384 计算所属 Slot
  • Slot 分布在多个主节点之间,每个主节点负责一段 Slot 范围
  • 每个 Slot 可包含多个键,主节点负责其 Slot 范围内的所有读写操作
# 查看集群 Slot 分布
redis-cli cluster slots

# 手动分配 Slot
redis-cli --cluster add-node new-node:6379 existing-node:6379
redis-cli --cluster reshard <target-node>:6379

为什么是 16384?

维度16384(16K)65536(64K)
心跳消息大小约 2KB(bitmap)约 8KB
网络带宽占用低(Gossip 协议 PING/PONG)
节点数上限建议 ≤ 1000理论上更多
Redis 设计权衡足够用且消息体紧凑带宽浪费

16384 是在心跳消息大小与节点扩展性之间的折中:16K 个 Slot 的 bitmap 仅需 2KB,在 Gossip 消息中传输效率高,且足以支撑 1000 个节点的集群规模。

4 Redis Cluster 的 Gossip 协议如何工作?

答案:

Redis Cluster 各节点通过 Gossip 协议Cluster Bus(端口 16379,即 data_port + 10000)上交换集群元信息。

Gossip 通信机制

  1. 每个节点每秒随机选取 cluster-node-timeout / 2 个节点发送 PING 消息
  2. 收到 PING 的节点回复 PONG
  3. PING / PONG 消息携带:
    • 发送者自身信息(node ID、IP、port、flags)
    • 发送者已知的其他节点信息(Gossip 段)
    • 当前纪元(currentEpoch)和配置纪元(configEpoch)
  4. 节点通过 Gossip 消息发现新节点,标记疑似故障节点(PFAIL -> FAIL)

关键参数

参数默认值说明
cluster-node-timeout15000ms节点超时阈值,影响 PFAIL 判定
cluster-slave-validity-factor10从节点故障转移有效性因子
cluster-migration-barrier1从节点迁移屏障

K8s 场景注意事项

  • Cluster Bus 端口需在 Service 和 NetworkPolicy 中额外暴露
  • Headless Service 需确保 Pod DNS 解析后 Cluster Bus 通信可达
  • cluster-announce-ipcluster-announce-port 需正确配置
5 Redis Cluster 节点扩缩容与 Slot 迁移如何执行?

答案:

扩容流程(添加新主节点):

# 1. 新节点加入集群
redis-cli --cluster add-node <new-node-ip>:6379 <existing-node-ip>:6379

# 2. 重新分配 Slot(在线迁移)
redis-cli --cluster reshard <target-node-ip>:6379 \
  --cluster-from <source-node-id> \
  --cluster-to <target-node-id> \
  --cluster-slots <slot-count>

Slot 迁移内部流程

  1. 目标节点执行 CLUSTER SETSLOT <slot> IMPORTING <source-node-id>
  2. 源节点执行 CLUSTER SETSLOT <slot> MIGRATING <target-node-id>
  3. 源节点执行 MIGRATE 命令逐键迁移数据
  4. 迁移完成后通知集群所有节点更新 Slot 归属
  5. 源节点删除已迁移的键
# 添加从节点
redis-cli --cluster add-node <new-node-ip>:6379 <existing-node-ip>:6379 \
  --cluster-slave --cluster-master-id <master-node-id>

# 删除节点
redis-cli --cluster del-node <node-ip>:6379 <node-id>

缩容流程

  1. 将待下线节点的 Slot 迁移至其他节点(--cluster reshard
  2. 确认 Slot 全部迁出后执行 --cluster del-node

K8s 上扩容:先 kubectl scale statefulset redis-cluster --replicas=N,再执行 redis-cli --cluster reshard 手动均衡 Slot。自动化方案依赖 Operator。

6 Redis Cluster 在 Kubernetes 上部署的核心挑战是什么?

答案:

挑战说明解决方案
Pod IP 变化Pod 重启后 IP 变更,nodes.conf 中的节点地址失效使用 StatefulSet + Headless Service 提供稳定 DNS 名;配置 cluster-announce-ip 为 Pod 域名
Cluster Bus 通信端口 16379(10000+6379)需路由可达Service 暴露 Cluster Bus 端口;NetworkPolicy 放行 TCP/16379
配置持久化nodes.conf 记录集群拓扑,Pod 重启后不能丢失挂载 PVC 存储 nodes.conf;或使用 ConfigMap + Operator 管理
Slot 迁移与 Pod 生命周期直接 kubectl delete pod 会导致 Slot 丢失缩容前先执行手动 Slot 迁移;使用 PDB 防止意外驱逐
客户端重定向Redis Cluster 返回 MOVED / ASK 错误,需客户端支持客户端使用支持集群模式的驱动(JedisCluster、go-redis cluster)
跨可用区延迟跨区 Gossip 通信延迟可能触发误判 PFAIL增大 cluster-node-timeout;配置 cluster-require-full-coverage no

配置示例

# redis.conf
cluster-enabled yes
cluster-config-file /data/nodes.conf
cluster-node-timeout 15000
cluster-require-full-coverage no
cluster-announce-ip $(hostname).redis-cluster-headless.redis.svc.cluster.local
cluster-announce-port 6379
cluster-announce-bus-port 16379
7 Redis Operator 生态有哪些选择?各自优劣是什么?

答案:

Operator维护方功能特点适用场景
Redis Enterprise OperatorRedis Inc.商业版,支持 Active-Active、集群自动管理、GUI 运维企业级生产环境,需要商业支持
Spotahome Redis OperatorSpotahome开源,支持 Sentinel 和 Cluster 模式,自动故障转移社区主流选择,中小规模集群
Ot Redis OperatorOT-CONTAINER-KIT开源,支持 Cluster 和 Sentinel,Leader/Follower 模式轻量级场景,K8s 原生集成

对比详情

能力Redis EnterpriseSpotahomeOt Operator
Sentinel 模式
Cluster 模式
自动扩缩容手动手动
Active-Active 地理分布
备份恢复✅(内置 S3)手动手动
Prometheus 监控集成
许可证商业Apache 2.0Apache 2.0
K8s CRD 管理丰富基础基础

Spotahome 示例 CR

apiVersion: databases.spotahome.com/v1
kind: RedisFailover
metadata:
  name: redisfailover
spec:
  sentinel:
    replicas: 3
  redis:
    replicas: 3
    storage:
      persistentVolumeClaim:
        metadata:
          name: redis-data
        spec:
          accessModes: ["ReadWriteOnce"]
          resources:
            requests:
              storage: 50Gi
8 Redis 持久化在 Kubernetes 上如何实现?

答案:

Redis 提供三种持久化策略,在 K8s 上均依赖 PVC 存储。

策略机制数据安全性性能影响K8s 实现要点
RDB定期全量内存快照可能丢失最近几分钟数据低(fork 子进程写入)PVC 存储 /data/dump.rdbsave 参数控制触发频率
AOF追加写日志最多丢 1 秒数据(everysec中(持续 IO 写入)PVC 存储 /data/appendonly.aof,配合 aof-rewrite 控制文件增长
RDB + AOF两者同时开启高(AOF 优先于 RDB 加载)较高PVC 同时存储两种文件,恢复时优先使用 AOF
No Persistence纯内存数据可丢失最优适用于纯缓存场景

RDB 配置

save 900 1       # 900 秒内至少 1 个键变更
save 300 10      # 300 秒内至少 10 个键变更
save 60 10000    # 60 秒内至少 10000 个键变更
dbfilename dump.rdb
dir /data

AOF 配置

appendonly yes
appendfsync everysec    # 每秒同步一次(推荐)
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

K8s StatefulSet 持久化配置

volumeClaimTemplates:
- metadata:
    name: data
  spec:
    accessModes: ["ReadWriteOnce"]
    storageClassName: "ssd-storage"
    resources:
      requests:
        storage: 100Gi

性能考量:RDB fork 阶段内存翻倍风险,K8s 需配置 memory limit >= maxmemory * 2 或使用 redis-check-rdb 定期校验。AOF 写入需确保底层存储 IOPS 满足 appendfsync everysec 的延迟要求。

9 Redis 使用 StatefulSet 与 PVC 存储如何配置?

答案:

StatefulSet 为 Redis 提供稳定的网络标识和持久化存储,是生产部署的基础。

核心设计要点

  1. ServiceName:绑定 Headless Service,生成稳定的 Pod DNS 名(<pod-name>.<service-name>.<namespace>.svc.cluster.local
  2. volumeClaimTemplates:每个 Pod 自动创建独立 PVC,保证存储隔离
  3. Pod 顺序管理podManagementPolicy 设为 Parallel 可并行启动(Sentinel/Cluster 场景)
  4. 存储类选择:使用 SSD/high-IOPS StorageClass

完整配置

# Headless Service
apiVersion: v1
kind: Service
metadata:
  name: redis-headless
spec:
  clusterIP: None
  selector:
    app: redis
  ports:
  - name: redis
    port: 6379
  - name: cluster-bus
    port: 16379
# StatefulSet
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: redis
spec:
  serviceName: redis-headless
  replicas: 6
  podManagementPolicy: Parallel
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      labels:
        app: redis
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values: ["redis"]
            topologyKey: kubernetes.io/hostname
      containers:
      - name: redis
        image: redis:7.2
        command: ["redis-server"]
        args: ["/etc/redis/redis.conf"]
        ports:
        - containerPort: 6379
          name: redis
        - containerPort: 16379
          name: cluster-bus
        resources:
          requests:
            cpu: "2"
            memory: 4Gi
          limits:
            cpu: "4"
            memory: 8Gi
        volumeMounts:
        - name: data
          mountPath: /data
        - name: config
          mountPath: /etc/redis
        readinessProbe:
          exec:
            command: ["redis-cli", "ping"]
          initialDelaySeconds: 10
          periodSeconds: 5
        livenessProbe:
          exec:
            command: ["redis-cli", "ping"]
          initialDelaySeconds: 30
          periodSeconds: 10
      volumes:
      - name: config
        configMap:
          name: redis-config
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: ["ReadWriteOnce"]
      storageClassName: "ssd-storage"
      resources:
        requests:
          storage: 100Gi
10 Redis 备份与恢复在 Kubernetes 上如何实现?

答案:

备份策略

方案类型RPO实现方式
RDB 快照 + S3全量分钟级CronJob 定时执行 BGSAVE,上传 dump.rdb 至 S3/MinIO
AOF 连续备份增量秒级sidecar 容器持续同步 /data/appendonly.aof 至对象存储
混合(RDB + AOF)全量 + 增量秒级RDB 为基准备份,AOF 为增量补充
Volume Snapshot存储层取决于 CSI利用 CSI Snapshot 创建 PVC 快照

CronJob 备份示例

apiVersion: batch/v1
kind: CronJob
metadata:
  name: redis-backup
spec:
  schedule: "0 */4 * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: backup
            image: redis:7.2
            env:
            - name: S3_BUCKET
              value: "redis-backups"
            command:
            - /bin/sh
            - -c
            - |
              redis-cli -h redis-0.redis-headless BGSAVE
              # 等待 BGSAVE 完成
              while [ $(redis-cli -h redis-0.redis-headless INFO persistence | grep rdb_bgsave_in_progress | cut -d: -f2) -eq 1 ]; do
                sleep 5
              done
              aws s3 cp /data/dump.rdb s3://$S3_BUCKET/redis-backup-$(date +%Y%m%d-%H%M).rdb              
            volumeMounts:
            - name: data
              mountPath: /data
          volumes:
          - name: data
            persistentVolumeClaim:
              claimName: data-redis-0
          restartPolicy: OnFailure

恢复流程

  1. 从 S3 下载最新 RDB 文件至 PVC
  2. 恢复 AOF 文件(如有)至 /data/appendonly.aof
  3. 启动 Redis,自动加载持久化文件
  4. 验证数据一致性:redis-cli INFO keyspace
  5. 切换流量至恢复后的实例
11 Redis 监控指标体系如何构建?

答案:

监控体系以 redis_exporter 为核心,通过 Prometheus 采集指标,Grafana 展示。

架构

Redis Pod(sidecar: redis_exporter)
  └── Prometheus(ServiceMonitor / PodMonitor)
        └── Grafana Dashboard
              └── AlertManager(告警规则)

核心指标分类

类别关键指标阈值建议
连接redis_connected_clientsredis_rejected_connections连接数 > 80% maxclients
内存redis_memory_used_bytesredis_memory_max_bytesused > 80% maxmemory
命中率redis_keyspace_hits_total / (hits + misses)命中率 < 90%
命令延迟redis_commands_duration_secondsP99 > 10ms
持久化redis_rdb_last_save_time_seconds最后保存 > 配置间隔
复制redis_master_repl_offset - redis_slave_repl_offset复制延迟 > 10MB
集群redis_cluster_slots_ok / redis_cluster_slots_total不可用 Slot > 0
键过期redis_expired_keys_totalredis_evicted_keys_total驱逐率持续增长

Prometheus 告警规则示例

groups:
- name: redis
  rules:
  - alert: RedisMemoryHigh
    expr: redis_memory_used_bytes / redis_memory_max_bytes > 0.85
    for: 5m
    labels:
      severity: warning
    annotations:
      summary: "Redis 内存使用率超过 85%"

  - alert: RedisDown
    expr: redis_up == 0
    for: 1m
    labels:
      severity: critical
    annotations:
      summary: "Redis 实例 XQOPEN $labels.instance XQCLOSE 不可达"

  - alert: RedisReplicationLag
    expr: (redis_master_repl_offset - redis_slave_repl_offset) > 10485760
    for: 5m
    labels:
      severity: warning
    annotations:
      summary: "Redis 主从复制延迟超过 10MB"
12 Redis 慢查询日志与性能诊断如何进行?

答案:

慢查询日志记录执行时间超过阈值的命令。

# 配置慢查询
CONFIG SET slowlog-log-slower-than 10000   # 超过 10ms 记录
CONFIG SET slowlog-max-len 128             # 最多保留 128 条

# 查看慢查询
SLOWLOG GET 10    # 最近 10 条
SLOWLOG LEN       # 总数
SLOWLOG RESET     # 清空

性能诊断方法

方法用途示例
SLOWLOG GET定位慢命令识别 KEYS *、O(N) 命令
INFO commandstats命令调用统计分析命令分布与耗时
redis-cli --latency网络延迟采样判断网络瓶颈
redis-cli --latency-history延迟时间序列检测延迟波动
redis-cli --bigkeys大 Key 扫描发现内存热点
redis-cli --memkeys内存消耗排序定位内存占用
MEMORY STATS内存使用详情分析碎片率
redis-benchmark性能压测基线测试

K8s 场景性能诊断清单

  1. 检查 CPU Throttling(container_cpu_cfs_throttled_seconds_total
  2. 检查内存是否触及 Limit 导致 OOM Kill
  3. 检查磁盘 IOPS 是否满足 AOF appendfsync 需求
  4. 检查网络延迟(redis-cli --latency 从应用 Pod 测试)
  5. 检查 NUMA 亲和性与 CPU 绑定(taskset
13 Redis 内存管理策略有哪些?

答案:

maxmemory 与逐出策略

maxmemory 4gb
maxmemory-policy allkeys-lru
逐出策略行为适用场景
noeviction不逐出,写操作返回错误数据不允许丢失
volatile-lru从设置了 TTL 的键中 LRU 逐出缓存 + 持久键混合
allkeys-lru所有键中 LRU 逐出纯缓存场景
volatile-lfu设置了 TTL 的键中 LFU 逐出热点数据保护
allkeys-lfu所有键中 LFU 逐出访问频率敏感
volatile-random随机逐出设置了 TTL 的键缓存淘汰均匀
allkeys-random随机逐出所有键所有键同等重要
volatile-ttl优先逐出 TTL 短的键按过期时间淘汰

内存碎片整理

# 查看碎片率
INFO memory   # mem_fragmentation_ratio

# 自动碎片整理(Redis 4.0+)
CONFIG SET activedefrag yes
CONFIG SET active-defrag-ignore-bytes 100mb
CONFIG SET active-defrag-threshold-lower 10   # 碎片率 > 1.1

mem_fragmentation_ratio = used_memory_rss / used_memory,大于 1.5 表示碎片严重。

K8s 内存管理注意

  • resources.limits.memory 需大于 maxmemory,预留 fork RDB 和系统开销
  • 建议比例:limits.memory >= maxmemory * 1.3 + 500MB
  • 开启 oom-score-adj 避免 OOM Killer 误杀
14 Redis Pipeline 与 Batch 操作有什么不同?

答案:

维度PipelineBatch(MGET/MSET)事务(MULTI/EXEC)
实现方式客户端缓存多条命令一次性发送单条命令操作多个 Key将多条命令打包原子执行
原子性不保证单个命令本身原子保证(EXEC 前不被打断)
网络往返1 次 RTT(N 条命令)1 次 RTT(1 条命令)1 次 RTT(N 条命令)
执行顺序按发送顺序执行N/A按添加顺序执行
错误处理某命令失败不影响其他整体成功或失败某命令语法错误时全部不执行

Pipeline 使用示例(Go)

pipe := rdb.Pipeline()
incr := pipe.Incr(ctx, "pipeline_counter")
pipe.Expire(ctx, "pipeline_counter", time.Hour)
cmds, err := pipe.Exec(ctx)

注意事项

  • Pipeline 内命令数量不宜过大,建议单次 ≤ 100 条
  • Pipeline 不保证原子性,中间可能插入其他客户端的命令
  • Redis Cluster 下 Pipeline 要求所有 Key 在同一个 Slot(可用 {} hash tag 控制)
15 Redis Pub/Sub 与 Stream 有什么区别?

答案:

维度Pub/SubStream
消息持久化不持久,消费者不在线即丢失持久化至内存/RDB/AOF
消费者组不支持支持(XGROUP),可负载均衡
消息回溯不支持支持(按 ID 范围读取)
消息确认支持(XACK
适用场景实时通知、聊天事件溯源、消息队列、日志收集
内存管理不堆积需设置 MAXLEN 限制长度

Stream 基本操作

# 写入
XADD mystream * field1 value1 field2 value2

# 读取(阻塞)
XREAD BLOCK 0 STREAMS mystream 0

# 消费者组
XGROUP CREATE mystream mygroup $ MKSTREAM
XREADGROUP GROUP mygroup consumer1 BLOCK 0 STREAMS mystream >

# 确认
XACK mystream mygroup <message-id>

# 限制长度
XADD mystream MAXLEN ~ 10000 * field value

K8s Stream 注意事项

  • Stream 数据存储在内存中,需注意 maxmemory 限制
  • 消费者组信息存储在 Redis 内存中,故障转移后需重建或持久化消费偏移
16 Redis 分布式锁方案有哪些?

答案:

方案原理安全性适用场景
SET NX + EXSET key value NX EX <ttl>单实例安全单机 Redis,低可靠性要求
Redisson(RedLock)多数节点加锁成功视为获得锁高(防脑裂)多节点 Redis,金融级场景
SET + Lua 释放Lua 脚本原子校验 value 后删除防误删单实例需防锁被他人释放

SET NX 正确实现

# 加锁
SET lock:order:123 unique-client-id NX EX 30

# 释放(Lua 脚本保证原子性)
EVAL "
  if redis.call('GET', KEYS[1]) == ARGV[1] then
    return redis.call('DEL', KEYS[1])
  else
    return 0
  end
" 1 lock:order:123 unique-client-id

Redisson RedLock 原理

  1. 客户端向 N 个独立 Redis 实例依次请求加锁
  2. 设置锁超时远小于 TTL(如 TTL=30s,超时=5ms)
  3. 当 ≥ N/2+1 个实例加锁成功,且总耗时 < TTL,锁获取成功
  4. 实际有效时间 = TTL - 获取耗时
  5. 释放时向所有实例发送释放命令

K8s 场景注意:RedLock 要求 Redis 实例彼此独立(不同节点、不同可用区),K8s 上需配合 podAntiAffinity 部署。

17 缓存穿透、击穿、雪崩如何防护?

答案:

问题定义防护方案
缓存穿透查询不存在的数据,请求直达数据库布隆过滤器、空值缓存(短 TTL)、参数校验
缓存击穿热点 Key 过期瞬间高并发直达数据库互斥锁(SET NX)、逻辑过期 + 异步刷新、永不过期
缓存雪崩大量 Key 同时过期或 Redis 宕机TTL 随机化、多级缓存、限流降级、Redis 高可用

布隆过滤器(RedisBloom 模块)

# 添加元素
BF.ADD cache-filter "key:12345"
BF.EXISTS cache-filter "key:12345"

# 大规模导入
BF.RESERVE large-filter 0.01 1000000   # 错误率 1%,100 万容量
BF.MADD large-filter key1 key2 key3

热点 Key 互斥锁方案(伪代码)

func GetData(key string) (string, error) {
    val, err := rdb.Get(ctx, key).Result()
    if err == redis.Nil {
        // 缓存未命中,使用互斥锁
        lockKey := "lock:" + key
        locked, _ := rdb.SetNX(ctx, lockKey, "1", 10*time.Second).Result()
        if locked {
            defer rdb.Del(ctx, lockKey)
            val = queryDB(key)
            rdb.Set(ctx, key, val, 30*time.Minute)
        } else {
            time.Sleep(100 * time.Millisecond)
            return GetData(key) // 重试
        }
    }
    return val, err
}

TTL 随机化EXPIRE key <base_ttl + random(0, base_ttl * 0.3)>,避免集中过期。

18 大 Key 与热 Key 如何检测和处理?

答案:

检测手段

工具用途命令
redis-cli --bigkeys扫描最大 Keyredis-cli -h <host> --bigkeys
redis-cli --hotkeys扫描热 Key(需 maxmemory-policy 为 LFU)redis-cli -h <host> --hotkeys
MEMORY USAGE <key>精确计算 Key 内存占用MEMORY USAGE user:10001
OBJECT FREQ <key>LFU 访问频率OBJECT FREQ user:10001
redis-rdb-tools离线分析 RDB 文件rdb -c memory dump.rdb
SCAN + DEBUG OBJECT渐进式扫描SCAN 0 MATCH * COUNT 1000

大 Key 定义

数据类型阈值
String> 10MB
List / Set / ZSet / Hash元素数量 > 10000
Stream消息数 > 100000 或总大小 > 50MB

处理策略

  1. 拆分:Hash 大 Key 按业务维度拆分为多个小 Hash
  2. 压缩:String 大 Key 先压缩再存储(snappy/gzip)
  3. 删除:分批渐进式删除,避免阻塞
    # Hash 分批删除字段
    HSCAN key 0 COUNT 100 → HDEL key field1 field2 ...
    
  4. 迁移:使用 MIGRATE 将单个大 Key 迁移至独立实例
  5. 架构调整:热 Key 前置本地缓存(如 Caffeine、BigCache)
19 Redis 客户端连接池如何配置?

答案:

连接池关键参数(以 go-redis 为例)

参数说明建议值
PoolSize最大连接数CPU 核数 × 4 ~ 10
MinIdleConns最小空闲连接PoolSize × 0.2
MaxIdleConns最大空闲连接PoolSize
PoolTimeout等待连接超时5s ~ 10s
IdleTimeout空闲连接回收时间5min
IdleCheckFrequency空闲检查频率1min
ConnMaxLifetime连接最大生命周期30min ~ 1h

go-redis 配置示例

rdb := redis.NewClient(&redis.Options{
    Addr:         "redis-headless:6379",
    Password:     os.Getenv("REDIS_PASSWORD"),
    DB:           0,
    PoolSize:     50,
    MinIdleConns: 10,
    MaxIdleConns: 50,
    PoolTimeout:  5 * time.Second,
    IdleTimeout:  5 * time.Minute,
    DialTimeout:  5 * time.Second,
    ReadTimeout:  3 * time.Second,
    WriteTimeout: 3 * time.Second,
})

Redis 服务端连接参数

maxclients 10000
timeout 300          # 空闲连接超时秒数
tcp-keepalive 60     # TCP keepalive 间隔
tcp-backlog 511      # TCP 完成队列大小

K8s 场景注意

  • 连接池大小需配合 Pod 副本数计算总连接数,不超过 maxclients
  • Service Mesh(如 Istio)的 sidecar 可能增加连接,需留余量
  • IdleTimeout 应小于 Service/负载均衡的空闲超时,避免半开连接
20 Redis SSL/TLS 加密与 ACL 权限如何控制?

答案:

TLS 配置

# redis.conf
tls-port 6380
port 0                          # 禁用非 TLS 端口
tls-cert-file /etc/tls/tls.crt
tls-key-file /etc/tls/tls.key
tls-ca-cert-file /etc/tls/ca.crt
tls-auth-clients yes            # 要求客户端证书
tls-protocols "TLSv1.2 TLSv1.3"
tls-ciphers DEFAULT:!MEDIUM

K8s cert-manager 集成

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: redis-tls
spec:
  secretName: redis-tls-secret
  duration: 2160h
  renewBefore: 360h
  dnsNames:
  - "redis-0.redis-headless.redis.svc.cluster.local"
  - "*.redis-headless.redis.svc.cluster.local"
  issuerRef:
    name: ca-issuer
    kind: ClusterIssuer

ACL 权限控制(Redis 6.0+)

# 创建用户
ACL SETUSER reader on >password ~* +@read -@write -@dangerous
ACL SETUSER writer on >password ~* +@all -@dangerous
ACL SETUSER appuser on >password ~prefix:* +@all

# 查看用户
ACL LIST
ACL GETUSER reader

# 配置文件持久化
aclfile /etc/redis/users.acl

ACL 权限规则

规则含义
on / off启用/禁用用户
>password设置密码
~pattern可访问的键模式(~* 表示所有键)
+@category授权命令类别(+@read 只读)
-command禁用特定命令(-FLUSHALL
+command授权特定命令
21 Redis Read-Only Replica 读写分离如何实现?

答案:

配置从节点可读

# 从节点配置
replica-read-only yes        # 从节点只读(默认)
# 或
replica-read-only no         # 允许写入(数据会被主节点覆盖)

客户端读写分离策略

策略原理一致性
应用层路由客户端维护主从连接,写操作用主连接,读操作用从连接最终一致(存在复制延迟)
Proxy 层Proxy 解析命令类型自动路由(如 Twemproxy、Codis)最终一致
一致性读写后紧接的读强制走主节点强一致

go-redis 读写分离示例

// 主节点(写入)
master := redis.NewClient(&redis.Options{
    Addr: "redis-master:6379",
})
// 从节点(读取)
slaves := redis.NewRing(&redis.RingOptions{
    Addrs: map[string]string{
        "slave0": "redis-slave-0.redis-headless:6379",
        "slave1": "redis-slave-1.redis-headless:6379",
    },
})

// 业务代码
master.Set(ctx, "key", "value", 0)
slaves.Get(ctx, "key")

一致性边界:Redis 主从复制是异步的,读从可能读到旧数据。需要强一致性的读操作必须走主节点。可通过 WAIT 命令将异步复制变为半同步:

SET key value
WAIT 1 1000    # 等待至少 1 个从节点确认,超时 1000ms
22 Redis 在线迁移方案有哪些?

答案:

方案原理停机时间适用场景
Redis-Shake解析 RDB + AOF,全量 + 增量同步秒级(切换瞬间)跨集群迁移、云上云下迁移
主从复制切换新实例 REPLICAOF 旧主,同步后切换秒级同版本升级、迁移
RDB 导入BGSAVE → 拷贝 RDB → 新实例加载分钟级停机维护窗口
MIGRATE单 Key 原子迁移无(逐 Key)集群 Slot 迁移
SCAN + DUMP/RESTORE分批序列化传输无(逐批)选择性数据迁移

Redis-Shake 迁移流程

# Redis-Shake K8s Job
apiVersion: batch/v1
kind: Job
metadata:
  name: redis-shake-migration
spec:
  template:
    spec:
      containers:
      - name: redis-shake
        image: apsaradb/redis-shake:latest
        command:
        - redis-shake.linux
        args:
        - -type=sync            # sync / restore / scan
        - -conf=/etc/shake.toml
        volumeMounts:
        - name: config
          mountPath: /etc
      volumes:
      - name: config
        configMap:
          name: redis-shake-config
      restartPolicy: Never

shak.toml 配置

[sync_reader]
address = "source-redis:6379"
password = "source-pwd"

[redis_writer]
address = "target-redis:6379"
password = "target-pwd"

迁移步骤

  1. 部署 Redis-Shake 同步任务
  2. 等待全量 + 增量追平(延迟 < 1ms)
  3. 停止源端写入
  4. 确认数据一致后切换客户端连接至目标
  5. 停止 Redis-Shake,清理源实例
23 Redis 拓扑感知调度如何通过 Pod Anti-Affinity 实现?

答案:

Pod Anti-Affinity 确保 Redis 实例分散在不同节点 / 可用区,避免单点故障。

三层调度策略

策略级别topologyKey效果
节点级反亲和kubernetes.io/hostname同节点不部署多个 Redis Pod
可用区级反亲和topology.kubernetes.io/zone同可用区最多一个副本
软反亲和preferredDuringScheduling优先分散,资源不足时可堆叠

完整配置

affinity:
  podAntiAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
    - labelSelector:
        matchExpressions:
        - key: app
          operator: In
          values: ["redis"]
      topologyKey: kubernetes.io/hostname
  nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
      - matchExpressions:
        - key: node-role.kubernetes.io/redis
          operator: Exists

TopologySpreadConstraints(替代 Anti-Affinity)

topologySpreadConstraints:
- maxSkew: 1
  topologyKey: topology.kubernetes.io/zone
  whenUnsatisfiable: DoNotSchedule
  labelSelector:
    matchLabels:
      app: redis
- maxSkew: 1
  topologyKey: kubernetes.io/hostname
  whenUnsatisfiable: ScheduleAnyway
  labelSelector:
    matchLabels:
      app: redis

Node Selector / Taint-Toleration:Redis Pod 专属节点池,通过 taint 隔离其他工作负载:

tolerations:
- key: "dedicated"
  operator: "Equal"
  value: "redis"
  effect: "NoSchedule"
nodeSelector:
  node-group: redis
24 Redis Lua 脚本与事务有什么区别?

答案:

维度Lua 脚本MULTI/EXEC 事务WATCH 乐观锁
原子性完全原子,执行期间不可中断命令打包,但不回滚不保证原子性
条件逻辑支持(if/else/循环)不支持(命令固定)支持 CAS
网络往返1 次1 次多次
错误处理脚本执行失败不回滚已执行部分语法错误全部不执行,运行时错误不回滚竞争失败需重试

Lua 脚本示例

-- 分布式限流:令牌桶
local key = KEYS[1]
local capacity = tonumber(ARGV[1])
local rate = tonumber(ARGV[2])
local now = tonumber(ARGV[3])

local tokens = tonumber(redis.call('HGET', key, 'tokens')) or capacity
local last_time = tonumber(redis.call('HGET', key, 'last_time')) or now

local delta = math.max(0, now - last_time)
local new_tokens = math.min(capacity, tokens + delta * rate)

if new_tokens >= 1 then
    redis.call('HSET', key, 'tokens', new_tokens - 1)
    redis.call('HSET', key, 'last_time', now)
    return 1
else
    redis.call('HSET', key, 'tokens', new_tokens)
    redis.call('HSET', key, 'last_time', now)
    return 0
end
EVAL "<script>" 1 "rate_limit:user:123" 10 0.5 <current_timestamp>

WATCH 乐观锁

WATCH balance:user:100
val = GET balance:user:100
MULTI
SET balance:user:100 <val - amount>
EXEC   # 如果 key 被修改,EXEC 返回 nil
25 RedisJSON、RedisSearch、RedisTimeSeries 模块有何用途?

答案:

模块用途核心命令K8s 部署方式
RedisJSONJSON 文档存储与查询JSON.SETJSON.GETJSON.ARRAPPENDredislabs/rejson 镜像
RedisSearch全文搜索、向量搜索FT.CREATEFT.SEARCHFT.AGGREGATEredislabs/redisearch 镜像
RedisTimeSeries时序数据存储与聚合TS.CREATETS.ADDTS.RANGEredislabs/redistimeseries 镜像
RedisBloom布隆过滤器、计数、布谷鸟BF.ADDCF.ADDCMS.INCRBYredislabs/rebloom 镜像
RedisGraph图数据库GRAPH.QUERYredislabs/redisgraph 镜像

模块加载方式

# redis.conf
loadmodule /usr/lib/redis/modules/redisearch.so
loadmodule /usr/lib/redis/modules/rejson.so
loadmodule /usr/lib/redis/modules/redistimeseries.so

Redis Stack 镜像(一站式)

containers:
- name: redis
  image: redis/redis-stack-server:7.2.0-v10
  # 已内置 RedisJSON、RedisSearch、RedisTimeSeries、RedisBloom、RedisGraph

RedisSearch 全文搜索

FT.CREATE idx:articles ON JSON PREFIX 1 article: SCHEMA \
  $.title AS title TEXT SORTABLE \
  $.content AS content TEXT \
  $.tags.* AS tags TAG

FT.SEARCH idx:articles "@title:kubernetes @tags:{redis}" RETURN 2 title tags

RedisTimeSeries 时序聚合

TS.CREATE ts:sensor:temp RETENTION 86400000 LABELS sensor_id 1
TS.ADD ts:sensor:temp * 23.5
TS.RANGE ts:sensor:temp <start> <end> AGGREGATION avg 60000   # 1 分钟均值
26 PodDisruptionBudget 如何保障 Redis 在节点维护时的高可用?

答案:

PodDisruptionBudget(PDB) 限制同时不可用的 Pod 数量,防止节点维护或集群自动缩放时批量驱逐 Redis Pod。

PDB 配置

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: redis-pdb
spec:
  maxUnavailable: 1        # Sentinel:最多 1 个 Pod 不可用
  selector:
    matchLabels:
      app: redis

# Cluster 模式:每个 StatefulSet 至少保留 N-1 个可用
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: redis-cluster-pdb
spec:
  minAvailable: 5          # 6 节点集群至少保持 5 个可用
  selector:
    matchLabels:
      app: redis-cluster

PDB 与故障转移联动

  1. kubectl drain <node> 触发 Pod 驱逐
  2. PDB 限制同时驱逐数量
  3. 驱逐的 Pod 如果是主节点,Sentinel 触发故障转移
  4. 从节点提升为新主后,流量切换到新主
  5. 被驱逐的 Pod 在新节点重新调度

多重保障策略

保障手段作用
PDB限制驱逐数量,防止集群大规模不可用
Pod Anti-Affinity分散在不同节点,降低同时被驱逐风险
Pod PriorityRedis Pod 优先级高于普通 Pod,避免被优先驱逐
terminationGracePeriodSeconds足够时间(≥ 60s)完成 BGSAVE 持久化
27 Redis 跨集群复制与灾备如何实现?

答案:

方案同步方式RPORTO适用场景
异地从节点异步复制(REPLICAOF秒级分钟级同地域跨可用区
双写应用层同时写两个集群取决于应用实现秒级最终一致性容忍场景
Redis-Shake 持续同步RDB + AOF 解析同步秒级分钟级跨云迁移、异地灾备
Redis Enterprise Active-ActiveCRDT 冲突解决毫秒级秒级全球分布式,需要商业许可
RDB 定时备份 + 异地恢复全量备份 + S3 同步小时级30 分钟+低成本灾备

异地从节点配置

# 灾备集群从节点
REPLICAOF <primary-cluster-master-ip> 6379

Redis-Shake 灾备同步(持续模式)

[sync_reader]
address = "primary-cluster-redis:6379"

[redis_writer]
address = "dr-cluster-redis:6379"

[advanced]
dir = /data
ncpu = 4

灾备演练流程

  1. 停止源集群写入
  2. 确认灾备集群数据同步完成(对比 master_repl_offset
  3. 灾备集群从节点执行 REPLICAOF NO ONE 提升为主
  4. DNS/Service 切换流量至灾备集群
  5. 验证业务功能完整性
  6. 数据反向同步:灾备 → 原集群恢复后回流
28 Redis vs KeyDB vs Dragonfly 对比

答案:

维度Redis 7.2KeyDBDragonfly
架构单线程事件循环(I/O 多线程)多线程(真正的并行处理)多线程(无共享架构)
线程模型1 个主线程 + I/O 线程(6.0+)N 个工作线程并行执行命令分片 + 事务引擎
性能~100K QPS(单核)~500K QPS(多核)~1M+ QPS(多核)
兼容性Redis 原生协议Redis 协议完全兼容Redis 协议兼容(部分命令不支持)
内存效率基准与 Redis 相当比 Redis 节省 30%+ 内存(DashTable)
持久化RDB + AOFRDB + AOF自定义快照格式
集群Redis Cluster(原生)Active Replication(多主)1.0+ 支持集群
Lua 脚本完全支持完全支持有限支持(无 EVAL
模块RedisJSON/Search/TS/Bloom/Graph部分支持不支持原生模块
K8s 部署成熟(多个 Operator)社区支持官方 Helm Chart
许可证RSALv2 / SSPLv1BSD 3-ClauseBSL(Business Source License)
适用场景通用缓存/队列/会话多核高吞吐缓存大内存成本敏感场景

选型建议

  • Redis:生态最成熟,模块丰富,企业级支持,K8s Operator 完善
  • KeyDB:需要多线程并行处理的纯缓存场景,Redis 协议无缝迁移
  • Dragonfly:内存成本敏感,不需要 Redis 模块,追求极致吞吐
29 Redis on Kubernetes 常见故障排查

答案:

故障现象可能原因排查步骤解决方案
OOM Kill内存超出 Limitkubectl describe pod 查看 OOMKilled;检查 maxmemory 配置增加 resources.limits.memory;降低 maxmemory;启用逐出策略
CPU ThrottlingCPU Limit 过低检查 container_cpu_cfs_throttled 指标提高 CPU Limit 或使用 Guaranteed QoS
启动失败RDB/AOF 损坏redis-check-rdb /data/dump.rdbredis-check-aof /data/appendonly.aof修复或删除损坏文件重新启动
复制延迟网络带宽不足、主写入量大INFO replication 查看 master_repl_offset - slave_repl_offset增大网络带宽;使用 repl-diskless-sync
脑裂Sentinel quorum 不满足;网络分区检查 Sentinel 日志;SENTINEL MASTER mymaster调整 quorum 为 N/2+1;配置 min-slaves-to-write
集群状态 fail主节点宕机且无从节点接管 SlotCLUSTER INFO 查看 cluster_state修复故障节点;CLUSTER FAILOVER 手动切换
磁盘空间满RDB/AOF 文件增长过大df -h /data调整 auto-aof-rewrite-min-size;扩展 PVC
客户端连接泄漏未正确关闭连接CLIENT LIST 查看连接数增长应用端 Connection Pool 设置 MaxLifetime
Pod 无法调度PDB + Anti-Affinity 冲突kubectl describe pod 查看 Events调整 PDB minAvailable;增加节点
慢查询阻塞KEYS *、O(N) 命令SLOWLOG GET 10替换为 SCAN;RENAME 禁用危险命令

排查常用命令

# K8s 侧
kubectl describe pod redis-0
kubectl logs redis-0 -c redis --tail=200
kubectl exec -it redis-0 -- redis-cli INFO
kubectl exec -it redis-0 -- redis-cli CLUSTER INFO
kubectl get events --field-selector involvedObject.name=redis-0

# Redis 侧
redis-cli INFO all
redis-cli CLUSTER NODES
redis-cli SLOWLOG GET 20
redis-cli CLIENT LIST
redis-cli MEMORY STATS
redis-cli --latency -h <host>
30 Redis on Kubernetes 生产环境最佳实践总结

答案:

部署架构

  1. 使用 StatefulSet 而非 Deployment,保障稳定的网络标识和持久化存储
  2. 配置 Headless ServiceclusterIP: None),为每个 Pod 提供独立 DNS 记录
  3. 设置 Pod Anti-Affinity 确保 Redis 实例分散在不同节点 / 可用区
  4. 使用 Operator(Spotahome / Ot Operator)管理 Sentinel 或 Cluster 拓扑,减少运维复杂度
  5. 配置 PodDisruptionBudget 防止节点维护时批量驱逐

资源配置

  1. resources.limits.memory >= maxmemory * 1.5,为 RDB fork 和系统开销留余量
  2. resources.requests.memory = resources.limits.memory(Guaranteed QoS),避免被 OOM Kill
  3. resources.requests.cpu >= 2,生产环境不共享 CPU
  4. 使用 SSD / NVMe StorageClass 且 IOPS >= 3000
  5. PVC 大小按 maxmemory * 1.5(RDB)或 maxmemory * 3(AOF)预估

高可用与可靠性

  1. Sentinel 部署 3 个或 5 个(奇数),quorum = N/2 + 1
  2. 配置 min-replicas-to-write 1min-replicas-max-lag 10,防止主节点脑裂后写入丢失
  3. Cluster 模式每个主节点至少配 1 个从节点,跨可用区分布
  4. 配置 cluster-require-full-coverage no,允许部分 Slot 不可用时集群继续服务
  5. 定期执行备份演练和故障转移演练

持久化与备份

  1. 生产环境开启 AOF(everysec)+ RDB 混合持久化
  2. 配置 auto-aof-rewrite-percentage 100auto-aof-rewrite-min-size 64mb
  3. 使用 CronJob 定时执行 RDB 备份并上传至对象存储(S3/MinIO)
  4. 定期执行 redis-check-rdbredis-check-aof 验证备份文件完整性
  5. 备份数据跨区域复制(S3 Cross-Region Replication)

安全

  1. 为每个应用创建独立 ACL 用户,最小权限原则(+@read -@write -@dangerous
  2. 启用 TLS 加密通信(tls-port),使用 cert-manager 自动管理证书
  3. 重命名或禁用危险命令:rename-command FLUSHALL ""rename-command CONFIG ""
  4. 配置 protected-mode yesbind 限制访问网段
  5. 不暴露 Redis 端口至公网,使用 NetworkPolicy 限制入站流量

监控与告警

  1. 部署 redis_exporter 作为 Sidecar 容器,通过 Prometheus PodMonitor 采集指标
  2. 配置关键告警:内存 > 80%、连接数 > 80%、命中率 < 90%、复制延迟 > 10MB
  3. 使用 Grafana Dashboard(如 763)集中展示 Redis 集群状态
  4. 集成日志采集(Filebeat/Fluentd → Elasticsearch/Loki),结构化解析 Redis 日志
  5. 设置慢查询告警:SLOWLOG 增长速率异常时触发通知