摘要
- Redis 7 + ACL 单节点、主从、哨兵、集群构建方法
- 本文基于
redis-7.4.7 - Redis官网:https://redis.io/
redis安装
sh
# 下载到指定目录
mkdir -p /usr/local/soft
wget https://download.redis.io/releases/redis-7.4.7.tar.gz -P /usr/local/soft
# 解压
cd /usr/local/soft
tar -zxvf redis-7.4.7.tar.gz
# 编译
sudo yum install gcc gcc-c++ -y
cd redis-7.4.7
# 编译,会将编译好的可执行文件放在src目录下
make
# 编译安装到指定目录,redis相关命令会被安装到 $(pwd)/build_dir/bin目录下
mkdir build_dir
make install PREFIX=$(pwd)/build_dir
# 编辑配置文件,不建议在原有配置文件中修改,可以新建一个配置文件
cp redis.conf redis-6379.conf
vim redis-6379.conf #见下面的配置信息
# 加入环境变量 /etc/bashrc,注意这里要是单引号,否则 $PATH 会被解析
echo 'export PATH=$PATH:/usr/local/soft/redis-7.4.7/build_dir/bin' >> /etc/bashrc
source /etc/bashrc
# 验证
redis-cli --version
# 输出
redis-cli 7.4.7
单节点
配置
redis-6379.conf的主要配置
sh
# 端口,默认 6379
port 6379
# 后台启动,默认 no
daemonize yes
# yes: 当没有 bind / requirepass / ACL 时,只允许本机访问
# no: 允许任意访问
# 这里会启用ACL,所以设置为 yes,默认 yes
protected-mode yes
# 注释掉bind,绑定的是自己机器网卡的ip,如果有多块网卡可以配多个ip,代表允许客户端通过机器的哪些网卡ip去访问,内网一般可以不配置
#bind 127.0.0.1
# 可以配置为,本机IP 内网IP 外网IP
# bind 127.0.0.1 10.250.0.103 18.163.188.20
# 关闭rdb快照,因为会启用混合持久化,所以这里不需要开启rdb快照
# 这里只是关闭了自动快照,如果手动执行了 bgsave 命令,还是会生成一个 dump.rdb 文件
save ""
# 设置dir路径,redis日志、aof和rdb文件都会生成在这个路径下,需要提前创建好这个目录
dir /usr/local/soft/dir-redis7/6379
# 开启aof,实际上只需要开启这个配置,以下aof相关配置默认即可,默认 no
# 开启aof,重启redis时,会主动加载 appendonlydir 下的 相关 aof文件进行数据恢复
# 生产环境必须开启
appendonly yes
# aof文件名称,默认 appendonly.aof
appendfilename "appendonly.aof"
# aof文件保存目录,基于为当前dir路径,默认值就是 appendonlydir
# redis6+会生成3个文件,每个文件都以 appendfilename 配置的文件名称开头,如下:
# appendonly.aof.N.base.rdb:每次触发aof重写时都会生成这个文件,N是当前aof文件序号,base.rdb是当前rdb文件
# appendonly.aof.N.incr.aof:记录每个写操作命令
# appendonly.aof.manifest:记录 base.rdb 和 incr.aof 文件的最新索引(N)
appenddirname "appendonlydir"
# aof将数据fsync到磁盘的策略,默认即可,表示每秒一次,故障时最多会丢失一秒的数据,默认 everysec
appendfsync everysec
# 自动触发aof重写需要满足如下条件,如果需要手动触发aof重写,可以执行 BGREWRITEAOF 命令
# 重写时会删除旧的 appendonly.aof.N.base.rdb(RDB 快照) 文件,生成新的 appendonly.aof.N+1.base.rdb
# 当 AOF 重写完成后,当前的 appendonly.aof.N.incr.aof 会封存,Redis 会新建一个 appendonly.aof.N+1.incr.aof 来继续记录写命令
# 而此时 appendonly.aof.manifest(索引文件) 也会记录最新的 appendonly.aof.N.incr.aof 和 appendonly.aof.N.base.rdb
# aof文件至少要达到64M才会自动重写,文件太小恢复速度本来就很快,重写的意义不大,默认即可,默认 64mb
auto-aof-rewrite-min-size 64mb
# aof文件自上一次重写后文件大小增长了100%则再次触发重写,默认 100
auto-aof-rewrite-percentage 100
# 开启混合持久化,4.0以后版本支持,需要先开启aof,默认 yes
# 开启混合持久化后,appendonly.aof.N.base.rdb 的文件后缀就是 rdb,否则是 aof
aof-use-rdb-preamble yes
# 防止 AOF 意外截断导致 Redis 起不来,默认 yes
aof-load-truncated yes
# pid文件路径,默认 /var/run/redis_6379.pid
pidfile "redis_6379.pid"
# 日志文件名称,默认 ""
logfile "6379.log"
# 指定最大内存,单位bytes,这里设置4G
# 如果不设置最大内存,redis会默认为物理内存,达到上限时会频繁与磁盘发生交换,使redis性能急剧下降
maxmemory 4294967296
# 达到最大内存时的清除策略,推荐 allkeys-lru,淘汰很久没被访问过的数据,基于最近一次的访问时间
# volatile-lru: 只会淘汰「设置了过期时间」的 key
# allkeys-lru: 淘汰最久没有被访问过的数据,最近最久没被访问的
# allkeys-lfu: 淘汰最不经常被访问过的数据,访问次数最少的
# noeviction: 不淘汰,默认
maxmemory-policy allkeys-lru
# 慢查询日志
# 单位微妙,这里表示10毫秒,即超过10毫秒的操作都会记录下来
slowlog-log-slower-than 10000
# 设置慢查询日志记录保存数量,如果数量已满会删除最早的记录
slowlog-max-len 1024
# 性能优化
# Redis 用多少附加线程来处理网络 I/O(不是执行命令),推荐:核心数 / 2,默认为1,超过 8 几乎就没有明显收益了
io-threads 4
# 多线程同时用于 读取 + 写回, no :多线程仅用于 写回客户端
# 推荐>=4核才开启,否则开了反而更慢
io-threads-do-reads yes
# 操作系统 TCP 层的健康检测,默认值300,这里如果 60 秒内没有数据流动,内核会发送探测包,判断连接是否活着
tcp-keepalive 60
# 客户端在 "多少秒不操作" 就强制断开,0:永不超时(推荐),默认 0
# 因为有 tcp-keepalive 60,会每 60 秒检测一下对面还在不在,如果不在,Redis 会主动断开该连接,所以不会导致连接永不释放
timeout 0
# 最大客户端连接数,默认 10000
maxclients 10000
# 禁用危险命令,根据需要自行添加,redis6+ 支持在 acl 文件中为不同的用户禁用危险命令
rename-command FLUSHALL ""
rename-command FLUSHDB ""
# rename-command SHUTDOWN ""
# rename-command CONFIG ""
# rename-command KEYS ""
# rename-command SAVE ""
# rename-command BGSAVE ""
# rename-command DEBUG ""
# rename-command EVAL ""
# rename-command SCRIPT ""
# Redis 在内部存在大量"隐式删除"场景(过期、覆盖写、eviction、rename、replication flush 等)。默认情况下,这些删除都是同步阻塞的(类似 DEL),可能导致主线程卡顿。Lazy Freeing 机制允许 Redis 在后台线程中释放大对象的内存(类似 UNLINK),从而减少阻塞。
# 当启用 maxmemory + eviction 策略(如 allkeys-lru、volatile-ttl)时,被淘汰的 key 是否采用异步释放(后台线程 UNLINK 模式)。适用场景:高吞吐、高并发、大对象(如大型 hash、set、zset、列表)场景才明显受益。
lazyfree-lazy-eviction yes
# 当 key 到期(EXPIRE 触发删除)时,是否异步释放其 Value。各种使用带 TTL 的缓存系统,尤其 value 是大型对象(JSON、大 Set、Hash 等)。过期 key 数量大、对象结构大时,推荐开启。
lazyfree-lazy-expire yes
# 针对"服务器内部因命令副作用导致的删除操作"是否异步释放,例如:
# RENAME 替换旧 key 时删除旧 value
# SET 操作覆盖旧值时删除旧 value
# SUNIONSTORE / SORT STORE 覆盖目标 key 时删除旧 value
# 重写函数、脚本时删除旧对象
# 适用场景:对象特别大,且存在覆盖写、rename 操作频繁的应用。
lazyfree-lazy-server-del yes
# 当副本(Replica/Slave)因全量同步而执行 FLUSHDB 时,是否异步释放原有数据。
# 此配置只影响副本不会影响主节点。
replica-lazy-flush yes
# 让用户执行 DEL 时也自动使用异步释放(等价于默认把 DEL 转换为 UNLINK)。
# 代码中大量使用 DEL 删除大对象又不方便统一改成 UNLINK 时。
# 一般生产环境我们倾向保持显式的语义(DEL/UNLINK),不建议轻易改写 DEL 行为。
lazyfree-lazy-user-del no
# FLUSHDB / FLUSHALL / SCRIPT FLUSH / FUNCTION FLUSH 在未显式指定 SYNC/ASYNC 时是否异步执行
# 大部分情况下建议保持 no,由应用决定是否用 FLUSHDB ASYNC。
lazyfree-lazy-user-flush no
# 开启 ACL 文件
aclfile /usr/local/soft/redis-7.4.7/users.acl
Redis 7 支持的淘汰策略
| 策略 | 是否只淘汰带 TTL 的 key | 淘汰规则 | 说明 |
|---|---|---|---|
| noeviction | ❌ 不淘汰 | 不删任何 key | 内存满了直接返回错误(默认) |
| allkeys-lru | ❌ 所有 key | 最近最久未使用 | ✅ 最常用 |
| allkeys-lfu | ❌ 所有 key | 访问频率最少 | ✅ 热点场景最好 |
| allkeys-random | ❌ 所有 key | 随机删除 | ❌ 很少用 |
| volatile-lru | ✅ 只淘汰有 TTL 的 | 最近最久未使用 | 你之前用的 |
| volatile-lfu | ✅ 只淘汰有 TTL 的 | 访问频率最少 | 较少使用 |
| volatile-random | ✅ 只淘汰有 TTL 的 | 随机删除 | 很少用 |
| volatile-ttl | ✅ 只淘汰有 TTL 的 | TTL 最小(马上过期的) | 特殊场景用 |
ACL 配置
users.acl文件:该文件不支持添加注释,所以使用时需要去掉注释行,关于ACL的详细说明,后面会有专门的文章介绍
bash
# 关闭默认用户,禁止匿名访问
user default off
# 超级管理员
user admin on ~* &* +@all >123456
redis 服务启动与关闭
bash
# 启动服务
redis-server redis-6379.conf
# 登录服务,本机访问可以省略 -h -p
redis-cli -h 127.0.0.1 -p 6379 --user admin --pass 123456
# 或者
redis-cli -u redis://admin:123456@127.0.0.1:6379
# 关闭服务
# shutdown == shutdown save
redis-cli -u redis://admin:123456@127.0.0.1:6379 shutdown
# 关闭服务,不保存数据,已经开了 AOF(尤其是 everysec) 的场景下,可以
redis-cli -u redis://admin:123456@127.0.0.1:6379 shutdown nosave
| 场景 | 推荐命令 |
|---|---|
| 正常下线(生产) | shutdown nosave |
| 已开 AOF | shutdown nosave |
| 数据很大 | shutdown nosave |
| 单机调试 | shutdown |
| 确定要生成快照 | shutdown save |
| 强制杀死(redis卡死) | kill -9(极端情况) |
单节点优点
- 单机部署简单方便
单节点缺点
- 不保证数据的可靠性,不适用于数据可靠性要求高的场景
- 单点故障导致无法提供服务,或者硬盘损坏导致数据丢失
- redis单节点最大qps为10w(取决于单核cpu的处理能力),超过这个qps就需要做前端限流
主从
规划
bash
master 10.250.0.235
slave1 10.250.0.58
slave2 10.250.0.36
配置
- 主从配置时,主节点不需要做任何修改
- 从节点配置文件增加同步主节点信息,其余配置与主节点相同
sh
# 指定主节点,从节点会从主节点同步数据,这里10.250.0.235 6379是主节点的ip和端口号
replicaof 10.250.0.235 6379
# 配置从节点只读,默认开启,避免数据写入从节点导致主从数据不一致
replica-read-only yes
# 如果主节点开启了ACL认证,则从节点需要设置主节点的认证信息,这里设置为管理员帐号
masteruser admin
masterauth 123456
启动
- 此时启动从节点
redis-server redis-6379.conf,会自动从主节点同步数据,同步前如果从节点已经有数据,则会先清除原有数据再进行同步 - 主节点接收到从节点的同步请求后,会通过bgsave将内存数据dump到rdb文件中并传递给从节点
- 主节点生成rdb文件并传递给从节点期间会继续处理客户端的请求,并将这部分数据缓存到内存中,待从节点接收到主节点发过来的rdb文件并完成内存加载后,主节点会将这部分缓存在内存中的数据发送给从节点
- 从节点相当于主节点的备份,主节点挂了,从节点不能自动切换为主节点,如果需要自动切换,可以使用哨兵或者集群部署方式
- 此时登录master的redis并执行
info replication命令
bash
# Replication
role:master # 表示当前是从节点
connected_slaves:1 # 从节点数量
slave0:ip=10.250.0.36,port=6379,state=online,offset=56,lag=1 # 从节点信息
master_failover_state:no-failover # 主节点切换状态,无
master_replid:93e564c18d27418e52fc40254c764d76c1dc3f67 # 主节点的复制ID
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:56 # 主节点复制的偏移量
second_repl_offset:-1
repl_backlog_active:1 # 是否开启复制回放
repl_backlog_size:1048576 # 复制回放缓冲区大小
repl_backlog_first_byte_offset:1 # 复制回放缓冲区第一个字节的偏移量
repl_backlog_histlen:56 # 复制回放缓冲区历史长度
- 此时登录从节点的redis并执行
info replication命令
bash
# Replication
role:slave # 表示当前是从节点
master_host:10.250.0.235 # 主节点的ip
master_port:6379 # 主节点的端口
master_link_status:up # 主从节点连接状态,up 表示已经连接上主节点
master_last_io_seconds_ago:6 # 主从节点最后一次io操作时间
master_sync_in_progress:0 # 主从节点是否正在同步数据,0表示已完成
slave_read_repl_offset:112 # 从节点已经读取的复制偏移量
slave_repl_offset:112 # 从节点已经写入的复制偏移量
slave_priority:100 # 从节点的优先级
slave_read_only:1 # 从节点是否只读
replica_announced:1 # 从节点是否被其他节点所代理
connected_slaves:0 # 从节点所代理的从节点数量
master_failover_state:no-failover # 主节点的故障转移状态,no-failover表示没有进行故障转移
master_replid:93e564c18d27418e52fc40254c764d76c1dc3f67 # 主节点的复制ID
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:112 # 主节点已经写入的复制偏移量 slave_read_repl_offset ≈ master_repl_offset,说明数据已经同步
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:15
repl_backlog_histlen:98
主从数据同步是否完成判断规则
- 在从节点上执行命令
info replication
| 字段 | 正常值 | 说明 |
|---|---|---|
role |
slave | 表示当前是从节点 |
master_link_status |
up | 表示已经连上主库 |
master_sync_in_progress |
0 | 同步不在进行中 = 已完成 |
slave_read_repl_offset ≈ master_repl_offset |
接近 | 说明数据已追上 |
添加新的从节点
- 参照上面的配置,再添加一个从节点后,在主节点执行命令
info replication
bash
# Replication
role:master
connected_slaves:2 # 从节点数量
slave0:ip=10.250.0.36,port=6379,state=online,offset=4313,lag=0
slave1:ip=10.250.0.58,port=6379,state=online,offset=4313,lag=0 # 第二个从节点信息
master_failover_state:no-failover
master_replid:93e564c18d27418e52fc40254c764d76c1dc3f67
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:4313
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:4313
主从优点
- 对请求进行读写分离,提高处理效率
- 可以提供多个副本,提高数据安全性
主从缺点
- 不具备自动容错和恢复功能,主节点故障,集群则无法进行工作,可用性比较低,从节点升主节点需要人工手动干预
哨兵
规划
- 创建三个哨兵,为了方便就在上面主从配置的3台服务器上启动哨兵
bash
master 10.250.0.235
slave1 10.250.0.58
slave2 10.250.0.36
sentinel1 10.250.0.71
sentinel2 10.250.0.131
sentinel3 10.250.0.63
配置
- 分别编辑各自的
sentinel.conf
sh
# 端口号
port 26379
# 后台启动
daemonize yes
# dir,需要提前创建好
dir /usr/local/soft/dir-redis7/sentinel
# pid文件路径
pidfile redis-sentinel.pid
# 日志文件名称
logfile "sentinel.log"
# 配置监听的主节点地址和端口,mymaster为自定义的名称,最后的2表示,只要有2个哨兵节点认为主节点挂了就会进行重新选主,一般设置为sentinel总数/2+1
sentinel monitor mymaster 10.250.0.235 6379 2
# 主观下线时间(5秒),默认30秒
sentinel down-after-milliseconds mymaster 5000
# 故障转移超时时间 60秒,默认180秒
sentinel failover-timeout mymaster 60000
# 每次允许多少个slave重新同步,默认就是 1
sentinel parallel-syncs mymaster 1
# acl用户名和密码,这里为了方便也是用的管理员帐号
sentinel auth-user mymaster admin
sentinel auth-pass mymaster 123456
启动
- 分别启动三个哨兵节点
redis-sentinel sentinel.conf,此时登录哨兵节点redis-cli -p 26379,并执行info Sentinel命令,查看其是否正确识别了主从
sh
# Sentinel
sentinel_masters:1 # 哨兵集群中主从节点的数量
sentinel_tilt:0 # 是否处于 tilt 状态
sentinel_tilt_since_seconds:-1 # tilt 状态的开始时间
sentinel_running_scripts:0 # 正在运行的脚本数量
sentinel_scripts_queue_length:0 # 脚本队列长度
sentinel_simulate_failure_flags:0 # 模拟故障的标志
master0:name=mymaster,status=ok,address=10.250.0.235:6379,slaves=2,sentinels=3 # 主节点的地址,从节点数量和哨兵数量
- 此时查看
sentinel.conf可以在文件最后看到从节点信息和其它的哨兵节点信息(但实测无法感知其它哨兵节点),类似于
sh
# Generated by CONFIG REWRITE
latency-tracking-info-percentiles 50 99 99.9 # 延迟追踪信息百分比
user default on nopass sanitize-payload ~* &* +@all # 用户信息,当前的哨兵没有开启权限认证,所以缺省为default用户,无密码
sentinel myid 8b9d55a581f2e41b4f8d92f4a9434d9b8a78b3e6 # 本节点的id
sentinel config-epoch mymaster 0
sentinel leader-epoch mymaster 0
sentinel current-epoch 0
# 一个从节点信息
sentinel known-replica mymaster 10.250.0.58 6379
# 另一个从节点信息
sentinel known-replica mymaster 10.250.0.36 6379
# 一个哨兵节点信息
sentinel known-sentinel mymaster 10.250.0.71 26379 b230f6a6076c23eed1923b29027d9ba7b24bee5a
# 另一个哨兵节点信息
sentinel known-sentinel mymaster 10.250.0.63 26379 74ef58ef3616a27cc63d83bcfe422f15e11731b8
主从切换
- 此时关闭master节点(10.250.0.235:6379),然后登录哨兵节点查看
info Sentinel,就会发现master节点变成了从节点其中的一个了 - 此时再次开启原master节点,会发现其变成了从节点,相应的配置文件(redis-6379.conf)也发生了变更
bash
# Generated by CONFIG REWRITE
latency-tracking-info-percentiles 50 99 99.9
replicaof 10.250.0.36 6379
save 3600 1
save 300 100
save 60 10000
- 这里有一点需要注意,就是master节点重启前也需要配置如下认证信息,因为master在哨兵模式下发生故障后重新启动会变成slave
bash
# 如果主节点开启了ACL认证,则从节点需要设置主节点的认证信息,这里设置为管理员帐号
masteruser admin
masterauth 123456
- 顺便说一下,关闭哨兵服务的命令如下:
bash
redis-cli -p 26379 shutdown
哨兵优点
- 主节点故障,可以自动在从节点中重新选主
哨兵缺点
- 哨兵单点故障,则集群无法完整自主选举主节点,所以需要对哨兵集群部署,增加服务器成本,但是并没有提升负载
- 另外,主节点故障时,哨兵介入有时间差,会导致响应延迟
- 从节点仅作为备份不提供对外服务,只有当master出现故障时其晋升为master后才能提供服务,所以不支持读写分离
集群
规划
- 搭建6个redis的集群,3主3从
bash
redis1 10.250.0.235
redis2 10.250.0.58
redis3 10.250.0.36
redis4 10.250.0.71
redis5 10.250.0.131
redis6 10.250.0.63
配置
- 还是基于单节点配置文件,只是将节点配置成集群模式,redis-6379.conf文件增加如下信息
sh
# ACL认证,所有节点都要配置
masteruser "admin"
masterauth "123456"
# 启动集群模式
cluster-enabled yes
# 集群节点信息文件,这里最好和port对应上
cluster-config-file nodes-6379.conf
# 集群节点间通信的超时时间,单位毫秒,默认15000,这个时间别设置太短,避免网络抖动等原因干扰
cluster-node-timeout 15000
# 写数据成功最少同步的slave数量,默认数据写入master就返回给client了,加上这个配置,就需要数据同步到指定数量的slave后才能返回,
# 注意这个配置不仅会延长client的等待时间,而且可能会影响集群的可用性,比如这里配置至少同步1个slave,但是如果此时master对应可用的slave不足1个,集群就不能提供服务了,所以建议每个master至少配置了2个以上的slave时才开启这个配置
# 开启这个配置可以预防集群脑裂问题,默认为3
min-replicas-to-write 1
# 如果集群中某个master/slave全部挂掉,整个集群的其它master/slave是否还可以对外提供服务,默认yes,不能
# 如果设置为no,则表示依旧可以提供服务,不过如果有key落在了挂掉的主从上就会失败
cluster-require-full-coverage yes
创建集群
- 分别启动6个redis服务
sh
redis-server redis-6379.conf
- 创建集群,3主3从,注意创建集群前所有redis不能有数据,如果有需要先清空(删除dir配置的目录中的所有文件即可),然后在任意一个redis执行
sh
redis-cli --user admin --pass 123456 --cluster create --cluster-replicas 1 10.250.0.235:6379 10.250.0.58:6379 10.250.0.36:6379 10.250.0.71:6379 10.250.0.131:6379 10.250.0.63:6379
# 参数说明:
# --cluster create:创建集群
# --cluster-replicas 1:设置从节点数量,这里设置为1,表示每个主节点都对应一个从节点
# 这里配置了6个节点组建集群,要保证每个master节点都有一个从节点,所以刚好是3主3从
- 此时会列出集群内主从和槽位的分配方案,输入
yes即可
sh
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 10.250.0.131:6379 to 10.250.0.235:6379
Adding replica 10.250.0.63:6379 to 10.250.0.58:6379
Adding replica 10.250.0.71:6379 to 10.250.0.36:6379
M: afcb754ce8cc79122fe9bcd8c1567bbf13258fce 10.250.0.235:6379
slots:[0-5460] (5461 slots) master
M: 2527142b7cffaf6bb92608550c919a0db0607c39 10.250.0.58:6379
slots:[5461-10922] (5462 slots) master
M: 88762f7d0bbb99bbaf68556ef5fd113cba46d295 10.250.0.36:6379
slots:[10923-16383] (5461 slots) master
S: 547fbd934293ddb5eb770e111b65910f4e604023 10.250.0.71:6379
replicates 88762f7d0bbb99bbaf68556ef5fd113cba46d295
S: 17b33861e6b086b86902fe75f79e5b878bd26f94 10.250.0.131:6379
replicates afcb754ce8cc79122fe9bcd8c1567bbf13258fce
S: 119e5823cccafef506b9b256aad206678eab7d41 10.250.0.63:6379
replicates 2527142b7cffaf6bb92608550c919a0db0607c39
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join
.
>>> Performing Cluster Check (using node 10.250.0.235:6379)
M: afcb754ce8cc79122fe9bcd8c1567bbf13258fce 10.250.0.235:6379
slots:[0-5460] (5461 slots) master
1 additional replica(s)
S: 547fbd934293ddb5eb770e111b65910f4e604023 10.250.0.71:6379
slots: (0 slots) slave
replicates 88762f7d0bbb99bbaf68556ef5fd113cba46d295
S: 119e5823cccafef506b9b256aad206678eab7d41 10.250.0.63:6379
slots: (0 slots) slave
replicates 2527142b7cffaf6bb92608550c919a0db0607c39
S: 17b33861e6b086b86902fe75f79e5b878bd26f94 10.250.0.131:6379
slots: (0 slots) slave
replicates afcb754ce8cc79122fe9bcd8c1567bbf13258fce
M: 88762f7d0bbb99bbaf68556ef5fd113cba46d295 10.250.0.36:6379
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
M: 2527142b7cffaf6bb92608550c919a0db0607c39 10.250.0.58:6379
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
查看集群状态
- 登录集群并查询集群配置信息
sh
# -c 表示以集群模式登录,-h 集群内任意ip
redis-cli --user admin --pass 123456 -c -h 10.250.0.235
- 查看集群信息
sh
10.250.0.235:6379> cluster info
# 输出集群状态
cluster_state:ok # 集群整体状态:ok 表示集群当前可用、健康
cluster_slots_assigned:16384 # 已分配的哈希槽数量(总槽数固定为 16384)
cluster_slots_ok:16384 # 正常工作的槽数量(等于已分配槽,说明全部正常)
cluster_slots_pfail:0 # 处于"疑似失败(pfail)"状态的槽数量
cluster_slots_fail:0 # 被判定为失败(fail)的槽数量
cluster_known_nodes:6 # 当前节点已知的集群节点总数(主节点 + 从节点)
cluster_size:3 # 主节点数量(3 主节点,典型的 3 主 3 从结构)
cluster_current_epoch:6 # 集群当前的全局纪元(用于选举和配置同步)
cluster_my_epoch:1 # 当前这个节点所在的纪元
cluster_stats_messages_ping_sent:159 # 当前节点已发送的 PING 消息数量(心跳)
cluster_stats_messages_pong_sent:156 # 当前节点已发送的 PONG 消息数量(心跳响应)
cluster_stats_messages_sent:315 # 当前节点发送的所有集群消息总数
cluster_stats_messages_ping_received:151 # 当前节点收到的 PING 消息数量
cluster_stats_messages_pong_received:159 # 当前节点收到的 PONG 消息数量
cluster_stats_messages_meet_received:5 # 当前节点收到的 MEET 消息数量(新节点加入发现)
cluster_stats_messages_received:315 # 当前节点收到的所有集群消息总数
total_cluster_links_buffer_limit_exceeded:0 # 因输出缓冲区超限被强制断开的集群连接数(0 = 正常)
- 查看节点列表
sh
10.250.0.235:6379> cluster nodes
# 当前节点(myself),角色为 master,负责槽位 0-5460,是本机所在的主节点
afcb754ce8cc79122fe9bcd8c1567bbf13258fce 10.250.0.235:6379@16379 myself,master - 0 0 1 connected 0-5460
# 从节点(slave),对应的主节点是 10.250.0.36 这个 master(负责槽位 10923-16383)
547fbd934293ddb5eb770e111b65910f4e604023 10.250.0.71:6379@16379 slave 88762f7d0bbb99bbaf68556ef5fd113cba46d295 0 1765033120132 3 connected
# 从节点(slave),对应的主节点是 10.250.0.58 这个 master(负责槽位 5461-10922)
119e5823cccafef506b9b256aad206678eab7d41 10.250.0.63:6379@16379 slave 2527142b7cffaf6bb92608550c919a0db0607c39 0 1765033118000 2 connected
# 从节点(slave),对应的主节点是当前节点 10.250.0.235(负责槽位 0-5460)
17b33861e6b086b86902fe75f79e5b878bd26f94 10.250.0.131:6379@16379 slave afcb754ce8cc79122fe9bcd8c1567bbf13258fce 0 1765033119000 1 connected
# 主节点(master),负责槽位 10923-16383(集群中第三段槽位)
88762f7d0bbb99bbaf68556ef5fd113cba46d295 10.250.0.36:6379@16379 master - 0 1765033117000 3 connected 10923-16383
# 主节点(master),负责槽位 5461-10922(集群中第二段槽位)
2527142b7cffaf6bb92608550c919a0db0607c39 10.250.0.58:6379@16379 master - 0 1765033119126 2 connected 5461-10922
- 此时查看
nodes-6379.conf也会看到和上面一样的节点信息
主从切换
- 此时关闭其中一个master节点,比如
10.250.0.36,则其对应的slave节点10.250.0.71会切换为新的master节点,此时10.250.0.36的状态最终变为fail
sh
10.250.0.235:6379> cluster nodes
## 输出
afcb754ce8cc79122fe9bcd8c1567bbf13258fce 10.250.0.235:6379@16379 myself,master - 0 0 1 connected 0-5460
547fbd934293ddb5eb770e111b65910f4e604023 10.250.0.71:6379@16379 master - 0 1765033733534 7 connected 10923-16383
119e5823cccafef506b9b256aad206678eab7d41 10.250.0.63:6379@16379 slave 2527142b7cffaf6bb92608550c919a0db0607c39 0 1765033734542 2 connected
17b33861e6b086b86902fe75f79e5b878bd26f94 10.250.0.131:6379@16379 slave afcb754ce8cc79122fe9bcd8c1567bbf13258fce 0 1765033732526 1 connected
88762f7d0bbb99bbaf68556ef5fd113cba46d295 10.250.0.36:6379@16379 master,fail - 1765033690200 1765033686170 3 connected
2527142b7cffaf6bb92608550c919a0db0607c39 10.250.0.58:6379@16379 master - 0 1765033735550 2 connected 5461-10922
- 再次启动
10.250.0.36,其会变成10.250.0.71的slave节点
sh
10.250.0.235:6379> cluster nodes
## 输出
afcb754ce8cc79122fe9bcd8c1567bbf13258fce 10.250.0.235:6379@16379 myself,master - 0 0 1 connected 0-5460
547fbd934293ddb5eb770e111b65910f4e604023 10.250.0.71:6379@16379 master - 0 1765033859000 7 connected 10923-16383
119e5823cccafef506b9b256aad206678eab7d41 10.250.0.63:6379@16379 slave 2527142b7cffaf6bb92608550c919a0db0607c39 0 1765033860493 2 connected
17b33861e6b086b86902fe75f79e5b878bd26f94 10.250.0.131:6379@16379 slave afcb754ce8cc79122fe9bcd8c1567bbf13258fce 0 1765033858000 1 connected
88762f7d0bbb99bbaf68556ef5fd113cba46d295 10.250.0.36:6379@16379 slave 547fbd934293ddb5eb770e111b65910f4e604023 0 1765033859488 7 connected
2527142b7cffaf6bb92608550c919a0db0607c39 10.250.0.58:6379@16379 master - 0 1765033859000 2 connected 5461-10922
- 若此时还是希望
10.250.0.36做为主节点,则需要先登录10.250.0.36,再执行如下命令
sh
# 注意要加 -c
redis-cli -c --user admin --pass 123456 -h 10.250.0.36
10.250.0.36:6379> cluster failover # 安全版(等待复制同步)
# 或者
10.250.0.36:6379> CLUSTER FAILOVER TAKEOVER # TAKEOVER 会立即切主,不会等待复制数据,非常强制(生产环境不推荐)
集群的启动与关闭
- 关闭集群,6个redis分别关闭
sh
redis-cli -c -h 10.250.0.235 -p 6379 --user admin --pass 123456 shutdown
- 重启集群,6个redis分别启动即可
sh
redis-server redis-6379.conf
如何计算槽位
- redis集群会将2的14次幂(16384)的slot平均分配到所有master上,然后对key进行hash后计算应该存储到那个slot
sh
HASH_SLOT=CRC16(key) mod 16384
# 跳转重定位
10.250.0.235:6379> set name zhangsan
# 输出
-> Redirected to slot [5798] located at 10.250.0.58:6379
OK
- mset/mget要求key都落在同一个slot上,每个key都加上哈希标签(Hash Tag)--
{xxx},其必须用{}括起来,xxx可以是任意字符串,计算slot时,只会根据xxx来计算hash值,这样就保证了所有key中包含{xxx}的key都会落到同一个slot,{xxx}可以放在key的任意位置
sh
10.250.0.235:6379> mset name1 lisi name2 wangwu
(error) CROSSSLOT Keys in request don't hash to the same slot
10.250.0.235:6379> mset {user}:name1 lisi {user}:name2 wangwu
OK
10.250.0.235:6379> mget {user}:name1 {user}:name2
-> Redirected to slot [5474] located at 10.250.0.58:6379
1) "lisi"
2) "wangwu"
注意:
{...} 让多个 key 落在同一个 slot,支持跨 key 原子操作
只有 第一个匹配的 {...} 会被当做 tag
{} 不能嵌套
空标签 {} 是非法的
{abc} 和 {def} 就落不同 slot
- 如何判断 key 落在哪个节点?
sh
10.250.0.235:6379> cluster keyslot name1
# 输出的数值就表示 slot
(integer) 5461 # slot值
- 如何查看指定的slot中有多少个key?
sh
10.250.0.235:6379> cluster countkeysinslot 5461
# 输出
(integer) 1 # 表示slot中key的个数,这里显示只有1个key
注意事项
-
集群推举新的master时要求至少一半的master同意,所以一个集群至少需要3个master,官方推荐master节点数为奇数,比如3个和4个master节点,都至多允许一个master节点挂掉时进行选主,但是3个master可以节省资源
-
集群通过
10000+port这个端口号进行集群间通信,所以除了要开放prot这个端口,还要开放10000+port这个端口 -
有关redis集群及其水平扩展的进一步说明,后面会有专门的文章介绍。
集群优点
- 无中心架构,集群内部自行维护数据的分片和主从的切换
- 数据分片存储,提供很高的访问效率
- 高可用性,可实现部分节点不可用时,集群仍可用
- 高扩展性,可以横向扩展1000个节点后依旧保证访问效率,扩容缩容都支持
集群缺点
- 数据通过异步复制,不保证数据的强一致性
- 不支持多数据库空间,单机下的redis可以支持到16个数据库,集群模式下只能使用1个数据库空间,即db 0
- 不支持跨slot操作,如使用mset、mget目前只支持具有相同slot值的Key执行批量操作
- Key作为数据分区的最小粒度,不能将一个很大的键值对象如hash、list等映射到不同的节点
- Key事务操作支持有限,只支持多key在同一节点上的事务操作,当多个Key分布于不同的节点上时无法使用事务功能
- 不建议使用pipeline和multi-keys操作
- 另外,如果集群内节点时间不同步,可能存在脏写
redis-cli 的 Cluster 管理命令(用于管理/创建/调整集群)
| 命令 | 作用 | 示例 |
|---|---|---|
redis-cli --cluster create host1:port host2:port ... |
创建 Redis Cluster 集群(自动分配 slots) | redis-cli --cluster create 10.0.0.1:7000 10.0.0.2:7000 --cluster-replicas 1 |
redis-cli --cluster check host:port |
检查集群状态、健壮性 | redis-cli --cluster check 10.0.0.1:7000 |
redis-cli --cluster info host:port |
显示集群结构、节点、slots 分布 | redis-cli --cluster info 10.0.0.1:7000 |
redis-cli --cluster fix host:port |
修复 slots 移动、节点中断等异常状态 | redis-cli --cluster fix 10.0.0.1:7000 |
redis-cli --cluster rebalance host:port |
重新分布 slots(自动均衡负载) | redis-cli --cluster rebalance 10.0.0.1:7000 |
redis-cli --cluster rebalance --cluster-threshold <percent> host:port |
手动设置 rebalance 阈值;低于此阈值才会调整(百分比) | redis-cli --cluster rebalance --cluster-threshold 1 10.0.0.1:7000 |
redis-cli --cluster add-node newHost:newPort existingHost:existingPort |
将新节点加入集群 | redis-cli --cluster add-node 10.0.0.3:7000 10.0.0.1:7000 |
redis-cli --cluster add-node --cluster-slave newHost:newPort existingMaster:port |
将新节点作为某主节点的 slave 加入 | redis-cli --cluster add-node --cluster-slave 10.0.0.4:7000 10.0.0.1:7000 |
redis-cli --cluster del-node host:port node_id |
从集群删除某节点(必须无 slots) | redis-cli --cluster del-node 10.0.0.1:7000 <node-id> |
redis-cli --cluster call host:port command ... |
在所有节点上批量执行命令 | redis-cli --cluster call 10.0.0.1:7000 PING |
redis-cli --cluster help |
显示 cluster 子命令帮助 | redis-cli --cluster help |
Redis 内置的 Cluster 操作命令
| 命令 | 作用 | 示例 |
|---|---|---|
CLUSTER INFO |
查看集群状态、failover 状态、slot 分布统计 | CLUSTER INFO |
CLUSTER NODES |
显示所有节点和角色(master/slave) | CLUSTER NODES |
CLUSTER MYID |
输出本节点 ID | CLUSTER MYID |
CLUSTER REPLICATE <node-id> |
将当前节点设置为某个 master 的 slave | CLUSTER REPLICATE <node-id> |
CLUSTER FAILOVER |
主动 failover(slave 升级为 master) | 从节点执行:CLUSTER FAILOVER |
CLUSTER FAILOVER FORCE |
不等待复制同步,立刻切主 | CLUSTER FAILOVER FORCE |
CLUSTER FAILOVER TAKEOVER |
强制接管 slot(完全覆盖 master)(危险) | CLUSTER FAILOVER TAKEOVER |
CLUSTER ADDSLOTS <slot...> |
将 slots 分配给当前 master | CLUSTER ADDSLOTS 0 1 2 |
CLUSTER DELSLOTS <slot...> |
从当前节点移除 slots | CLUSTER DELSLOTS 0 1 2 |
CLUSTER SETSLOT <slot> NODE <node-id> |
直接指定某个 slot 属于某个节点 | CLUSTER SETSLOT 0 NODE <id> |
CLUSTER SETSLOT <slot> IMPORTING <node-id> |
设定迁移 key 的源节点(用于 slot 迁移) | - |
CLUSTER SETSLOT <slot> MIGRATING <node-id> |
设定迁移 key 的目标节点(用于 slot 迁移) | - |
CLUSTER KEYSLOT key |
返回 key 所属 slot | CLUSTER KEYSLOT mykey |
CLUSTER COUNT-FAILURE-REPORTS <node-id> |
查看某节点的 fail 票数 | - |
CLUSTER RESET |
删除节点全部集群信息(用来重置为 standalone) | CLUSTER RESET HARD |