目标、需求:
為上層應用提供高可靠、低延遲、低(無限接近0)資料損失的Redis緩存服務
方案概述:
采用同一網絡内的三台主機(可以是實體主機、虛拟機或docker容器),要求三台主機之間都能互相通路,每一台主機上都安裝redis-server、redis-sentinel和keepalived。
redis-server負責提供Redis緩存服務,三台主機間的關系是master-slave-slave
redis-sentinel負責提供Redis高可用,三台主機間的關系與redis-server相同
keepalived負責提供VIP位址供上層應用使用,三台主機的關系是master-backup-backup,VIP始終在redis-server master上,保證對上層應用可寫可讀。
三台主機的備援度是1,也就是說當有一台主機當機時另外兩台中至少有一台主機可以提供Redis緩存服務
方案原理:
redis-server提供單執行個體的Redis緩存服務,redis-sentinel能在一台主機挂掉時自動的将某一台可用主機上的redis-server由slave狀态切換成master狀态。
keepalived通過vrrp_script檢測目前主機上的redis-server是否以master狀态運作,如果目前主機上的redis-server正在以master狀态運作,則将vrrp_instance标記為存活狀态,并配置設定VIP;如果目前主機上的redis-server正在以slave狀态運作,則将vrrp_instance标記為錯誤狀态。當某台主機當機後,其他兩台主機上的keepalived會将VIP切換到新的master(目前主機上的redis-server正在以master狀态運作)上。
由于keepalived的VIP切換有延遲(大約40ms),是以上層應用不能過分依賴Redis,例如大規模并發性的短時間内向Redis插入大量資料。
連接配接圖

