redis.conf 配置文件

  • redis.conf 配置项说明如下:
  1. Redis默认不是以守护进程的方式运行,可以通过该配置项修改,使用yes启用守护进程, 在该模式下,redis会在后台运行,并将进程pid号写入至redis.conf选项pidfile设置的文件中,此时redis将一直运行,除非手动kill该进程。但当daemonize选项设置成no时,当前界面将进入redis的命令行界面,exit强制退出或者关闭连接工具(putty,xshell等)都会导致redis进程退出。
    daemonize no

  2. 当Redis以守护进程方式运行时,Redis默认会把pid写入/var/run/redis.pid文件,可以通过pidfile指定
    pidfile /var/run/redis.pid

  3. 指定Redis监听端口,默认端口为6379,为什么选用6379作为默认端口,因为6379在手机按键上MERZ对应的号码,而MERZ取自意大利歌女Alessia Merz的名字
    port 6379

  4. 绑定的主机地址
    bind 127.0.0.1

  5. 当 客户端闲置多长时间后关闭连接,如果指定为0,表示关闭该功能
    timeout 300

  6. 指定日志记录级别,Redis总共支持四个级别:debug、verbose、notice、warning,默认为verbose
    loglevel verbose

  7. 日志记录方式,默认为标准输出,如果配置Redis为守护进程方式运行,而这里又配置为日志记录方式为标准输出,则日志将会发送给/dev/null
    logfile stdout

  8. 设置数据库的数量,默认数据库为0,可以使用SELECT <dbid>命令在连接上指定数据库id
    databases 16

  9. 指定在多长时间内,有多少次更新操作,就将数据同步到数据文件,可以多个条件配合
    save <seconds> <changes>

    • Redis默认配置文件中提供了三个条件:
      save 900 1
      save 300 10
      save 60 10000
    • 分别表示900秒(15分钟)内有1个更改,300秒(5分钟)内有10个更改以及60秒内有10000个更改。
  10. 指定存储至本地数据库时是否压缩数据,默认为yes,Redis采用LZF压缩,如果为了节省CPU时间,可以关闭该选项,但会导致数据库文件变的巨大
    rdbcompression yes

  11. 指定本地数据库文件名,默认值为dump.rdb
    dbfilename dump.rdb

  12. 指定本地数据库存放目录
    dir ./

  13. 设置当本机为slav服务时,设置master服务的IP地址及端口,在Redis启动时,它会自动从master进行数据同步
    slaveof <masterip> <masterport>

  14. 当master服务设置了密码保护时,slav服务连接master的密码
    masterauth <master-password>

  15. 设置Redis连接密码,如果配置了连接密码,客户端在连接Redis时需要通过AUTH <password>命令提供密码,默认关闭
    requirepass 123456

  16. 设置同一时间最大客户端连接数,默认无限制,Redis可以同时打开的客户端连接数为Redis进程可以打开的最大文件描述符数,如果设置 maxclients 0,表示不作限制。当客户端连接数到达限制时,Redis会关闭新的连接并向客户端返回max number of clients reached错误信息
    maxclients 128

  17. 指定Redis最大内存限制,Redis在启动时会把数据加载到内存中,达到最大内存后,Redis会先尝试清除已到期或即将到期的Key,当此方法处理 后,仍然到达最大内存设置,将无法再进行写入操作,但仍然可以进行读取操作。Redis新的vm机制,会把Key存放内存,Value会存放在swap区
    maxmemory <bytes>

  18. 指定是否在每次更新操作后进行日志记录,Redis在默认情况下是异步的把数据写入磁盘,如果不开启,可能会在断电时导致一段时间内的数据丢失。因为 redis本身同步数据文件是按上面save条件来同步的,所以有的数据会在一段时间内只存在于内存中。默认为no
    appendonly no

  19. 指定更新日志文件名,默认为appendonly.aof
    appendfilename appendonly.aof

  20. 指定更新日志条件,共有3个可选值:
    no:表示等操作系统进行数据缓存同步到磁盘(快)
    always:表示每次更新操作后手动调用fsync()将数据写到磁盘(慢,安全)
    everysec:表示每秒同步一次(折中,默认值)
    appendfsync everysec

  21. 指定是否启用虚拟内存机制,默认值为no,简单的介绍一下,VM机制将数据分页存放,由Redis将访问量较少的页即冷数据swap到磁盘上,访问多的页面由磁盘自动换出到内存中(在后面的文章我会仔细分析Redis的VM机制)
    vm-enabled no

  22. 虚拟内存文件路径,默认值为/tmp/redis.swap,不可多个Redis实例共享
    vm-swap-file /tmp/redis.swap

  23. 将所有大于vm-max-memory的数据存入虚拟内存,无论vm-max-memory设置多小,所有索引数据都是内存存储的(Redis的索引数据 就是keys),也就是说,当vm-max-memory设置为0的时候,其实是所有value都存在于磁盘。默认值为0
    vm-max-memory 0

  24. Redis swap文件分成了很多的page,一个对象可以保存在多个page上面,但一个page上不能被多个对象共享,vm-page-size是要根据存储的 数据大小来设定的,作者建议如果存储很多小对象,page大小最好设置为32或者64bytes;如果存储很大大对象,则可以使用更大的page,如果不 确定,就使用默认值
    vm-page-size 32

  25. 设置swap文件中的page数量,由于页表(一种表示页面空闲或使用的bitmap)是在放在内存中的,,在磁盘上每8个pages将消耗1byte的内存。
    vm-pages 134217728

  26. 设置访问swap文件的线程数,最好不要超过机器的核数,如果设置为0,那么所有对swap文件的操作都是串行的,可能会造成比较长时间的延迟。默认值为4
    vm-max-threads 4

  27. 设置在向客户端应答时,是否把较小的包合并为一个包发送,默认为开启
    glueoutputbuf yes

  28. 指定在超过一定的数量或者最大的元素超过某一临界值时,采用一种特殊的哈希算法
    hash-max-zipmap-entries 64
    hash-max-zipmap-value 512

  29. 指定是否激活重置哈希,默认为开启(后面在介绍Redis的哈希算法时具体介绍)
    activerehashing yes

  30. 指定包含其它的配置文件,可以在同一主机上多个Redis实例之间使用同一份配置文件,而同时各个实例又拥有自己的特定配置文件
    include /path/to/local.conf

  31. Redis中的内存维护策略: redis作为优秀的中间缓存件,时常会存储大量的数据,即使采取了集群部署来动态扩容,也应该即使的整理内存,维持系统性能。在redis中有两种解决方案

    • 一是为数据设置超时时间,
    • 二是采用LRU算法动态将不用的数据删除。内存管理的一种页面置换算法,对于在内存中但又不用的数据块(内存块)叫做LRU,操作系统会根据哪些数据属于LRU而将其移出内存而腾出空间来加载另外的数据。
    1. volatile-lru:设定超时时间的数据中,删除最不常使用的数据.
    2. allkeys-lru:查询所有的key中最近最不常使用的数据进行删除,这是应用最广泛的策略.
    3. volatile-random:在已经设定了超时的数据中随机删除.
    4. allkeys-random:查询所有的key,之后随机删除.
    5. volatile-ttl:查询全部设定超时时间的数据,之后排序,将马上将要过期的数据进行删除操作.
    6. noeviction:如果设置为该属性,则不会进行删除操作,如果内存溢出则报错返回.
    7. volatile-lfu:从所有配置了过期时间的键中驱逐使用频率最少的键
    8. allkeys-lfu:从所有键中驱逐使用频率最少的键

