Redis的持久化以及三種工作模式實現HA

Redis:

Redis是一款優秀的結構數據存儲系統,由於出色的併發性能廣爲關注,可用作:數據庫、緩存、消息隊列;同類型的還有memcached,但是由於memcache支持的結構類型較少,並且不能夠將數據持久化,慢慢的被redis所取代。

Redis支持的數據結構:字符串、列表(數組)、hashes(關聯數組)、集合、有序集合、bitmaps、hyperloglogs、空間索引;本篇博客簡單介紹redis對於實現高可用和持久化的四種redis工作模式,進入正題:

[TOC]

進入正題簡單介紹

單機模式
單機模式下,對於數據的落地,根據業務數據的重要程度選擇是RDB還是AOF備份;

RDB:snapshotting, 二進制格式;按事先定製的策略,週期性地將數據從內存同步至磁盤;數據文件默認爲dump.rdb;
客戶端顯式使用SAVE或BGSAVE命令來手動啓動快照保存機制;
SAVE:同步,即在主線程中保存快照,此時會阻塞所有客戶端請求;
BGSAVE:異步;backgroud

AOF:Append Only File, fsync 記錄每次寫操作至指定的文件尾部實現的持久化;當redis重啓時,可通過重新執行文件中的命令在內存中重建出數據庫;
BGREWRITEAOF:AOF文件重寫;
不會讀取正在使用AOF文件,而是通過將內存中的數據以命令的方式保存至臨時文件中,完成之後替換原來的AOF文件;
注意:持久機制本身不能取代備份;應該制訂備份策略,對redis庫定期備份;

RDB與AOF同時啓用: 
    (1) BGSAVE和BGREWRITEAOF不會同時進行;
    (2) Redis服務器啓動時用持久化的數據文件恢復數據,會優先使用AOF;

RDB相關的配置:
*save <seconds> <changes>

    save 900 1
    save 300 10
    save 60 10000
    save 5  200000

表示:三個策略滿足其中任意一個均會觸發SNAPSHOTTING操作;900s內至少有一個key有變化,300s內至少有10個key有變化,60s內至少有1W個key發生變化;

    stop-writes-on-bgsave-error yes     ## dump操作出現錯誤時,是否禁止新的寫入操作請求;    
    rdbcompression yes              ##啓用壓縮
    rdbchecksum yes                 ##檢測完整性

    dbfilename dump.rdb:指定rdb文件名
    *dir /var/lib/redis:rdb文件的存儲路徑

AOF相關的配置
*appendonly no
appendfilename "appendonly.aof"

    *appendfsync 
        Redis supports three different modes:
            no:redis不執行主動同步操作,而是OS進行決定何時同步;
            everysec:每秒一次;  ##推薦
            always:每語句一次;

        no-appendfsync-on-rewrite no
            是否在後臺執行aof重寫期間不調用fsync,默認爲no,表示調用;

        auto-aof-rewrite-percentage 100
        auto-aof-rewrite-min-size 64mb  
            上述兩個條件同時滿足時,方會觸發重寫AOF;與上次aof文件大小相比,其增長量超過100%,且大小不少於64MB; 

        aof-load-truncated yes

主從複製模式
實現原理:主從模式實現比較簡單,類似於MySQL的主從,選定一個主機當做master,在從節點配置指向master的地址端口及口令信息即可實現;master開啓主從模式後,會對當前自身進行快照RDB,然後基於網絡發送給slave節點,在此之前用戶可以選擇直接基於網絡發送給slave快照還是先創建快照文件於磁盤中,而後將其發送給從節點;與此同時新接收的數據將放在repl-backlog-size中作爲緩衝區,當slave接收RDB成功後,master將repl-backlog-size內容在陸續發送給slave

開始之前所有Redis首先進行基本安全加固:

vim /etc/redis.conf
...
bind 192.168.2.128
requirepass "ifan"
...
挑選一臺Redis作爲Master節點:

Slave:
127.0.0.1:6379> SLAVEOF 192.168.2.128 6379
127.0.0.1:6379> CONFIG SET masterauth "ifan"
127.0.0.1:6379> CONFIG REWRITE
Master查看主從複製狀態:

192.168.2.128:6379> INFO Replication

Replication

