本文将使用 Docker 实现 Redis 常见的几种部署模式,包括单机模式、主从复制模式、哨兵模式、集群模式。

总结一下这几种模式的优缺点:

部署模式核心思想优点缺点适用场景
单机模式单个实例简单、性能高单点故障、容量有限开发、测试、非核心缓存
主从复制一主多从,读写分离数据备份、读扩展Master 单点、需手动故障转移需要数据备份和高读吞吐量的场景
哨兵模式监控+主从,自动故障转移自动故障转移、高可用容量仍受限、配置较复杂对可用性要求高,但数据量不大的场景
集群模式数据分片,去中心化横向扩展、高可用、高吞吐配置复杂、部分命令受限海量数据、高并发、高可用的超大规模场景

单机模式

这是最简单的、最基础的部署方式,配置和维护比较简单,但是数据可靠性不高,适用于开发和测试环境,以及一些对可靠性要求不高的业务,能够容忍缓存数据丢失,可以轻易重建缓存。

compose.yaml 配置如下:

services:
  redis-standalone:
    image: redis:latest
    container_name: redis-standalone
    restart: unless-stopped
    ports:
      - 6379:6379/tcp
    volumes:
      - ./data:/data:rw
      - ./redis.conf:/usr/local/etc/redis/redis.conf:ro
    command: >-
      redis-server /usr/local/etc/redis/redis.conf

redis.conf 配置如下:

bind 0.0.0.0
port 6379
requirepass xueye.io

appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec

auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-use-rdb-preamble yes

save 900 1
save 300 10
save 60 10000

dbfilename dump.rdb
rdbcompression yes

maxmemory 512mb
maxmemory-policy allkeys-lru

主从模式

主从模式解决了单机模式的单点故障和数据备份的问题,包含一个主节点(Master)和多个从节点(Slave)。

写操作只在 Master 节点进行,Master 节点的数据会自动、异步地复制到所有 Slave 节点。Slave 节点通常是只读的,可以分担请求压力。

优点是 Master 宕机后,Slave 仍然可以提供读服务。Slave 作为 Master 节点的冗余备份,提高了数据安全性。

缺点是,Master 宕机后需要手动将一个 Slave 提升为新 Master。

主从模式适用于对写入性要求不高,能够接受手动故障转移的场景。

compose.yaml 配置如下:

services:
  redis-master:
    image: redis:latest
    container_name: redis-master
    command: redis-server /usr/local/etc/redis/redis.conf
    ports:
      - "6379:6379"
    volumes:
      - ./config/redis.conf:/usr/local/etc/redis/redis.conf
      - ./master/data:/data
    restart: always
    networks:
      - redis-net

  redis-slave-1:
    image: redis:latest
    container_name: redis-slave-1
    command: redis-server /usr/local/etc/redis/redis.conf --replicaof redis-master 6379
    ports:
      - "6380:6379"
    volumes:
      - ./config/redis.conf:/usr/local/etc/redis/redis.conf
      - ./slave1/data:/data
    restart: always
    depends_on:
      - redis-master
    networks:
      - redis-net

  redis-slave-2:
    image: redis:latest
    container_name: redis-slave-2
    command: redis-server /usr/local/etc/redis/redis.conf --replicaof redis-master 6379
    ports:
      - "6381:6379"
    volumes:
      - ./config/redis.conf:/usr/local/etc/redis/redis.conf
      - ./slave2/data:/data
    restart: always
    depends_on:
      - redis-master
    networks:
      - redis-net

networks:
  redis-net:
    driver: bridge

redis.conf 配置如下:

bind 0.0.0.0
port 6379

requirepass simaek.com
masterauth simaek.com

appendonly yes
appendfsync everysec

save 900 1
save 300 10
save 60 10000

replica-read-only yes

验证主从关系:

docker-compose exec -it redis-master redis-cli

进入redis-cli之后,进行认证并查看信息。

127.0.0.1:6379> AUTH simaek.com
OK
127.0.0.1:6379> INFO replication
# Replication
role:master
connected_slaves:2
slave0:ip=192.168.144.3,port=6379,state=online,offset=126,lag=1
slave1:ip=192.168.144.4,port=6379,state=online,offset=126,lag=1
master_failover_state:no-failover
master_replid:099f05d52c796d1f1d52843b53402aa1e15a01c6
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:126
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:15
repl_backlog_histlen:112

可以尝试在 Master 和 Slave 读写数据进行进一步测试,这里就不展开了。

哨兵模式

哨兵模式在主从模式的基础上,引入了“哨兵(Sentinel)”进程来监控整个 Redis 系统,实现了自动故障转移。

由一个或者多个 Sentinel 进程组成的集群,用于监控所有的 Master 和 Slave 节点。当 Sentinel 检测到 Master 节点宕机时,会自动在 Slave 节点中选举出一个新的 Master,然后更新其他 Slave 节点的配置,让它们复制新的 Master,并且通知客户端新的 Master 地址。

哨兵模式解决了主从模式的可用问题,但是性能上还是受限于 Master 单节点。

