Redis:node
Redis是一款優秀的結構數據存儲系統,因爲出色的併發性能廣爲關注,可用做:數據庫、緩存、消息隊列;同類型的還有memcached,可是因爲memcache支持的結構類型較少,而且不可以將數據持久化,慢慢的被redis所取代。redis
Redis支持的數據結構:字符串、列表(數組)、hashes(關聯數組)、集合、有序集合、bitmaps、hyperloglogs、空間索引;本篇博客簡單介紹redis對於實現高可用和持久化的四種redis工做模式,進入正題:算法
[TOC]數據庫
進入正題簡單介紹vim
單機模式下,對於數據的落地,根據業務數據的重要程度選擇是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節點:ruby
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查看主從複製狀態:bash
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便可
主從複製調優參數: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 # 從節點延遲時長超出此處指定的時長時,主節點會拒絕寫入操做;
原理實現:哨兵模式的實現是基於主從模式的一種補充,在主從模式下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信息
實現原理: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"