如圖所示,有三台主機,分别辨別為Redis1、Redis2、Redis3,它們的角色和配置等資訊如下:
主機辨別 | Redis1 | Redis2 | Redis3 |
IP位址 | 192.168.1.241 | 192.168.1.242 | 192.168.1.243 |
預設配置 | redis-server master keepalived master | redis-server slave keepalived backup | |
VIP |
配置步驟:
步驟概述
- 安裝:安裝redis-server、redis-sentinel、keepalived
- 配置:配置Redis1、Redis2、Redis3,主要是編輯各個服務的配置檔案
- 啟動:先啟動redis-server、再啟動redis-sentinel、最後自動keepalived
- 驗證:模拟一台主機當機,主機網絡中斷,redis-server、redis-sentinel、keepalived三個服務任何一個發生故障的情況。
(1)設定主機名(建議設定為fqdn格式)、同步時間、更改檔案描述符最大打開數量、更改核心參數、配置編譯環境、配置防火牆在此就不贅述了。
(2)安裝redis-server和redis-sentinel
Redis的安裝由于隻是bin二進制可執行檔案和data目錄比較重要,是以簡化安裝如下
# http://download.redis.io/redis-stable.tar.gz
wget -c http://download.redis.io/releases/redis-3.0.7.tar.gz
tar zxf redis-3.0.7.tar.gz
cd redis-3.0.7
make
cd
\cp redis-3.0.7/src/redis-benchmark /usr/local/sbin/
\cp redis-3.0.7/src/redis-check-aof /usr/local/sbin/
\cp redis-3.0.7/src/redis-check-dump /usr/local/sbin/
\cp redis-3.0.7/src/redis-cli /usr/local/sbin/
\cp redis-3.0.7/src/redis-sentinel /usr/local/sbin/
\cp redis-3.0.7/src/redis-server /usr/local/sbin/
mkdir /etc/redis
mkdir -p /data/redis-6379/
(3)安裝keepalived
# http://www.keepalived.org/documentation.html
wget -c http://www.keepalived.org/software/keepalived-1.2.19.tar.gz
tar zxf keepalived-1.2.19.tar.gz
cd keepalived-1.2.19
./configure --prefix=/usr/local/keepalived
make
make install
cp /usr/local/keepalived/sbin/keepalived /usr/sbin/
cp /usr/local/keepalived/etc/sysconfig/keepalived /etc/sysconfig/
cp /usr/local/keepalived/etc/rc.d/init.d/keepalived /etc/init.d/
mkdir /etc/keepalived
keepalived的配置檔案在後面添加
(4)Redis1上的redis-sentinel配置檔案
cat >/etc/redis/sentinel.conf<<eof
port 26379
dir /tmp
sentinel monitor mymaster 192.168.1.241 6379 2
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000
eof
Redis2和Redis3上的redis-sentinel配置檔案與Redis1上的redis-sentinel配置檔案内容相同。
(5)Redis1上的redis-server的配置檔案
cat > /etc/redis/redis-6379.conf <<eof
# maxmemory 268435456
maxmemory 256mb
daemonize yes
pidfile /data/redis-6379/redis-6379.pid
port 6379
bind 0.0.0.0
tcp-backlog 511
timeout 0
tcp-keepalive 0
loglevel notice
logfile /data/redis-6379/redis.log
databases 16
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dumpredis-6379.rdb
dir /data/redis-6379
slave-serve-stale-data yes
slave-read-only yes
repl-diskless-sync no
repl-diskless-sync-delay 5
# repl-ping-slave-period 10
# repl-timeout 60
repl-disable-tcp-nodelay no
# repl-backlog-size 1mb
# repl-backlog-ttl 3600
slave-priority 100
# min-slaves-to-write 3
# min-slaves-max-lag 10
appendonly no
appendfilename "appendonly.aof"
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes
lua-time-limit 5000
slowlog-log-slower-than 10000
slowlog-max-len 128
latency-monitor-threshold 0
notify-keyspace-events ""
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
eof
(6)Redis2與Redis3上的redis-server配置檔案
cat > /etc/redis/redis-6379.conf <<eof
slaveof 192.168.1.241 6379
# maxmemory 268435456
maxmemory 256mb
daemonize yes
pidfile /data/redis-6379/redis-6379.pid
port 6379
bind 0.0.0.0
tcp-backlog 511
timeout 0
tcp-keepalive 0
loglevel notice
logfile /data/redis-6379/redis.log
databases 16
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dumpredis-6379.rdb
dir /data/redis-6379
slave-serve-stale-data yes
slave-read-only yes
repl-diskless-sync no
repl-diskless-sync-delay 5
# repl-ping-slave-period 10
# repl-timeout 60
repl-disable-tcp-nodelay no
# repl-backlog-size 1mb
# repl-backlog-ttl 3600
slave-priority 100
# min-slaves-to-write 3
# min-slaves-max-lag 10
appendonly no
appendfilename "appendonly.aof"
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes
lua-time-limit 5000
slowlog-log-slower-than 10000
slowlog-max-len 128
latency-monitor-threshold 0
notify-keyspace-events ""
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
eof
(7)Redis1上的keepalived配置檔案(vim /etc/keepalived/keepalived.conf)
關于keepalived的配置檔案
keepalived的配置檔案預設是沒有的,當然sample&example檔案還是有的,通常在PREFIX/etc/sample目錄下。
keepalived master和backup(backups)之間不同的是:
1.優先級的不同,master的優先級priority的數字要高一些
2.global_defs段的router_id都不一樣,實際中可以用任意名字區分也可以用主機名區分
3.backup的配置檔案中還有一個nopreempt字段,意思是設定為非搶占模式,作用是讓master優先擷取到VIP,并保證VIP是在原先的master上。
! Configuration File for keepalived
global_defs {
notification_email {
root@localhost
}
notification_email_from keepalived@localhost
smtp_server 127.0.0.1
smtp_connect_timeout 10
router_id keepalivedha_1
}
vrrp_script chk_http_port {
script "redis-cli info | grep role:master >/dev/null 2>&1"
interval 1
timeout 2
fall 2
rise 1
}
vrrp_sync_group VG_1 {
group {
VI_1
}
}
vrrp_instance VI_1 {
state BACKUP
interface eth1
#use_vmac keepalived
#vmac_xmit_base
mcast_src_ip 192.168.1.241
smtp_alert
virtual_router_id 20
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass password
}
virtual_ipaddress {
192.168.1.245
}
track_script {
chk_http_port
}
}
(8)Redis2上的keepalived配置檔案
! Configuration File for keepalived
global_defs {
notification_email {
root@localhost
}
notification_email_from keepalived@localhost
smtp_server 127.0.0.1
smtp_connect_timeout 10
router_id keepalivedha_2
}
vrrp_script chk_http_port {
script "redis-cli info | grep role:master >/dev/null 2>&1"
interval 1
timeout 2
fall 2
rise 1
}
vrrp_sync_group VG_1 {
group {
VI_1
}
}
vrrp_instance VI_1 {
state BACKUP
interface eth2
#use_vmac keepalived
#vmac_xmit_base
mcast_src_ip 192.168.1.242
smtp_alert
virtual_router_id 20
priority 99
advert_int 1
authentication {
auth_type PASS
auth_pass password
}
virtual_ipaddress {
192.168.1.245
}
track_script {
chk_http_port
}
nopreempt
}
(9)Redis3上的keepalived配置檔案
! Configuration File for keepalived
global_defs {
notification_email {
root@localhost
}
notification_email_from keepalived@localhost
smtp_server 127.0.0.1
smtp_connect_timeout 10
router_id keepalivedha_3
}
vrrp_script chk_http_port {
script "redis-cli info | grep role:master >/dev/null 2>&1"
interval 1
timeout 2
fall 2
rise 1
}
vrrp_sync_group VG_1 {
group {
VI_1
}
}
vrrp_instance VI_1 {
state BACKUP
interface eth1
#use_vmac keepalived
#vmac_xmit_base
mcast_src_ip 192.168.1.243
smtp_alert
virtual_router_id 20
priority 98
advert_int 1
authentication {
auth_type PASS
auth_pass password
}
virtual_ipaddress {
192.168.1.245
}
track_script {
chk_http_port
}
nopreempt
}
(10)啟動redis-server
redis-server /etc/redis/redis-6379.conf
tail /data/redis-6379/redis.log
(11)啟動redis-sentinel
redis-sentinel /etc/redis/redis-sentinel.conf
tail /data/redis-6379/redis-sentinel.log
(12)啟動keepalived
service keepalived start
tail /var/log/messages
如果keepalived啟動後日志如下圖顯示則表示啟動成功。
測試
測試分兩塊内容,一是測試鍵值對的set與get,二是測試自增唯一id的是否可用。
(1)測試Redis鍵值對的set與get
先不模拟故障,先測試一下redis的set、get和複制情況
再測試一下模拟故障出現時redis的set、get和複制情況
停掉Redis1上的redis-server
Redis1上的VIP已經被移除
檢視Redis2上的Redis狀态和VIP狀态
通過上圖的***文字可以看出,VIP已經漂移到新的redis-server master了。
注意:VIP的漂移過程是需要時間的,時間大概需要10*4ms左右,如下圖所示:
此時再次測試一下Redis的複制情況
由上圖可見,Redis複制情況正常,上層應用依然可以使用Redis緩存服務。
(2)測試自增唯一id(autoincrementing unique identifier)
概念參考:http://redis-cookbook.readthedocs.org/en/latest/z.html
自增唯一id最常見的應用就是作為關系型資料庫的主鍵,因為主鍵必須確定每個資料項都有唯一id。
它也可以在不支援自增唯一id的資料庫中(比如MongoDB)用來替代唯一id(uniqueidentifier,通常是一個哈希值)
它也可以為使用者提供更好的URL:比如将/topic/4e491e229f328b0cd900010d修改為/topic/10086。
一個自增唯一id對象最重要的是保證值的唯一性,要做到這一點,自增id的自增incr操作必須是一個原子操作,它應該能在一個原子時間内完成以下兩件事:
增加id值,傳回目前id值,并且它也沒有減法decr和清零reset等操作,因為這些操作破壞了唯一性。
get操作一般隻用于内部檢查,比如觀察值是否溢出,但在一般情況下,自增唯一id對象應該隻有一個incr操作。
先測試一下主機健康狀态下的自增唯一id
發現各個redis-server中的自增唯一id是好用的。再測試一下某台主機故障狀态下的自增唯一id。先根據ip addr找到VIP的主機位置,或者通過redis-cli info檢視role找到redis-server master的主機,然後kill redis-server pid,再進行測試。
由此發現redis-server中的自增唯一id仍然是好用的。
故障切換的步驟
如果一台主機當機,則主機啟動後,先啟動redis-server、再啟動redis-sentinel、最後自動keepalived。
如果多台主機當機,則按照一台主機當機的步驟做同樣處理。
如果一台主機的中的某台服務停止,則直接啟動該服務即可。
參考
Redis Documention http://redis.io/documentation
Redis High Availability http://redis.io/topics/sentinel
Redis Replication http://redis.io/topics/replication
後續