sentinel:
上一篇提到了主從切換,sentinel的作用是將這個過程自動化,實現高可用。
它的主要功能有以下幾點:
- 不時地監控redis是否按照預期良好地運行;
- 如果發現某個redis節點運行出現狀況,能夠通知另外一個進程(例如它的客戶端);
- 能夠進行自動切換。當一個master節點不可用時,能夠選舉出master的多個slave(如果有超過一個slave的話)中的一個來作為新的master,其它的slave節點會將它所追隨的master的地址改為被提升為master的slave的新地址。
Sentinel本身也支持集群,只使用單個sentinel進程來監控redis集群是不可靠的,當sentinel進程宕掉后,sentinel本身也有單點問題。所以有必要將sentinel集群,這樣有幾個好處:
- 如果只有一個sentinel進程,如果這個進程運行出錯,或者是網絡堵塞,那么將無法實現redis集群的主備切換(單點問題)。
- 如果有多個sentinel,redis的客戶端可以隨意地連接任意一個sentinel來獲得關于redis集群中的信息。
- sentinel集群自身也需要多數機制,也就是2個sentinel進程時,掛掉一個另一個就不可用了。
在默認情況下,Sentinel 使用TCP端口26379(普通 Redis 服務器使用的是 6379)。Sentinel 接受 Redis 協議格式的命令請求, 所以可以使用 redis-cli 或者任何其他 Redis 客戶端來與 Sentinel 進行通訊。
# redis-cli -p 26379
127.0.0.1:26379> ping
PONG
啟動sentinel:
配置文件:
port 26329
sentinel monitor myredis 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 60000
sentinel failover-timeout mymaster 180000
sentinel parallel-syncs mymaster 1
sentinel notification-script <master-name> <script-path>
- 第一行port指定sentinel端口。
- 第二行monitor配置指示 Sentinel 去監視一個名為 myredis 的主redis實例。Sentinel的配置文件中不必說明此主實例屬下的從實例,因為Sentinel實例可以通過詢問主實例來獲得所有從實例的信息。
行末2表示將這個主實例判斷為失效至少需要2個 Sentinel 進程的同意,只要同意 Sentinel 的數量不達標,自動failover就不會執行。 - down-after-milliseconds 選項指定了 Sentinel 認為Redis實例已經失效所需的毫秒數。當實例超過該時間沒有返回PING,或者直接返回錯誤, 那么 Sentinel 將這個實例標記為主觀下線(subjectively down,簡稱 SDOWN )。
- failover-timeout表示如果在該時間(ms)內未能完成failover操作,則認為該failover失敗。
- parallel-syncs如果全部從實例一起對新的主實例進行同步, 那么就可能會造成所有從Redis實例在短時間內全部不可用的情況出現。可以通過將這個值設為 1 來保證每次只有一個slave處于不能處理命令請求的狀態。
- notification-script指定sentinel檢測到該監控的redis實例指向的實例異常時,調用的報警腳本。該配置項可選。
啟動Sentinel:
Sentinel只是一個運行在特殊模式下的 Redis實例, 你可以在啟動一個普通 Redis實例時通過給定 –sentinel 選項來啟動 Redis Sentinel 實例。如:
redis-server /path/to/sentinel.conf --sentinel
當然也可以運行Redis自帶的redis-sentinel,如:
redis-sentinel /path/to/sentinel.conf
Sentinel集群:
1.和其他集群不同,你無須設置其他Sentinel的地址,Sentinel進程可以通過發布與訂閱來自動發現正在監視相同主實例的其他Sentinel。當一個 Sentinel 發現一個新的 Sentinel 時,它會將新的 Sentinel 添加到一個列表中,這個列表保存了 Sentinel 已知的,監視同一個主服務器的所有其他Sentinel。
2.Sentinel集群中的Sentinel不會再同一時刻并發去failover同一個master,第一個進行failover的Sentinel如果失敗了(上文配置的failover-timeout),另外一個才會重新進行failover,以此類推。
3.當Sentinel將一個slave選舉為master并發送SLAVE OF NO ONE
后,即使其它的slave還沒針對新master重新配置自己,failover也被認為是成功了。
4.上述過度過程中,若此時重啟old master,則redis集群將處于無master狀態,此時只能手動修改配置文件,然后重新啟動集群.
5.Master-Slave切換后,Sentinel會改寫master,slave和sentinel的conf配置文件。
6.一旦一個Sentinel成功地對一個master進行了failover,它將會把關于master的最新配置通過廣播形式通知其它sentinel,其它的Sentinel則更新對應master的配置。
例子:
啟動redis:
/etc/redis/redis-m.conf
daemonize yes
pidfile /var/run/redis/redis-server-m.pid
port 6379
loglevel notice
logfile /var/log/redis/redis-server-m.log
databases 16
##disable snapshot
save ""
dir /app/redis-m
appendonly yes
appendfilename "appendonly.aof"
##not to be a slave
#slaveof no one
/etc/redis/redis-s1.conf
daemonize yes
pidfile /var/run/redis/redis-server-s1.pid
port 6380
loglevel warning
logfile /var/log/redis/redis-server-s1.log
databases 16
##disable snapshot
save ""
dir /app/redis-s1
appendonly yes
appendfilename "appendonly.aof"
##to be a slave
slaveof 127.0.0.1 6379
/etc/redis/redis-s2.conf
daemonize yes
pidfile /var/run/redis/redis-server-s2.pid
port 6381
loglevel warning
logfile /var/log/redis/redis-server-s2.log
databases 16
##disable snapshot,enable AOF
save ""
dir /app/redis-s2
appendonly yes
appendfilename "appendonly.aof"
##to be a slave
slaveof 127.0.0.1 6379
查看進程實例:
ps -ef | grep redis
root 17560 1 0 09:48 ? 00:00:00 redis-server *:6379
root 17572 1 0 09:48 ? 00:00:00 redis-server *:6380
root 17609 1 0 09:49 ? 00:00:00 redis-server *:6381
redis-cli
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6380,state=online,offset=547,lag=0
slave1:ip=127.0.0.1,port=6381,state=online,offset=547,lag=1
master_repl_offset:547
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:546
啟動sentinel:
sentinel1.conf
port 26379
sentinel monitor myredis 127.0.0.1 6379 2
sentinel down-after-milliseconds myredis 60000
sentinel failover-timeout myredis 180000
sentinel parallel-syncs myredis 1
sentinel notification-script myredis /etc/redis/log-issues.sh
sentinel2.conf
port 26380
sentinel monitor myredis 127.0.0.1 6379 2
sentinel down-after-milliseconds myredis 60000
sentinel failover-timeout myredis 180000
sentinel parallel-syncs myredis 1
sentinel notification-script myredis /etc/redis/log-issues.sh
log-issues.sh
#!/bin/bash
echo "master failovered at `date`" > /root/redis_issues.log
-------啟動--------
nohup redis-sentinel /etc/redis/sentinel1.conf
nohup redis-sentinel /etc/redis/sentinel2.conf
連接:
127.0.0.1:26379> sentinel masters
1) 1) "name"
2) "myredis"
3) "ip"
4) "127.0.0.1"
5) "port"
6) "6379"
7) "runid"
8) "a88ffd6548e333f3ac895cf1b890ef32a527dd66"
......
37) "parallel-syncs"
38) "1"
39) "notification-script"
40) "/etc/redis/log-issues.sh"
127.0.0.1:26379> sentinel slaves myredis
1) 1) "name"
2) "127.0.0.1:6381"
3) "ip"
4) "127.0.0.1"
5) "port"
6) "6381"
......
2) 1) "name"
2) "127.0.0.1:6380"
3) "ip"
4) "127.0.0.1"
5) "port"
6) "6380"
sentinel reset : 重置所有名字和給定模式 pattern 相匹配的主服務器。 pattern 參數是一個 Glob 風格的模式。 重置操作清除該sentinel的所保存的所有狀態信息,并進行一次重新的發現過程。
127.0.0.1:26379> sentinel reset myredis
(integer) 1
sentinel failover :進行一次主動的failover。即在不詢問其他 Sentinel 意見的情況下, 強制開始一次自動故障遷移 。發起故障轉移的 Sentinel 會向其他 Sentinel 發送一個新的配置,其他 Sentinel 會根據這個配置進行相應的更新。
127.0.0.1:26379> sentinel failover myredis
OK
master切換到了localhost:6381上:
127.0.0.1:26379> sentinel get-master-addr-by-name myredis
1) "127.0.0.1"
2) "6381"
## redis-cli中
redis-cli
127.0.0.1:6379> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6381
master_link_status:up
客戶端:
sentinel 的 failover 過程對客戶端是透明的,以Java Jedis為例:
public class RedisSentinelClient {
/**
* @param args
*/
public static void main(String[] args) {
Set sentinels = new HashSet();
sentinels.add(new HostAndPort("172.30.37.73", 26379).toString());
sentinels.add(new HostAndPort("172.30.37.73", 26380).toString());
sentinels.add(new HostAndPort("172.30.37.73", 26381).toString());
JedisSentinelPool sentinelPool = new JedisSentinelPool("myredis", sentinels);
System.out.println("Current master: " + sentinelPool.getCurrentHostMaster().toString());
Jedis master = sentinelPool.getResource();
master.set("username","tom");
sentinelPool.returnResource(master);
Jedis master2 = sentinelPool.getResource();
String value = master2.get("username");
System.out.println("username: " + value);
master2.close();
sentinelPool.destroy();
}
}
cluster:
1.一個 Redis 集群包含 16384 個哈希槽(hash slot),數據庫中的每個鍵都屬于這 16384 個哈希槽的其中一個,集群中的每個節點負責處理一部分哈希槽。 例如一個集群有三個節點,其中:
節點 A 負責處理 0 號至 5500 號哈希槽。
節點 B 負責處理 5501 號至 11000 號哈希槽。
節點 C 負責處理 11001 號至 16384 號哈希槽。
這種將哈希槽分布到不同節點的做法使得用戶可以很容易地向集群中添加或者刪除節點。例如:
如果用戶將新節點 D 添加到集群中, 那么集群只需要將節點 A 、B 、 C 中的某些槽移動到節點 D 就可以了。
如果用戶要從集群中移除節點 A , 那么集群只需要將節點 A 中的所有哈希槽移動到節點 B 和節點 C , 然后再移除空白(不包含任何哈希槽)的節點 A 就可以了。
2.Redis 集群對節點使用了主從復制功能: 集群中的每個節點都有 1 個至 N 個復制品(replica), 其中一個復制品為主節點(master), 而其余的 N-1 個復制品為從節點(slave)。
3.Redis 集群的節點間通過Gossip協議通信。
命令:
//集群(cluster)
CLUSTER INFO 打印集群的信息
CLUSTER NODES 列出集群當前已知的所有節點(node),以及這些節點的相關信息。
//節點(node)
CLUSTER MEET <ip> <port> 將 ip 和 port 所指定的節點添加到集群當中,讓它成為集群的一份子。
CLUSTER FORGET <node_id> 從集群中移除 node_id 指定的節點。
CLUSTER REPLICATE <node_id> 將當前節點設置為 node_id 指定的節點的從節點。
CLUSTER SAVECONFIG 將節點的配置文件保存到硬盤里面。
//槽(slot)
CLUSTER ADDSLOTS <slot> [slot ...] 將一個或多個槽(slot)指派(assign)給當前節點。
CLUSTER DELSLOTS <slot> [slot ...] 移除一個或多個槽對當前節點的指派。
CLUSTER FLUSHSLOTS 移除指派給當前節點的所有槽,讓當前節點變成一個沒有指派任何槽的節點。
CLUSTER SETSLOT <slot> NODE <node_id> 將槽 slot 指派給 node_id 指定的節點,如果槽已經指派給另一個節點,那么先讓另一個節點刪除該槽>,然后再進行指派。
CLUSTER SETSLOT <slot> MIGRATING <node_id> 將本節點的槽 slot 遷移到 node_id 指定的節點中。
CLUSTER SETSLOT <slot> IMPORTING <node_id> 從 node_id 指定的節點中導入槽 slot 到本節點。
CLUSTER SETSLOT <slot> STABLE 取消對槽 slot 的導入(import)或者遷移(migrate)。
//鍵 (key)
CLUSTER KEYSLOT <key> 計算鍵 key 應該被放置在哪個槽上。
CLUSTER COUNTKEYSINSLOT <slot> 返回槽 slot 目前包含的鍵值對數量。
CLUSTER GETKEYSINSLOT <slot> <count> 返回 count 個 slot 槽中的鍵。
啟動集群:
Redis Cluster如果數據冗余是1的話,至少要3個Master和3個Slave
192.168.XXX.XXX:7000
192.168.XXX.XXX:7001
192.168.XXX.XXX:7002
192.168.XXX.XXX:7003
192.168.XXX.XXX:7004
192.168.XXX.XXX:7005
配置文件:
bind 192.168.XXX.XXX //不能綁定到192.168.XXX.XXX或localhost,否則指導客戶端重定向時會報”Connection refused”的錯誤。
port 7000
daemonize yes //后臺運行
cluster-enabled yes //開啟Cluster
cluster-require-full-coverage no //默認是yes,只要有結點宕機導致16384個槽沒全被覆蓋,整個集群就全部停止服務,改為no。
cluster-config-file nodes_7000.conf //集群配置文件,這個配置文件不是要我們去配的,而是Redis運行時保存配置的文件,所以我們也不可以修改這個文件。
cluster-node-timeout 5000 //超時多久則認為它宕機了。
appendonly yes
logfile ./redis_7000.log
啟動各節點:redis-server redis_700X.conf
安裝基于ruby的集群工具:
yum install ruby rubygems -y
wget https://rubygems.org/downloads/redis-3.2.1.gem
gem install -l redis-3.2.1.gem
cp redis-3.2.1/src/redis-trib.rb /usr/local/bin/redis-trib //復制集群管理程序到/usr/local/bin
創建集群:
redis-trib 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
--replicas 1 表示我們希望為集群中的每個主節點創建一個從節點,以上命令的意思就是讓 redis-trib 程序創建一個包含三個主節點和三個從節點的集群。
測試:
$ redis-cli -c -p 7000
redis 127.0.0.1:7000> set foo bar
-> Redirected to slot [12182] located at 127.0.0.1:7002
OK
redis 127.0.0.1:7002> set hello world
-> Redirected to slot [866] located at 127.0.0.1:7000
OK
redis 127.0.0.1:7000> get foo
-> Redirected to slot [12182] located at 127.0.0.1:7002
"bar"
redis 127.0.0.1:7000> get hello
-> Redirected to slot [866] located at 127.0.0.1:7000
"world"
值被放到了相應節點的Hash槽中,redis-cli不斷在700X節點之前重定向跳轉。如果啟動時不加-c選項的話,就能看到以錯誤形式顯示出的MOVED重定向消息。
重新分片:
重分片基本上就是從部分節點移動哈希槽到另外一部分節點上去,像創建集群一樣也是通過使用 redis-trib 工具來完成。
添加新節點:
現在按照相同方法啟動了兩個新實例7010,7011。其中7010為主,7011為從。
//添加主節點
redis-trib.rb add-node 192.168.1.100:7010 192.168.1.100:7000
//查看節點信息,7010被成功添加
redis-cli -c -h 192.168.1.100 -p 7000 cluster nodes
...
...
0d1f9c979684e0bffc8230c7bb6c7c0d37d8a5a9 192.168.1.100:7010 master - 0 1442452249525 0 connected
...
...
//添加從節點
redis-trib.rb add-node --slave --master-id 0d1f9c979684e0bffc8230c7bb6c7c0d37d8a5a9 192.168.1.100:7011 192.168.1.100:7000
遷移Slot:
# redis-trib.rb reshard 192.168.1.100:7000 //下面是主要過程
How many slots do you want to move (from 1 to 16384)? 1000 //設置slot數1000
What is the receiving node ID? 03ccad2ba5dd1e062464bc7590400441fafb63f2 //新節點node 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:all //表示全部節點重新洗牌
Do you want to proceed with the proposed reshard plan (yes/no)? yes //確認重新分
刪除節點:
如果刪除的是主節點,她有slot,需要先去掉分配的slot,然后在刪除主節點
# redis-trib.rb reshard 192.168.10.219:6378 //取消分配的slot,下面是主要過程
How many slots do you want to move (from 1 to 16384)? 1000 //被刪除master的所有slot數量
What is the receiving node ID? 5d8ef5a7fbd72ac586bef04fa6de8a88c0671052 //接收6378節點slot的master
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:03ccad2ba5dd1e062464bc7590400441fafb63f2 //被刪除master的node-id
Source node #2:done
Do you want to proceed with the proposed reshard plan (yes/no)? yes //取消slot后,reshard
//現在節點上已經沒有slot
# redis-trib.rb del-node 192.168.10.219:6378 '03ccad2ba5dd1e062464bc7590400441fafb63f2'
Jedis客戶端:
Set<HostAndPort> jedisClusterNodes = new HashSet<HostAndPort>();
//Jedis Cluster will attempt to discover cluster nodes automatically
jedisClusterNodes.add(new HostAndPort("127.0.0.1", 7379));
JedisCluster jc = new JedisCluster(jedisClusterNodes);
jc.set("foo", "bar");
String value = jc.get("foo");
要點:
Sentinel是一種自動failover的解決方案。Cluster是一種分片的方案,自帶failover,使用Cluster時不需要再用Sentinel。
Sentinel:
1.Sentinel的作用是將主從切換自動化。
2.Sentinel本身也支持集群,用Sentinel集群來監控redis集群。
3.Sentinel集群自身也需要多數機制。(避免單點問題,至少需要三個)
4.Sentinel只是一個運行在特殊模式下的 Redis實例。
通過給定 –sentinel 選項來啟動 Redis Sentinel 實例。如:
redis-server /path/to/sentinel.conf --sentinel
當然也可以運行Redis自帶的redis-sentinel,如:
redis-sentinel /path/to/sentinel.conf
在默認情況下,Sentinel 使用TCP端口26379。 可以使用 redis-cli 進行通訊 redis-cli -p 26379。
4.Sentinel進程可以通過發布與訂閱來自動發現正在監視相同主實例的其他Sentinel,只需監控相同主實例無須設置其他Sentinel的地址。
#配置文件conf中
sentinel monitor myredis 127.0.0.1 6379 2
5.Sentinel 的 failover 過程對代碼客戶端是透明的。
參考:
http://blog.csdn.net/dc_726/article/details/48552531
http://blog.51yip.com/nosql/1726.html
http://carlosfu.iteye.com/blog/2243483