主从复制

  • redis服务器的硬盘损坏了可能会导致数据丢失, 主从复制机制就可以避免单点故障,设置一个主数据库和n个从数据库

    • 所有的写操作都在主数据库执行

    • 读操作在从数据库执行

    • 为保证数据的一致性,将主数据库的数据会复制到从数据库。

说明:

  1. 主redis中的数据有两个副本(replication)即从redis1和从redis2,即使一台redis服务器宕机其它两台redis服务也可以继续提供服务。

  2. 主redis中的数据和从redis上的数据保持实时同步,当主redis写入数据时通过主从复制机制会复制到两个从redis服务上。

  3. 只有一个主redis,可以有多个从redis。

  4. 主从复制不会阻塞master,在同步数据时,master 可以继续处理client 请求

  5. 一个redis可以即是主又是从,如下图:

设置主从复制

  • 192.168.2.100为主服务器,192.168.2.101为从服务器
  1. 配置主服务器配置文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    [root@localhost redis-4.0.10]# vim /etc/redis/6379.conf
    # 1. 将bind 127.0.0.1这行注释或者指定ip。(本例是注释,即所有ip都能连接)
    # bind 127.0.0.1
    # 2. 开启守护进程
    daemonize yes
    # 3. 设置访问密码 建议线上把密码设置非常复杂,最好能在第2步中指定ip
    requirepass 123456
    # 4. 设置客户端最大连接数(maxclients),默认是10000,可根据需求更改
    maxclients 10000
    # 5. 最大内存(默认不受限制,建议还是设置个低于服务器内存的值)
    # maxmemory <bytes>
    # 6. 内存策略,如果内存足够用则不用管,如果内存不够用,建议设置最近最少使用策略(LRU),默认是内存不够则报错
    # maxmemory-policy noevication
  2. 使用配置文件启动redis服务

    1
    [root@localhost redis-4.0.10]# service redisd start
  3. 配置从服务器有2种方式:

    • 在redis.conf中设置slaveof :slaveof <masterip> <masterport>
    • 使用redis-cli客户端连接到redis服务,执行slaveof命令: slaveof <masterip> <masterport>,重启后将失去主从复制关系
    • 5.0后新版本的命令是replicaof,旧版本是slaveof命令
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    # 1. 前四步与主服务器配置基本一致
    # 2. 配置所属主服务器ip和端口
    slaveof 192.168.2.100 6379
    # replicaof 192.168.2.100 6379
    # 3. 配置所属主服务器的密码
    masterauth 123456
    # 4. 从服务器通常是只读,所以要配置只读(默认是只读,不要更改即可)
    slave-read-only yes
    # replica-read-only yes
    # 5. 如果是同一机器下 port 更改为与主redis(6379)不相同既可
  4. 启动从节点的redis

    1
    ./redis-server ~/config/slave/redis.conf &
  5. Redis客户端连接上6379端口,查看Redis主从关系

    • role:角色信息
    • slaveX:从库信息
    • connected_slaves:从库数量
  6. # 查看主从信息
    info replication
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59

    ## Sentinel哨兵模式

    - 主从复制同时存在以下几个问题:
    - 一旦主节点宕机,从节点晋升成主节点,同时需要修改应用方的主节点地址,还需要命令所有 从节点去复制新的主节点,整个过程需要人工干预。
    - 主节点的写能力受到单机的限制。
    - 主节点的存储能力受到单机的限制。
    - 原生复制的弊端在早期的版本中也会比较突出,比如:Redis 复制中断后,从节点会发起 psync。此时如果同步不成功,则会进行全量同步,主库执行全量备份的同时,可能会造成毫秒或秒级的卡顿。

    ### Sentinel的架构

    - Sentinel[ˈsentɪnl],哨兵机制就是解决我们以上主从复制存在缺陷(选举问题),保证我们的Redis高可用,实现自动化故障发现与故障转移。

    - 该系统执行以下三个任务:

    - 监控:哨兵会不断检查你的主服务器和从服务器是否运作正常。
    - 提醒:当被监控的某个Redis服务器出现问题时,哨兵可以通过API给程序员发送通知
    - 自动故障转移:主服务器宕机,哨兵会开始一次自动故障转移操作,升级一个从服务器为主服务器,并让其他从服务器改为复制新的主服务器.
    - 配置提供者:在 Redis Sentinel 模式下,客户端应用在初始化时连接的是 Sentinel 节点集合,从中获取主节点的信息。

    ![](6-Redis主从复制与哨兵模式/24151fd4648b290dae9b8e664208eafa.jpg)

    ### Sentinel配置

    - Redis 源码中包含了一个名为 sentinel.conf 的文件, 这个文件是一个带有详细注释的 Sentinel 配置文件示例。

    - 运行一个 Sentinel 所需的最少配置如下所示:

    ```sh
    1)sentinel monitor mymaster 192.168.10.202 6379 2
    Sentine监听的maste地址,第一个参数是给master起的名字,第二个参数为master IP,第三个为master端口,第四个为当该master挂了的时候,若想将该master判为失效,
    在Sentine集群中必须至少2个Sentinel同意才行,只要该数量不达标,则就不会发生故障迁移。
    值一般为:sentinel总数/2 +1 ,master才算真正失效
    2)sentinel down-after-milliseconds mymaster 30000
    表示master被当前sentinel实例认定为失效的间隔时间,在这段时间内一直没有给Sentine返回有效信息,则认定该master主观下线。

    只有在足够数量的 Sentinel 都将一个服务器标记为主观下线之后, 服务器才会被标记为客观下线,将服务器标记为客观下线所需的 Sentinel 数量由对主服务器的配置决定。

    3)sentinel parallel-syncs mymaster 2
    当在执行故障转移时,设置几个slave同时进行切换master,该值越大,则可能就有越多的slave在切换master时不可用,可以将该值设置为1,即一个一个来,这样在某个

    slave进行切换master同步数据时,其余的slave还能正常工作,以此保证每次只有一个从服务器处于不能处理命令请求的状态。

    4)sentinel can-failover mymaster yes
    在sentinel检测到O_DOWN后,是否对这台redis启动failover机制

    5)sentinel auth-pass mymaster 20180408
    设置sentinel连接的master和slave的密码,这个需要和redis.conf文件中设置的密码一样

    6)sentinel failover-timeout mymaster 180000
    failover过期时间,当failover开始后,在此时间内仍然没有触发任何failover操作,当前sentinel将会认为此次failoer失败。
    执行故障迁移超时时间,即在指定时间内没有大多数的sentinel 反馈master下线,该故障迁移计划则失效

    7)sentinel config-epoch mymaster 0
    选项指定了在执行故障转移时, 最多可以有多少个从服务器同时对新的主服务器进行同步。这个数字越小, 完成故障转移所需的时间就越长。

    8)sentinel notification-script mymaster /var/redis/notify.sh
    当failover时,可以指定一个"通知"脚本用来告知当前集群的情况。
    脚本被允许执行的最大时间为60秒,如果超时,脚本将会被终止(KILL)

9)sentinel leader-epoch mymaster 0
同时一时间最多0个slave可同时更新配置,建议数字不要太大,以免影响正常对外提供服务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
### 搭建

1. 从源码中复制sentinel.conf,这里建立二个哨兵sentinel1,sentinel2

2. 通过修改配置**不同端口**以及其他信息,可以启动一个sentinel集群

```sh
port 26379
daemonize yes
pidfile /var/run/redis-sentinel-26379.pid
logfile "26379.log"
#工作路径,注意路径不要和主重复
dir "/usr/local/redis/data"
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel auth-pass mymaster 123456
# master或slave多长时间(默认30秒)不能使用后标记为s_down状态。
sentinel down-after-milliseconds mymaster 5000
#若sentinel在该配置值内未能完成failover操作(即故障时master/slave自动切换),则认为本次failover失败。
sentinel failover-timeout mymaster 18000
#指定了在执行故障转移时, 最多可以有多少个从服务器同时对新的主服务器进行同步,有几个slave就设置几个
sentinel parallel-syncs mymaster 2
1
2
3
4
5
6
7
port 26380
daemonize yes
pidfile /var/run/redis-sentinel-26380.pid
logfile "26380.log"
dir "/usr/local/redis/data"
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel auth-pass mymaster 123456
  1. 启动

    1
    2
    3
    4
    5
    [root@zy01 redis]# mkdir data
    [root@zy01 redis]# cd conf/
    [root@zy01 conf]# redis-sentinel sentinel1.conf
    [root@zy01 conf]# redis-sentinel sentinel2.conf

  2. 查看文件cat sentinel1.conf ,在最后出现了下面的一些内容

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    # Generated by CONFIG REWRITE
    protected-mode no
    user default on nopass ~* &* +@all
    sentinel myid 95e50b7f9db387c25b0d0a33cdfe1744f52f67dc
    sentinel config-epoch mymaster 0
    sentinel leader-epoch mymaster 0
    sentinel current-epoch 0
    sentinel known-replica mymaster 127.0.0.1 6381
    sentinel known-replica mymaster 127.0.0.1 6380
    sentinel known-sentinel mymaster 127.0.0.1 26380 9c31e456f0bfd3fbad41dddaedfa64d2a11970e6
  3. 连接哨兵节点

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    [root@zy01 conf]# redis-cli -p 26379
    127.0.0.1:26379> ping
    PONG
    127.0.0.1:26379> info
    # Server
    redis_version:6.2.3
    redis_git_sha1:00000000
    redis_git_dirty:0
    redis_build_id:37744dab1e0d5d0c
    redis_mode:sentinel
    os:Linux 4.18.0-80.el8.x86_64 x86_64
    arch_bits:64
    multiplexing_api:epoll
    atomicvar_api:c11-builtin
    gcc_version:8.3.1
    process_id:22026
    process_supervised:no
    run_id:fbf4cf65e97342c3afacfc50e5b036fc456e0e90
    tcp_port:26379
    server_time_usec:1621866579936231
    uptime_in_seconds:308
    uptime_in_days:0
    hz:11
    configured_hz:10
    lru_clock:11253843
    executable:/usr/local/redis/conf/redis-sentinel
    config_file:/usr/local/redis/conf/sentinel1.conf
    io_threads_active:0

    # Clients
    connected_clients:2
    cluster_connections:0
    maxclients:10000
    client_recent_max_input_buffer:32
    client_recent_max_output_buffer:0
    blocked_clients:0
    tracking_clients:0
    clients_in_timeout_table:0

    # CPU
    used_cpu_sys:0.515450
    used_cpu_user:0.291560
    used_cpu_sys_children:0.000000
    used_cpu_user_children:0.000000
    used_cpu_sys_main_thread:0.511878
    used_cpu_user_main_thread:0.282033

    # Stats
    total_connections_received:3
    total_commands_processed:409
    instantaneous_ops_per_sec:2
    total_net_input_bytes:21986
    total_net_output_bytes:7010
    instantaneous_input_kbps:0.14
    instantaneous_output_kbps:0.02
    rejected_connections:0
    sync_full:0
    sync_partial_ok:0
    sync_partial_err:0
    expired_keys:0
    expired_stale_perc:0.00
    expired_time_cap_reached_count:0
    expire_cycle_cpu_milliseconds:7
    evicted_keys:0
    keyspace_hits:0
    keyspace_misses:0
    pubsub_channels:0
    pubsub_patterns:0
    latest_fork_usec:0
    total_forks:0
    migrate_cached_sockets:0
    slave_expires_tracked_keys:0
    active_defrag_hits:0
    active_defrag_misses:0
    active_defrag_key_hits:0
    active_defrag_key_misses:0
    tracking_total_keys:0
    tracking_total_items:0
    tracking_total_prefixes:0
    unexpected_error_replies:0
    total_error_replies:1
    dump_payload_sanitizations:0
    total_reads_processed:402
    total_writes_processed:400
    io_threaded_reads_processed:0
    io_threaded_writes_processed:0

    # Sentinel
    sentinel_masters:1
    sentinel_tilt:0
    sentinel_running_scripts:0
    sentinel_scripts_queue_length:0
    sentinel_simulate_failure_flags:0
    master0:name=mymaster,status=ok,address=127.0.0.1:6379,slaves=2,sentinels=2
  4. 通过哨兵查看集群状态

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    [root@alex redis-4.0.10]# ./src/redis-cli -p 26379
    127.0.0.1:26379> sentinel master mymaster
    1) "name"
    2) "mymaster"
    3) "ip"
    4) "172.16.0.169"
    5) "port"
    6) "6379"
    7) "runid"

    127.0.0.1:26379> sentinel slaves mymaster
    1) 1) "name" # slave 1
    2) "127.0.0.1:6381"
    3) "ip"
    4) "127.0.0.1"
    5) "port"
    6) "6381"
    7) "runid"
    8) "0419f313098f6af1b4ccdb189d6beb22edf27a1c"

    2) 1) "name" # slave2
    2) "127.0.0.1:6380"
    3) "ip"
    4) "127.0.0.1"
    5) "port"
    6) "6380"
    7) "runid"
    8) "5b00b502a93245f7916efd1f564bd40b16aa7b22"
  5. 模拟主节点down掉,观察sentinel的状态

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    # 模拟主down掉
    [zy@zy01 ~]$ ps -ef | grep redis
    root 21780 1 0 21:46 ? 00:00:03 redis-server 127.0.0.1:6379
    root 21786 1 0 21:46 ? 00:00:02 redis-server 127.0.0.1:6380
    root 21794 1 0 21:46 ? 00:00:02 redis-server 127.0.0.1:6381
    root 22026 1 0 22:24 ? 00:00:01 redis-sentinel *:26379 [sentinel]
    root 22032 1 0 22:25 ? 00:00:01 redis-sentinel *:26380 [sentinel]
    root 22039 15820 0 22:29 pts/2 00:00:00 redis-cli -p 26379
    zy 22044 21807 0 22:35 pts/1 00:00:00 grep --color=auto redis
    [zy@zy01 ~]$ sudo kill -9 21780

    # 查看节点
    127.0.0.1:26379> sentinel master mymaster
    1) "name"
    2) "mymaster"
    3) "ip"
    4) "127.0.0.1"
    5) "port"
    6) "6380" # 已经从6379切换到6380
    7) "runid"
    8) "0419f313098f6af1b4ccdb189d6beb22edf27a1c"

    # 相应的slave也做了切换
    127.0.0.1:26379> sentinel slaves mymaster
    1) 1) "name"
    2) "127.0.0.1:6379"
    3) "ip"
    4) "127.0.0.1"
    5) "port"
    6) "6379"
    7) "runid"
    8) ""
    9) "flags"
    10) "s_down,slave,disconnected"

    2) 1) "name"
    2) "127.0.0.1:6380"
    3) "ip"
    4) "127.0.0.1"
    5) "port"
    6) "6381"
    7) "runid"
    8) "5b00b502a93245f7916efd1f564bd40b16aa7b22"

