# 部署

Redis 常见的部署架构:

  • 单实例
  • 主从集群
  • 主从+哨兵集群
  • Codis 集群
    • 中国豌豆荚开源的一个集群框架,比较重,更新较慢。
    • 分成多个独立工作的小组,每组是一个小的主从集群。用一个 proxy 统一代理所有组。
  • Cluster 集群
    • 由 Redis 官方发布。

# 单实例

# 部署

  • 用 yum 安装:

    yum install -y http://rpms.famillecollet.com/enterprise/remi-release-7.rpm
    yum --enablerepo=remi install redis
    

    然后启动:

    redis-server                      # 启动 Redis 服务器
                /opt/redis/redis.conf # 使用指定的配置文件
    

    或者用 systemctl 启动:

    systemctl start redis             # 默认的启动命令是 /usr/bin/redis-server /etc/redis/redis.conf
    systemctl enable redis
    
  • 或者用 docker-compose 部署:

    version: "3"
    
    services:
      mongo:
        container_name: redis
        image: redis:5.0.14
        command: redis-server redis.conf
        restart: unless-stopped
        ports:
          - 6379:6379
        volumes:
          - .:/data     # 挂载工作目录,其中包含配置文件 redis.conf
    

# 客户端

命令:

redis-cli               # 启动客户端(默认连接到本地 6379 端口的服务器,使用 0 号数据库)
          -h 127.0.0.1  # 指定服务器的 IP 地址
          -p 6379       # 指定服务器的端口
          -a ******     # 指定密码
          -n 0          # 使用 0 号数据库
          [command]     # 不进入客户端的终端,只是执行一条命令
            -r 10       # 重复执行该命令 3 次
            -i 1.2      # 每次重复执行的间隔时长为 1.2s(可以使用小数)
  • 执行 redis-cli 命令时,即使不能连接到 Redis 服务器,也会进入客户端的终端。
  • 启动客户端时,默认不会进行密码认证。如果服务器设置了密码,此时就无权进行任何操作(连 ping 都不行),必须先完成密码认证。
  • 登录服务器之后,可以执行一些命令,比如:
    auth ******                   # 填入密码,进行认证
    ping                          # 测试能否连接到 Redis 服务器
    
    client list                   # 显示与服务器连接的所有客户端
    client kill 127.0.0.1:59372   # 关闭一个客户端的连接
    quit                          # 退出当前客户端,关闭其连接
    
    info                          # 显示服务器的详细信息
    monitor                       # 实时显示服务器执行的所有命令,以及对应的时间戳、数据库、客户端,不包括 auth、quit 命令。这会大幅降低服务器执行速度
    shutdown                      # 正常终止服务器(相当于发送 SIGTERM 信号)
    
    • 命令名不区分大小写。
    • 单个命令的执行都具有原子性。
    • Redis 客户端可以一次发送多条命令(基于 pipeline),减少服务器的响应次数,提高效率。
    • info 的参数含义 (opens new window)
  • 例:
    redis-cli info                                       # 查询 redis 的信息
    redis-cli -r 100 -i 1 info | grep used_memory_human  # 轮询 Redis 占用的内存
    
  • Redis 支持通过 Shell 管道符一次传入多条命令来执行。如下:
    echo -e "dbsize\ndbsize" | redis-cli
    

# 备份数据

Redis 服务器将数据保存在内存中,当 Redis 终止时,内存中的数据都会丢失。可采用以下方法持久化保存数据:

# RDB 模式

  • :如果在 n 秒内有至少 m 个 key 被改动,则将此时 Redis 的所有数据保存到备份文件中。
  • RDB 模式的备份开销小、精度低,但是恢复速度快。
  • Redis 默认启用 RDB 模式,相关配置如下:
    dbfilename dump.rdb   # 采用的备份文件的文件名
    
    # 默认采用以下备份策略
    save 3600 1           # 如果在 3600 秒内有 1 个 key 被改动,则备份一次
    save 300 100          # 在 300 秒内有 100 个 key 被改动
    save 60 10000         # 在 60 秒内有 10000 个 key 被改动
    
    # stop-writes-on-bgsave-error yes   # 当执行 bgsave 时,禁止写入数据
    # rdbcompression yes                # 是否压缩 RDB 备份文件
    
  • 可以执行以下命令,立即将数据保存到 dump.rdb 文件中:
    save      # 将所有数据保存到备份文件中(在前台执行)
    bgsave    # 在后台执行 save 命令
    
  • redis-port

