高可用 Redis 集群部署

Mar 18, 2021 14:00 · 1399 words · 3 minute read Redis Linux

Linux 操作系统设置

  1. 修改 Linux backlog 设置

    backlog 参数描述的是服务器端 TCP ESTABELLISHED 状态对应的全连接队列长度。

    $ echo "net.core.somaxconn=65535" >> /etc/sysctl.conf
    $ sysctl -p
    
  2. 关掉透明大页(THP)

    $ echo never | sudo tee /sys/kernel/mm/transparent_hugepage/defrag
    $ echo never | sudo tee /sys/kernel/mm/transparent_hugepage/enabled
    

搭建主从 Redis 集群

Redis 原生支持主从库模式,保证数据副本的一致,主从库读写分离。

  1. 将 server1 设置为 Redis 集群主库,修改 redis 配置文件 /etc/redis.conf:

    bind 0.0.0.0
    
    appendonly yes
    
    appendfsync everysec
    
    • appendonly 打开 AOF 日志,开启持久化
    • appendfsync 设置 AOF 日志落盘时机
      • always 同步写盘,可靠性高但是对性能影响较大
      • everysec 每秒写盘,性能适中,宕机会丢失 1s 内的数据
      • no 先把日志写入缓冲区,又操作系统代理写盘,性能最好但是宕机会丢失比较多的数据
  2. 将 server2 和 server3 设置为 Redis 集群从库,修改 redis 配置文件 /etc/redis.conf:

    bind 0.0.0.0
    
    appendonly yes
    
    appendfsync everysec
    
    slaveof ${server1_ip} 6379
    
  3. 在所有节点上启动 redis 进程:

    $ systemctl start redis && systemctl enable redis
    

主从库模式虽然通过读写分离避免了数据不一致的问题,但还是面临主库故障的风险。

从库第一次与主库同步会全量复制,此时主库会生成和传输 RDB 文件,如果从库数量很多而且都和主库全量复制,会导致主库忙于 fork 子进程生成 RDB 文件,阻塞主线程处理请求,从而导致主库响应速度变慢,而且 RDB 文件传输还会占用大量网络带宽。可以选用“主-从-从”模式将主库生成和传输 RDB 文件的压力以级联的方式分散到从库上

搭建哨兵集群

哨兵是 redis 一种特殊的运行模式,负责监控、选主与通知。

  • 监控:周期性地给所有主从库发送心跳(PING 命令),如果从库未正常响应,就将其标记为“下线状态”;如果主库未正常响应,除标记下线外还会切换主库
    • “主观下线(sdown)”:当前哨兵主库/从库对心跳响应超时。
    • “客观下线(odown)”:大多数哨兵实例都判断主库已经主观下线,只有主库被标记为客观下线时才会触发主从切换
  • 选主:按一定的规选择一个从库实例作为新的主库。
  • 通知:哨兵将新主库的信息发送给其他从库,让它们与新主库建立主从关系和复制数据;将新主库的连接信息发送给客户端。

为了避免哨兵故障后无法切换主从库,以及降低主库下线的误判几率,我们也需要多个哨兵实例组成哨兵集群。

  1. 在所有节点上修改 redis 哨兵配置文件 /etc/redis-sentinel.conf:

    bind 0.0.0.0
    
    sentinel monitor mymaster ${server1_ip} 6379 2
    
    sentinel down-after-milliseconds mymaster 5000
    
    sentinel failover-timeout mymaster 10000
    
    • sentinel monitor <master-name> <ip> <redis-port> <quorum>
      • master-name 主库名称
      • <ip> <redis-port> 哨兵监控的主库 IP 与端口
      • <quorum> 赞成票数,一般为哨兵数量 / 2 + 1
    • down-after-milliseconds 主观下线判断时间
    • failover-timeout 故障切换超时时间
  2. 在所有节点上启动 redis 哨兵进程:

    $ systemctl start redis-sentinel && systemctl enable redis-sentinel
    

通过 SENTINEL get-master-addr-by-name <master-name> 来查询当前主库:

$ redis-cli -p 26379
127.0.0.1:26379> ping
PONG
SENTINEL get-master-addr-by-name mymaster

1) "10.211.55.33"
2) "6379"

手动关掉当前主库 systemctl stop redis

$ tail -f /var/log/redis/sentinel.log
1538:X 15 Mar 22:15:26.179 # +sdown master mymaster 10.211.55.33 6379
1538:X 15 Mar 22:15:26.271 # +new-epoch 48
1538:X 15 Mar 22:15:26.272 # +vote-for-leader 6e4877799e0fd14c5d2ae4b92d3d13fcd5ae47d5 48
1538:X 15 Mar 22:15:27.287 # +odown master mymaster 10.211.55.33 6379 #quorum 3/2
1538:X 15 Mar 22:15:27.287 # Next failover delay: I will not start a failover before Mon Mar 15 22:15:47 2021
1538:X 15 Mar 22:15:27.331 # +config-update-from sentinel 6e4877799e0fd14c5d2ae4b92d3d13fcd5ae47d5 10.211.55.34 26379 @ mymaster 10.211.55.33 6379
1538:X 15 Mar 22:15:27.331 # +switch-master mymaster 10.211.55.33 6379 10.211.55.32 6379
1538:X 15 Mar 22:15:27.331 * +slave slave 10.211.55.34:6379 10.211.55.34 6379 @ mymaster 10.211.55.32 6379
1538:X 15 Mar 22:15:27.331 * +slave slave 10.211.55.33:6379 10.211.55.33 6379 @ mymaster 10.211.55.32 6379
1538:X 15 Mar 22:15:32.378 # +sdown slave 10.211.55.33:6379 10.211.55.33 6379 @ mymaster 10.211.55.32 6379

从哨兵的日志中看到主库从 10.211.55.33 切换到了 10.211.55.32。

哨兵选主过程

  1. 筛选

    1. 检查从库的网络连接状况

      down-after-milliseconds 就是最大连接超时时间,当断连次数超过一定阈值,此从库会被筛掉。

  2. 打分,一共三轮,只要有一轮某个从库得分最高就会直接被选为主库,不会进入下一轮

    1. 从库优先级

      通过 slave-priority 参数给从库设置优先级,优先级最高的从库得分高

    2. 从库复制进度

      和主库同步进度最接近的从库得分高

    3. 从库 ID 号

      ID 号最小的从库得分高

  3. 通知

    哨兵将新主库的地址写入自己实例的 pub/sub (switch-master)中,客户端需要订阅这个 pub/sub,可以感知到主库发生切换并拿到最新的主库地址。所以 redis 客户端一般会连接哨兵来获取主库相关信息。