日志分析

  • 打开哨兵的日志

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    # 该哨兵认为该节点已经主观宕机,也就是sdown
    5508:X 28 May 2021 16:09:30.105 # +sdown master mymaster 192.168.56.101 6379
    # 哨兵集群内有超过两个哨兵都认为master sdown了,因此转化成客观宕机,也就是odown。
    5508:X 28 May 2021 16:09:30.196 # +odown master mymaster 192.168.56.101 6379 #quorum 2/2
    # 递增集群状态版本号,这个版本号将被接下来选举出的新的master采用。
    5508:X 28 May 2021 16:09:30.196 # +new-epoch 1
    # 开始对IP为192.168.50.121,端口为6379,名为"mymaster"的Redis集群进行故障转移。
    5508:X 28 May 2021 16:09:30.196 # +try-failover master mymaster 192.168.56.101 6379
    # 在哨兵集群中投票选举出一个哨兵,作为本次执行故障转移操作的leader。 当前哨兵投票给自己满意的slave
    5508:X 28 May 2021 16:09:30.200 # +vote-for-leader c990635de9ed91282027c9ab9a3167ac4b4087b4 1
    # 另一个哨兵投票给自己满意的slave
    5508:X 28 May 2021 16:09:30.208 # 68ff8149e5eefeb2da9685172c5f01844a63480e voted for c990635de9ed91282027c9ab9a3167ac4b4087b4 1
    # 在哨兵集群中再次确认进行故障转移的leader是哪一个slave。
    5508:X 28 May 2021 16:09:30.285 # +elected-leader master mymaster 192.168.56.101 6379
    # leader开始在集群中寻找合适的slave。(从这里可以看出,找出新的slave不单单是通过投票,可能还和其它的因素有关。)
    5508:X 28 May 2021 16:09:30.285 # +failover-state-select-slave master mymaster 192.168.56.101 6379
    5508:X 28 May 2021 16:09:30.358 # -failover-abort-no-good-slave master mymaster 192.168.56.101 6379
    5508:X 28 May 2021 16:09:30.434 # Next failover delay: I will not start a failover before Fri May 28 16:15:30 2021
    5508:X 28 May 2021 16:15:30.388 # +new-epoch 2

