5、Redis主從複製php
5.1 redis複製特性html
a 使用異步複製。node
b 一個主服務器能夠有多個從服務器。python
c 從服務器也能夠有本身的從服務器。nginx
d 複製功能不會阻塞主服務器。redis
f 能夠經過複製功能來讓主服務器免於執行持久化操做,由從服務器去執行持久化操做便可。數據庫
關閉主服務器持久化時,複製功能的數據安全vim
當配置Redis複製功能時,強烈建議打開主服務器的持久化功能。不然的話,因爲延遲等問題,部署的服務應該要避免自動拉起。安全
爲了幫助理解主服務器關閉持久化時自動拉起的危險性,參考一下如下會致使主從服務器數據所有丟失的例子:ruby
1. 假設節點A爲主服務器,而且關閉了持久化。而且節點B和節點C從節點A複製數據
2. 節點A崩潰,而後由自動拉起服務重啓了節點A。因爲節點A的持久化被關閉了,因此重啓以後沒有任何數據
3. 節點B和節點C將從節點A複製數據,可是A的數據是空的,因而就把自身保存的數據副本刪除
在關閉主服務器上的持久化,並同時開啓自動拉起進程的狀況下,即使使用Sentinel來實現Redis的高可用性,也是很是危險的。由於主服務器可能拉起得很是快,以致於Sentinel在配置的心跳時間間隔內沒有檢測到主服務器已被重啓,而後仍是會執行上面的數據丟失的流程。
不管什麼時候,數據安全都是極其重要的,因此應該禁止主服務器關閉持久化的同時自動拉起。
5.2 主從複製原理
redis 主從同步有兩種方式(或者所兩個階段):全同步和部分同步。
主從剛剛鏈接的時候,進行全同步;全同步結束後,進行部分同步。固然,若是有須要,slave 在任什麼時候候均可以發起全同步。
redis 策略是,不管如何,首先會嘗試進行部分同步,如不成功,要求從機進行全同步,並啓動 BGSAVE……BGSAVE 結束後,傳輸 RDB 文件;若是成功,容許從機進行部分同步,並傳輸積壓空間中的數據。
下面這幅圖,總結了主從同步的機制:
主從複製原理:
1. 從服務器向主服務器發送 SYNC 命令。
2. 接到 SYNC 命令的主服務器會調用BGSAVE 命令,建立一個 RDB 文件,並使用緩衝區記錄接下來執行的全部寫命令。
3. 當主服務器執行完 BGSAVE 命令時,它會向從服務器發送 RDB 文件,而從服務器則會接收並載入這個文件。
4. 主服務器將緩衝區儲存的全部寫命令發送給從服務器執行。
命令的傳播:
在主從服務器完成同步以後,主服務器每執行一個寫命令,它都會將被執行的寫命令發送給從服務器執行,這個操做被稱爲「命令傳播」(command propagate)。
命令傳播是一個持續的過程:只要複製仍在繼續,命令傳播就會一直進行,使得主從服務器的狀態能夠一直保持一致。
5.3 複製中的SYNC與PSYNC
在 Redis 2.8 版本以前,斷線以後重連的從服務器總要執行一次完整重同步(full resynchronization)操做。
從 Redis 2.8 開始,Redis 使用 PSYNC命令代替 SYNC 命令。PSYNC 比起 SYNC 的最大改進在於 PSYNC 實現了部分重同步(partial resync)特性:在主從服務器斷線而且從新鏈接的時候,只要條件容許,PSYNC 可讓主服務器只向從服務器同步斷線期間缺失的數據,而不用從新向從服務器同步整個數據庫。
5.4 複製的一致性問題
在讀寫分離環境下,客戶端向主服務器發送寫命令 SET n 10086,主服務器在執行這個寫命令以後,向客戶端返回回覆,並將這個寫命令傳播給從服務器。
接到回覆的客戶端繼續向從服務器發送讀命令 GET n ,而且由於網絡狀態的緣由,客戶端的 GET命令比主服務器傳播的 SET 命令更快到達了從服務器。
由於從服務器鍵 n 的值還未被更新,因此客戶端在從服務器讀取到的將是一個錯誤(過時)的 n值。
5.5 複製安全性提高
主服務器只在有至少 N 個從服務器的狀況下,才執行寫操做從 Redis 2.8 開始,爲了保證數據的安全性,能夠經過配置,讓主服務器只在有至少 N 個當前已鏈接從服務器的狀況下,才執行寫命令。
不過,由於 Redis 使用異步複製,因此主服務器發送的寫數據並不必定會被從服務器接收到,所以,數據丟失的可能性仍然是存在的。
經過如下兩個參數保證數據的安全:
min-slaves-to-write <number of slaves>
min-slaves-max-lag <number of seconds>
5.6 Redis主從複製實踐
在安裝redis時就進行了多實例的配置
準備兩個或兩個以上redis實例
6380/redis-server
6380/redis.conf
6381/redis-server
6381/redis.conf
6382/redis-server
6382/redis.conf
配置文件示例:
bind 127.0.0.1 10.0.0.186
port 6380
daemonize yes
pidfile /var/run/redis_6380.pid
loglevel notice
logfile "/var/log/redis_6380.log"
dbfilename dump.rdb
dir /application/redis/6380/
appendonly no
appendfilename "appendonly.aof"
appendfsync everysec
slowlog-log-slower-than 10000
slowlog-max-len 128
protected-mode no
啓動:
./6380/redis-server ./6380/redis.conf
./6381/redis-server ./6381/redis.conf
./6382/redis-server ./6382/redis.conf
複製環境說明:
主節點:6380
從節點:638一、6382
開啓主從(在6381 6382實例中執行)
redis-cli -p 6381/6382
SLAVEOF 127.0.0.1 6380
至此redis主從複製完成
5.7 Redis主從複製管理
主從複製狀態監控:info replication
主從切換: slaveof no one
Redis-Sentinel是Redis官方推薦的高可用性(HA)解決方案,當用Redis作Master-slave的高可用方案時,假如master宕機了,Redis自己(包括它的不少客戶端)都沒有實現自動進行主備切換,而Redis-sentinel自己也是一個獨立運行的進程,它能監控多個master-slave集羣,發現master宕機後能進行自動切換。
Sentinel 是一個監視器,它能夠根據被監視實例的身份和狀態來判斷應該執行何種動做。
6.1 Redis Sentinel 功能
監控(Monitoring):
Sentinel 會不斷地檢查你的主服務器和從服務器是否運做正常。
提醒(Notification):
當被監控的某個 Redis 服務器出現問題時,Sentinel 能夠經過 API 向管理員或者其餘應用程序發送通知。
自動故障遷移(Automatic failover):
當一個主服務器不能正常工做時,Sentinel 會開始一次自動故障遷移操做,它會將失效主服務器的其中一個從服務器升級爲新的主服務器,並讓失效主服務器的其餘從服務器改成複製新的主服務器;當客戶端試圖鏈接失效的主服務器時,集羣也會向客戶端返回新主服務器的地址,使得集羣可使用新主服務器代替失效服務器。
6.2 服務器鏈接
發現並鏈接主服務器:
Sentinel 經過用戶給定的配置文件來發現主服務器。
Sentinel 會與被監視的主服務器建立
兩個網絡鏈接:
命令鏈接用於向主服務器發送命令。
訂閱鏈接用於訂閱指定的頻道,從而發現監視同一主服務器的其餘 Sentinel。
發現並鏈接從服務器:
Sentinel 經過向主服務器發送 INFO 命令來自動得到全部從服務器的地址。
跟主服務器同樣,Sentinel 會與每一個被發現的從服務器建立命令鏈接和訂閱鏈接。
發現其餘 Sentinel:
Sentinel 會經過命令鏈接向被監視的主從服務器發送「HELLO」 信息,該消息包含Sentinel 的 IP、端口號、ID 等內容,以此來向其餘 Sentinel 宣告本身的存在。與此同時Sentinel 會經過訂閱鏈接接收其餘 Sentinel 的「HELLO」信息,以此來發現監視同一個主服務器的其餘 Sentinel 。
sentinel1 經過發送HELLO 信息來讓sentinel2 和 sentinel3發現本身,其餘兩個sentinel 也會進行相似的操做。
多個Sentienl之間的連接:
Sentinel 之間只會互相建立命令鏈接,用於進行通訊。由於已經有主從服務器做爲發送和接收 HELLO 信息的中介,因此 Sentinel之間不會建立訂閱鏈接。
6.3 檢測實例的狀態
Sentinel 使用 PING 命令來檢測實例的狀態:若是實例在指定的時間內沒有返回回覆,或者返回錯誤的回覆,那麼該實例會被Sentinel 判斷爲下線。
Redis 的 Sentinel 中關於下線(down)有兩個不一樣的概念:
主觀下線(Subjectively Down,簡稱 SDOWN)指的是單個Sentinel 實例對服務器作出的下線判斷。
客觀下線(Objectively Down,簡稱 ODOWN)指的是多個Sentinel 實例在對同一個服務器作出 SDOWN 判斷,而且經過SENTINEL is-master-down-by-addr 命令互相交流以後,得出的服務器下線判斷。(一個 Sentinel 能夠經過向另外一個Sentinel 發送SENTINEL is-master-down-by-addr 命令來詢問對方是否定爲給定的服務器已下線。)
若是一個服務器沒有在 master-down-after-milliseconds 選項所指定的時間內,對向它發送 PING 命令的 Sentinel 返回一個有效回覆(valid reply),那麼 Sentinel 就會將這個服務器標記爲主觀下線。
6.4 故障轉移FAILOVER
一次故障轉移操做由如下步驟組成:
1. 發現主服務器已經進入客觀下線狀態。
2. 基於Raft leader election 協議,進行投票選舉
3. 若是當選失敗,那麼在設定的故障遷移超時時間的兩倍以後,從新嘗試當選。若是當選成功,那麼執行如下步驟。
4. 選出一個從服務器,並將它升級爲主服務器。
5. 向被選中的從服務器發送 SLAVEOF NO ONE 命令,讓它轉變爲主服務器。
6. 經過發佈與訂閱功能,將更新後的配置傳播給全部其餘 Sentinel,其餘 Sentinel 對它們本身的配置進行更新。
7. 向已下線主服務器的從服務器發送 SLAVEOF 命令,讓它們去複製新的主服務器。
8. 當全部從服務器都已經開始複製新的主服務器時,leader Sentinel 終止此次故障遷移操做。
6.5 配置sentinel
建立程序目錄
cd /application
mkdir 26380
cp /usr/local/redis/src/redis-sentinel ./26380/
cd 26380
編輯配置文件
vim sentinel.conf
port 26380
dir "/tmp"
sentinel monitor mymaster 127.0.0.1 6380 2
sentinel down-after-milliseconds mymaster 60000
sentinel config-epoch mymaster 0
啓動sentinel
./redis-sentinel ./sentinel.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
6.6 Sentinel命令操做
命令 | 描述 |
PING | 返回 PONG |
SENTINEL masters | 列出全部被監視的主服務器 |
SENTINEL slaves <master name> | |
SENTINEL get-master-addr-by-name <master name> | 返回給定名字的主服務器的 IP 地址和端口號。 |
SENTINEL reset <pattern> | 重置全部名字和給定模式 pattern 相匹配的主服務器 |
SENTINEL failover <master name> | 當主服務器失效時,在不詢問其餘 Sentinel 意見的狀況下,強制開始一次自動故障遷移。 |
6.7 Sentinel發佈與訂閱信息
客戶端能夠將 Sentinel 看做是一個只提供了訂閱功能的 Redis 服務器:你不可使用 PUBLISH 命令向這個服務器發送信息,但你能夠用 SUBSCRIBE 命令或者 PSUBSCRIBE 命令,經過訂閱給定的頻道來獲取相應的事件提醒。
一個頻道可以接收和這個頻道的名字相同的事件。好比說,名爲 +sdown 的頻道就能夠接收全部實例進入主觀下線(SDOWN)狀態的事件。
經過執行 PSUBSCRIBE * 命令能夠接收全部事件信息。
如下列出的是客戶端能夠經過訂閱來得到的頻道和信息的格式:
注意,當格式中包含 instance details 字樣時,表示頻道所返回的信息中包
含了如下用於識別目標實例的內容:
<instance-type> <name> <ip> <port> @ <master-name> <master-ip> <master-port>
@ 字符以後的內容用於指定主服務器, 這些內容是可選的, 它們僅在 @ 字符以前的內容指定的實例不是主服務器時使用。
7、Redis cluster
7.1 Redis集羣
Redis 集羣是一個能夠在多個 Redis 節點之間進行數據共享的設施(installation)。
Redis 集羣不支持那些須要同時處理多個鍵的 Redis 命令,由於執行這些命令須要在多個 Redis 節點之間移動數據,而且在高負載的狀況下,這些命令將下降 Redis 集羣的性能,並致使不可預測的行爲。
Redis 集羣經過分區(partition)來提供必定程度的可用性(availability):即便集羣中有一部分節點失效或者沒法進行通信,集羣也能夠繼續處理命令請求。將數據自動切分(split)到多個節點的能力。
當集羣中的一部分節點失效或者沒法進行通信時,仍然能夠繼續處理命令請求的能力。
7.2 Redis 集羣數據共享
Redis 集羣使用數據分片(sharding)而非一致性哈希(consistency hashing)來實現: 一個 Redis 集羣包含 16384 個哈希槽(hash slot),數據庫中的每一個鍵都屬於這 16384 個哈希槽的其中一個, 集羣使用公式CRC16(key) % 16384 來計算鍵 key 屬於哪一個槽, 其中 CRC16(key) 語句用於計算鍵 key 的 CRC16 校驗和 。
節點 A 負責處理 0 號至 5500 號哈希槽。
節點 B 負責處理 5501 號至 11000 號哈希槽。
節點 C 負責處理 11001 號至 16384 號哈希槽。
槽的計算公式
集羣使用公式 CRC16(key) & 16383 計算鍵 key屬於哪一個槽。
7.3 集羣運行機制
全部的redis節點彼此互聯(PING-PONG機制),內部使用二進制協議優化傳輸速度和帶寬.
節點的fail是經過集羣中超過半數的master節點檢測失效時才失效。
客戶端與redis節點直連,不須要中間proxy層.客戶端不須要鏈接集羣全部節點,鏈接集羣中任何一個可用節點便可
把全部的物理節點映射到[0-16383]slot上,cluster 負責維護node<->slot<->key
爲了使得集羣在一部分節點下線或者沒法與集羣的大多數(majority)節點進行通信的狀況下,仍然能夠正常運做,Redis 集羣對節點使用了主從複製功能:集羣中的每一個節點都有 1 個至 N 個複製品(replica),其中一個複製品爲主節點(master),而其他的N-1 個複製品爲從節點(slave)。
在以前列舉的節點 A 、B 、C 的例子中,若是節點 B 下線了,那麼集羣將沒法正常運行,由於集羣找不到節點來處理5501號至11000號的哈希槽。
假如在建立集羣的時候(或者至少在節點 B下線以前),咱們爲主節點B添加了從節點 B1,那麼當主節點 B下線的時候,集羣就會將B1設置爲新的主節點,並讓它代替下線的主節點B,繼續處理5501號至11000號的哈希槽,這樣集羣就不會由於主節點B的下線而沒法正常運做了。
不過若是節點B和B1都下線的話,Redis集羣仍是會中止運做。
集羣的複製特性重用了 SLAVEOF 命令的代碼,因此集羣節點的複製行爲和SLAVEOF 命令的複製行爲徹底相同。
7.4 集羣的故障轉移
1. 在集羣裏面,節點會對其餘節點進行下線檢測。
2. 當一個主節點下線時,集羣裏面的其餘主節點負責對下線主節點進行故障移。
3. 換句話說,集羣的節點集成了下線檢測和故障轉移等相似 Sentinel 的功能。
4. 由於 Sentinel 是一個獨立運行的監控程序,而集羣的下線檢測和故障轉移等功能是集成在節點裏面的,它們的運行模式很是地不一樣,因此儘管這二者的功能很類似,但集羣的實現沒有重用 Sentinel 的代碼。
在集羣裏面執行命令的兩種狀況
命令發送到了正確的節點:
命令要處理的鍵所在的槽正好是由接收命令的節點負責,那麼該節點執行命令,就像單機 Redis 服務器同樣。
槽位說明:
7000: 槽 0~5000
7001:槽 5001~10000
7002:槽 10001~16383
命令發送到了錯誤的節點:
接收到命令的節點並不是處理鍵所在槽的節點,那麼節點將向客戶端返回一個轉向(redirection)錯誤,告知客戶端應該到哪一個節點去執行這個命令,客戶端會根據錯誤提示的信息,從新向正確的節點發送命令。
7.5 關於轉向錯誤
在集羣中的節點會互相告知對方,本身負責處理哪些槽。
集羣中的每一個節點都會記錄 16384 個槽分別由哪一個節點負責,從而造成一個「槽表」(slot table)。
節點在接收到命令請求時,會經過槽表檢查鍵所在的槽是否由本節點處理:
若是是的話,那麼節點直接執行命令;
若是不是的話,那麼節點就從槽表裏面提取出正確節點的地址信息,而後返回轉向錯誤。
7.6 配置集羣
前期準備
# EPEL源安裝ruby支持
yum install ruby rubygems -y
使用國內源
gem source -a http://mirrors.aliyun.com/rubygems/ -remove https://rubygems.org/
# gem sources --add https://gems.ruby-china.org/ --remove https://rubygems.org/
# 安裝redis支持
gem install redis -v 3.3.3
gem sources -l
配置文件
Redis 集羣由多個運行在集羣模式(cluster mode)下的 Redis 實例組成, 實例的集羣模式須要經過配置來開啓, 開啓集羣模式的實例將可使用集羣特有的功能和命令。
如下是一個包含了最少選項的集羣配置文件示例:
port 7000
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
建立程序目錄
cd /application/redis
mkdir 7000 7001 7002 7003 7004 7005
拷貝應用
for i in 0 1 2 3 4 5
do
cp /usr/local/redis/src/redis-server ./700$i
done
建立配置文件
for i in 7000 7001 7002 7003 7004 7005
do
echo "port $i
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes" > $i/redis.conf
done
啓動redis集羣
for i in 7000 7001 7002 7003 7004 7005
do
cd $i
./redis-server ./redis.conf &
cd ../
done
建立集羣
cd /usr/local/redis/src/
./redis-trib.rb --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
給定 redis-trib.rb 程序的命令是 create,這表示咱們但願建立一個新的集羣。
選項 --replicas 1 表示咱們但願爲集羣中的每一個主節點建立一個從節點。
7.7 集羣管理
寫數據,查看集羣狀態
redis-cli -c -p 7000
set foo bar
get foo
從新分片實踐
cd /usr/local/redis/src/
./redis-trib.rb reshard 127.0.0.1:7000
集羣狀態
redis-cli -p 7000 cluster nodes | grep master
故障轉移
redis-cli -p 7002 debug segfault
查看狀態
redis-cli -p 7000 cluster nodes | grep master
增長新的節點
./redis-trib.rb add-node 127.0.0.1:7006 127.0.0.1:7000
刪除一個節點
redis-trib del-node ip:port '<node-id>'
刪除master節點以前首先要使用reshard移除master的所有slot,而後再刪除當前節點
添加一個從節點
./redis-trib.rb add-node --slave --master-id $[nodeid] 127.0.0.1:7008 127.0.0.1:7000
7.8 狀態說明
集羣最近一次向節點發送 PING 命令以後,過去了多長時間還沒接到回覆。
節點最近一次返回 PONG 回覆的時間。
節點的配置節點(configuration epoch)
本節點的網絡鏈接狀況:例如 connected 。
節點目前包含的槽:例如 127.0.0.1:7001 目前包含號碼爲 5960 至 10921 的哈希槽。
8.1 PHP使用redis
tar zxvf 2.2.7.tar.gz
cd phpredis-2.2.7
/application/php/bin/phpize
./configure --with-php-config=/application/php/bin/php-config
make && make install
echo 'extension="redis.so"' >> /application/php/lib/php.ini
service php-fpm restart
service nginx restart
鏈接測試代碼
[root@oldboy ~]# 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");
?>
8.2 Python鏈接redis
安裝軟件包
[root@Redis ~]# yum install python-pip ipython -y
[root@Redis ~]# pip install redis
測試
[root@Redis ~]# ipython
In [1]: import redis
In [2]: oldboy = redis.StrictRedis(host='localhost', port=6379, db=0)
In [3]: oldboy.set('blog','blog.oldboyedu.com')
Out[3]: True
In [4]: oldboy.get('blog')
Out[4]: 'blog.oldboyedu.com'
本文內容來自 老男孩Linux雲計算運維優秀學員課後筆記整理
https://mp.weixin.qq.com/s?__biz=MzI4NDM5NzE4Ng==&mid=2247484105&idx=1&sn=a9752cf7be8541a4eabc5084efca66be&chksm=ebfd5924dc8ad032940f036f4abd5a1377d12ee10e252a4e4863c533b547c4530afb41fbbde3&mpshare=1&scene=1&srcid=120313ShBKvyrxrvYtajK1eC#rd