# AOF 模式

  • :将 Redis 服务器执行的所有命令保存到备份文件中。
  • AOF 模式的备份精度高,但是备份开销大、恢复速度慢,因为要逐条执行命令。
  • 相关配置:
    appendonly yes                  # 是否启用 AOF 模式,默认禁用
    appendfilename appendonly.aof   # 采用的备份文件的文件名
    
    # 备份的策略
    # appendfsync always            # 每执行一条命令就保存一次(最慢、最安全)
    appendfsync everysec            # 每隔一秒保存一次,这是默认策略
    # appendfsync no                # 由 Redis 自己决定什么时候保存(间隔时间可能达几十秒)
    
    # 默认配置了以下参数,自动重写 aof 文件
    # auto-aof-rewrite-min-size 67108864    # aof 文件考虑自动重写的最小体积
    # auto-aof-rewrite-percentage 100       # aof 文件超过最小体积时,每增大 100% 就重写一次
    
  • 执行 bgrewriteaof ,会在后台 fork 一个 Redis 实例去简化 aof 文件,去掉重复、冗余的命令。
  • 如果要恢复数据,将 dump.rdb 或 appendonly.aof 拷贝到 Redis 工作目录下,然后重启 Redis 服务器即可。
    • Redis 启动时会读取工作目录下的 dump.rdb ,将其中的数据载入内存。
    • 如果 appendonly 为 yes ,则 Redis 只会载入 appendonly.aof 。

# 混合模式

  • :每隔一段时间就用 RDB 模式做一次全量备份,期间用 AOF 模式做增量备份。
  • 启用方法:开启 AOF 模式,并加入配置 aof-use-rdb-preamble yes
  • 生成的备份文件按 dump.aof 的格式命名,文件中先存储 rdb 的内容(以 REDIS 开头),然后存储 aof 的内容。
    • Redis 会先将数据保存到一个临时文件中,再用它替换 dump.aof 文件,因此保存失败也不会影响原来的备份。
    • 在混合模式下,每次重写 aof 文件,都会将 aof 文件的内容全部重写为 rdb 形式。

# 主从集群

  • 与 MySQL 的主从集群类似,能实现数据备份、读写分离。
  • 当 slave 从 master 拉取数据进行同步时,slave 会被阻塞、不能操作,但 master 不会被阻塞。
  • 为了政治正确,Redis 逐渐将 slave 改名为 replica 。

# 部署

  1. 部署 master 时,像普通实例一样配置即可。
  2. 部署 slave 时,先像普通实例一样配置,再在配置文件中添加以下信息:
    replicaof 10.0.0.1 6379    # 声明该节点为 slave 身份,及其 master 地址
    replica-read-only yes      # 当该节点为 slave 时,只允许读操作
    masterauth ******          # master 的密码
    
  3. 进入 master 的终端,查看主从信息:
    127.0.0.1:6379> info Replication
    # Replication
    role:master                                                        # 该节点的角色是 master
    connected_slaves:2                                                 # 该节点有两个已连接的 slave
    slave0:ip=10.244.3.216,port=6379,state=online,offset=19856,lag=0   # 第一个 slave ,状态为在线,数据偏移量为 19856 ,对于 master 没有延迟
    slave1:ip=10.244.57.151,port=6379,state=online,offset=19561,lag=1
    master_replid:be1cd14e4facf4720f25d5baace8df62cabd8ee7
    master_replid2:0000000000000000000000000000000000000000
    master_repl_offset:19856                                           # master 目前的数据偏移量为 19856
    second_repl_offset:-1
    
  4. 进入 slave 的终端,查看主从信息:
    127.0.0.1:6379> info Replication
    # Replication
    role:slave              # 该节点的角色是 slave
    master_host:10.0.0.1    # master 的地址
    master_port:6379        # master 的端口号
    master_link_status:up   # 与 master 的连接状态为正常
    
    • 一个 slave 也可以被其它 slave 连接,此时会显示 connected_slaves 。
    • master_link_status: down 的可能原因:
      • master 的 host、port 无效。
      • master 监听的是 127.0.0.1 ,不允许 slave 连接。
      • slave 没有使用正确的密码连接到 master 。
      • slave 被某事阻塞了、不能行动,比如正在从备份文件恢复数据,或正在从 master 拉取要同步的数据。
    • 如果发现 slave 不能连接到 master ,可以在 slave 主机上手动执行 redis-cli 命令试试登录 master 。