主观下线和客观下线

  • 主观下线:指的是单个 Sentinel 实例对服务器做出的下线判断。
  • 客观下线:指的是多个 Sentinel 实例在对同一个服务器做出SDOWN主观下线判断。

Sentinel的工作原理

  1. 每个 Sentinel 以每秒一次的频率向它所知的主服务器、从服务器以及其他 Sentinel 实例发送一个 PING 命令。

  2. 如果一个实例距离最后一次有效回复 PING 命令的时间超过 down-after-milliseconds 所指定的值,那么这个实例会被 Sentinel 标记为主观下线。

  3. 如果一个主服务器被标记为主观下线,那么正在监视这个服务器的所有 Sentinel 节点,要以每秒一次的频率确认主服务器的确进入了主观下线状态。

  4. 如果一个主服务器被标记为主观下线,并且有足够数量的 Sentinel(至少要达到配置文件指定的数量)在指定的时间范围内同意这一判断,那么这个主服务器被标记为客观下线。

  5. 一般情况下,每个 Sentinel 会以每 10 秒一次的频率向它已知的所有主服务器和从服务器发送 INFO 命令。

    当一个主服务器被标记为客观下线时,Sentinel 向下线主服务器的所有从服务器发送 INFO 命令的频率,会从 10 秒一次改为每秒一次。

  6. Sentinel 和其他 Sentinel 协商客观下线的主节点的状态,如果处于 SDOWN 状态,则投票自动选出新的主节点,将剩余从节点指向新的主节点进行数据复制。

  7. 当没有足够数量的 Sentinel 同意主服务器下线时,主服务器的客观下线状态就会被移除。

  8. 当主服务器重新向 Sentinel 的 PING 命令返回有效回复时,主服务器的主观下线状态就会被移除。

