1. 程式人生 > >Redis 哨兵模式與Redis集群

Redis 哨兵模式與Redis集群

redis

第1章 Redis哨兵模式:

技術分享圖片

1.1 sentinel的功能:

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

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

3. 自動故障遷移

1.2 服務器連接:

1.2.1 sentinel通過用戶配置的配置文件來發現主服務器

技術分享圖片

sentinel會與被監視的主服務器創建兩個網絡連接:

1. 命令連接用於向主服務器發送命令

2. 訂閱連接用於訂閱指定的頻道,從而發現

1.1.1 發現並連接從服務器:

技術分享圖片

1.1.1 發現其他sentinel:

sentinel會通過命令連接想被監視的主從服務器發送hello信息,該消息包含sentinelIP,端口號,ID等內容,以此來向其他sentinel 宣告自己的存在,於此同時sentinel會通過訂閱連接接受其他sentinelhello信息,以此來發現監視同一個主服務器的其他sentinel

技術分享圖片

1.1.1 多個sentinel之間的連接:

sentinel之間只會互相創建命令連接,用於進行通信,因為已經有主從服務器作為發送和接受hello信息的中介,所以sentinel之間不會創建訂閱連接

技術分享圖片

1.1 failover:

一次故障轉移的步驟:

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

2. 基於

Raft leader election 協議 進行投票選舉

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

4. 選出一個從服務器,並將它升級為主服務器。

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

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

7. 向已下線主服務器的從服務器發送 SLAVEOF 命令,讓它們去復制新的主服務器。

8. 當所有從服務器都已經開始復制新的主服務器時, leader Sentinel

終止這次故障遷移操作。

1.2 部署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

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

sentinel leader-epoch mymaster 1

sentinel config-epoch mymaster 1

1.2.1 確認一主兩從環境良好,然後宕掉6380節點:

127.0.0.1:6380> shutdown

1.2.2 等待進行驗證:

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

# 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_size:1048576

repl_backlog_first_byte_offset:0

repl_backlog_histlen:0

第2章 Redis cluster

2.1 redis集群

? redis集群是一個可以在多個redis節點之間進行數據共享的設施

? redis集群不支持哪些需要同時處理多個鍵的redis命令,因為執行這些命令需要在多個redis節點之間移動數據,並且在高負載的情況下,這些命令將降低redis集群的性能,並導致不可預測的行為

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

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

2.2 redis集群數據共享

redis集群使用數據分片,而非一致性hash來實現,一個redis集群包含16384個哈希槽,數據庫中的每個鍵都屬於這16384哈希槽其中一個,集群使用公式來計算鍵值屬於哪個槽

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

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

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

技術分享圖片

1.1 集群運行機制

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

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

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

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

技術分享圖片

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

在之前列舉的節點 A B C 的例子中, 如果節點 B 下線了, 那麽集群將無法正常運行, 因為集群找不到節點來處理 5501 號至 11000 號的哈希

槽。

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

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

1.1 集群的故障轉移

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

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

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

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

1.2 在集群中執行命令的兩種情況:

示例1-1 命令發送到正確的節點 : 就像單機redis服務器一樣

技術分享圖片

? 槽位說明:

7000: 0~5000

7001:槽 5001~10000

7002:槽 10001~16383

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

接受到命令的節點並非處理鍵所在槽節點,那麽節點將向客戶端返回一個轉向錯誤,告知客戶端應該到哪個節點上去執行命令,客戶端根據錯誤提示信息重新執行命令

技術分享圖片

? date位於2022,該槽由7000負責,但錯誤發送到了7001,7001向客戶端返回轉向錯誤

技術分享圖片

? 客戶端根據錯誤提示,轉向到7000,並重新發送命令

1.1 redis cluster部署:

? 安裝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)

M: 41679c9a4392f205496746f51fe2d167ce307c86 127.0.0.1:7000

slots:0-5460 (5461 slots) master

1 additional replica(s)

S: 1b87225b8c1c8a9ebfbb9ac37a8c5a963d569513 127.0.0.1:7003

slots: (0 slots) slave

replicates 41679c9a4392f205496746f51fe2d167ce307c86

S: 4a3c744e24e980f84836d6cd708dcadf5e505158 127.0.0.1:7005

slots: (0 slots) slave

replicates e61c8c741d9a1e69ca2d9a6f36e46177915393c0

M: e61c8c741d9a1e69ca2d9a6f36e46177915393c0 127.0.0.1:7002

slots:10923-16383 (5461 slots) master

1 additional replica(s)

M: b22bd736f693bf1573c0e3aff0403516871865ce 127.0.0.1:7001

slots:5461-10922 (5462 slots) master

1 additional replica(s)

S: 3b26d115ce27c4e72b60a5fd4985658dacfe44fb 127.0.0.1:7004

slots: (0 slots) slave

replicates b22bd736f693bf1573c0e3aff0403516871865ce

[OK] All nodes agree about slots configuration.

>>> Check for open slots...

>>> Check slots coverage...

[OK] All 16384 slots covered.

[root@gitlab data]#

1.2 集群管理:

? 寫入數據:

[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

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

M: 41679c9a4392f205496746f51fe2d167ce307c86 127.0.0.1:7000

slots:0-5460 (5461 slots) master

1 additional replica(s)

S: 1b87225b8c1c8a9ebfbb9ac37a8c5a963d569513 127.0.0.1:7003

slots: (0 slots) slave

replicates 41679c9a4392f205496746f51fe2d167ce307c86

S: 4a3c744e24e980f84836d6cd708dcadf5e505158 127.0.0.1:7005

slots: (0 slots) slave

replicates e61c8c741d9a1e69ca2d9a6f36e46177915393c0

M: e61c8c741d9a1e69ca2d9a6f36e46177915393c0 127.0.0.1:7002

slots:10923-16383 (5461 slots) master

1 additional replica(s)

M: b22bd736f693bf1573c0e3aff0403516871865ce 127.0.0.1:7001

slots:5461-10922 (5462 slots) master

1 additional replica(s)

S: 3b26d115ce27c4e72b60a5fd4985658dacfe44fb 127.0.0.1:7004

slots: (0 slots) slave

replicates b22bd736f693bf1573c0e3aff0403516871865ce

[OK] All nodes agree about slots configuration.

>>> Check for open slots...

>>> Check slots coverage...

[OK] All 16384 slots covered.

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

slots:0-5460 (5461 slots) master

1 additional replica(s)

Destination node:

M: e61c8c741d9a1e69ca2d9a6f36e46177915393c0 127.0.0.1:7002

slots:10923-16383 (5461 slots) master

1 additional replica(s)

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@gitlab data]# ps -ef |grep redis

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:

2.1 PHP連接redis

? 連接測試代碼

[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();

?>

? 字符串操作

<?php

//連接本地的 Redis 服務

$redis = new Redis();

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

echo "Connection to server sucessfully";

//設置 redis 字符串數據

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

// 獲取存儲的數據並輸出

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

>get("tutorial-name");

?>

2.2 Python連接redis

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'

2.3 redis cluster的連接與操作

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")

True

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

bar

2.4 sentinel集群連接並操作:

>>> 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')

'bar'


Redis 哨兵模式與Redis集群