role:master
connected_slaves:1
slave0:ip=192.168.2.129,port=6379,state=online,offset=981,lag=1
master_repl_offset:981
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:980
192.168.2.128:6379>
Redis的主從複製支持多slave,同樣支持鏈式複製,只需要各級slave指定自己的master即可

Master以非阻塞方式同步數據至slave主機;
主從複製調優參數:repl(主從複製)

slave-serve-stale-data yes

表明slave會繼續應答來自client的請求,但這些數據可能已經過期(因爲連接中斷導致無法從master同步)。若配置爲no,則slave除正常應答"INFO"和"SLAVEOF"命令外,其餘來自客戶端的請求命令均會得到"SYNC with master in progress"的應答,直到該slave與master的連接重建成功或該slave被提升爲master。

slave-read-only yes

從狀態下,不可寫

repl-diskless-sync no

no,則是Disk-backend主節點新創建快照文件於磁盤中,而後將其發送給從節點;

# yes,主節點新創建快照後直接通過網絡套接字文件發送給從節點;爲了實現並行複製,通常需要在複製啓動前延遲一個時間段;

repl-diskless-sync-delay 5

配置當收到第一個請求時,等待多個5個slave一起來請求之間的間隔時間。

repl-ping-slave-period 10

配置master與slave的心跳間隔10秒

repl-timeout 60

主從複製的超時時間

repl-disable-tcp-nodelay no

禁止Nagle算法,允許小包的發送,不等待一有變化就發送。對於延時敏感型,同時數據傳輸量比較小的應用,

repl-backlog-size 1mb

設置備份的工作儲備大小。工作儲備是一個緩衝區,當從站斷開一段時間的情況時,它替從站接收存儲數據,因此當從站重連時,通常不需要完全備份,只需要一個部分同步就可以,即把從站斷開時錯過的一部分數據接收。工作儲備越大,從站可以斷開並稍後執行部分同步的斷開時間就越長。

slave-priority 100

複製集羣中,主節點故障時,sentinel應用場景中的主節點選舉時使用的優先級;數字越小優先級越高,但0表示不參與選舉;

min-slaves-to-write 3

主節點僅允許其能夠通信的從節點數量大於等於此處的值時接受寫操作;

min-slaves-max-lag 10

從節點延遲時長超出此處指定的時長時,主節點會拒絕寫入操作;

哨兵模式(3臺)
原理實現:哨兵模式的實現是基於主從模式的一種補充,在主從模式下slave是不允許寫入的,那麼當master宕機後,整個業務將不能夠繼續寫入,而哨兵則是爲此做的了一個補充,當master出現故障後,每個redis服務都會工作一個sentinel(哨兵)時刻監控redis6379端口,一旦發生故障,基於quorum機制投票決定進而實現故障轉移,重新選舉master提供寫操作,sentinel擁有調用外部腳本實現報警的能力

注意:配置哨兵之前清先配置主從複製

[root@node1 ~]# vim /etc/redis-sentinel.conf
...
port 26379
bind 0.0.0.0 ##綁定端口,否則會被進入安全模式,僅允許127.0.0.1鏈接
sentinel monitor mymaster 192.168.2.129 6379 2 ##集羣名,master地址,端口,投票2票及以上才進行故障轉移
sentinel down-after-milliseconds mymaster 10000 ##監控到指定的集羣的主節點異常狀態持續多久方纔將標記爲“故障”;
sentinel failover-timeout mymaster 120000 ##sentinel必須在此指定的時長內完成故障轉移操作,否則,將視爲故障轉移操作失敗;
sentinel auth-pass mymaster ifan ##哨兵集羣,配置鏈接6379端口密碼

sentinel notification-script <master-name> <script-path> ##調用腳本報警功能

...
哨兵模式管理指令

