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模式下無論客戶端請求到達集群的任何節點都能夠被提供服務,但是要求客戶端是智能客戶端,因為客戶端到達節點存取數據進行取模得到數據可存放和讀取的服務器位置,由客戶端自行發起第二次訪問
關於集群實現其他方案:Codis / cerberus.
地址 | 服務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"
Redis的四種工作模式實現