天天看點

Redis 哨兵模式與Redis叢集

第1章 Redis哨兵模式:

Redis 哨兵模式與Redis叢集

1.      監控,sentinel會不斷的檢查你的主伺服器和從伺服器是否運作正常

2.      提醒.當被監控的某個redis伺服器出現問題時,sentinel可以通過API向管理者或者其他應用程式發送通知

3.      自動故障遷移

Redis 哨兵模式與Redis叢集

sentinel會與被監視的主伺服器建立兩個網絡連接配接:

1.      指令連接配接用于向主伺服器發送指令

2.      訂閱連接配接用于訂閱指定的頻道,進而發現

Redis 哨兵模式與Redis叢集

sentinel會通過指令連接配接想被監視的主從伺服器發送hello資訊,該消息包含sentinel的IP,端口号,ID等内容,以此來向其他sentinel 宣告自己的存在,于此同時sentinel會通過訂閱連接配接接受其他sentinel的hello資訊,以此來發現監視同一個主伺服器的其他sentinel

Redis 哨兵模式與Redis叢集

sentinel之間隻會互相建立指令連接配接,用于進行通信,因為已經有主從伺服器作為發送和接受hello資訊的中介,是以sentinel之間不會建立訂閱連接配接

Redis 哨兵模式與Redis叢集

一次故障轉移的步驟:

1.   發現主伺服器已經進入客觀下線狀态。

2.   基于Raft leader election 協定 , 進行投票選舉

3.   如果當選失敗,那麼在設定的故障遷移逾時時間的兩倍之後,重新嘗試當選。 如果當選成功, 那麼執行以下步驟。

4.   選出一個從伺服器,并将它更新為主伺服器。

5.   向被選中的從伺服器發送 SLAVEOF NO ONE 指令,讓它轉變為主伺服器。

6.   通過釋出與訂閱功能, 将更新後的配置傳播給所有其他 Sentinel ,其他 Sentinel 對它們自己的配置進行更新。

7.   向已下線主伺服器的從伺服器發送 SLAVEOF 指令,讓它們去複制新的主伺服器。

8.   當所有從伺服器都已經開始複制新的主伺服器時, leader Sentinel 終止這次故障遷移操作。

建立目錄

[root@gitlab data]# mkdir 26380

[root@gitlab data]# cd 26380/

編寫配置檔案

[root@gitlab 26380]# vim sentienl.conf

port 26380

dir "/data/26380"

sentinel monitor mymaster 127.0.0.1 6380 1

sentinel down-after-milliseconds mymaster 60000

啟動sentinel服務

redis-sentinel /data/26380/sentienl.conf &

配置檔案說明:

# 指定監控master

sentinel monitor mymaster 127.0.0.1 6370 2

# {2表示多少個sentinel同意}

# 安全資訊

sentinel auth-pass mymaster root

# 超過15000毫秒後認為主機當機

sentinel down-after-milliseconds mymaster 15000

# 當主從切換多久後認為主從切換失敗

sentinel failover-timeout mymaster 900000

# 這兩個配置後面的數量主從機需要一樣,epoch為master的版本

sentinel leader-epoch mymaster 1

sentinel config-epoch mymaster 1

127.0.0.1:6380> shutdown

127.0.0.1:6381> info replication

# Replication

role:master

connected_slaves:1

slave0:ip=127.0.0.1,port=6382,state=online,offset=1928,lag=1

master_repl_offset:1928

repl_backlog_active:1

repl_backlog_size:1048576

repl_backlog_first_byte_offset:2

repl_backlog_histlen:1927

repl_backlog_histlen:0

127.0.0.1:6382> info replication

role:slave

master_host:127.0.0.1

master_port:6381

master_link_status:up

master_last_io_seconds_ago:2

master_sync_in_progress:0

slave_repl_offset:2474

slave_priority:100

slave_read_only:1

connected_slaves:0

master_repl_offset:0

repl_backlog_active:0

repl_backlog_first_byte_offset:0

第2章 Redis cluster

Ø  redis叢集是一個可以在多個redis節點之間進行資料共享的設施

Ø  redis叢集不支援哪些需要同時處理多個鍵的redis指令,因為執行這些指令需要在多個redis節點之間移動資料,并且在高負載的情況下,這些指令将降低redis叢集的性能,并導緻不可預測的行為