redis-cli -h SENTINEL_HOST -p SENTINEL_PORT
redis-cli>
SENTINEL masters ##查看當前master信息
SENTINEL slaves <MASTER_NAME> ##查看master下的從節點信息
SENTINEL failover <MASTER_NAME> ##手動進行故障轉移,需要指定集羣名
SENTINEL get-master-addr-by-name <MASTER_NAME> ##查看當前master的ip信息
集羣模式(6臺)
實現原理:redis cluster是一種無中心的分佈式服務,最小由3臺即可實現集羣(但不能保證高可用的能力)3臺爲master節點,3臺爲slave節點(master正常時不提供服務),當客戶端的存請求到達redis集羣時會將數據進行hash取模,根據redis集羣建立劃分的slot進行存儲,取的時候根據取模的slot進行取值,cluster模式下無論客戶端請求到達集羣的任何節點都能夠被提供服務,但是在非集羣(-c選項)要求客戶端是智能客戶端,因爲客戶端到達節點存取數據進行取模得到數據可存放和讀取的服務器位置,由客戶端自行發起第二次訪問

關於集羣實現其他方案:Codis / cerberus.
這裏我們實現以下Redis的Cluster集羣模式,很多博客在最後集羣實現時採用了ruby的集羣工具實現槽位自動劃分,考慮到工具還要大費周折的佈置環境,這裏我們全部手動來實現

地址 服務1 服務2 系統版本
192.168.2.128(node1) 6379(master) 6380(slave) CentOS7.2
192.168.2.129(node2) 6379(master) 6380(slave) CentOS7.2
192.168.2.130(node3) 6379(master) 6380(slave) CentOS7.2
三臺機器分別安裝redis:

[root@node1 ~]# ntpdate -u ntp.aliyun.com
[root@node1 ~]# yum install redis -y
[root@node1 ~]# cp /etc/redis.conf /etc/redis_6379.conf
[root@node1 ~]# cp /etc/redis.conf /etc/redis_6380.conf
[root@node1 ~]# egrep -v "^#|^$" /etc/redis_6379.conf
...
bind 0.0.0.0
port 6379
pidfile /var/run/redis_6379.pid
logfile /var/log/redis/redis_6379.log
cluster-enabled yes
cluster-config-file nodes-6379.conf
cluster-node-timeout 15000
cluster-slave-validity-factor 10
cluster-migration-barrier 1
...

注:爲了完成多實例,請將/etc/redis_6380.conf和6379配置保持一致,端口除外
優化內存相關選項:

echo "vm.overcommit_memory=1" >> /etc/sysctl.conf ; sysctl -p
echo "511" > /proc/sys/net/core/somaxconn
echo never > /sys/kernel/mm/transparent_hugepage/enabled
啓動服務:

[root@node1 ~]# redis-server /etc/redis_6379.conf &
[root@node1 ~]# redis-server /etc/redis_6380.conf &
檢查日誌是否有異常信息:

[root@node1 ~]# tail -f /var/log/redis/redis_6379.log
[root@node1 ~]# tail -f /var/log/redis/redis_6380.log
添加機器到集羣:

redis-cli -h 192.168.2.128 -p 6379 -c
192.168.2.128:6379> CLUSTER MEET 192.168.2.129 6379
192.168.2.128:6379> CLUSTER MEET 192.168.2.130 6379
查看集羣狀態:

192.168.2.128:6379> CLUSTER INFO ##查看集羣狀態
cluster_state:fail ##集羣狀態,之所以會失敗是因爲我們還沒有對集羣進行槽位的劃分
cluster_slots_assigned:0 #分配的slot數
cluster_slots_ok:0 #正確的slot數
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:3 #當前的節點數
cluster_size:0
cluster_current_epoch:2
cluster_my_epoch:0
cluster_stats_messages_sent:171
cluster_stats_messages_received:171

192.168.2.128:6379> CLUSTER NODES
2a9f356a7362c535056f4311057d78269c7aa6d4 192.168.2.129:6379 master - 0 1551782791382 1 connected 5462-10922
af69cb43def99109d4e9a2e15b0fa41b1998bb02 192.168.2.130:6379 master - 0 1551782790373 0 connected 10923-16383
32db6806120a01ce90fa8641bd15abd3c6a55408 192.168.2.128:6379 myself,master - 0 0 2 connected 0-5461
劃分槽位(根據master的數量進行劃分,一共16384個槽位):

[root@node1 ~]# for k in {0..5461};do redis-cli -h 192.168.2.128 -p 6379 CLUSTER ADDSLOTS $k;done
[root@node1 ~]# for k in {5462..10922};do redis-cli -h 192.168.2.129 -p 6379 CLUSTER ADDSLOTS $k;done
[root@node1 ~]# for k in {10923..16383};do redis-cli -h 192.168.2.130 -p 6379 CLUSTER ADDSLOTS $k;done
再次查看集羣狀態:

192.168.2.128:6379> CLUSTER INFO ##查看集羣狀態
cluster_state:ok
cluster_slots_assigned:16384 ##劃分的總槽位
cluster_slots_ok:16384 ##成功劃分的槽位
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:3
cluster_size:3
cluster_current_epoch:2
cluster_my_epoch:0
cluster_stats_messages_sent:3495
cluster_stats_messages_received:3495
添加從節點

redis-cli -h 192.168.2.128 -p 6379 -c
192.168.2.128:6379> CLUSTER MEET 192.168.2.128 6380
192.168.2.128:6379> CLUSTER MEET 192.168.2.129 6380
192.168.2.128:6379> CLUSTER MEET 192.168.2.130 6380
劃分槽位(根據master的數量進行劃分,一共16384個槽位):

[root@node1 ~]# for k in {0..5461};do redis-cli -h 192.168.2.128 -p 6380 CLUSTER ADDSLOTS $k;done
[root@node1 ~]# for k in {5462..10922};do redis-cli -h 192.168.2.129 -p 6380 CLUSTER ADDSLOTS $k;done
[root@node1 ~]# for k in {10923..16383};do redis-cli -h 192.168.2.130 -p 6380 CLUSTER ADDSLOTS $k;done
查看集羣節點數:

192.168.2.128:6379> CLUSTER INFO ##查看集羣狀態
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6 ##所有節點數6個
cluster_size:3 ##主節點數3個
cluster_current_epoch:5
cluster_my_epoch:0
cluster_stats_messages_sent:4388
cluster_stats_messages_received:4388
查看狀態:

192.168.2.128:6379> CLUSTER NODES
2a9f356a7362c535056f4311057d78269c7aa6d4 192.168.2.129:6379 master - 0 1551782791382 1 connected 5462-10922
af69cb43def99109d4e9a2e15b0fa41b1998bb02 192.168.2.130:6379 master - 0 1551782790373 0 connected 10923-16383
30744bd131bb249850d0f1ff5a91fae751b86fe3 192.168.2.130:6380 master - 0 1551782788357 5 connected
65d1453da3e1e3dc2a1e4bdefac375c4890e8555 192.168.2.128:6380 master - 0 1551782789362 3 connected
85461b05a18125b2301a615d217af4dacf35879c 192.168.2.129:6380 master - 0 1551782792388 4 connected
32db6806120a01ce90fa8641bd15abd3c6a55408 192.168.2.128:6379 myself,master - 0 0 2 connected 0-5461

注意:此時的六個節點全部是以master的身份,我們需要將後需添加的3個變更爲從節點,並基於master進行復制
修改從節點狀態:

128(6380) -- 複製 --> 129 (6379)
129(6380) -- 複製 --> 130 (6379)
130(6380) -- 複製 --> 128 (6379)

redis-cli -h 192.168.2.128 -p 6380 -c CLUSTER REPLICATE 2a9f356a7362c535056f4311057d78269c7aa6d4

redis-cli -h 192.168.2.129 -p 6380 -c CLUSTER REPLICATE af69cb43def99109d4e9a2e15b0fa41b1998bb02

redis-cli -h 192.168.2.130 -p 6380 -c CLUSTER REPLICATE 32db6806120a01ce90fa8641bd15abd3c6a55408

再次查看節點狀態(此時的主從關係已經形成):

192.168.2.128:6379> CLUSTER NODES
2a9f356a7362c535056f4311057d78269c7aa6d4 192.168.2.129:6379 master - 0 1551786306436 1 connected 5462-10922
af69cb43def99109d4e9a2e15b0fa41b1998bb02 192.168.2.130:6379 master - 0 1551786305391 0 connected 10923-16383
30744bd131bb249850d0f1ff5a91fae751b86fe3 192.168.2.130:6380 slave 32db6806120a01ce90fa8641bd15abd3c6a55408 0 1551786308514 5 connected
65d1453da3e1e3dc2a1e4bdefac375c4890e8555 192.168.2.128:6380 slave 2a9f356a7362c535056f4311057d78269c7aa6d4 0 1551786307471 3 connected
85461b05a18125b2301a615d217af4dacf35879c 192.168.2.129:6380 slave af69cb43def99109d4e9a2e15b0fa41b1998bb02 0 1551786303303 4 connected
32db6806120a01ce90fa8641bd15abd3c6a55408 192.168.2.128:6379 myself,master - 0 0 2 connected 0-5461
到此Redis3.2版本的集羣已經搭建完成,redis-cluster需要智能客戶端,在寫入時需要智能判斷