# 主从+哨兵集群

# 哨兵

:Sentinel ,一种以特殊模式运行的 Redis 服务器,能监控主从集群的 Redis 服务器,自动进行主从切换,实现 Redis 的高可用。

  • 官方文档 (opens new window)
  • 用户的 Redis 客户端需要先连接到任一哨兵,查询到当前 Redis 集群中的 master 地址,再连接到 master 。
  • 哨兵启动时首先要连接到 master ,以 master 为中介发现其它 slave、哨兵。
    • 哨兵每隔 10 秒会向 master 发送 info 命令,发现连接到该 master 的所有 slave 。
    • 哨兵每隔 2 秒会在 master 的消息队列中发布消息,被其它哨兵订阅。这样哨兵之间就可以相互发现、通信。(哨兵之间不会直接通信)
    • 哨兵每隔 1 秒会向所有 Redis 服务器、其它哨兵发送 ping 请求,如果在一定时间内没收到响应就认为对方下线了。
      • 如果一个哨兵认为 master 下线了(称为主观下线),就会向其它哨兵广播这一消息。
      • 如果超过 quorum 数量的哨兵都认为 master 下线了(称为客观下线),就会开始救援。
  • 哨兵在运行时会自动重写自己的配置文件,在主从切换时还会通过 config rewrite 命令重写 master 和 slave 的配置文件。
    • 哨兵会在自己的配置文件中记录已发现的 known-replica、known-sentinel 。即使它们下线,依然不会从配置文件中删掉。
  • 哨兵的救援过程称为 failover ,主要流程如下:
    1. 哨兵们投票选出一个 leader 哨兵。一个哨兵需要获得超过半数的投票才能获选。
    2. leader 哨兵选出一个合格的 slave 担任新的 master.
    3. leader 哨兵通知其它哨兵、修改所有 slave 的配置,让它们连接到新 master 。救援完成。
    4. 如果救援过程失败,则重新开始救援,又要重新投票。
    5. 如果救援完成之后,旧 master 重新上线,哨兵会修改它的配置,将它的角色改为 slave 。

注意:不要让 master 执行耗时过久的操作,否则容易阻塞 master 过久,触发主从切换。从 master 变成 slave 之后,即使该操作执行成功,也不会影响其它节点。

# 部署

  • 可以用 Redis 安装自带的命令直接启动哨兵:

    redis-sentinel /opt/redis/sentinel.conf
    

    也可以将 redis-server 以哨兵模式启动:

    redis-server /opt/redis/sentinel.conf --sentinel
    
  • 可以用 docker-compose 部署 Redis + Sentinel :

    version: "3"
    
    services:
      redis:
        image: redis:6.0.8
        working_dir: /opt/redis
        command: redis-server redis.conf
        restart: unless-stopped
        ports:
          - 6379:6379
        volumes:
          - .:/opt/redis
    
      sentinel:
        image: redis:6.0.8
        working_dir: /opt/redis
        command: redis-server sentinel.conf --sentinel
        restart: unless-stopped
        ports:
          - 6379:6379
        volumes:
          - .:/opt/redis
    
    • 哨兵只用到了一个配置文件 sentinel.conf ,因此可以与 Redis 共用一个挂载目录。
  • 通常在多个主机上部署多个哨兵,构成分布式集群,实现哨兵本身的高可用。

    • 哨兵不必与 Redis 服务器运行在同一主机上,只需与各个 Redis 服务器的物理网络连通。
    • 哨兵集群需要部署至少三个、奇数个哨兵服务器。如果启动的哨兵数过少,就可能达不到同意救援的哨兵数。