Ø  redis叢集通過分區來提供一定程度的可用性,即使叢集中有一部分節點失效或者無法進行通訊,叢集也可以繼續處理指令請求,将資料自動切分到多個節點的能力

Ø  當叢集中的一部分節點失效或者無法進行通訊時,仍然可以繼續處理指令請求的能力

redis叢集使用資料分片,而非一緻性hash來實作,一個redis叢集包含16384個哈希槽,資料庫中的每個鍵都屬于這16384哈希槽其中一個,叢集使用公式來計算鍵值屬于哪個槽

節點 A 負責處理 0 号至 5500 号哈希槽。

節點 B 負責處理 5501 号至 11000 号哈希槽。

節點 C 負責處理 11001 号至 16384 号哈希槽。

Redis 哨兵模式與Redis叢集

Ø  所有的redis節點彼此互聯ping-pong機制,内部使用二進制協定傳輸速度和帶寬

Ø  節點的fail是通過叢集中超過半數的master節點檢測失效時才失效

Ø  用戶端與redis節點直連,不需要中間proxy層,客戶頓不需要連接配接叢集中所有節點,連接配接叢集中任何一個可用節點即可

把所有的實體節點映射到哈希槽上,cluster負責維護

Redis 哨兵模式與Redis叢集

為了使得叢集在一部分加點下線或者無法與叢集中大多數節點進行通訊的情況下,仍然可以正常運作,redis叢集對節點使用了主從複制功能:叢集中每個節點都有1個至N個複制品,其中一個複制品為主節點,而其餘的N-1個複制品為從節點

在之前列舉的節點 A 、B 、C 的例子中, 如果節點 B 下線了, 那麼叢集将無法正常運作, 因為叢集找不到節點來處理 5501 号至 11000  号的哈希

槽。

假如在建立叢集的時候(或者至少在節點 B 下線之前), 我們為主節點  B添加了從節點 B1 , 那麼當主節點 B 下線的時候, 叢集就會将 B1  設定為新的主節點, 并讓它代替下線的主節點 B , 繼續處理 5501 号至  11000 号的哈希槽, 這樣叢集就不會因為主節點 B  的下線而無法正常運作了。

不過如果節點 B 和 B1 都下線的話, Redis 叢集還是會停止運作。

1.      在叢集中,節點會對其它節點進行下線檢測

2.      當一個主節點下線時,叢集中其它節點負責對下年主節點進行故障轉移

3.      換句話說,叢集的節點內建了下線檢測和故障轉移等類似sentinel的功能

4.      因為sentinel是一個獨立運作的監控程式,而叢集的下線檢測和故障轉移等功能是內建在節點中的,他們的運作模式非常的不同,是以盡管這兩者的功能很相似,但叢集的實作沒有重用sentinel的代碼

示例1-1 指令發送到正确的節點 : 就像單機redis伺服器一樣

Redis 哨兵模式與Redis叢集

Ø  槽位說明:

7000: 槽 0~5000

7001:槽 5001~10000

7002:槽 10001~16383

示例1-1 指令發送到了錯誤的節點:

接受到指令的節點并非處理鍵所在槽節點,那麼節點将向用戶端傳回一個轉向錯誤,告知用戶端應該到哪個節點上去執行指令,用戶端根據錯誤提示資訊重新執行指令

Redis 哨兵模式與Redis叢集

Ø  鍵date位于2022槽,該槽由7000負責,但錯誤發送到了7001上,7001向用戶端傳回轉向錯誤

Redis 哨兵模式與Redis叢集

Ø  用戶端根據錯誤提示,轉向到7000,并重新發送指令

Ø  安裝ruby支援

yum install ruby rubygems -y

gem sources -a http://mirrors.aliyun.com/rubygems/ 

gem sources  --remove http://rubygems.org/

gem sources -l

gem install redis -v 3.3.3

Ø  建立程式目錄:

[root@gitlab data]# mkdir {7000..7005}

Ø  編寫配置檔案:

port 7000

daemonize yes

pidfile /data/7000/redis.pid

loglevel notice

logfile "/data/7000/redis.log"

dbfilename dump.rdb

