Redis哨兵(sentinel)高可用搭建

背景:测试环境的redis集群被运维回收,无奈之下只能自己动手,Redis高可用集群架构的搭建。

·原理

Redis的哨兵(sentinel)系统用于管理多个redis服务器,该系统执行以下三个任务:

·监控(Monitoring):哨兵(sentinel)会不断地检查你的Master和Slave是否运作正常。

·提醒(Notification):当被监控的某个Redis出现问题时,哨兵(sentinel)可以通过API向管理员或者其他应用程序发送通知。

·自动故障迁移(Automatic failover):当一个Master不能正常工作时,哨兵(sentinel)会开始一次自动故障迁移操作,它会将失效Master的其中一个Slave升级为新的Master,并让失效Master的其他Slave改为复制新的Master;当客户端试图连接失效的Master时,集群也会向客户端返回新Master的地址,使得集群可以使用Master代替失效Master,客户端实际获取的链接是哨兵返回的。

哨兵(sentinel)是一个分布式系统,你可以在一个架构中运行多个哨兵(sentinel)进程,这些进程使用流言协议(gossipprotocols)来接收关于Master是否下线的信息,并使用投票协议(agreement protocols)来决定是否执行自动故障迁移,以及选择哪个Slave作为新的Master。

每个哨兵(sentinel)会向其它哨兵(sentinel)、master、slave定时发送消息,以确认对方是否”活”着,如果发现对方在指定时间(可配置)内未回应,则暂时认为对方已挂(所谓的”主观认为宕机” Subjective Down,简称sdown).

若“哨兵群”中的多数sentinel,都报告某一master没响应,系统才认为该master"彻底死亡"(即:客观上的真正down机,Objective Down,简称odown),通过一定的vote算法,从剩下的slave节点中,选一台提升为master,然后自动修改相关配置.

虽然哨兵(sentinel)释出为一个单独的可执行文件redis-sentinel ,但实际上它只是一个运行在特殊模式下的Redis服务器,你可以在启动一个普通Redis服务器时通过给定--sentinel选项来启动哨兵(sentinel).

哨兵(sentinel)的一些设计思路和zookeeper非常类似。

一、架构图


多个哨兵架构图

二、下载

首页地址:http://redis.io/

最新稳定版下载地址:http://download.redis.io/releases/redis-3.2.9.tar.gz

# tar -xvf redis-3.2.9.tar.gz

# cd redis-3.2.9

# make install

# make test

三、命令

redis-server    -h

Usage: ./redis-server [/path/to/redis.conf] [options]

./redis-server - (read config from stdin)

./redis-server -v or --version

./redis-server -h or --help

./redis-server --test-memory

Examples:

./redis-server (run the server with default conf)

./redis-server /etc/redis/6379.conf

./redis-server --port 7777

./redis-server --port 7777 --slaveof 127.0.0.1 8888

./redis-server /etc/myredis.conf --loglevel verbose

Sentinel mode:

./redis-server /etc/sentinel.conf --sentinel

四、配置

daemonize yes        ###daemon后台运行模式

pidfile /var/run/redis.pid  ###pidfile

port 6379  ###默认6379端口

tcp-backlog 511  ###tcp三次握手等待确认ack最大的队列数

bind 192.168.65.128 127.0.0.1  ###绑定的监听的ip,可同时写多个ip,不写默认监控0.0.0.0

timeout 0  ###关闭空闲客户端,0为disable

tcp-keepalive 0 ###是否开启tcp长连接,定义socket连接timeout时长

loglevel notice  ###定义日志级别为notice(输出必要的日志)

logfile "/var/log/redis/redis.log" ###定义日志输出目录

databases 16  ###允许redis定义的最大的db簇

###开启rdb并应用如下数据保存策略,aof和rdb可同时启用,aof强烈建议开启

save 900 1

save 300 10

save 60 10000

stop-writes-on-bgsave-error yes  ###有任何问题导致的bgsave失败都停止redis的对外服务

rdbcompression yes  ###rdb写压缩

rdbchecksum yes  ###rdb文件正确性检查

dbfilename dump.rdb  ###rdb文件名称(dump.rdb可任意起)

dir /var/lib/redis/    ###rdb文件写目录

# slaveof     ###如果是slave需要写明master

# masterauth     ###master密码验证(因为redis基于内存,作用不大)