for k in {1..10};do redis-cli -h 192.168.2.128 -p 6379 SET key$k val$k;done ##我們先不以集羣模式來寸數據,由於redis的分片機制,會提醒你本次數據要交給xx主機存

(error) MOVED 9189 192.168.2.129:6379
OK
OK
(error) MOVED 13120 192.168.2.130:6379
(error) MOVED 9057 192.168.2.129:6379
OK
OK
(error) MOVED 13004 192.168.2.130:6379
(error) MOVED 8941 192.168.2.129:6379
(error) MOVED 5850 192.168.2.129:6379

for k in {11..20};do redis-cli -c -h 192.168.2.128 -p 6379 SET key$k val$k;done ##我們以集羣模式去存,則全部存成功

OK
OK
OK
OK
OK
OK
OK
OK
OK
OK

for k in {11..20};do redis-cli -h 192.168.2.128 -p 6379 GET key$k;done ##不以集羣模式讀取

"val11"
(error) MOVED 13976 192.168.2.130:6379
(error) MOVED 9913 192.168.2.129:6379
(error) MOVED 5726 192.168.2.129:6379
"val15"
(error) MOVED 13852 192.168.2.130:6379
(error) MOVED 9789 192.168.2.129:6379
(error) MOVED 6098 192.168.2.129:6379
"val19"
"val20"

redis-cli -h 192.168.2.130 -p 6379 GET key12 ##根據上面提示的數據存儲位置去讀取,則讀取成功

"val12"
模擬故障:

ps -ef|grep redis

root 2805 1 0 18:31 ? 00:00:09 redis-server 0.0.0.0:6379 [cluster]
root 2809 1 0 18:31 ? 00:00:06 redis-server 0.0.0.0:6380 [cluster]

kill -9 2805

殺掉進程後先是先是集羣狀態fail稍後slave上線對外提供服務

192.168.2.128:6380> cluster nodes
2a9f356a7362c535056f4311057d78269c7aa6d4 192.168.2.129:6379 master - 0 1551787432472 1 connected 5462-10922
32db6806120a01ce90fa8641bd15abd3c6a55408 192.168.2.128:6379 master,fail - 1551787261748 1551787257954 2 disconnected
85461b05a18125b2301a615d217af4dacf35879c 192.168.2.129:6380 slave af69cb43def99109d4e9a2e15b0fa41b1998bb02 0 1551787433495 4 connected
30744bd131bb249850d0f1ff5a91fae751b86fe3 192.168.2.130:6380 master - 0 1551787431446 7 connected 0-5461
65d1453da3e1e3dc2a1e4bdefac375c4890e8555 192.168.2.128:6380 myself,slave 2a9f356a7362c535056f4311057d78269c7aa6d4 0 0 3 connected
af69cb43def99109d4e9a2e15b0fa41b1998bb02 192.168.2.130:6379 master - 0 1551787427638 0 connected 10923-16383
注:當集羣中master出現宕機時,slave纔會對外提供服務,否則slave接收請求時會提示該數據能夠提供服務的位置,當6臺機器的master的全部宕機,slave上線,除非宕機超過一半,集羣不再成立,機器下線後,重新上線以slave的身份工作

宕機前:

redis-cli -h 192.168.2.128 -p 6380 GET key13 ##宕機前129的備節128(6380)點不提供服務,提示該數據能提供服務的服務器位置

(error) MOVED 9913 192.168.2.129:6379

redis-cli -h 192.168.2.129 -p 6379 GET key13 ##根據提示,129的6379能夠提供服務

"val13"

模擬宕機後:

redis-cli -h 192.168.2.129 -p 6379 GET key13 ##129的6379不能夠提供服務

Could not connect to Redis at 192.168.2.129:6379: Connection refused

redis-cli -h 192.168.2.128 -p 6380 GET key13 ##129的備節點128(6380)提供服務

"val13"

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章