# 配置

  • sentinel.conf 的配置示例:
    bind 0.0.0.0
    port 26379
    requirepass ******      # Redis 5.0 开始支持给哨兵设置密码
    # protected-mode yes    # 哨兵不能开启 protected-mode 模式
    # daemonize yes
    dir /opt/redis/
    logfile /var/log/redis-sentinel.log
    pidfile /var/run/redis-sentinel.pid
    
    sentinel monitor master1 10.0.0.1 6379 2      # 监控的 master ,最后的 2 表示 quorum
    sentinel auth-pass master1 ******             # master 的密码
    sentinel down-after-milliseconds master1 5000 # master 断开连接的超时时间(单位 ms)
    sentinel parallel-syncs master1 1             # 选出新 master 之后,同时安排多少个 slave 与它开始同步
    sentinel failover-timeout master1 30000       # 救援过程的超时时间(单位 ms),超过该时间之后就认为救援失败
    sentinel deny-scripts-reconfig yes            # 禁止在 Redis 终端用 SENTINEL SET 进行配置
    
    • 关于 sentinel monitor :
      • 哨兵在启动时只会连接到 master ,因此只需要配置当前 master 的 ip 和密码。
      • master 的名称由用户自定义。即使发生了主从切换,新的 master 也会使用原来的 master 名。
      • quorum(法定人数)表示至少有多少个哨兵认为 master 下线了,就开始救援。
      • quorum 为 2 时,集群中至少要有 2 个哨兵,否则不能自动切换主从。
    • 哨兵每隔 1 秒会向 master 发出 ping 请求,如果超过 down-after-milliseconds 时间之后仍未收到 master 的响应,则认为 master 下线了。
    • 主从切换时,哨兵会自动配置各个 slave 的 replicaof 参数,但不会自动配置其密码,因此要事先在 master、slave 的配置文件中设置两份相同的密码:
      requirepass ******
      masterauth  ******
      

# 管理

  • 执行 redis-cli -p 26379 可进入哨兵的终端,常用的命令如下:

    info                         # 显示该哨兵的信息
    SENTINEL masters             # 显示所有 master 的信息
    SENTINEL slaves <master>     # 显示指定 master 的所有 slave 的信息
    SENTINEL sentinels <master>  # 显示指定 master 的所有哨兵的信息
    SENTINEL failover <master>   # 强制开始一次救援,切换 master
    SENTINEL reset <master>      # 清空本机哨兵关于指定 master 的所有配置信息,按当前配置重写配置文件
    
  • 例:向哨兵查询当前的主从信息

    [[email protected] ~]# redis-cli -p 26379 info Sentinel
    # Sentinel
    sentinel_masters:1           # 监控了 1 个 master
    sentinel_tilt:0
    sentinel_running_scripts:0
    sentinel_scripts_queue_length:0
    sentinel_simulate_failure_flags:0
    master0:name=master1,status=ok,address=10.244.67.5:6379,slaves=2,sentinels=3    # 监控的第一个 master 的主从信息
    
    • 最后一行记录了已发现的 slave、sentinel 数量。
    • 哨兵会记录发现过的所有 slave、sentinel ,即使它们断开连接也不会移除,因此记录的数量可能比实际在线数多,执行 SENTINEL reset <master> 命令才会重新计数。
      • 用 Docker 部署时,应该让 sentinel 使用主机网卡。否则主机内通信时使用自身网卡、主机间通信时使用主机网卡,就会记录下两倍数量的哨兵地址。
      • 在 k8s 上部署时,如果填写的 master 地址是其 Service 名,则会解析成 Service IP 。但各个 Redis 节点之间实际上是通过 Pod IP 进行通信,导致第一次主从切换之后哨兵会多计数一个 Redis 节点。此时,需要对每个哨兵执行 SENTINEL reset master1 命令,清除掉多余的 IP 。

# 日志分析