slave-serve-stale-data no  ###如果master宕机,slave是否还正常对外服务no->停止服务

slave-read-only yes    ###salve是否只读(默认只读)

repl-diskless-sync no  ### Disk-backed 启动新进程写rdb到disk,然后增量传输到salves 方式二:Diskless 直接写salve sockers,而不用写disk,当网络好的时候建议使用,但因为用到的是M/S+哨兵的架构,随时可能会进行主从切换,这个方式暂时不用

repl-diskless-sync-delay 5  ##每5s传输一次diskless开启后生效

repl-ping-slave-period 3  ###slave每3s ping测试master是否存活

repl-timeout 10      ###定义replicationtxuq  timeout时间,这里的时间要大于repl-ping-slave-period里的时间,不然后一直发生low traffic between the master and the slave问题

repl-disable-tcp-nodelay no  ###yes 一条线40milliseconds发送一次数据包,意识着更小的tcp packets和更小的带宽去发送到slave,但在slave会有相应的数据延迟. No,刚相反.一般上我们期望是低延迟,所以最选择no是个不错的选择,但如果在复杂的网络情况或m/s之间有很多中继网络,这里建议修改为yes

slave-priority 100  ###slave优先级别,数字越小优先级别越高,当master宕机时优先选取slave

# min-slaves-to-write 3  ###当最少有3个slave延迟<= 10s时,master才正常提供服务

# min-slaves-max-lag 10  ###当最少有3个slave延迟<= 10s时,master才正常提供服务

maxmemory  1536000000  #1.5G  ###单位 ,redis最大占用内存数,当超过1.5G时redis会根据策略清理内存key

#############6种清理策略

# volatile-lru -> remove the key with an expire set using an LRU algorithm ##推荐使用

# allkeys-lru -> remove any key according to the LRU algorithm

# volatile-random -> remove a random key with an expire set

# allkeys-random -> remove a random key, any key

# volatile-ttl -> remove the key with the nearest expire time (minor TTL)

# noeviction -> don't expire at all, just return an error on write operations

maxmemory-policy volatile-lru  ###使用LRU算法清理过期key

# maxmemory-samples 3  ###LRU算法和TTL算法均为模糊算法,该精确算法redis会选择3个keys选择最少使用的一个key进行删除, 个人不建议使用

appendonly yes  ###aof持久化,增量写disk,比aof慢,但更可靠.企业的默认选择,有的会两者均开启

appendfilename "appendonly.aof"  ###aof文件名

# appendfsync always

appendfsync everysec  ###建议使用该方式

# appendfsync no

no-appendfsync-on-rewrite no  ##建议no, 主要用来缓和redis调用fsync()写数据时间长的问题.当BGSAVE或BGREWRITEAOF被调用期间,fsync()进程将阻止被调用,即相当于

auto-aof-rewrite-percentage 100  ###当文件大小达到64mb的100%大小时开始rewrite aof文件

auto-aof-rewrite-min-size 64mb  ###当文件大小达到64mb的100%大小时开始rewrite aof文件

aof-load-truncated yes  ###当aof文件被损坏时,redis将返回错误并退出

lua-time-limit 5000  ###LUA scripts最大执行时间,单位(milliseconds),超出后返回查询错误并写日志

slowlog-log-slower-than 10000 ###单位microseconds(毫秒) 1000000 microseconds=1 s,记录执行时长超过10000 microseconds的执行命令

slowlog-max-len 128  ###最大长度为128

latency-monitor-threshold 0  ###监控相关,关闭就好

notify-keyspace-events ""  ###空表示关闭,发布相关key的操作记录到所有 client

######下面是高级设置,个人保持为默认配置

hash-max-ziplist-entries 512

hash-max-ziplist-value 64

list-max-ziplist-entries 512

list-max-ziplist-value 64

set-max-intset-entries 512

zset-max-ziplist-entries 128

zset-max-ziplist-value 64

hll-sparse-max-bytes 3000

activerehashing yes

client-output-buffer-limit normal 0 0 0

client-output-buffer-limit slave 256mb 64mb 60

client-output-buffer-limit pubsub 32mb 8mb 60

hz 10

aof-rewrite-incremental-fsync yes

五、启动

# redis-server /etc/redis/redis.conf

六、查看

# redis-cli -h Mrds