两种数据丢失的情况

  • 主备切换的过程,可能会导致数据丢失
    • 异步复制导致的数据丢失: old master node 中内存的数据没来得及给slave node就挂了,重新选举后的master会丢失这部分数据。
    • 脑裂导致的数据丢失:某个master所在机器突然脱离了正常的网络,跟其他slave机器不能连接,但是实际上master还运行着,此时哨兵可能就会认为master宕机了,然后开启选举,将其他slave切换成了master这个时候,集群里就会有两个master,也就是所谓的脑裂。此时虽然某个slave被切换成了master,但是可能client还没来得及切换到新的master,还继续写向旧master的数据可能也丢失了,因此旧master再次恢复的时候,会被作为一个slave挂到新的master上去,自己的数据会清空,重新从新的master复制数据

减少异步复制数据丢失

  • min-slaves-max-lag 10:数据复制和同步的延迟不能超过10秒
  • 如果说一旦所有的slave,数据复制和同步的延迟都超过了10秒钟,那么这个时候,master就不会再接收任何请求了,上面两个配置可以减少异步复制和脑裂导致的数据丢失
  • 有了min-slaves-max-lag这个配置,就可以确保说,一旦slave复制数据和ack延时太长,就认为可能master宕机后损失的数据太多了,那么就拒绝写请求,这样可以把master宕机时由于部分数据未同步到slave导致的数据丢失降低的可控范围内
  • 客户端做降级处理,写到本地磁盘,client对外的接收请求做降级限流处理,减慢请求速度,或者将数据临时写入kafka消息队列,每隔10分钟去队列去除,尝试重新发送到master节点。

减少脑裂的数据丢失

  • min-slaves-to-write 1 :要求至少有1个slave

  • 如果一个master出现了脑裂,跟其他slave丢了连接,那么上面两个配置同时配置可以确保说,如果不能继续给指定数量的slave发送数据,而且slave超过10秒没有给自己ack消息,那么就直接拒绝客户端的写请求

  • 这样脑裂后的旧master就不会接受client的新数据,也就避免了数据丢失

  • 上面的配置就确保了,如果跟任何一个slave丢了连接,在10秒后发现没有slave给自己ack,那么就拒绝新的写请求,因此在脑裂场景下,最多就丢失10秒的数据