dir /data/7000

protected-mode no

cluster-enabled yes

cluster-config-file nodes.conf

cluster-node-timeout 5000

appendonly yes

Ø  啟動執行個體:

for i in {0..5};do redis-server /data/700${i}/redis.conf ; done

[root@gitlab data]# ps -ef |grep redis

root      19490      1  0 03:40 ?        00:00:00 redis-server *:7000 [cluster]

root      19492      1  0 03:40 ?        00:00:00 redis-server *:7001 [cluster]

root      19494      1  0 03:40 ?        00:00:00 redis-server *:7002 [cluster]

root      19496      1  0 03:40 ?        00:00:00 redis-server *:7003 [cluster]

root      19498      1  0 03:40 ?        00:00:00 redis-server *:7004 [cluster]

root      19500      1  0 03:40 ?        00:00:00 redis-server *:7005 [cluster]

Ø  加載節點并啟動叢集:

[root@gitlab data]# redis-trib.rb create --replicas 1 127.0.0.1:7000 127.0.0.1:7001 \

> 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005

>>> Creating cluster

>>> Performing hash slots allocation on 6 nodes...

Using 3 masters:

127.0.0.1:7000

127.0.0.1:7001

127.0.0.1:7002

Adding replica 127.0.0.1:7003 to 127.0.0.1:7000

Adding replica 127.0.0.1:7004 to 127.0.0.1:7001

Adding replica 127.0.0.1:7005 to 127.0.0.1:7002

M: 41679c9a4392f205496746f51fe2d167ce307c86 127.0.0.1:7000

   slots:0-5460 (5461 slots) master

M: b22bd736f693bf1573c0e3aff0403516871865ce 127.0.0.1:7001

   slots:5461-10922 (5462 slots) master

M: e61c8c741d9a1e69ca2d9a6f36e46177915393c0 127.0.0.1:7002

   slots:10923-16383 (5461 slots) master

S: 1b87225b8c1c8a9ebfbb9ac37a8c5a963d569513 127.0.0.1:7003

   replicates 41679c9a4392f205496746f51fe2d167ce307c86

S: 3b26d115ce27c4e72b60a5fd4985658dacfe44fb 127.0.0.1:7004

   replicates b22bd736f693bf1573c0e3aff0403516871865ce

S: 4a3c744e24e980f84836d6cd708dcadf5e505158 127.0.0.1:7005

   replicates e61c8c741d9a1e69ca2d9a6f36e46177915393c0

Can I set the above configuration? (type 'yes' to accept): yes

>>> Nodes configuration updated

>>> Assign a different config epoch to each node

>>> Sending CLUSTER MEET messages to join the cluster

Waiting for the cluster to join...

>>> Performing Cluster Check (using node 127.0.0.1:7000)

   1 additional replica(s)

   slots: (0 slots) slave

[OK] All nodes agree about slots configuration.

>>> Check for open slots...

>>> Check slots coverage...

[OK] All 16384 slots covered.

[root@gitlab data]#

Ø  寫入資料:

[root@gitlab data]# redis-cli -c -p 7000

127.0.0.1:7000> set too bar

OK

127.0.0.1:7000> get too

"bar"

Ø  檢視叢集狀态:

[root@gitlab data]# redis-cli -p 7000 cluster nodes | grep master

41679c9a4392f205496746f51fe2d167ce307c86 127.0.0.1:7000 myself,master - 0 0 1 connected 0-5460

e61c8c741d9a1e69ca2d9a6f36e46177915393c0 127.0.0.1:7002 master - 0 1523908330620 3 connected 10923-16383

b22bd736f693bf1573c0e3aff0403516871865ce 127.0.0.1:7001 master - 0 1523908331131 2 connected 5461-10922

Ø  重新分片實戰:

[root@gitlab data]# redis-trib.rb reshard 127.0.0.1:7000

How many slots do you want to move (from 1 to 16384)?    3               分三個槽位出去

What is the receiving node ID? e61c8c741d9a1e69ca2d9a6f36e46177915393c0  接受節點的ID

Please enter all the source node IDs.

  Type 'all' to use all the nodes as source nodes for the hash slots.

  Type 'done' once you entered all the source nodes IDs.