Mrds:6379> info

# Server

redis_version:2.8.19    ###redis版本号

redis_git_sha1:00000000  ###git SHA1

redis_git_dirty:0  ###git dirty flag

redis_build_id:78796c63e58b72dc

redis_mode:standalone  ###redis运行模式

os:Linux 2.6.32-431.el6.x86_64 x86_64  ###os版本号

arch_bits:64  ###64位架构

multiplexing_api:epoll  ###调用epoll算法

gcc_version:4.4.7  ###gcc版本号

process_id:25899  ###服务器进程PID

run_id:eae356ac1098c13b68f2b00fd7e1c9f93b1c6a2c  ###Redis的随机标识符(用于sentinel和集群)

tcp_port:6379  ###Redis监听的端口号

uptime_in_seconds:6419 ###Redis运行时长(s为单位)

uptime_in_days:0  ###Redis运行时长(天为单位)

hz:10

lru_clock:10737922  ###以分钟为单位的自增时钟,用于LRU管理

config_file:/etc/redis/redis.conf  ###redis配置文件

# Clients

connected_clients:1  ###已连接客户端的数量(不包括通过从属服务器连接的客户端)

client_longest_output_list:0  ###当前连接的客户端中最长的输出列表

client_biggest_input_buf:0  ###当前连接的客户端中最大的输出缓存

blocked_clients:0  ###正在等待阻塞命令(BLPOP、BRPOP、BRPOPLPUSH)的客户端的数量 需监控

# Memory

used_memory:2281560  ###由 Redis 分配器分配的内存总量,以字节(byte)为单位

used_memory_human:2.18M  ###以更友好的格式输出redis占用的内存

used_memory_rss:2699264  ###从操作系统的角度,返回 Redis 已分配的内存总量(俗称常驻集大小)。这个值和 top 、 ps 等命令的输出一致

used_memory_peak:22141272  ### Redis 的内存消耗峰值(以字节为单位)

used_memory_peak_human:21.12M  ###以更友好的格式输出redis峰值内存占用

used_memory_lua:35840  ###LUA引擎所使用的内存大小

mem_fragmentation_ratio:1.18  ###used_memory_rss 和 used_memory 之间的比率

mem_allocator:jemalloc-3.6.0

###在理想情况下, used_memory_rss 的值应该只比 used_memory 稍微高一点儿。当 rss > used ,且两者的值相差较大时,表示存在(内部或外部的)内存碎片。内存碎片的比率可以通过 mem_fragmentation_ratio 的值看出。

当 used > rss 时,表示 Redis 的部分内存被操作系统换出到交换空间了,在这种情况下,操作可能会产生明显的延迟。

# Persistence

loading:0  ###记录服务器是否正在载入持久化文件

rdb_changes_since_last_save:0  ###距离最近一次成功创建持久化文件之后,经过了多少秒

rdb_bgsave_in_progress:0  ###记录了服务器是否正在创建 RDB 文件

rdb_last_save_time:1420023749  ###最近一次成功创建 RDB 文件的 UNIX 时间戳

rdb_last_bgsave_status:ok  ###最近一次创建 RDB 文件的结果是成功还是失败

rdb_last_bgsave_time_sec:0  ###最近一次创建 RDB 文件耗费的秒数

rdb_current_bgsave_time_sec:-1  ###如果服务器正在创建 RDB 文件,那么这个域记录的就是当前的创建操作已经耗费的秒数

aof_enabled:1  ###AOF 是否处于打开状态

aof_rewrite_in_progress:0  ###服务器是否正在创建 AOF 文件

aof_rewrite_scheduled:0  ###RDB 文件创建完毕之后,是否需要执行预约的 AOF 重写操作

aof_last_rewrite_time_sec:-1  ###最近一次创建 AOF 文件耗费的时长

aof_current_rewrite_time_sec:-1  ###如果服务器正在创建 AOF 文件,那么这个域记录的就是当前的创建操作已经耗费的秒数

aof_last_bgrewrite_status:ok  ###最近一次创建 AOF 文件的结果是成功还是失败

aof_last_write_status:ok

aof_current_size:176265  ###AOF 文件目前的大小

aof_base_size:176265  ###服务器启动时或者 AOF 重写最近一次执行之后,AOF 文件的大小