这里在三个主机上分别部署一个 redis-server 和 redis-sentinel ,构成一主二从集群。

  • 终止 master 时,可见某个哨兵的日志如下:

    202:X 18 Nov 2019 17:02:12.468 # +sdown master master1 10.244.79.33 6379                       # 当前哨兵认为 master 已下线
    202:X 18 Nov 2019 17:02:12.583 # +new-epoch 12                                                 # 开始第 12 次救援
    202:X 18 Nov 2019 17:02:12.590 # +vote-for-leader 4f648158ea1a5f8df03b001396042d18cef56367 12  # 投票给编号*367 的哨兵,希望它当 leader
    202:X 18 Nov 2019 17:02:13.547 # +odown master master1 10.244.79.33 6379 #quorum 3/2           # 有 3 个哨兵认为 master 下线,决定开始救援
    202:X 18 Nov 2019 17:02:13.644 # +switch-master master1 10.244.79.33 6379 10.244.53.62 6379    # 将 master 从*.33 节点改为*.62 节点
    202:X 18 Nov 2019 17:02:13.645 * +slave slave 10.244.25.157:6379 10.244.25.157 6379 @ master1 10.244.53.62 6379  # 添加*.157 节点作为 slave
    263:X 18 Nov 2019 17:02:16.355 * +slave slave 10.244.79.33:6379 10.244.79.33 6379 @ master1 10.244.53.62 6379
    
    • +sdown :主观下线
    • +odown :客观下线
    • -sdown :主观上线(不会记录客观上线)
    • +reboot :重启
    • 比如一个 slave 被阻塞过长的时间之后,会被哨兵先后记录 +sdown、-sdown ,但不会记录 +reboot 。
  • 如果 failover 失败,哨兵们会重新开始救援,日志如下:

    503:X 18 Nov 2019 15:55:18.755 # +new-epoch 21
    503:X 18 Nov 2019 15:55:18.755 # +try-failover master master1 10.244.79.33 6379
    503:X 18 Nov 2019 15:55:18.762 # +vote-for-leader 91ad63983edf9cfc378b2a37491c5621ebfceff5 21   # 投票给编号*ff5 的哨兵,希望它当 leader
    503:X 18 Nov 2019 15:55:18.776 # dc54b5f06544c8bf830b9f8b00f199b96e0e6b16 voted for 91ad63983edf9cfc378b2a37491c5621ebfceff5 21  # 另一个哨兵也投票给编号*ff5 的哨兵
    503:X 18 Nov 2019 15:55:18.778 # d2ceda111b29ad5f6e3932864fb2b7ca8e2309ff voted for 91ad63983edf9cfc378b2a37491c5621ebfceff5 21
    503:X 18 Nov 2019 15:55:18.817 # +elected-leader master master1 10.244.79.33 6379
    503:X 18 Nov 2019 15:55:18.818 # +failover-state-select-slave master master1 10.244.79.33 6379   # 准备选出*.33 节点当 master
    503:X 18 Nov 2019 15:55:18.894 # -failover-abort-no-good-slave master master1 10.244.79.33 6379  # 发现*.33 节点不合格,不能当 master
    503:X 18 Nov 2019 15:55:18.995 # Next failover delay: I will not start a failover before Mon Nov 18 15:55:39 2019
    

# Cluster 集群

架构图:

  • 将整个集群空间划分成 16384 个槽位(slot),将每个写入的 key 随机分配到一个 slot 中。
    • 给 key 分配 slot 的算法是 CRC16[key]%16384 :先计算 key 的 CRC16 哈希值,再对 16384 取模运算,结果就是要分配的 slot 序号。
    • 客户端可以访问集群中的任一 node ,如果读取的 key 不存在,则请求会被转到正确的 node 。
  • 集群中的每个 node 存储一部分 slot 。
    • 如果一个 node 故障,则它存储的所有 slot 都会丢失。因此通常把每个 node 部署成主从集群,以保证高可用。
    • 当集群中的 node 增加或减少时,就需要重新给每个节点分配 slot ,这会导致 key 的迁移。
  • 优点
    • 容易横向扩展。
  • 缺点
    • 只能使用 0 号数据库。
    • 操作多个 Key 时,它们可能存储在不同的 Redis 中,导致不能进行 mset 等批量操作、不能实现事务的原子性。