哨兵模式适用于要求可用性,但是数据量可以被单个 Redis 实例承载的场景。

沿用主从的配置,新增加额外的哨兵集群。

compose.yaml 配置如下:

services:
# --snip--
  sentinel-1:
    image: redis:latest
    container_name: sentinel-1
    command: redis-server /usr/local/etc/redis/sentinel.conf --sentinel
    ports:
      - "26379:26379"
    volumes:
      - ./config/sentinel.conf:/usr/local/etc/redis/sentinel.conf
    restart: always
    depends_on:
      - redis-master
      - redis-slave-1
      - redis-slave-2
    networks:
      - redis-net
  sentinel-2:
    image: redis:latest
    container_name: sentinel-2
    command: redis-server /usr/local/etc/redis/sentinel.conf --sentinel
    ports:
      - "26380:26379"
    volumes:
      - ./config/sentinel.conf:/usr/local/etc/redis/sentinel.conf
    restart: always
    depends_on:
      - redis-master
      - redis-slave-1
      - redis-slave-2
    networks:
      - redis-net
  sentinel-3:
    image: redis:latest
    container_name: sentinel-3
    command: redis-server /usr/local/etc/redis/sentinel.conf --sentinel
    ports:
      - "26381:26379"
    volumes:
      - ./config/sentinel.conf:/usr/local/etc/redis/sentinel.conf
    restart: always
    depends_on:
      - redis-master
      - redis-slave-1
      - redis-slave-2
    networks:
      - redis-net

sentinel.conf 配置如下:

# 哨兵默认监听端口
port 26379

sentinel resolve-hostnames yes

# 告诉哨兵去监控哪个主节点
# 格式: sentinel monitor <master-group-name> <ip> <port> <quorum>
# <master-group-name>: 主节点集群的逻辑名称,由你自定义,客户端会用它来发现主节点。
# <ip>: 主节点的地址。在 Docker Compose 中,我们使用服务名 redis-master。
# <port>: 主节点的端口。
# <quorum>: 法定人数。表示至少需要多少个哨兵同意,才能判定主节点下线并触发故障转移。
#           对于3个哨兵,设置为2是最佳实践 (N/2 + 1)。
sentinel monitor mymaster redis-master 6379 2

# 主节点被判定为下线前,需要多长时间无响应(毫秒)
sentinel down-after-milliseconds mymaster 5000

# 故障转移超时时间(毫秒)。如果在这个时间内,从节点没有被提升为新主节点,则本次故障转移失败。
sentinel failover-timeout mymaster 60000

# 在故障转移后,一次可以有多少个从节点同时对新的主节点进行同步。
# 设置为1可以减少新主节点的压力。
sentinel parallel-syncs mymaster 1

# 由于我们的 Redis 节点设置了密码,哨兵必须使用此密码才能连接。
# <master-group-name> 必须与上面的 monitor 指令中的名称一致。
sentinel auth-pass mymaster simaek.com

连接到哨兵节点检查状态:

docker-compose exec -it sentinel-1 redis-cli -p 26379

127.0.0.1:26379> INFO sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_tilt_since_seconds:-1
sentinel_total_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=192.168.160.3:6379,slaves=2,sentinels=3

127.0.0.1:26379> SENTINEL get-master-addr-by-name mymaster
1) "192.168.160.3"
2) "6379"

可以尝试模拟 Master 宕机,访问哨兵和选举出的新 Master 节点查看主从状态。此处不在赘述。

这里有一个问题,在使用容器部署的时候,集群获取到的都是内部的地址。由于容器采用的 NAT 映射,在外部进行访问的时候,需要配置通告宿主机真实地址。

集群模式

当数据量巨大,单个 Master 无法容纳时,就需要集群模式了。集群模式会将数据分散存储在多个 Master 节点上。Redis 集群预设为 16384 个哈希槽,每个 Key 经过 CRC16 算法计算后,分配到其中一个槽,每个 Master 节点负责一部分槽。

集群中每个节点都互相连接,知道其他节点负责哪些槽。客户端可以连接到任意一个节点,如果 Key 不在该节点,节点会被自动重定向到正确的节点。

另外每个 Master 节点都可以有自己的 Slave 节点,当某个 Master 宕机时,其对应的 Slave 会自动提升为新的 Master,接管其负责的哈希槽,保证集群的可用性。

集群模式的优点是可以通过增加节点来横向拓展容量和吞吐量,突破了单机内存限制。另外继承了类似哨兵模式的故障转移功能,不再需要引入哨兵来保证可用性。

但是集群模式也不是没有缺点,首先部署和维护相比其他模式都要复杂的多。对于跨多个 Slot 操作支持有限,除非这些 Key 恰好在同一个 Slot。Lua 脚本和事务也必须保证操作的 Key 在同一个节点上。

集群模式适用于海量数据的存储,数据量超过单机容量上限。需要高可用和高并发的场景也需要使用集群模式。

compose.yaml 配置如下:

services:
  redis-1:
    image: redis:latest
    container_name: redis-1
    # 【关键】通告自己的主机名,并加载配置
    command: redis-server /usr/local/etc/redis/redis.conf --cluster-announce-hostname redis-1
    ports:
      - "7001:6379"
      - "17001:16379" # 集群总线端口
    volumes:
      - ./config:/usr/local/etc/redis
      - ./data/node-1:/data
    restart: always
    networks:
      - redis-net

  redis-2:
    image: redis:latest
    container_name: redis-2
    command: redis-server /usr/local/etc/redis/redis.conf --cluster-announce-hostname redis-2
    ports:
      - "7002:6379"
      - "17002:16379"
    volumes:
      - ./config:/usr/local/etc/redis
      - ./data/node-2:/data
    restart: always
    networks:
      - redis-net

  redis-3:
    image: redis:latest
    container_name: redis-3
    command: redis-server /usr/local/etc/redis/redis.conf --cluster-announce-hostname redis-3
    ports:
      - "7003:6379"
      - "17003:16379"
    volumes:
      - ./config:/usr/local/etc/redis
      - ./data/node-3:/data
    restart: always
    networks:
      - redis-net

  redis-4:
    image: redis:latest
    container_name: redis-4
    command: redis-server /usr/local/etc/redis/redis.conf --cluster-announce-hostname redis-4
    ports:
      - "7004:6379"
      - "17004:16379"
    volumes:
      - ./config:/usr/local/etc/redis
      - ./data/node-4:/data
    restart: always
    networks:
      - redis-net

  redis-5:
    image: redis:latest
    container_name: redis-5
    command: redis-server /usr/local/etc/redis/redis.conf --cluster-announce-hostname redis-5
    ports:
      - "7005:6379"
      - "17005:16379"
    volumes:
      - ./config:/usr/local/etc/redis
      - ./data/node-5:/data
    restart: always
    networks:
      - redis-net

  redis-6:
    image: redis:latest
    container_name: redis-6
    command: redis-server /usr/local/etc/redis/redis.conf --cluster-announce-hostname redis-6
    ports:
      - "7006:6379"
      - "17006:16379"
    volumes:
      - ./config:/usr/local/etc/redis
      - ./data/node-6:/data
    restart: always
    networks:
      - redis-net

networks:
  redis-net:
    driver: bridge

redis.conf 配置如下:

bind 0.0.0.0
port 6379

# 【集群模式核心配置】
# 开启集群模式
cluster-enabled yes

# 每个节点都有一个集群配置文件,由 Redis 自动维护,记录了集群中的其他节点等信息。
# 这个文件不能被多个节点共享,但因为我们为每个容器挂载了独立的数据目录,所以没问题。
cluster-config-file nodes.conf

# 节点超时时间(毫秒)
cluster-node-timeout 5000

# 【持久化与安全】
# 开启 AOF
appendonly yes
appendfsync everysec

# 设置密码。所有节点密码必须相同。
requirepass simaek.com
masterauth simaek.com

创建集群:

进入任何一个容器内执行。

docker-compose exec -it redis-1 redis-cli \
    --cluster create \
    redis-1:6379 \
    redis-2:6379 \
    redis-3:6379 \
    redis-4:6379 \
    redis-5:6379 \
    redis-6:6379 \
    --cluster-replicas 1 \
    -a simaek.com

--cluster-replicas 1 意味着为每个创建的主节点分配 1 个从节点。因为我们有 6 个节点,所以它会自动创建 3 个主节点和 3 个从节点。

执行上述命令后,redis-cli 会计算出一个分片计划,并让你确认。

**>>> Performing hash slots allocation on 6 nodes...**
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica redis-5:6379 to redis-1:6379
Adding replica redis-6:6379 to redis-2:6379
Adding replica redis-4:6379 to redis-3:6379

# --snip--

Can I set the cluster configuration? (type 'yes' to accept):

连接集群验证
这里使用-c来连接开启集群模式(自动重定向),必须提供密码,否则重定向到其他节点时会没有权限。

docker-compose exec -it redis-1 redis-cli -c -a simaek.com

127.0.0.1:6379> CLUSTER INFO
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:6
cluster_my_epoch:1
cluster_stats_messages_ping_sent:365
cluster_stats_messages_pong_sent:363
cluster_stats_messages_sent:728
cluster_stats_messages_ping_received:358
cluster_stats_messages_pong_received:365
cluster_stats_messages_meet_received:5
cluster_stats_messages_received:728
total_cluster_links_buffer_limit_exceeded:0
cluster_slot_migration_active_tasks:0
cluster_slot_migration_active_trim_running:0
cluster_slot_migration_active_trim_current_job_keys:0
cluster_slot_migration_active_trim_current_job_trimmed:0
cluster_slot_migration_stats_active_trim_started:0
cluster_slot_migration_stats_active_trim_completed:0s
cluster_slot_migration_stats_active_trim_cancelled:0

测试数据分片和重定向:

127.0.0.1:6379> set hello "redis"
127.0.0.1:6379> set other "simaek"
-> Redirected to slot [11361] located at 192.168.176.4:6379
OK
192.168.176.4:6379>
最后修改:2025 年 12 月 25 日
如果觉得我的文章对你有用,请随意赞赏