aof_pending_rewrite:0  ###是否有 AOF 重写操作在等待 RDB 文件创建完毕之后执行

aof_buffer_length:0  ###AOF 缓冲区的大小

aof_rewrite_buffer_length:0  ###AOF 重写缓冲区的大小

aof_pending_bio_fsync:0  ###后台 I/O 队列里面,等待执行的 fsync 调用数量

aof_delayed_fsync:0    ###被延迟的 fsync 调用数量

# Stats

total_connections_received:8466  ###服务器已接受的连接请求数量

total_commands_processed:900668  ###服务器已执行的命令数量

instantaneous_ops_per_sec:1  ###服务器每秒钟执行的命令数量

total_net_input_bytes:82724170

total_net_output_bytes:39509080

instantaneous_input_kbps:0.07

instantaneous_output_kbps:0.02

rejected_connections:0  ###因为最大客户端数量限制而被拒绝的连接请求数量

sync_full:2

sync_partial_ok:0

sync_partial_err:0

expired_keys:0  ###因为过期而被自动删除的数据库键数量

evicted_keys:0  ###因为最大内存容量限制而被驱逐(evict)的键数量。

keyspace_hits:0  ###查找数据库键成功的次数。

keyspace_misses:500000  ###查找数据库键失败的次数。

pubsub_channels:0  ###目前被订阅的频道数量

pubsub_patterns:0  ###目前被订阅的模式数量

latest_fork_usec:402  ###最近一次 fork() 操作耗费的毫秒数

# Replication

role:master  ###如果当前服务器没有在复制任何其他服务器,那么这个域的值就是 master ;否则的话,这个域的值就是 slave 。注意,在创建复制链的时候,一个从服务器也可能是另一个服务器的主服务器

connected_slaves:2  ###2个slaves

slave0:ip=192.168.65.130,port=6379,state=online,offset=1639,lag=1

slave1:ip=192.168.65.129,port=6379,state=online,offset=1639,lag=0

master_repl_offset:1639

repl_backlog_active:1

repl_backlog_size:1048576

repl_backlog_first_byte_offset:2

repl_backlog_histlen:1638

# CPU

used_cpu_sys:41.87  ###Redis 服务器耗费的系统 CPU

used_cpu_user:17.82  ###Redis 服务器耗费的用户 CPU

used_cpu_sys_children:0.01  ###后台进程耗费的系统 CPU

used_cpu_user_children:0.01  ###后台进程耗费的用户 CPU

# Keyspace

db0:keys=3101,expires=0,avg_ttl=0  ###keyspace 部分记录了数据库相关的统计信息,比如数据库的键数量、数据库已经被删除的过期键数量等。对于每个数据库,这个部分都会添加一行以下格式的信息

七、Sentinel(哨兵)配置

vi sentinel.conf

启动前:

port 26379

dir "/var/lib/redis/tmp"  ###定义目录存放

sentinel auth-passmymaster vhreal   ##密码

sentinel monitor mymaster 192.168.65.128 6379 2    ###监控mymaster(可自定义-但只能包括A-z 0-9和”._-”)

sentinel down-after-milliseconds mymaster 30000  ###mymaster多久不响应认为SDOWN

sentinel parallel-syncs mymaster 1  ###指定最大同时同步新maser配置的salve数量

sentinel failover-timeout mymaster 180000  ###2次failover切换时间

启动后(redis.con配置文件完全由sentinel控制,请不要再随意手动改动):

port 26379

dir "/var/lib/redis/tmp"

sentinel monitor mymaster 192.168.65.128 6379 2

sentinel config-epoch mymaster 18  ###确认mymater SDOWN时长

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

sentinel known-slave mymaster 192.168.65.129 6379  ###已知的slave

sentinel known-slave mymaster 192.168.65.130 6379  ###已知的slave

sentinel known-sentinel mymaster 192.168.65.130 26379 be964e6330ee1eaa9a6b5a97417e866448c0ae40    ###已知slave的唯一id

sentinel known-sentinel mymaster 192.168.65.129 26379 3e468037d5dda0bbd86adc3e47b29c04f2afe9e6  ###已知slave的唯一id

sentinel current-epoch 18  ####当前可同时同步的salve数最大同步阀值

开启sentinel

开启sentinel后会后redis.conf会由sentinel进行管理