Source node #1:41679c9a4392f205496746f51fe2d167ce307c86                  給出節點的ID

Source node #2:done                                                     沒有了就添寫done

Ready to move 3 slots.

  Source nodes:

    M: 41679c9a4392f205496746f51fe2d167ce307c86 127.0.0.1:7000

  Destination node:

    M: e61c8c741d9a1e69ca2d9a6f36e46177915393c0 127.0.0.1:7002

  Resharding plan:

    Moving slot 0 from 41679c9a4392f205496746f51fe2d167ce307c86

    Moving slot 1 from 41679c9a4392f205496746f51fe2d167ce307c86

    Moving slot 2 from 41679c9a4392f205496746f51fe2d167ce307c86

Do you want to proceed with the proposed reshard plan (yes/no)? yes

Moving slot 0 from 127.0.0.1:7000 to 127.0.0.1:7002:

Moving slot 1 from 127.0.0.1:7000 to 127.0.0.1:7002:

Moving slot 2 from 127.0.0.1:7000 to 127.0.0.1:7002:

Ø  删除一個節點:

如果節點上還有slot的話,是無法進行删除的

[root@gitlab data]# redis-trib.rb del-node 127.0.0.1:7000 '41679c9a4392f205496746f51fe2d167ce307c86'

>>> Removing node 41679c9a4392f205496746f51fe2d167ce307c86 from cluster 127.0.0.1:7000

>>> Sending CLUSTER FORGET messages to the cluster...

>>> SHUTDOWN the node.

節點在删除後,服務自動關閉了,要添加回來的話需要重新啟動

root      19492      1  0 03:40 ?        00:00:12 redis-server *:7001 [cluster]

root      19494      1  0 03:40 ?        00:00:20 redis-server *:7002 [cluster]

root      19496      1  0 03:40 ?        00:00:04 redis-server *:7003 [cluster]

root      19498      1  0 03:40 ?        00:00:04 redis-server *:7004 [cluster]

root      19500      1  0 03:40 ?        00:00:04 redis-server *:7005 [cluster]

root      19608  19171  0 04:33 pts/3    00:00:00 grep --color=auto redis

[root@gitlab data]# redis-server /data/7000/redis.conf

Ø  添加一個節點:

redis-trib.rb add-node 127.0.0.1:7000 127.0.0.1:7002

注意:添加節點時,要保證節點是全新的

Ø  添加一個從節點:

redis-trib.rb add-node --slave --master-id $[nodeid] 127.0.0.1:7008 127.0.0.1:7000

第2章 Redis API:

Ø  連接配接測試代碼

[root@clsn ~]# cat /application/nginx/html/check.php

<?php

//連接配接本地的 Redis 服務

$redis = new Redis();

$redis->connect('127.0.0.1', 6379);

echo "Connection to server sucessfully";

//檢視服務是否運作

echo "Server is running: " . $redis->ping();

?>

Ø  字元串操作

//設定 redis 字元串資料

$redis->set("tutorial-name", "Redis tutorial");

// 擷取存儲的資料并輸出

echo "Stored string in redis:: " . $redis-

>get("tutorial-name");

unzip redis-py-master.zip

python setup.py install

>>> r = redis.StrictRedis(host='localhost', port=6379, db=0, password='')

>>> r.set('foo', 'bar')

True

>>> r.get('foo')

'bar'

python連接配接的話要2.7以上版本才支援:

>>> from rediscluster import StrictRedisCluster

>>> startup_nodes = [{"host": "127.0.0.1", "port": "7000"}]

>>> rc = StrictRedisCluster(startup_nodes=startup_nodes, decode_responses=True)

>>> rc.set("foo", "bar")

>>> print(rc.get("foo"))

bar

>>> from redis.sentinel import Sentinel 

>>> sentinel = Sentinel([('localhost', 26380)], socket_timeout=0.1) 

>>> sentinel.discover_master('mymaster') 

>>> sentinel.discover_slaves('mymaster') 

>>> master = sentinel.master_for('mymaster', socket_timeout=0.1) 

>>> slave = sentinel.slave_for('mymaster', socket_timeout=0.1) 

>>> master.set('oldboy', '123') 

>>> slave.get('oldboy') 

繼續閱讀