# redis-server /etc/redis/sentinel.conf --sentinel &> /var/log/redis/sentinel.log &

八、常用命令

redis-cli

redis-server

auth

set key value

get key

九、问题解决

1、Redis主从配置异常解决:Error condition on socket for SYNC: Connection refused

Redis主从集群时,从服务器上的redis日志报错:

32677:S08Feb16:14:38.947*ConnectingtoMASTER172.168.10.70:637932677:S08Feb16:14:38.948*MASTER<->SLAVEsyncstarted32677:S08Feb16:14:38.948#ErrorconditiononsocketforSYNC:Connectionrefused32677:S08Feb16:14:39.950*ConnectingtoMASTER172.168.10.70:637932677:S08Feb16:14:39.950*MASTER<->SLAVEsyncstarted32677:S08Feb16:14:39.950#ErrorconditiononsocketforSYNC:Connectionrefused32677:S08Feb16:14:40.952*ConnectingtoMASTER172.168.10.70:637932677:S08Feb16:14:40.952*MASTER<->SLAVEsyncstarted32677:S08Feb16:14:40.953#ErrorconditiononsocketforSYNC:Connectionrefused

解决方案:

在redis主服务器上的redis.conf中修改bind字段,将

bind 127.0.0.1

修改为

bind 0.0.0.0

又或者直接注释掉bind字段

# bind 127.0.0.1

原因:

如果redis主服务器绑定了127.0.0.1,那么跨服务器IP的访问就会失败,从服务器用IP和端口访问主的时候,主服务器发现本机6379端口绑在了127.0.0.1上,也就是只能本机才能访问,外部请求会被过滤,这是Linux的网络安全策略管理的。如果bind的IP地址是172.168.10.70,那么本机通过localhost和127.0.0.1、或者直接输入命令redis-cli登录本机redis也就会失败了。只能加上本机ip才能访问到。

所以,在研发、测试环境可以考虑bind 0.0.0.0,线上生产环境建议绑定IP地址。

2、(DENIED Redis is running in protected mode)

Java程序中使用JedisSentinelPool建立redis连接池

Set sentinels = new HashSet();

sentinels.add("172.17.16.7:26379");

sentinels.add("172.17.16.8:26379");

sentinels.add("172.17.16.9:26379");

JedisSentinelPool sentinelPool = new JedisSentinelPool("mymaster", sentinels, "123456");

报错:

DENIED Redis is running in protected mode because protected mode is enabled, no bind address was specified, no authentication password is requested to    clients.......

使用redis-cli -h 127.0.0.1 -p 26379连接sentinel可以执行命令,而使用redis-cli -h 172.17.16.7 -p 26379连接sentinel执行命令则会报同上的错误。

查遍了网上没有找到相关的问题分析和解决的案例,所以只能自己猜测和排查,初步怀疑是通过172.17.16.7访问sentinel时受限。

由于此错误和redis server的protect-mode为yes的访问错误颇为相似,官方在redis.conf的注释说明中有protected-mode这一配置项,但sentinel.conf的注释中完全没有提到过该配置项,我很疑惑,但还是尝试在sentinel.conf中加入

protected-mode no

之后保存并重新启动sentinel,之后用Java程序建立连接池,没有报错,且可以对redis server进行数据处理,问题解决。

本文原创,转载请注明出处!

推荐阅读更多精彩内容

  • 1.1 资料 ,最好的入门小册子,可以先于一切文档之前看,免费。 作者Antirez的博客,Antirez维护的R...
    JefferyLcm阅读 15,101评论 1 43
  • 最近公司项目要求Redis集群且高可用,在查询了一系列文章,再结合项目实际情况,所以采用了这一套高可用集群方案 方...
    Crazy_Coder阅读 442评论 0 4
  • Redis 配置文件示例 注意:想要读取配置文件,Redis的第一个参数必须是文件的路径 ./redis-serv...
    起个名忒难阅读 347评论 0 1
  • 1- 水平分区 VS 垂直分区 分区是分割数据到多个Redis实例的处理过程,因此每个实例只保存key的一个子集。...
    zhanglbjames阅读 2,589评论 0 3
  • 春季:3月--5月 上衣:休闲装、户外装、运动服等都适用,建议外套选双层或厚层的。 里衣:毛衣或保暖套装类,怕冷、...
    风途户外俱乐部阅读 75评论 0 0