1. 程式人生 > 實用技巧 >Redis-資料庫入門

Redis-資料庫入門

Redis 資料庫

1. 簡介

​ Redis 是基於鍵值對(Key-value)儲存資料庫。

官網

1.1 特性

1)高速讀寫

2)支援資料持久化

3)支援弱事務

4)支援高可用,分片叢集

1.2 應用場景

1)資料快取

2)排行榜應用

3)訊息佇列,釋出訂閱

2. 部署

2.1 軟體安裝部署

2.1.1 單機部署(RHEL6.8)

# 1. 解壓軟體
cd /ups/soft
tar -xf redis-5.0.0.tar.gz -C /ups/app/

# 2. make編譯
cd /ups/app/redis-5.0.0
make -j $(cat /proc/cpuinfo |grep -c processor)

cd /ups/app/redis-5.0.0/src
make test -j $(cat /proc/cpuinfo |grep -c processor)

# 3. make install
# 安裝到指定目錄
make install PREFIX=/ups/app/redis


2.1.2 Redis引數配置

#1. 編輯配置檔案
mkdir -p /ups/app/redis/conf
cp /ups/app/redis-5.0.0/redis.conf /ups/app/redis/conf/redis.conf
#2. 日誌目錄
mkdir -p /ups/app/redis/logs

ln -s /ups/app/redis/config/redis.conf /etc/redis.conf

#3. 預設情況,Redis不是在後臺執行,我們需要把redis放在後臺執行,將daemonize的值改為yes
vim /etc/redis.conf
daemonize yes
logfile "/ups/app/redis/logs/redisd.log"
bind 127.0.0.1 192.168.10.162

2.2 多例項

# 1. 新建配置檔案,配置埠,目錄,程序檔案等配置,如下:
vi /ups/app/redis/conf/6380.conf
port 6380
daemonize yes
pidfile /var/run/redis-6380.pid
logfile "/ups/app/redis5/logs/6380.log"
dir "/ups/data/redisdata/6380/"

vi /ups/app/redis/conf/6381.conf
port 6381
daemonize yes
pidfile /var/run/redis-6381.pid
logfile "/ups/app/redis5/logs/6381.log"
dir "/ups/data/redisdata/6381/"

# 2. 啟動
/ups/app/redis5/bin/redis-server /ups/app/redis/conf/6380.conf
/ups/app/redis5/bin/redis-server /ups/app/redis/conf/6381.conf

redis配置引數說明

屬性 說明
daemonize 如果值是“yes”,則啟動服務的時候是後臺守護程序形式,如果值是“no”,則相反
pidfile 指定儲存Redis程序號的檔案路徑
port 指定當前Redis服務的埠,預設為6379
tcp-backlog 此引數確定了TCP連線中已完成佇列(完成三次握手之後)的長度, 當然此值必須不大於Linux系統定義的/proc/sys/net/core/somaxconn值,預設是511,而Linux的預設引數值是128。當系統併發量大並且客戶端速度緩慢的時候,可以將這二個引數一起參考設定。
timeout 客戶端和Redis服務端的連線超時時間,預設是0,表示永不超時。
tcp-keepalive 如果值非0,單位是秒,表示將週期性的使用SO_KEEPALIVE檢測客戶端是否還處於健康狀態,避免伺服器一直阻塞,官方給出的建議值是60S。
loglevel Redis總共支援四個級別:debug、verbose、notice、warning。Debug:記錄很多資訊,用於開發和測試;Varbose:有用的資訊,不像debug會記錄那麼多;Notice:普通的verbose,常用於生產環境;Warning:只有非常重要或者嚴重的資訊會記錄到日誌;預設是notice級別。
logfile 日誌的儲存路徑
databases 可用的資料庫數,預設值為16,預設資料庫為0,資料庫範圍在0-(database-1)之間,個人覺得DB的概念類似於名稱空間
save 儲存資料庫快照資訊到磁碟,其對應的值有兩個,比如save 300 10表示:300秒內至少有300個key被改變時,觸發儲存資訊到磁碟的事件。
stop-writes-on-bgsave-error 當持久化出現錯誤之後,是否繼續提供寫服務
rdbcompression 持久化到RDB檔案時,是否壓縮,“yes”為壓縮,“no”則反之
rdbchecksum 讀取和寫入的時候是否支援CRC64校驗,預設是開啟的
dbfilename 映象檔案的名字
dir 當前工作目錄,配置檔案和映象檔案等都在此目錄下
masterauth 設定訪問master伺服器的密碼
slave-serve-stale-data 當slave伺服器和master伺服器失去連線後,或者當資料正在複製傳輸的時候,如果此引數值設定“yes”,slave伺服器可以繼續接受客戶端的請求,否則,會返回給請求的客戶端如下資訊“SYNC with master in progress”
slave-read-only 是否允許slave伺服器節點只提供讀服務
repl-disable-tcp-nodelay 指定向slave同步資料時,是否禁用socket的NO_DELAY選 項。若配置為“yes”,則禁用NO_DELAY,則TCP協議棧會合並小包統一發送,這樣可以減少主從節點間的包數量並節省頻寬,但會增加資料同步到 slave的時間。若配置為“no”,表明啟用NO_DELAY,則TCP協議棧不會延遲小包的傳送時機,這樣資料同步的延時會減少,但需要更大的頻寬。 通常情況下,應該配置為no以降低同步延時,但在主從節點間網路負載已經很高的情況下,可以配置為yes。
slave-priority 指定slave的優先順序。在不只1個slave存在的部署環境下,當master宕機時,Redis Sentinel會將priority值最小的slave提升為master。需要注意的是,若該配置項為0,則對應的slave永遠不會自動提升為master。
appendonly 開啟append only 模式之後,redis 會把所接收到的每一次寫操作請求都追加到appendonly.aof 檔案中,當redis 重新啟動時,會從該檔案恢復出之前的狀態。但是這樣會造成appendonly.aof 檔案過大,所以redis 還支援了BGREWRITEAOF 指令,對appendonly.aof 進行重新整理。預設是不開啟的。
appendfilename 預設為appendonly.aof。
appendfsync 設定aof的同步頻率,有三種選擇always、everysec、no,預設是everysec表示每秒同步一次。
no-appendfsync-on-rewrite 指定是否在後臺aof檔案rewrite期間呼叫fsync,預設為no,表示要呼叫fsync(無論後臺是否有子程序在刷盤)。Redis在後臺寫RDB檔案或重寫afo檔案期間會存在大量磁碟IO,此時,在某些linux系統中,呼叫fsync可能會阻塞。
auto-aof-rewrite-percentage 指定Redis重寫aof檔案的條件,預設為100,表示與上次rewrite的aof檔案大小相比,當前aof檔案增長量超過上次afo檔案大小的100%時,就會觸發background rewrite。若配置為0,則會禁用自動rewrite
auto-aof-rewrite-min-size 指定觸發rewrite的aof檔案大小。若aof檔案小於該值,即使當前檔案的增量比例達到auto-aof-rewrite-percentage的配置值,也不會觸發自動rewrite。即這兩個配置項同時滿足時,才會觸發rewrite。
lua-time-limit 一個Lua指令碼最長的執行時間,單位為毫秒,如果為0或負數表示無限執行時間,預設為5000
notify-keyspace-events 見參考3,按鍵通知事件
aof-rewrite-incremental-fsync aof rewrite過程中,是否採取增量檔案同步策略,預設為“yes”。 rewrite過程中,每32M資料進行一次檔案同步,這樣可以減少aof大檔案寫入對磁碟的操作次數

2.3 OS 引數配置

#1. 持久配置啟效
cp /etc/sysctl.conf{,_$(date +%Y%m%d)}
echo -e "\n# redis config\nvm.overcommit_memory=1\nnet.core.somaxconn= 1024" >> /etc/sysctl.conf

#2. 大記憶體頁-臨時配置
echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo 511 > /proc/sys/net/core/somaxconn

cat >> /etc/rc.local <<EOF
echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo 511 > /proc/sys/net/core/somaxconn
EOF

2.4 配置使用者環境變數

cat > /etc/profile.d/redis.sh << EOF
export REDIS_HOME=/ups/app/redis
export PATH=\${REDIS_HOME}/bin:\${PATH}
EOF

. /etc/profile.d/redis.sh

2.5 啟動指令碼

vi /ups/app/redis/config/redisd
#!/bin/sh
#
# redis        init file for starting up the redis daemon
#
# chkconfig:  35 90 10
# description: Starts and stops the redis daemon.

# Source function library.
. /etc/rc.d/init.d/functions
# APP HOME PATH
REDIS_BIN_DIR=/ups/app/redis/bin
PROG="redis-server"
# 指定redis的配置檔案路徑
CONFIG="/etc/redis.conf"
# 指定redis-server命令的位置(whereis redis-server)
EXEC="${REDIS_BIN_DIR}/${PROG}"
HOSTIP=$(hostname -i)
# 指定redis的監聽埠(和配置檔案裡保持一致)
PORT=$(grep '^port' ${CONFIG}|awk '{print $NF}')
# 指定redis的pid檔案路徑(和配置檔案裡保持一致)
PIDFILE=$(grep '^pidfile' ${CONFIG}|awk '{print $NF}')
# 指定redis客戶端位置
REDIS_CLI=${REDIS_BIN_DIR}/redis-cli
# REDIS_USER
REDIS_USER=${USER}
LOCKFILE=/var/lock/subsys/${PROG}.lk
RETVAL=0

start() {
    # [[ -f "${CONFIG}" ]] || exit 1
    # [[ -x "${EXEC}" ]] || exit 1
    if [[ ! -e "${PIDFILE}" ]]; then
        # echo -n $"Starting ${PROG}: "
        daemon --user ${REDIS_USER} ${EXEC} ${CONFIG}
        # $EXEC $CONFIG
        [[ "$?" = "0" ]] && touch ${LOCKFILE}
        return ${RETVAL}
    fi
}
stop() {
    if [[ -f "${PIDFILE}" ]]; then
        # PID=$(cat $PIDFILE)
        # echo -n $"Stopping ${PROG}: "
        ${REDIS_CLI} -h ${HOSTIP} -p ${PORT} SHUTDOWN
        # killproc -p $PIDFILE ${PROG}
        sleep 2
        while [[ -f "${PIDFILE}" ]]; do
            sleep 1
        done
        [[ ! -f "${PIDFILE}" ]] && rm -f ${LOCKFILE}
        return ${RETVAL}
    fi
}
restart() {
    stop
    start
}

# status() {
#     status -p ${PIDFILE} ${PROG}
#     return ${RETVAL}
# }

case "$1" in
    start)
        $1
        ;;
    stop)
        $1
        ;;
    restart)
        $1
        ;;
    *)
        echo $"Usage: $0 {start|stop|restart}"
        exit 1
esac

# -------------------------------------------------------------------

# 開機啟動配置

ln -s /ups/app/redis/config/redisd /etc/init.d/redisd

chmod +x /ups/app/redis/config/redisd /etc/init.d/redisd

chkconfig --add redisd

chkconfig --level 35 redisd on

2.6 管理服務

# 啟動redis服務
cd /ups/app/redis
./bin/redis-server ./conf/redis.conf

# 關閉redis服務
./bin/redis-cli shutdown

rhel7配置服務

cat > /usr/lib/systemd/system/redis.service <<-EOF
[Unit]
Description=Redis
After=network.target

[Service]
ExecStart=/ups/app/redis5/bin/redis-server /ups/app/redis5/redis.conf --daemonize no
ExecStop=/ups/app/redis5/bin/redis-cli -h 127.0.0.1 -p 6379 shutdown

[Install]
WantedBy=multi-user.target
EOF

--載入配置
systemctl daemon-reload
--啟動
systemctl start redis
--關閉
systemctl stop redis
--檢視狀態
systemctl status redis
--開機啟動
systemctl enable redis
--關閉開機自啟
systemctl disable redis

3. 資料持久化

Redis提供了兩種不同的持久化方法可以將資料儲存在磁碟中,一種叫快照持久化(RDB),另一種叫只追加檔案持久化(AOF)。

3.1 RDB

Redis通過建立快照的方式獲取某一時刻Redis中所有資料的副本。

3.1.1 建立快照的方式

1)客戶端直接通過命令BGSAVE或者SAVE來建立一個快照 (手動觸發)

-  BGSAVE 是通過redis呼叫fork來建立一個子程序,然後子程序負責將快照寫入磁碟,而父程序仍然繼續處理命令。
-  SAVE 是在沒有足夠的記憶體空間去執行BGSAVE或者無所謂等待的時候。執行SAVE命令過程中,redis不在響應任何其他命令,直到RDB過程完成為止.

2)在redis.conf中設定save配置選項

# 當在規定的時間內,Redis發生了寫操作的個數滿足條件,會觸發發生BGSAVE命令。
# save <seconds> <changes>
# 當用戶設定了多個save的選項配置,只要其中任一條滿足,Redis都會觸發一次BGSAVE操作,比如:900秒之內至少一次寫操作、300秒之內至少發生10次寫操作、60秒之內發生至少10000次寫操作都會觸發發生快照操作
save 900 1
save 300 10
save 60 10000

3)當Redis通過shutdown命令關閉伺服器請求時,會執行SAVE命令建立一個快照,如果使用kill -9 PID將不會建立快照。

4)將快照複製到其他伺服器從而完成Redis的主從複製

3.1.2 相關配置引數

# RDB檔名
dbfilename "dump.rdb"
# 匯出的rdb檔案是否壓縮
rdbcompress yes
# 後臺備份程序出錯時,主程序停不停止寫入? 主程序不停止容易造成資料不一致
stop-writes-on-bgsave-error no
# 匯入rbd恢復時資料時,要不要檢驗rdb的完整性 驗證版本是不是一致
rdbchecksum yes
save 60 1000

# RDB檔案和AOF檔案路徑
dir "/usr/local/var/db/redis"

3.1.3 優缺點

- 優點:
1. Redis載入RDB恢復資料遠遠快於AOF的方式;
2. RDB是一個緊湊壓縮的二進位制檔案, 代表Redis在某個時間點上的資料快照

- 缺點:
1. RDB方式資料沒辦法做到實時持久化/秒級持久化
2. RDB檔案使用特定二進位制格式儲存,存在老版本Redis服務無法相容新版RDB格式的問題

Note

​ 在只使用快照持久化來報錯資料時,如果系統崩潰或者強殺,使用者將會丟失最近一次生成快照之後更改的所有資料。因此如果應用程式對於兩次快照間丟失的資料可接受,利用快照就是一個很好的方式,但是往往一些系統對於丟失幾分鐘的資料都不可接受,比如高頻的電子商務系統。

​ 此外,如果Redis儲存的資料量長達數十G的時候,沒執行一次快照需要花費大量時間,嚴重影響到伺服器的效能。因此,針對上述的問題,可以使用AOF方式來持久化資料。

3.2 AOF(append only file)

​ 在執行寫命令時,AOF持久化會將執行的寫命令也寫到AOF檔案的末尾,以此來記錄資料的變化。換句話說,將AOF檔案中包含的內容重新執行一遍,就可以恢復AOF檔案所記錄的資料集。Redis會在收到客戶端修改指令後,進行引數校驗,邏輯處理,如果沒問題就立即將該指令文字儲存到AOF檔案中。先執行執行才將日誌存檔

3.2.1 配置引數

# redis預設關閉AOF機制,可以將no改成yes實現AOF持久化
appendonly no
# AOF檔案
appendfilename "appendonly.aof"
# AOF持久化同步頻率,always表示每個Redis寫命令都要同步fsync寫入到磁碟中,但是這種方式會嚴重降低redis的速度;everysec表示每秒執行一次同步fsync,顯示的將多個寫命令同步到磁碟中;no表示讓作業系統來決定應該何時進行同步fsync,Linux系統往往可能30秒才會執行一次
# appendfsync always
appendfsync everysec
# appendfsync no

# 在日誌進行BGREWRITEAOF時,如果設定為yes表示新寫操作不進行同步fsync,只是暫存在緩衝區裡,避免造成磁碟IO操作衝突,等重寫完成後在寫入。redis中預設為no  
no-appendfsync-on-rewrite no   
# 當前AOF檔案大小是上次日誌重寫時的AOF檔案大小兩倍時,發生BGREWRITEAOF操作。  
auto-aof-rewrite-percentage 100  
#當前AOF檔案執行BGREWRITEAOF命令的最小值,避免剛開始啟動Reids時由於檔案尺寸較小導致頻繁的BGREWRITEAOF。  
auto-aof-rewrite-min-size 64mb  
# Redis再恢復時,忽略最後一條可能存在問題的指令(因為最後一條指令可能存在問題,比如寫一半時突然斷電了)
aof-load-truncated yes
#Redis4.0新增RDB-AOF混合持久化格式,在開啟了這個功能之後,AOF重寫產生的檔案將同時包含RDB格式的內容和AOF格式的內容,其中RDB格式的內容用於記錄已有的資料,而AOF格式的記憶體則用於記錄最近發生了變化的資料,這樣Redis就可以同時兼有RDB持久化和AOF持久化的優點(既能夠快速地生成重寫檔案,也能夠在出現問題時,快速地載入資料)。
aof-use-rdb-preamble no

3.2.2 重寫/壓縮AOF檔案

​ 隨著寫操作的不斷增加,AOF檔案會越來越大。例如你遞增一個計數器100次,那麼最終結果就是資料集裡的計數器的值為最終的遞增結果,但是AOF檔案裡卻會把這100次操作完整的記錄下來。而事實上要恢復這個記錄,只需要1個命令就行了,也就是說AOF檔案裡那100條命令其實可以精簡為1條。所以Redis支援這樣一個功能:在不中斷服務的情況下在後臺重建AOF檔案。

Redis提供bgrewriteaof指令用於對AOF日誌進行瘦身,其原理是開闢一個子程序對記憶體進行遍歷,轉換成一系列的操作指令,序列化到新的AOF日誌檔案中。序列化完畢後再將操作期間增量的AOF日誌追加到新AOF日誌檔案中,追加完畢後就立即替代舊的AOF檔案。

-- AOF重寫過程可以手動觸發和自動觸發:
- 手動觸發: 直接呼叫bgrewriteaof命令
- 自動觸發: 根據auto-aof-rewrite-min-size和auto-aof-rewrite-percentage引數確定自動觸發時機

自動觸發時機=aof_current_size>auto-aof-rewrite-minsize && (aof_current_size-aof_base_size)/aof_base_size>=auto-aof-rewritepercentage
其中aof_current_size和aof_base_size可以在info Persistence統計資訊中檢視。

127.0.0.1:6379> info Persistence
# Persistence
loading:0
rdb_changes_since_last_save:0
rdb_bgsave_in_progress:0
rdb_last_save_time:1574128278
rdb_last_bgsave_status:ok
rdb_last_bgsave_time_sec:0
rdb_current_bgsave_time_sec:-1
rdb_last_cow_size:425984
aof_enabled:1
aof_rewrite_in_progress:0
aof_rewrite_scheduled:0
aof_last_rewrite_time_sec:0
aof_current_rewrite_time_sec:-1
aof_last_bgrewrite_status:ok
aof_last_write_status:ok
aof_last_cow_size:462848
aof_current_size:988
aof_base_size:988
aof_pending_rewrite:0
aof_buffer_length:0
aof_rewrite_buffer_length:0
aof_pending_bio_fsync:0
aof_delayed_fsync:0
127.0.0.1:6379> 

[2] Persistence指標
屬性名稱 屬性值
rdb_bgsave_in_progress bgsave子程序是否正在執行
rdb_current_bgsave_time_sec 當前執行bgsave的時間,-1表示未執行
aof_enabled 是否開啟AOF功能
aof_rewrite_in_progress AOF重寫子程序是否正在執行
aof_rewrite_scheduled 在bgsave結束後是否執行AOF重寫
aof_current_rewrite_time_sec 當前執行AOF重寫的時間,-1表示未執行
aof_current_size AOF檔案當前的位元組數
aof_base_size AOF上次重寫rewrite的位元組數

3.2.3 AOF緩衝區檔案同步策略

通過引數appendfsync控制AOF緩衝區檔案同步

說明
always 命令寫入AOF_buf後呼叫系統fsync操作同步到AOF檔案,fsync完成後執行緒返回
everysec 命令寫入aof_buf後呼叫系統write操作,write完成後執行緒返回。fsync同步檔案操作喲專門的執行緒每秒呼叫一次
no 命令寫入aof_buf後呼叫系統write操作,不對AOF檔案做fsync同步,同步硬碟操作又作業系統負責,通常同步週期最長30秒

4. API 介面

4.1 redis-cli

4.1.1 全域性命令

[1] 檢視所有鍵(key/scan)
127.0.0.1:6379> select 0
OK
127.0.0.1:6379> set hello world
OK
127.0.0.1:6379> keys *
1) "hello"
127.0.0.1:6379>

-- keys pattern : pattern 使用glob風格萬用字元;glob是shell使用的路徑萬用字元

scan cursor [MATCH pattern] [COUNT count] : cursor是一個遊標,第一遍從0開始,每次遍歷完返回當前遊標值,直到遊標值為0,表示遍歷結束。

127.0.0.1:6379> scan 0
1) "9"
2)  1) "user:1_2:inter"
    2) "user:2"
    3) "user:ranking"
    4) "user:ranking:1"
    5) "user:1_2:diff"
    6) "user:2:follow"
    7) "myset"
    8) "listkey"
    9) "hello"
   10) "user:ranking:1_inter_2"
127.0.0.1:6379> scan 9
1) "0"
2) 1) "user:ranking:2"
   2) "mylist"
   3) "user:ranking:1_union_2"
   4) "user:1:follow"
   5) "user:1_2:union"
   6) "java"
   7) "python"
   8) "user:1"
127.0.0.1:6379> 

hscan: 雜湊型別遍歷
zscan: 有序集合遍歷
sscan: 集合型別遍歷
[2] 檢視當前庫鍵總數 (dbsize)
127.0.0.1:6379> dbsize
(integer) 1
127.0.0.1:6379>

127.0.0.1:6379> set java jedis
OK
127.0.0.1:6379> set python redis-py
OK
127.0.0.1:6379>
127.0.0.1:6379> dbsize
(integer) 3
127.0.0.1:6379> keys *
1) "hello"
2) "python"
3) "java"
127.0.0.1:6379>
[3]檢視鍵是否存在 (exists)
exists key [key ...]

127.0.0.1:6379> exists hello
(integer) 1
127.0.0.1:6379> exists hello java
(integer) 2
127.0.0.1:6379>
[4]刪除鍵 (del)
del key [key ...]

127.0.0.1:6379> del java
(integer) 1
127.0.0.1:6379> exists java
(integer) 0
127.0.0.1:6379>
[5]鍵過期 (expire)
expire key seconds : 設定鍵過期時間,單位:秒
expireat key timestamp: 鍵在秒級時間戳後過期
pexpire key milliseconds: 鍵在milliseconds毫秒後過期
pexpireat key milliseconds-timestamp鍵在毫秒級時間戳timestamp後過期
ttl key : 查詢鍵剩餘過期時間
pttl key: 查詢鍵剩餘過期時間,精度更高,達到毫秒級
persist命令可以將鍵的過期時間清除

127.0.0.1:6379> set hello world
OK
127.0.0.1:6379> expire hello 10
(integer) 1
127.0.0.1:6379> ttl hello
(integer) 7
127.0.0.1:6379> 
127.0.0.1:6379> ttl hello
(integer) 3
# 結果為-2,表示鍵已經過期被刪除
127.0.0.1:6379> ttl hello
(integer) -2
127.0.0.1:6379> get hello
(nil)
127.0.0.1:6379> get python
"redis-py"
127.0.0.1:6379>
[6] 鍵資料結構型別 (type)
type key

127.0.0.1:6379> type python
string
127.0.0.1:6379> get python
"redis-py"
127.0.0.1:6379> type python
string
127.0.0.1:6379> rpush mylist a b c d e
(integer) 5
127.0.0.1:6379> type mylist
list
127.0.0.1:6379> 
[7] 鍵重新命名(rename)
rename/renamenx key newkey

127.0.0.1:6379> get python
"redis-pyhelloworld"
127.0.0.1:6379> rename python java
OK
127.0.0.1:6379> get python
(nil)
127.0.0.1:6379> get java
"redis-pyhelloworld"
127.0.0.1:6379>

127.0.0.1:6379> renamenx java python
(integer) 0
127.0.0.1:6379>
[9]隨機返回一個鍵 (randomkey)
127.0.0.1:6379> randomkey 
"user:ranking:1_inter_2"
127.0.0.1:6379> 
[9]切換資料庫(select)
select index

127.0.0.1:6379> select 2
OK
127.0.0.1:6379[2]>

-- flushdb/flushall命令用於清除資料庫, 兩者的區別的是flushdb只清除當前資料庫, flushall會清除所有資料庫
[10] 鍵遷移
move key db :redis內部資料庫間遷移資料


dump + restore : 不同例項間資料遷移資料
dump key
restore key ttl serialized-value [REPLACE]


migrate: 具有原子性,例項間資料遷移資料
migrate host port key| destination-db timeout [COPY] [REPLACE] [KEYS key]
第一, 整個過程是原子執行的, 不需要在多個Redis例項上開啟客戶端的, 只需要在源Redis上執行migrate命令即可。 
第二, migrate命令的資料傳輸直接在源Redis和目標Redis上完成的。 
第三, 目標Redis完成restore後會傳送OK給源Redis, 源Redis接收後會根據migrate對應的選項來決定是否在源Redis上刪除對應的鍵

對比

命令 作用域 原子性 支援多個鍵
move 例項之內
dump+restore 例項之間
migrate 例項之間
[11] 獲取配置 (config)
127.0.0.1:6379> config help
1) CONFIG <subcommand> arg arg ... arg. Subcommands are:
2) GET <pattern> -- Return parameters matching the glob-like <pattern> and their values.
3) SET <parameter> <value> -- Set parameter to value.
4) RESETSTAT -- Reset statistics reported by INFO.
5) REWRITE -- Rewrite the configuration file.
127.0.0.1:6379>
127.0.0.1:6379> CONFIG GET slowlog*
1) "slowlog-log-slower-than"
2) "10000"
3) "slowlog-max-len"
4) "128"
127.0.0.1:6379>

-- 修改配置
127.0.0.1:6379> config set slowlog-log-slower-than 1000
OK
-- 寫入檔案
127.0.0.1:6379> config rewrite
OK
127.0.0.1:6379>
-- 檢視慢查詢日誌
127.0.0.1:6379> slowlog get
1) 1) (integer) 0
   2) (integer) 1574122255
   3) (integer) 2609
   4) 1) "config"
      2) "rewrite"
   5) "127.0.0.1:62268"
   6) ""
127.0.0.1:6379> 

/*
每個慢查詢由四個屬性組成
- 慢查詢的標識ID
- 發生時間戳
- 命令耗時,單位為微秒
- 執行的命令和引數
- 客戶端網路套接字(ip: port)
*/

-- 獲取慢查詢日誌列表當前的長度
127.0.0.1:6379> slowlog len
(integer) 1
127.0.0.1:6379>

-- 慢查詢日誌重置(清空列表)
slowlog reset

4.1.2 字串string

[1] set
set key value [expiration EX seconds|PX milliseconds] [NX|XX]
- EX seconds : 為鍵設定秒級過期時間
- PX milliseconds : 為鍵設定毫秒級過期時間
- nx: 鍵必須不存在才可以設定成功
- xx: 鍵必須存在才可以設定成功

setex : setex key seconds value
setnx: setnx key value
[2] get
get key
[3] mset
mset key value [ key value...]
[4] mget
mget key [key ...]


mset/mget可以節省N此網路時間

[5] 計數
incr key: 用於對值做自增操作
decr key: 用於對值自減操作
incrby key increment : 對值自增指定數字
decrby key decrement : 對值自減指定數字
incybyfloat key increment : 對值自增浮點數
[6] append key value: 對值追加值
127.0.0.1:6379> get python
"redis-py"
127.0.0.1:6379> APPEND python helloworld
(integer) 18
127.0.0.1:6379> get python
"redis-pyhelloworld"
127.0.0.1:6379> 
[7] strlen key :字串長度
127.0.0.1:6379> get python
"redis-pyhelloworld"
127.0.0.1:6379> strlen python
(integer) 18
127.0.0.1:6379>
[8] getset key value: 設定並返回原值
127.0.0.1:6379> get hello
(nil)
127.0.0.1:6379> set hello wrold
OK
127.0.0.1:6379> get hello
"wrold"
127.0.0.1:6379> getset hello world
"wrold"
127.0.0.1:6379> get hello
"world"
127.0.0.1:6379>
[9] getrange key start end: 獲取部分字串
127.0.0.1:6379> get python
"redis-pyhelloworld"
127.0.0.1:6379> getrange python 1 3
"edi"
127.0.0.1:6379>

時間複雜度

4.1.3 雜湊hash

雜湊型別值鍵值本身又是一個鍵值對結構

[1] hset key field value: 設定值
127.0.0.1:6379> hset user:1 name tom
(integer) 1
127.0.0.1:6379>
[2] hget key field: 獲取值
127.0.0.1:6379> hget user:1 name
"tom"
127.0.0.1:6379>
[3] hdel key field [field ...] : 刪除field
127.0.0.1:6379> hdel user:1 name
(integer) 1
127.0.0.1:6379> hget user:1 name
(nil)
127.0.0.1:6379> hget user:1 age
(nil)
127.0.0.1:6379>
[4] hlen key : 計算field個數
127.0.0.1:6379> hset user:1 name tom
(integer) 1
127.0.0.1:6379> hset user:1 age 18
(integer) 1
127.0.0.1:6379> hlen user:1
(integer) 2
127.0.0.1:6379>
[5] 批量設定或獲取
hmget key field [field ...]
hmset key field value [field value ...]
127.0.0.1:6379> hmset user:2 name biao age 18 city gz
OK
127.0.0.1:6379> hmget user:2 name age city
1) "biao"
2) "18"
3) "gz"
127.0.0.1:6379>
[6] hexists key field: 判斷field是否存在
127.0.0.1:6379> hexists user:1 age 
(integer) 1
127.0.0.1:6379>
[7] hkeys key: 獲取所有field
127.0.0.1:6379> hkeys user:2
1) "name"
2) "age"
3) "city"
127.0.0.1:6379>
[8] hvals key : 獲取所有value
127.0.0.1:6379> hvals user:2
1) "biao"
2) "18"
3) "gz"
127.0.0.1:6379> 
[9] hgetall key: 獲取所有field-value
127.0.0.1:6379> hgetall user:2
1) "name"
2) "biao"
3) "age"
4) "18"
5) "city"
6) "gz"
127.0.0.1:6379>
[10] hscan key cursor [MATCH pattern] [COUNT count] : 遍歷
127.0.0.1:6379> hscan user:2 0
1) "0"
2) 1) "name"
   2) "biao"
   3) "age"
   4) "18"
   5) "city"
   6) "gz"
127.0.0.1:6379> hscan user:2 0 match name match city
1) "0"
2) 1) "city"
   2) "gz"
127.0.0.1:6379>
[11] hincrby/hincrbyfloat : 自增
hincrby key field increment
hincrbyfloat key field increment 
[12] hstrlen key field: 計算value字串長度
hstrlen key field
127.0.0.1:6379> hget user:1 name
"tom"
127.0.0.1:6379> hstrlen user:1 name
(integer) 3
127.0.0.1:6379>

時間複雜度

4.1.4 列表list

[1] 增加
# 1. rpush key value [value ...] : 從右邊插入元素
127.0.0.1:6379> rpush listkey a c b
(integer) 3
127.0.0.1:6379> lrange listkey 0 -1
1) "a"
2) "c"
3) "b"
127.0.0.1:6379> 

# 2. lpush key value [value ...] : 從左邊插入

# 3. linsert key BEFORE|AFTER pivot value : 向某個元素前或後插入
127.0.0.1:6379> linsert listkey before c java
(integer) 4
127.0.0.1:6379> lrange listkey 0 -1
1) "a"
2) "java"
3) "c"
4) "b"
127.0.0.1:6379>
[2] 查詢
#4. lrange key start stop: 獲取指定範圍元素列表

#5. lindex key index: 獲取列表指定索引下標元素
127.0.0.1:6379> lindex listkey -1
"b"
127.0.0.1:6379>

#6. llen key: 獲取列表長度
127.0.0.1:6379> llen listkey
(integer) 4
127.0.0.1:6379> 
[3] 刪除
#7. lpop key : 從列表左側彈出元素
127.0.0.1:6379> lrange listkey 0 -1
1) "a"
2) "java"
3) "c"
4) "b"
127.0.0.1:6379> lpop listkey
"a"
127.0.0.1:6379> lrange listkey 0 -1
1) "java"
2) "c"
3) "b"
127.0.0.1:6379> 

#8. rpop key: 列表右側彈出元素

#9. lrem key count value : 刪除指定元素
- count>0, 從左到右,刪除最多count個元素
- count<0, 從右到左,刪除最多count個元素
- count=0, 刪除所有

127.0.0.1:6379> lrem listkey 3 c
(integer) 1
127.0.0.1:6379> lrange listkey 0 -1
1) "java"
2) "b"
127.0.0.1:6379>
[4] 修改
# 10. ltrim key start stop: 按照索引範圍修剪元素
127.0.0.1:6379> ltrim key 1 3
OK
127.0.0.1:6379> lrange listkey 0 -1
1) "java"
2) "b"
127.0.0.1:6379> 

# 11. lset key index newvalue : 修改指定索引下標元素
127.0.0.1:6379> lrange listkey 0 -1
1) "java"
2) "b"
127.0.0.1:6379> lset listkey 1 python
OK
127.0.0.1:6379> lrange listkey 0 -1
1) "java"
2) "python"
127.0.0.1:6379> 
[5] 阻塞操作
# 12. 阻塞
blpop key [key ...] timeout
brpop key [key ...] timeout

時間複雜度

4.1.5 集合set

[1] CURD
#1. sadd key member [member ...] : 新增元素
127.0.0.1:6379> sadd myset a b d c e
(integer) 5

#2. srem key member [member ...] : 刪除元素
127.0.0.1:6379> srem myset b e
(integer) 2
127.0.0.1:6379>

#3. scard key : 計算元素個數
127.0.0.1:6379> scard myset
(integer) 3
127.0.0.1:6379>

#4. sismember key member: 判斷元素是否在集合中
127.0.0.1:6379> smembers myset
1) "d"
2) "c"
3) "a"
127.0.0.1:6379> sismember myset a
(integer) 1
127.0.0.1:6379> 

#5. srandmember key [count] : 隨機從集合返回指定個數元素(不會刪除元素)
127.0.0.1:6379> smembers myset
1) "g"
2) "e"
3) "d"
4) "c"
5) "f"
6) "b"
7) "a"
127.0.0.1:6379> srandmember myset 2
1) "f"
2) "d"
127.0.0.1:6379> smembers myset
1) "g"
2) "e"
3) "d"
4) "c"
5) "f"
6) "b"
7) "a"
127.0.0.1:6379>

#6. spop key : 從集合隨機彈出元素,並將他們從集合中刪除
127.0.0.1:6379> smembers myset
1) "g"
2) "e"
3) "d"
4) "c"
5) "f"
6) "b"
7) "a"
127.0.0.1:6379> spop myset 2
1) "a"
2) "c"
127.0.0.1:6379> smembers myset
1) "g"
2) "e"
3) "d"
4) "f"
5) "b"
127.0.0.1:6379> 

 #7.  smembers key: 獲取所有元素
127.0.0.1:6379> smembers myset
1) "d"
2) "c"
127.0.0.1:6379>
[2] 集合間運算
127.0.0.1:6379> sadd user:1:follow it music his sports
(integer) 4
127.0.0.1:6379> sadd user:2:follow it news ent sports
(integer) 4

#1. sinter key [key ...] : 交集
127.0.0.1:6379> sinter user:1:follow user:2:follow
1) "sports"
2) "it"
127.0.0.1:6379>

#2. sunion key [key ...] : 並集
127.0.0.1:6379> sunion user:1:follow user:2:follow
1) "sports"
2) "his"
3) "music"
4) "ent"
5) "it"
6) "news"
127.0.0.1:6379>

#3. sdiff key [key ...] : 差集
127.0.0.1:6379> sdiff user:1:follow user:2:follow
1) "music"
2) "his"
127.0.0.1:6379> sdiff user:2:follow user:1:follow
1) "ent"
2) "news"
127.0.0.1:6379>

#4. 儲存集合運算之後結果集操作
sinterstore destination key [key ...]
sunionstore destination key [key ...]
sdiffstore destination key [key ...]

127.0.0.1:6379> sinterstore user:1_2:inter user:1:follow user:2:follow
(integer) 2
127.0.0.1:6379> smembers user:1_2:inter
1) "sports"
2) "it"
127.0.0.1:6379>

127.0.0.1:6379> sunionstore user:1_2:union user:1:follow user:2:follow
(integer) 6
127.0.0.1:6379> smembers user:1_2:union
1) "his"
2) "music"
3) "sports"
4) "ent"
5) "it"
6) "news"
127.0.0.1:6379>

127.0.0.1:6379> sdiffstore user:1_2:diff user:1:follow user:2:follow
(integer) 2
127.0.0.1:6379> smembers user:1_2:diff
1) "music"
2) "his"
127.0.0.1:6379>
[3] 時間複雜度

4.1.6 有序集合SortedSet

[1] CURD
-- zadd key [NX|XX] [CH] [INCR] score member [score member ...] : 新增成員
127.0.0.1:6379> zadd user:ranking 251 tom
(integer) 1
127.0.0.1:6379>

- NX: member 必須不存在才可以新增
- XX: member 必須存在才可以新增,用於更新
- CH: 返回操作後有序集合和分數發生變化的個數
- INCR: 對score做增加

-- zcard key : 計算成員個數
127.0.0.1:6379> zcard user:ranking
(integer) 1
127.0.0.1:6379>

-- zscore key member: 計算成員分數
127.0.0.1:6379> zscore user:ranking tom
"251"
127.0.0.1:6379>

--  zadd user:ranking 1 kris 91 mike 200 frank 220 tim 250 martin

-- 計算成員排名 
zrank key member : 分數從高到低的排名
zrevrank key member : 分數從低到高的排名

-- zrem key member : 刪除成員
127.0.0.1:6379> zrem user:ranking tim
(integer) 1
127.0.0.1:6379> 

-- zincrby key increment member : 增加成員分數
127.0.0.1:6379> zscore user:ranking tom
"251"
127.0.0.1:6379> zincrby user:ranking 8 tom
"259"
127.0.0.1:6379>

-- 返回指定排名範圍的成員
zrange key start stop [WITHSCORES]   : 按照分值從低到高返回
zrevrange key start stop [WITHSCORES]

127.0.0.1:6379> zrange user:ranking 0 2 withscores
1) "kris"
2) "1"
3) "mike"
4) "91"
5) "frank"
6) "200"
127.0.0.1:6379> zrange user:ranking 0 -1 withscores
 1) "kris"
 2) "1"
 3) "mike"
 4) "91"
 5) "frank"
 6) "200"
 7) "martin"
 8) "250"
 9) "tom"
10) "259"
127.0.0.1:6379>

-- 返回指定分數範圍的成員(+inf代表無限大,-inf代表無限小)
zrangebyscore key min max [WITHSCORES] [LIMIT offset count]
zrevrangebyscore key max min [WITHSCORES] [LIMIT offset count]

127.0.0.1:6379> zrangebyscore user:ranking 100 250
1) "frank"
2) "martin"
127.0.0.1:6379>

127.0.0.1:6379> zrevrangebyscore user:ranking 300 20
1) "tom"
2) "martin"
3) "frank"
4) "mike"
127.0.0.1:6379> zrevrangebyscore user:ranking 300 20 withscores
1) "tom"
2) "259"
3) "martin"
4) "250"
5) "frank"
6) "200"
7) "mike"
8) "91"
127.0.0.1:6379>
127.0.0.1:6379> zrangebyscore user:ranking 100 +inf
1) "frank"
2) "martin"
3) "tom"
127.0.0.1:6379>

-- zcount key min max : 返回指定分數範圍成員個數
127.0.0.1:6379> zcount user:ranking 100 200
(integer) 1
127.0.0.1:6379> 

-- zremrangebyrank key start stop : 刪除指定排名內升序元素
-- zremrangebyscore key min max : 刪除指定分數範圍的成員

[2] 集合間運算
zadd user:ranking:1 1 kris 91 mike 200 frank 220 tim  250 martin 251 tom
zadd user:ranking:2 8 james 77 mike 625 martin 888 tom

-- 交集: zinterstore destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]
- numkeys : 需要計算鍵的個數
- weitht: 每個鍵的權重,在集合運算時,每個成員將自己的分值乘以這個權重
- aggregate: 集合運算後,分值彙總函式,預設求和

127.0.0.1:6379> zinterstore user:ranking:1_inter_2 2 user:ranking:1 user:ranking:2
(integer) 3
127.0.0.1:6379> zrange user:ranking:1_inter_2 0 -1 withscores
1) "mike"
2) "168"
3) "martin"
4) "875"
5) "tom"
6) "1139"
127.0.0.1:6379>

127.0.0.1:6379> zinterstore user:ranking:1_inter_2 2 user:ranking:1 user:ranking:2 weights 1 0.5 aggregate max
(integer) 3
127.0.0.1:6379> zrange user:ranking:1_inter_2 0 -1 withscores
1) "mike"
2) "91"
3) "martin"
4) "312.5"
5) "tom"
6) "444"
127.0.0.1:6379>

-- 並集: zunionstore destination numkeys key [key ...] [weights weight [weight ...]] [aggregate sum|min|max]
127.0.0.1:6379> zunionstore user:ranking:1_union_2 2 user:ranking:1 user:ranking:2
(integer) 7
127.0.0.1:6379> zrange user:ranking:1_union_2 0 -1 withscores
 1) "kris"
 2) "1"
 3) "james"
 4) "8"
 5) "mike"
 6) "168"
 7) "frank"
 8) "200"
 9) "tim"
10) "220"
11) "martin"
12) "875"
13) "tom"
14) "1139"
127.0.0.1:6379>
[3] 時間複雜度

4.1.7 釋出訂閱

Publisher可以向不同的Channel釋出訊息,Subscriber接收訂閱頻道的訊息。

命令 描述
PUBLISH channel msg 將資訊 message 傳送到指定的頻道 channel
SUBSCRIBE channel [channel ...] 訂閱頻道,可以同時訂閱多個頻道
UNSUBSCRIBE [channel ...] 取消訂閱指定的頻道, 如果不指定頻道,則會取消訂閱所有頻道
PSUBSCRIBE pattern [pattern ...] 訂閱一個或多個符合給定模式的頻道,每個模式以 * 作為匹配符,比如 it* 匹配所有以 it 開頭的頻道( it.news 、 it.blog 、 it.tweets 等等), news.* 匹配所有以 news. 開頭的頻道( news.it 、 news.global.today 等等),諸如此類
PUNSUBSCRIBE [pattern [pattern ...]] 退訂指定的規則, 如果沒有引數則會退訂所有規則
PUBSUB subcommand [argument [argument ...]] 檢視訂閱與釋出系統狀態

注意:使用釋出訂閱模式實現的訊息佇列,當有客戶端訂閱channel後只能收到後續釋出到該頻道的訊息,之前傳送的不會快取,必須Provider和Consumer同時線上。

4.1.8 客戶端管理

[1] client

語法

127.0.0.1:6379> client help
 1) CLIENT <subcommand> arg arg ... arg. Subcommands are:
 2) id                     -- Return the ID of the current connection.
 3) getname                -- Return the name of the current connection.
 4) kill <ip:port>         -- Kill connection made from <ip:port>.
 5) kill <option> <value> [option value ...] -- Kill connections. Options are:
 6)      addr <ip:port>                      -- Kill connection made from <ip:port>
 7)      type (normal|master|replica|pubsub) -- Kill connections by type.
 8)      skipme (yes|no)   -- Skip killing current connection (default: yes).
 9) list [options ...]     -- Return information about client connections. Options:
10)      type (normal|master|replica|pubsub) -- Return clients of specified type.
11) pause <timeout>        -- Suspend all Redis clients for <timout> milliseconds.
12) reply (on|off|skip)    -- Control the replies sent to the current connection.
13) setname <name>         -- Assign the name <name> to the current connection.
14) unblock <clientid> [TIMEOUT|ERROR] -- Unblock the specified blocked client.
127.0.0.1:6379>

例項

127.0.0.1:6379> client list
id=38 addr=127.0.0.1:11614 fd=9 name= age=89171 idle=58113 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=NULL
id=41 addr=127.0.0.1:64820 fd=10 name= age=4681 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=26 qbuf-free=32742 obl=0 oll=0 omem=0 events=r cmd=client
id=42 addr=127.0.0.1:65108 fd=8 name= age=4501 idle=2592 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=georadiusbymember
id=43 addr=127.0.0.1:15293 fd=11 name= age=324 idle=324 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=NULL
id=46 addr=127.0.0.1:15495 fd=12 name= age=200 idle=143 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=get
127.0.0.1:6379>

# 說明
-- 1. 標識: id、 addr、 fd、 name
- id:   客戶端連線的唯一標識, 這個id是隨著Redis的連線自增的, 重啟Redis後會重置為0。
- addr: 客戶端連線的ip和埠
- fd:   socket的檔案描述符, 與lsof命令結果中的fd是同一個, 如果fd=-1代表當前客戶端不是外部客戶端, 而是Redis內部的偽裝客戶端
- name: 客戶端的名字, 後面的client setName和client getName兩個命令會對其進行說明

-- 2. 輸入緩衝區: qbuf、 qbuf-free
- qbuf :     輸入緩衝區的總容量
- qbuf-free: 輸入緩衝區剩餘容量
-- 輸入緩衝區使用不當會產生下面2個問題:
-- 1. 一旦某個客戶端的輸入緩衝區超過1G, 客戶端將會被關閉
-- 2. 輸入緩衝區不受maxmemory控制, 假設一個Redis例項設定了maxmemory為4G, 已經儲存了2G資料, 但是如果此時輸入緩衝區使用了3G, 已經超過maxmemory限制, 可能會產生資料丟失、 鍵值淘汰、 OOM等情況

-- 3. 輸出緩衝區: obl、 oll、 omem  # 輸出緩衝區的容量可以通過引數client-outputbuffer-limit來進行設定
- obl:  代表固定緩衝區的長度
- oll:  動態緩衝區列表的長度
- omem: 代表使用的位元組數

-- 調整輸出緩衝區 : client-output-buffer-limit <class> <hard limit> <soft limit> <soft seconds>  
127.0.0.1:6379> config get client-output-buffer-limit
1) "client-output-buffer-limit"
2) "normal 0 0 0 slave 268435456 67108864 60 pubsub 33554432 8388608 60"
127.0.0.1:6379> config set client-output-buffer-limit 'normal 20mb 10mb 120'
OK
127.0.0.1:6379> config get client-output-buffer-limit
1) "client-output-buffer-limit"
2) "normal 20971520 10485760 120 slave 268435456 67108864 60 pubsub 33554432 8388608 60"
127.0.0.1:6379>

- class: 客戶端型別, 分為三種。  normal: 普通客戶端; | slave: slave客戶端, 用於複製; | pubsub: 釋出訂閱客戶端
- hard limit: 如果客戶端使用的輸出緩衝區大於<hard limit>, 客戶端會被立即關閉
- <soft limit>和<soft seconds>: 如果客戶端使用的輸出緩衝區超過了<softlimit>並且持續了<soft limit>秒, 客戶端會被立即關閉

-- 預設配置
$ grep client-output redis.conf |grep -v "^#"
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit replica 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60

-- 4. 客戶端的存活狀態
- age:  當前客戶端已經連線的時間
- idle: 最近一次的空閒時間

-- 5. 客戶端型別 flag
- flag: 用於標識當前客戶端的型別
/*
flag=N 代表當前是普通客戶端
flag=M 代表當前客戶端是master客戶端
flag=S 代表當前客戶端是slave客戶端
flag=O 代表當前客戶端正在執行monitor命令
flag=x 代表當前客戶端正在執行事務
flag=b 代表當前客戶端正在等待阻塞事件
flag=i 代表當前客戶端正在等待VM I/O
flag=d 代表一個受監視的鍵已被刪除,EXEC命令將失敗
flag=u 代表當前客戶端未被阻塞
flag=c 代表恢復完整輸出後,關閉連線
flag=A 代表儘可能快關閉連線
**/

-- 6. 
- db: 當前客戶端正在使用的資料庫索引下標
- events: 檔案描述符事件(r/w):r和w分別表示客戶端套接字可讀和可寫。
- cmd: 當前客戶端最後一次執行的命令,不包含引數

-- 7. 客戶端的限制maxclients和timeout
127.0.0.1:6379> config get maxclients
1) "maxclients"
2) "10000"
127.0.0.1:6379>
127.0.0.1:6379> config get timeout
1) "timeout"
2) "0"
127.0.0.1:6379>
-- 設定超時限制,預設為0,即不檢測空閒狀態
127.0.0.1:6379> config set timeout 30
OK
127.0.0.1:6379> config rewrite
OK
127.0.0.1:6379> 

-- monitor命令用於監控Redis正在執行的命令

[2] info
127.0.0.1:6379> info clients
# Clients
connected_clients:1
client_recent_max_input_buffer:2
client_recent_max_output_buffer:0
blocked_clients:0
127.0.0.1:6379>

127.0.0.1:6379> info stats
# Stats
total_connections_received:50
total_commands_processed:415396
instantaneous_ops_per_sec:0
total_net_input_bytes:13036266
total_net_output_bytes:1509467
instantaneous_input_kbps:0.00
instantaneous_output_kbps:0.00
rejected_connections:0
sync_full:0
sync_partial_ok:0
sync_partial_err:0
expired_keys:2
expired_stale_perc:0.00
expired_time_cap_reached_count:0
evicted_keys:0
keyspace_hits:314
keyspace_misses:137871
pubsub_channels:0
pubsub_patterns:0
latest_fork_usec:258
migrate_cached_sockets:0
slave_expires_tracked_keys:0
active_defrag_hits:0
active_defrag_misses:0
active_defrag_key_hits:0
active_defrag_key_misses:0
127.0.0.1:6379> 

引數說明:
·total_connections_received: Redis自啟動以來處理的客戶端連線數總數。
·rejected_connections: Redis自啟動以來拒絕的客戶端連線數, 需要重點監控
[3] 客戶端引數配置
timeout: 檢測客戶端空閒連線的超時時間, 一旦idle時間達到了timeout, 客戶端將會被關閉, 如果設定為0就不進行檢測
maxclients: 客戶端最大連線數
tcp-keepalive: 檢測TCP連線活性的週期, 預設值為0, 也就是不進行檢測, 如果需要設定, 建議為60, 那麼Redis會每隔60秒對它建立的TCP連線進行活性檢測, 防止大量死連線佔用系統資源
tcp-backlog: TCP三次握手後, 會將接受的連線放入佇列中, tcpbacklog就是佇列的大小, 它在Redis中的預設值是511。
這個引數會受到作業系統的影響, 例如在Linux作業系統中, 如果/proc/sys/net/core/somaxconn小於tcp-backlog,建議將/proc/sys/net/core/somaxconn設定更大
[4] 對比info和client
命令 優點 缺點
client list 能定位每個客戶端來定位問題 執行速度慢,頻繁執行存在阻塞Redis的可能
info clients 執行過程比client快,過程簡單 不能精確定位
不能顯示所有輸入緩衝區的總量,只能顯示最大值

4.1.9 事務

Redis支援是弱事務。

事務命令
命令 描述
DISCARD 取消事務,放棄執行事務塊內的所有命令。
EXEC 執行所有事務塊內的命令。
MULTI 標記一個事務塊的開始。
UNWATCH 取消 WATCH 命令對所有 key 的監視。
WATCH key [key ...] 監視一個(或多個) key ,如果在事務執行之前這個(或這些) key 被其他命令所改動,那麼事務將被打斷。
示例
ZADD salary 2000 user1
ZADD salary 3000 user2
ZRANGE salary 0 -1 WITHSCORES
MULTI
ZINCRBY salary 1000 user1
ZINCRBY salary -1000 user2
EXEC

4.2 python API

4.2.1 字串API

#!/usr/bin/env python
# -*- ecoding: utf-8 -*-

import redis
# 建立redis資料庫連線物件
conn = redis.Redis(host='192.168.10.162', port=6379)

''' 1. 字串自增自減 '''

# 嘗試獲取一個不存在的鍵(key2)
print(conn.get('key2'))
# 將鍵(key2)儲存的值加1,並以字串的方式返回整數
print(conn.incr('key2'))
# 將鍵(key2)儲存的值加15,並以字串的方式返回整數
print(conn.incr('key2', 15))
# 將鍵(key2)儲存的值減5,並以字串的方式返回整數
print(conn.decr('key2', 5))

''' 2. 字串字串操作和二進位制位操作 '''

# 將值value追加到給定鍵當前儲存的值末尾,並返回當前字串長度
# append key-name value
print(conn.append('new-key2', 'hello '))
print(conn.get('new-key2'))

print(conn.append('new-key2', 'world!'))
print(conn.get('new-key2'))

# 獲取一個包含start至end偏移量範圍的所有字元組成的子串(字串下標從0開始)
# getrange key-name start end
print(conn.getrange('new-key2', 0, 4))

# 將從start偏移量開始的字串設定為給定值
# setrange key-name start-offset value
print(conn.setrange('new-key2', 0, 'H'))
print(conn.setrange('new-key2', 6, 'W'))

print(conn.get('new-key2'))

print(conn.setrange('new-key2', 11, ', how are you?'))
print(conn.get('new-key2'))


# 將字串看作二進位制位串,並將二進位制位串中偏移量為offset的二進位制位的值設定位value
# setbit key-name offset value
print(conn.setbit('other-key2', 2, 1))
print(conn.setbit('other-key2', 7, 1))

print(conn.get('other-key2'))

# 將字串看作二進位制位串,並返回給定偏移量的二進位制位的值
print(conn.getbit('other-key2', 2))

# 統計二進位制位串中值為1的數量
# bitcount key-name [start end]
print(conn.bitcount('other-key2'))

print(conn.bitcount('other-key2', 1, 10))

# 並(and)、或(OR)、異或(XOR)、非(NOT)的按位運算操作,並將結果儲存在dest-key鍵
# bitop operation dest-key key-name [key-name ...]
print(conn.bitop('XOR', 'result-key', 'other-key2'))

4.2.2 列表API

#!/usr/bin/env python
# -*- ecoding: utf-8 -*-
import redis
# 建立redis的連線物件
conn = redis.Redis(host='192.168.10.162', port=6379)

"""
    1. 列表常用命令
"""
# 向列表右側插入元素,並返回列表元素個數
# rpush key-name value
print(conn.rpush('list-key', 'last'))
# 向列表左側插入元素,並返回列表元素個數
# lpush key-name value
print(conn.lpush('list-key', 'first'))

print(conn.rpush('list-key', 'new last'))

# 將列表左側第一個元素彈出
# lpop key-name
print(conn.lpop('list-key'))

# 將列表右側第一個元素彈出
# rpop key-name
print(conn.rpop('list-key'))

# 返回指定範圍列表元素
print(conn.lrange('list-key', 0, -1))
# 刪減列表元素,並返回命令執行結果True/False
print(conn.ltrim('list-key', 0, 0))
print(conn.lrange('list-key', 0, -1))

# 返回指定下標的列表元素
# lindex key-name offset
print(conn.lindex('list-key', 0))

''' 2. 列表間運算 '''
# 2個列表中插入元素
print(conn.rpush('list1', 'item1'))
print(conn.rpush('list1', 'item2'))

print(conn.rpush('list2', 'item3'))

print(conn.lrange('list1', 0, -1))
print(conn.lrange('list2', 0, -1))

# brpoplpush source-key dest-key timeout
# 將source-key最右端元素彈出,然後插入到dest-key最左端,並返回該元素
print('------- brpoplpush list1 list2 1 ------- ')
print(conn.brpoplpush('list1', 'list2', 1))
print(conn.lrange('list1', 0, -1))
print(conn.lrange('list2', 0, -1))
print('------- brpoplpush list2 list1 1 ------- ')
print(conn.brpoplpush('list2', 'list1', 1))
print(conn.lrange('list2', 0, -1))
print(conn.lrange('list1', 0, -1))

# blpop key-name [key-name ...] timeout
# 在第一個非空列表中彈出最左元素或在timeout秒內阻塞並等待可彈出元素
print('------- blpop list1 ------- ')
print(conn.blpop('list1', 1))
print(conn.lrange('list1', 0, -1))
print(conn.lrange('list2', 0, -1))
print('------- blpop list1 list2 ------- ')
print(conn.blpop(['list1','list2'], 1))
print(conn.lrange('list1', 0, -1))
print(conn.lrange('list2', 0, -1))

# brpop key-name [key-name ...] timeout
# 在第一個非空列表中彈出最右元素或在timeout秒內阻塞並等待可彈出元素
print('------- brpop list2 ------- ')
print(conn.brpop('list1', 1))
print(conn.lrange('list1', 0, -1))

# rpoplpush source-key dest-key
# 彈出source-key最右端元素並插入到dest-key的最左端,並返回該元素
print('------- rpoplpush list2 list1 ------- ')
print(conn.rpoplpush('list2', 'list1'))
print(conn.lrange('list2', 0, -1))
print(conn.lrange('list1', 0, -1))

4.2.3 HASH API

#!/usr/bin/env python
# -*- ecoding: utf-8 -*-
import redis
# 建立redis的連線物件
conn = redis.Redis(host='192.168.10.162', port=6379)

# hmset key-name key value [key value ...]
# 為雜湊一個或多個鍵設定值

print(conn.hmset('hm1-key', {'k1':'v1', 'k2': 'v2'}))
print(conn.hmset('hm2-key', {'k1':'v1', 'k2': 'v2', 'k3': 'v3'}))

# hmget key-name key [key] 獲取一個或多個鍵的值
print(conn.hmget('hm1-key', 'k1','k2') )

# hdel key-name key [key...] 刪除雜湊中一個或多個鍵值對,並返回刪除鍵值對的數量
print(conn.hdel('hm2-key', 'k1', 'k2'))

# hlen key-name 返回雜湊中鍵值對的數量
print(conn.hlen('hm1-key'),conn.hlen('hm2-key'))

# hmset key-name key value [key value ...]
# 為雜湊一個或多個鍵設定值

print(conn.hmset('hm1-key', {'k1':'v1', 'k2': 'v2'}))
print(conn.hmset('hm2-key', {'k1':'v1', 'k2': 'v2', 'k3': 'v3'}))

# hmget key-name key [key] 獲取一個或多個鍵的值
print(conn.hmget('hm1-key', 'k1','k2'))
"""
# hdel key-name key [key...] 刪除雜湊中一個或多個鍵值對,並返回刪除鍵值對的數量
print(conn.hdel('hm2-key', 'k1', 'k2'))

# hlen key-name 返回雜湊中鍵值對的數量
print(conn.hlen('hm1-key'),conn.hlen('hm2-key'))
"""

# hexists key-name key 檢查給定鍵是否存在雜湊中
print(conn.hexists('hm1-key', 'k3'))
print(conn.hexists('hm2-key', 'k3'))

# hkeys key-name 獲取雜湊所有鍵
print(conn.hkeys('hm1-key'))
print(conn.hkeys('hm2-key'))

# hvals key-name 獲取雜湊包含的所有值
print(conn.hvals('hm1-key'))
print(conn.hvals('hm2-key'))

# hgetall key-name 獲取雜湊所有的鍵值對
print(conn.hgetall('hm1-key'))
print(conn.hgetall('hm2-key'))

# hincrby key-name key increment 將鍵儲存的值加上 increment
print(conn.hincrby('hm2-key', 10))
print(conn.hgetall('hm2-key'))

# hincrbyfloat key-name key increment 將鍵儲存的值加上浮點數increment
print(conn.hincrby('hm2-key', 10.1))
print(conn.hgetall('hm2-key'))

4.2.4 集合API

#!/usr/bin/env python
# -*- ecoding: utf-8 -*-
import redis
# 建立redis的連線物件
conn = redis.Redis(host='192.168.10.162', port=6379)

"""
    1. 集合常用命令
"""

# sadd key-name item [item ...]
# 將一個或多個元素新增到集合,並返回被新增元素且不存在原集合裡面的元素數量

print(conn.sadd('set-key', 'item1', 'item2'))
# result := 2
print(conn.sadd('set-key', 'item1', 'item2', 'item3','item4'))
# result := 2

# scard key-name 返回集合元素的數量
# smembers key-name 返回集合的所有元素
print(conn.scard('set-key'),conn.smembers('set-key'))

# srem key-name item [item ...] 從集合中移除一個或多個元素,並返回被移除元素的數量
print(conn.srem('set-key', 'item1', 'item1'))

print(conn.smembers('set-key'))

# sismember key-name item 檢查元素item是否存在於集合裡面
print(conn.sismember('set-key', 'item1'))
print(conn.sismember('set-key', 'item2'))

# srandmember key-name [count] 從集合裡隨機返回一個或多個元素
# 當count為正數時,返回不重複隨機元素,當count為負數時,返回隨機元素可能出現重複
print(' ------ srandmember set-key -------- ')
print(conn.srandmember('set-key', 2))
print(conn.srandmember('set-key', 2))
print(conn.srandmember('set-key', -2))
print(conn.srandmember('set-key', -2))


# spop key-name 從集合中隨機移除一個元素,並返回被移除的元素
print(conn.spop('set-key'))

# smove source-key dest-key item
# 從source-key中移除item,並將元素item新增到dest-key集合,如果成功移除item,則返回true,否則返回失敗
print(conn.sadd('set1-key', 'item1', 'item2'))
# result := 2
print(conn.sadd('set2-key', 'item3','item4'))
print(conn.smove('set1-key', 'set2-key', 'item'))
print(conn.smove('set1-key', 'set2-key', 'item2'))

print(conn.smembers('set1-key'),conn.smembers('set2-key'))

"""
    2. 集合組合命令
"""

print(conn.sadd('set1-key', 'item1', 'item2'))
print(conn.sadd('set2-key', 'item2', 'item3', 'item4'))
print(conn.sadd('set3-key', 'item3', 'item4', 'item5'))
print(conn.smembers('set1-key'),conn.smembers('set2-key'),conn.smembers('set3-key'))

# sdiff key-name [key-name ...] 返回存在於第一個集合但不存在其他集合中的元素
print(conn.sdiff('set1-key'))
print(conn.sdiff('set1-key', 'set2-key'))
print(conn.sdiff('set1-key', 'set2-key', 'set3-key'))


# sdiffstore dest-key key-name [key-name ...] 將存在於第一個集合但不存在其他集合中的元素儲存到dest-key中,並返回數量
print(conn.sdiffstore('set-key', 'set1-key', 'set2-key'))
print(conn.smembers('set-key'))

# sinter key-name [key-name ...] 返回同時存在於所有集合的元素
print(conn.sinter('set2-key', 'set3-key'))

# sinterstore dest-name key-name [key-name ...] 將同時存在於所有集合的元素儲存到dest-name中
print(conn.sinterstore('set-key', 'set2-key', 'set3-key'))
print(conn.smembers('set-key'))

# sunion key-name [key-name ...] 返回至少存在於一個集合中的元素(並集)
print(conn.sunion('set1-key', 'set2-key'))

# sunionstore dest-name key-name [key-name ...] 將至少存在於一個集合中的元素儲存到dest-name,並返回其數量
print(conn.sunionstore('set-key', 'set2-key', 'set3-key'))
print(conn.smembers('set-key'))

4.2.5 有序集合API

#!/usr/bin/env python
# -*- ecoding: utf-8 -*-
import redis
# 建立redis的連線物件
conn = redis.Redis(host='192.168.10.162', port=6379)

# zadd key-name score member [score member ...] 將給定分值的成員新增到有序集合,並返回其數量
print(conn.zadd('zset-key', {'a': 3, 'b': 2, 'c': 1}))

# zrange key-name start stop [withscores]
# 返回排名介於start和stop之間的成員,如果有可選項withscores,則將成員分值也一起返回
print(conn.zrange('zset-key', 0, -1, withscores=True))

# zcard key-name 返回有序集合成員數量
print(conn.zcard('zset-key'))

# zcount key-name min max 返回分值介於min 和 max的成員數量
print(conn.zcount('zset-key', 1, 2))

# zincrby key-name increment member 將member成員的分值加上increment
print(conn.zincrby('zset-key',20, 'c'))
print(conn.zrange('zset-key', 0, -1, withscores=True))

# zrank key-name member 返回成員member在有序集合中的排名
print(conn.zrank('zset-key', 'a'))

# zscore key-name member 返回成員member的分值
print(conn.zscore('zset-key', 'a'))
print(conn.zrange('zset-key', 0, -1, withscores=True))

# zrem key-name member [member ...] 在有序集合中移除給定的成員並返回被移除成員數量
print(conn.zrem('zset-key', 'c', 'b'))
print(conn.zrange('zset-key', 0, -1, withscores=True))

# zadd key-name score member [score member ...] 將給定分值的成員新增到有序集合,並返回其數量
print(conn.zadd('zset-key', {'a': 3, 'b': 2, 'c': 1, 'd':10}))

# zrange key-name start stop [withscores]
# 返回排名介於start和stop之間的成員,如果有可選項withscores,則將成員分值也一起返回
print(conn.zrange('zset-key', 0, -1, withscores=True))

'''
   2. 有序集合中範圍型資料獲取及操作命令
'''

# zrevrange key-name start stop [withscores]
# 返回給定排名範圍內成員,按照分值大到小排列
print(conn.zrevrange('zset-key', 0, -1))
print(conn.zrevrange('zset-key', 0, -1, withscores=True))

# zrevrank key-name member 返回有序集合成員member的排名,按照分值大到小排序
print(conn.zrevrank('zset-key', 'c'))

# zrangebyscore key min max [withscores] [limit offset count]
# 返回集合中分值介於min與max間的成員,按照分值從小到大返回(待確認返回順序)
print(conn.zrangebyscore('zset-key', 0, 2, withscores=True))

# zremrangebyrank key-name start stop
# 移除有序集合中排名介於start和stop間的成員,並返回移除成員數量
print(conn.zremrangebyrank('zset-key', 4, 10))
print(conn.zrevrange('zset-key', 0, -1, withscores=True))

# zremrangebyscore key-name min max
# 移除分值介於min和max間的成員,並返回移除成員數量
print(conn.zremrangebyscore('zset-key', 10, 100))
print(conn.zrevrange('zset-key', 0, -1, withscores=True))

# zinterstore dest-key key-count key [key ...] [weights weight [weight ...]] [aggregate sum|min|max]
# 對給定集合執行交集運算,按照分值從大到小返回
conn.zadd('zset-1', {'a': 1, 'b': 2, 'c': 3})
conn.zadd('zset-2', {'b': 4, 'c': 1, 'd': 0})

# aggregate預設值為求和sum
print(conn.zinterstore('zset-i', ['zset-1', 'zset-2']))
print(conn.zrevrange('zset-i', 0, -1, withscores=True))
print(conn.zinterstore('zset-j', ['zset-1', 'zset-2'], aggregate='max'))
print(conn.zrevrange('zset-j', 0, -1, withscores=True))

# zunionstore dest-key key-count key [key ...] [weights weight [weight ...]] [aggregate sum|min|max]
# 對給定集合執行並集運算,按照分值從大到小返回
print(conn.zunionstore('zset-m', ['zset-1', 'zset-2'], aggregate='sum'))
print(conn.zrevrange('zset-m', 0, -1, withscores=True))
print(conn.zunionstore('zset-n', ['zset-1', 'zset-2'], aggregate='max'))
print(conn.zrevrange('zset-n', 0, -1, withscores=True))

4.2.6 釋出訂閱API

#!/usr/bin/env python
# -*- ecoding: utf-8 -*-
import redis
import threading
import time

# 釋出訊息通道
def publisher(n):
    time.sleep(1)
    # 在channel上定期(每隔1秒)釋出一條訊息
    for i in xrange(n):
        conn.publish('channel', i)
        time.sleep(1)

# 訂閱
def run_pubsub():
    # 啟動釋出執行緒,併發送3條訊息
    threading.Thread(target=publisher, args=(3,)).start()
    # 建立釋出訂閱物件
    pubsub = conn.pubsub()
    # 訂閱給定的頻道
    pubsub.subscribe(['channel'])
    #
    count = 0
    # 遍歷函式來監聽訂閱訊息
    for item in pubsub.listen():
        # 列印輸出接收到的每條訊息
        print item
        count += 1
        if count == 4:
            # 在接收到一條訂閱反饋訊息和3條釋出者傳送的訊息後,執行退訂操作並停止監聽
            pubsub.unsubscribe()
        # 客戶端接收到退訂反饋資訊後不再接收訊息
        if count == 5:
            break

if __name__ == "__main__":
    # 建立redis的連線物件
    conn = redis.Redis(host='192.168.10.162', port=6379)
    # 呼叫函式
    run_pubsub()

4.2.7 處理過期時間

#!/usr/bin/env python
# -*- ecoding: utf-8 -*-
import redis
#  建立redis的連線物件
conn = redis.Redis(host='192.168.10.162', port=6379)

conn.set('hello', 'world!')
print(conn.get('hello'))

# expire key-name seconds 讓給定的鍵在指定的秒數後過期
conn.expire('hello', 10)

# ttl key-name 檢視指定鍵距離過期時間的秒數
print(conn.ttl('hello'))

# pttl key-name 檢視指定鍵距離過期時間的毫秒數
print(conn.pttl('hello'))

# persist key-name 移除鍵的過期時間
conn.persist('hello')
print(conn.ttl('hello'))

# expireat key-name timestamp 將給定鍵過期時間設定為給定unix時間戳
# unix 使用具體時間換算成時間戳: echo $(date --date="2018-12-12 01:00:00" +%s)
# 時間戳換算具體時間: echo $(date -d @1544547600)
conn.expireat('hello' ,'1544547600')
print(conn.pttl('hello'))

# pexpire key-name milliseconds 讓給定的鍵在指定的毫秒數後過期
conn.pexpire('hello', 100)
print(conn.pttl('hello'))

# expireat key-name timestamp-milliseconds 將給定鍵過期時間設定為給定毫秒級精度unix時間戳
conn.expireat('hello' ,'1544547600')
print(conn.pttl('hello'))

5. 高可用

5.1 複製(replication)[master-slave]

主節點負責寫資料,從節點負責讀資料,主節點定期把資料同步到從節點保證資料一致性。

5.1.1 主從複製流程

1)儲存主節點資訊

-- 從節點執行slaveof後只儲存主節點的地址資訊便直接返回, 這時建立複製流程還沒有開始, 在從節點6380執行info replication可以看到如下資訊
master_host:127.0.0.1
master_port:6379
master_link_status:down

-- redis日誌列印如下資訊:
REPLICAOF 127.0.0.1:6379 enabled (user request from 'id=4 addr=127.0.0.1:41352 fd=9 name= age=0 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=42 qbuf-free=32726 obl=0 oll=0 omem=0 events=r cmd=slaveof')

2)主從建立socket連線

-- 從節點(slave) 內部通過每秒執行的定時任務維護複製相關邏輯,當定時任務發現存在新的主節點後, 會嘗試與該節點建立網路連線

-- redis日誌列印如下資訊:
Connecting to MASTER 127.0.0.1:6379
MASTER <-> REPLICA sync started

3)傳送ping命令(心跳檢測)

連線建立成功後從節點發送ping請求進行首次通訊,發PING命令用於檢測主從之間網路套接字是否可用和檢測主節點當前是否可接受處理命令。
從節點沒有收到主節點的pong回覆或者超時, 比如網路超時或者主節點正在阻塞無法響應命令, 從節點會斷開復制連線, 下次定時任務會發起重連。

-- redis日誌列印如下資訊:
Master replied to PING, replication can continue

4)許可權驗證

​ 如果主節點設定了requirepass引數, 則需要密碼驗證,從節點必須配置masterauth引數保證與主節點相同的密碼才能通過驗證; 如果驗證失敗複製將終止, 從節點重新發起復制流程。

5)同步資料集(RDB)

​ 主從複製連線正常通訊後, 對於首次建立複製的場景, 主節點呼叫BGsave命令,並將它傳送給從節點。把持有的資料全部發送給從節點, 這部分操作是耗時最長的步驟。

-- redis日誌列印如下資訊:
Trying a partial resynchronization (request 475f32d8676d4d9e58cc4c29cce2503b78ff966c:997).
Full resync from master: 913a1700ed78f16e5bdfef2e46726b49f0b3432c:996

6)命令持續複製(command propagate)

​ 當主節點把當前的資料同步給從節點後, 便完成了複製的建立流程。 接下來主節點會持續地把寫命令傳送給從節點, 保證主從資料一致性。

5.1.2 主從複製部署

[1] 配置方式
# 1. 在配置檔案中加入slaveof {masterHost} {masterPort}隨Redis啟動生效。
cat >> 6380.conf <<EOF
# slave settings
slaveof 127.0.0.1 6379
EOF

# 2. 在redis-server啟動命令後加入--slaveof{masterHost}{masterPort}生效。
./bin/redis-server 6380.conf --slaveof 127.0.0.1 6379 &

# 3. 直接使用命令: slaveof {masterHost} {masterPort}生效。
./bin/redis-server 6380.conf &
./bin/redis-cli -p 6380
127.0.0.1:6380> slaveof 127.0.0.1 6379

提高資料安全性引數

min-slaves-to-write <number of slaves>
min-slaves-max-lag <number of seconds>
[2] 檢查狀態
-- master
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=192.168.10.181,port=6380,state=online,offset=688,lag=0
master_replid:913a1700ed78f16e5bdfef2e46726b49f0b3432c
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:688
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:688
127.0.0.1:6379> 

-- slave
127.0.0.1:6380> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:up
master_last_io_seconds_ago:0
master_sync_in_progress:0
slave_repl_offset:702
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:913a1700ed78f16e5bdfef2e46726b49f0b3432c
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:702
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:225
repl_backlog_histlen:478
127.0.0.1:6380>
[3] 斷開復制

斷開復制主要流程:

  • 斷開與主節點複製關係。
  • 從節點晉升為主節點。

從節點斷開復制後並不會拋棄原有資料, 只是無法再獲取主節點上的資料變化

-- slave 
127.0.0.1:6380> slaveof no one
OK
127.0.0.1:6380> info replication
# Replication
role:master
connected_slaves:0
master_replid:475f32d8676d4d9e58cc4c29cce2503b78ff966c
master_replid2:913a1700ed78f16e5bdfef2e46726b49f0b3432c
master_repl_offset:996
second_repl_offset:997
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:225
repl_backlog_histlen:772
127.0.0.1:6380> 

[4] 切主

切主指把當前從節點對主節點的複製切換到另一個主節點。

切主操作流程如下:

  • 斷開與舊主節點複製關係。
  • 與新主節點建立複製關係。
  • 刪除從節點當前所有資料。
  • 對新主節點進行復制操作。
slaveof {newMasterIP} {newMasterPort}

-- slave 
127.0.0.1:6380> slaveof 192.168.10.151 6379
[5] 資料同步階段

redis2.8後使用psync命令完成主從資料同步(sync命令仍保留),同步過程分成: 全量複製和部分複製。

- 1. 全量複製:一般用於初次複製場景, Redis早期(即2.8之前版本)支援的複製功能只有全量複製(sync), 它會把主節點全部資料一次性發送給從節點, 當資料量較大時, 會對主從節點和網路造成很大的開銷
- 2. 部分複製:用於處理在主從複製中因網路閃斷等原因造成的資料丟失場景, 當從節點再次連上主節點後, 如果條件允許, 主節點會補發丟失資料給從節點。 
因為補發的資料遠遠小於全量資料, 可以有效避免全量複製的過高開銷。
PSYNC 比起 SYNC 的最大改進在於 PSYNC 實現了部分重同步(partial resync)特性:在主從伺服器斷線並且重新連線的時候,只要條件允許,PSYNC 可以讓主伺服器只向從伺服器同步斷線期間缺失的資料,而不用重新向從伺服器同步整個資料庫。
[6] 心跳檢測

主從心跳判斷機制

1) 主從節點彼此都有心跳檢測機制, 各自模擬成對方的客戶端進行通訊, 通過client list命令檢視複製相關客戶端資訊, 主節點的連線狀態為flags=M, 從節點連線狀態為flags=S。

2) 主節點預設每隔10秒對從節點發送ping命令, 判斷從節點的存活性和連線狀態。 可通過引數repl-ping-slave-period控制傳送頻率。

3) 從節點在主執行緒中每隔1秒傳送replconf ack{offset}命令, 給主節點上報自身當前的複製偏移量。

replconf命令主要作用如下:

  • 實時監測主從節點網路狀態。

  • 上報自身複製偏移量, 檢查複製資料是否丟失, 如果從節點資料丟失, 再從主節點的複製緩衝區中拉取丟失資料。

  • ·實現保證從節點的數量和延遲性功能, 通過min-slaves-to-write、 min-slaves-max-lag引數配置定義。

    ​ 主節點根據replconf命令判斷從節點超時時間, 體現在info replication統計中的lag資訊中,lag表示與從節點最後一次通訊延遲的秒數, 正常延遲應該在0和1之間。 如果超過repl-timeout配置的值(預設60秒) , 則判定從節點下線並斷開復制客戶端連線。 即使主節點判定從節點下線後, 如果從節點重新恢復, 心跳檢測會繼續進行。

5.2 哨兵(sentinel)

引入哨兵架構模式的自動故障恢復,解決上面主從架構中,當發生主節點宕機,只能通過手工切換的方式來恢復故障。Redis哨兵負責監控主節點的健康,當主節點不可用時,會自動選擇一個從節點切換為主節點。

功能

  • 監控:
  • 通知:
  • 自動故障轉移:
  • 配置提供者:

sentinel的3個定時任務

1) 每隔10秒, 每個Sentinel節點會向主節點和從節點發送info命令獲取最新的拓撲結構

  • 通過向主節點執行info命令, 獲取從節點的資訊, 這也是為什麼Sentinel節點不需要顯式配置監控從節點。
  • 當有新的從節點加入時都可以立刻感知出來。
  • 節點不可達或者故障轉移後, 可以通過info命令實時更新節點拓撲資訊。

2)每隔2秒, 每個Sentinel節點會向Redis資料節點的__sentinel__: hello頻道上傳送該Sentinel節點對於主節點的判斷以及當前Sentinel節點的資訊
同時每個Sentinel節點也會訂閱該頻道, 來了解其他Sentinel節點以及它們對主節點的判斷, 所以這個定時任務可以完成以下兩個工作:

  • 發現新的Sentinel節點: 通過訂閱主節點的__sentinel__: hello瞭解其他的Sentinel節點資訊, 如果是新加入的Sentinel節點, 將該Sentinel節點資訊儲存起來, 並與該Sentinel節點建立連線。
  • Sentinel節點之間交換主節點的狀態, 作為後面客觀下線以及領導者舉的依據。

3) 每隔1秒, 每個Sentinel節點會向主節點、 從節點、 其餘Sentinel節點發送一條ping命令做一次心跳檢測, 來確認這些節點當前是否可達。
通過上面的定時任務, Sentinel節點對主節點、 從節點、 其餘Sentinel節點都建立起連線, 實現了對每個節點的監控, 這個定時任務是節點失敗判定的重要依據。

主觀下線和客觀下線

主觀下線:
每個Sentinel節點會每隔1秒(即上面的第3個定時任務)對主節點、 從節點、 其他Sentinel節點發送ping命令做心跳檢測, 當這些節點超過down-after-milliseconds沒有進行有效回覆,
Sentinel節點就會對該節點做失敗判定, 這個行為叫做主觀下線。

客觀下線:
當Sentinel主觀下線的節點是主節點時, 該Sentinel節點會通過sentinel ismaster-down-by-addr命令向其他Sentinel節點詢問對主節點的判斷, 當超過<quorum>個數,
Sentinel節點認為主節點確實有問題, 這時該Sentinel節點會做出客觀下線的決定, 這樣客觀下線的含義是比較明顯了, 也就是大部分Sentinel節點都對主節點的下線做了同意的判定,
那麼這個判定就是客觀的

sentinel領導者選舉

Redis使用了Raft演算法實現領導者選舉。Raft 是能夠實現分散式系統強一致性的演算法,每個系統節點有三種狀態 Follower(追隨者),Candidate(候選人),Leader(領導者)。實現 Raft 演算法兩個最重要的事是:選主和複製日誌。

  • 每個線上的Sentinel節點都有資格成為領導者, 當它確認主節點主觀下線時候, 會向其他Sentinel節點發送sentinel is-master-down-by-addr命令,要求將自己設定為領導者。
  • 收到命令的Sentinel節點, 如果沒有同意過其他Sentinel節點的sentinelis-master-down-by-addr命令, 將同意該請求, 否則拒絕。
  • 如果該Sentinel節點發現自己的票數已經大於等於max(quorum,num(sentinels) /2+1) , 那麼它將成為領導者。
  • 如果此過程沒有選舉出領導者, 將進入下一次選舉。

​ raft是一個共識演算法(consensus algorithm),所謂共識,就是多個節點對某個事情達成一致的看法,即使是在部分節點故障、網路延時、網路分割的情況下。

故障轉移

領導者選舉出的Sentinel節點負責故障轉移, 具體步驟如下:
1) 在從節點列表中選出一個節點作為新的主節點, 選擇方法如下:
a) 過濾: “不健康”(主觀下線、 斷線) 、 5秒內沒有回覆過Sentinel節點ping響應、 與主節點失聯超過down-after-milliseconds*10秒。
b) 選擇slave-priority(從節點優先順序) 最高的從節點列表, 如果存在則返回, 不存在則繼續。
c) 選擇複製偏移量最大的從節點(複製的最完整) , 如果存在則返回, 不存在則繼續。
d) 選擇runid最小的從節點
2) Sentinel領導者節點會對第一步選出來的從節點執行slaveof no one命令讓其成為主節點。
3) Sentinel領導者節點會向剩餘的從節點發送命令, 讓它們成為新主節點的從節點, 複製規則和parallel-syncs引數有關。
4) Sentinel節點集合會將原來的主節點更新為從節點, 並保持著對其關注, 當其恢復後命令它去複製新的主節點。

環境部署

Sentinel叢集可以監控多套不同的主從複製,即1對多關係。

拓撲結構

主要包括兩部分:Redis Sentinel 叢集和 Redis 資料叢集

物理結構
角色 IP port 別名
master 127.0.0.1 6379
slave-1 127.0.0.1 6380
slave-2 127.0.0.1 6381
sentinel-1 127.0.0.1 26379
sentinel-2 127.0.0.1 26380
sentinel-3 127.0.0.1 26381
軟體安裝配置
-- 1. master
redis-server 6379.conf

-- 2. slave
cd /ups/app/redis5
./bin/redis-server 6380.conf
./bin/redis-server 6381.conf

-- 3. 驗證主從
[root@progs redis5]# ps -ef|grep redis-server
root      10045      1  0 Nov18 ?        00:09:15 /ups/app/redis5/bin/redis-server 192.168.10.181:6379
root      27112      1  0 08:25 ?        00:00:00 ./bin/redis-server 192.168.10.181:6380
root      27124      1  0 08:26 ?        00:00:00 ./bin/redis-server 192.168.10.181:6381
root      27141  26762  0 08:26 pts/0    00:00:00 grep --color=auto redis-server

[root@progs redis5]# rcli -p 6380 ping
PONG
[root@progs redis5]# rcli -p 6381 ping
PONG

-- master
[root@progs redis5]# rcli -h 127.0.0.1 -p 6379 info replication
# Replication
role:master
connected_slaves:2
slave0:ip=192.168.10.181,port=6380,state=online,offset=122474,lag=1
slave1:ip=192.168.10.181,port=6381,state=online,offset=122474,lag=0
master_replid:913a1700ed78f16e5bdfef2e46726b49f0b3432c
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:122474
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:122474
[root@progs redis5]# 

-- slave
[root@progs redis5]# rcli -h 127.0.0.1 -p 6380 info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:up
master_last_io_seconds_ago:1
master_sync_in_progress:0
slave_repl_offset:122740
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:913a1700ed78f16e5bdfef2e46726b49f0b3432c
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:122740
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:122349
repl_backlog_histlen:392
[root@progs redis5]# 

-- slave-2
[root@progs redis5]# rcli -h 127.0.0.1 -p 6381 info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:up
master_last_io_seconds_ago:7
master_sync_in_progress:0
slave_repl_offset:122754
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:913a1700ed78f16e5bdfef2e46726b49f0b3432c
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:122754
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:122349
repl_backlog_histlen:406

-- 4. 部署sentinel
-- sentinel-1
[root@progs redis5]# grep -Ev "^#|^$" 26379.conf 
port 26379
daemonize yes
pidfile /var/run/redis-sentinel-26379.pid
logfile "/ups/app/redis5/logs/sentinel-26379.log"
dir "/ups/data/redisdata"
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000
sentinel deny-scripts-reconfig yes
[root@progs redis5]# 
-- sentinel-2
[root@progs redis5]# grep -Ev "^#|^$" 26380.conf
port 26380
daemonize yes
pidfile /var/run/redis-sentinel-26380.pid
logfile "/ups/app/redis5/logs/sentinel-26380.log"
dir "/ups/data/redisdata"
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000
sentinel deny-scripts-reconfig yes

-- sentinel-2
[root@progs redis5]# grep -Ev "^#|^$" 26381.conf
port 26381
daemonize yes
pidfile /var/run/redis-sentinel-26381.pid
logfile "/ups/app/redis5/logs/sentinel-26381.log"
dir "/ups/data/redisdata"
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000
sentinel deny-scripts-reconfig yes
[root@progs redis5]# 

-- 啟動sentinel
cd /ups/app/redis5
./bin/redis-sentinel 26379.conf
./bin/redis-sentinel 26380.conf
./bin/redis-sentinel 26381.conf
or 
redis-server 26379.conf --sentinel

-- 5. 驗證確認
-- redis-cli -h 127.0.0.1 -p 26379 info Sentinel
[root@progs redis5]# rcli -h 127.0.0.1 -p 26379 info Sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=127.0.0.1:6379,slaves=2,sentinels=3
[root@progs redis5]#

-- 6. sentinel啟動後,會自動從主節點發現獲取有關從節點和其餘sentinel節點資訊,且更新配置檔案
[root@progs redis5]# grep -Ev "^#|^$" 26379.conf
port 26379
daemonize yes
pidfile "/var/run/redis-sentinel-26379.pid"
logfile "/ups/app/redis5/logs/sentinel-26379.log"
dir "/ups/data/redisdata"
sentinel myid 07b1e48cc7722621f1bebe64f2f84c9e03d6fbdd
sentinel deny-scripts-reconfig yes
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel config-epoch mymaster 0
sentinel leader-epoch mymaster 0
protected-mode no
sentinel known-replica mymaster 192.168.10.181 6381
sentinel known-replica mymaster 192.168.10.181 6380
sentinel known-sentinel mymaster 127.0.0.1 26381 46ca78c18e06a7f1d9a79df2dddef8626bd50c61
sentinel known-sentinel mymaster 127.0.0.1 26380 acbcd245e4e1aac9ae7a6527218bd5295209a9a2
sentinel current-epoch 0
[root@progs redis5]# 
引數詳解
-- 1. sentinel monitor
sentinel monitor <master-name> <ip> <port> <quorum>
# Sentinel節點會定期監控主節點

-- 2. 當所有sentinel節點啟動後,配置檔案發生變化:
- 1. 去掉了預設配置, 例如parallel-syncs、 failover-timeout、down-after-milliseconds引數
- 2. 新增相關配置,例如known-replica,known-sentinel

-- 3. <quorum>引數用於故障發現和判定
# quorum 至少要有max(quorum, num(sentinels) /2+1) 個Sentinel節點參與選舉, 才能選出領導者Sentinel, 從而完成故障轉移

-- 4. down-after-milliseconds
sentinel down-after-milliseconds <master-name> <times>
# 每個Sentinel節點都要通過定期傳送ping命令來判斷Redis資料節點和其餘Sentinel節點是否可達, 如果超過了down-after-milliseconds配置的時間且沒
有有效的回覆, 則判定節點不可達, <times>(單位為毫秒) 就是超時時間。

-- 5. parallel-syncs
sentinel parallel-syncs <master-name> <nums>
# 當Sentinel節點集合對主節點故障判定達成一致時, Sentinel領導者節點會做故障轉移操作, 選出新的主節點, 原來的從節點會向新的主節點發起復制操作, 
parallel-syncs就是用來限制在一次故障轉移之後, 每次向新的主節點發起復制操作的從節點個數。 如果這個引數配置的比較大, 那麼多個從節點會向新的主節點同時發起複製操作,
 儘管複製操作通常不會阻塞主節點,但是同時向主節點發起復制, 必然會對主節點所在的機器造成一定的網路和磁碟IO開銷.
 
 -- 6. failover-timeout
 sentinel failover-timeout <master-name> <times>
 # failover-timeout通常被解釋成故障轉移超時時間, 但實際上它作用於故障轉移的各個階段:
    a) 選出合適從節點。
    b) 晉升選出的從節點為主節點。
    c) 命令其餘從節點複製新的主節點。
    d) 等待原主節點恢復後命令它去複製新的主節點。
  failover-timeout的作用具體體現在四個方面:
    1) 如果Redis Sentinel對一個主節點故障轉移失敗, 那麼下次再對該主節點做故障轉移的起始時間是failover-timeout的2倍。
    2) 在b) 階段時, 如果Sentinel節點向a) 階段選出來的從節點執行slaveof no one一直失敗(例如該從節點此時出現故障) , 當此過程超過failover-timeout時, 則故障轉移失敗。
    3) 在b) 階段如果執行成功, Sentinel節點還會執行info命令來確認a)階段選出來的節點確實晉升為主節點, 如果此過程執行時間超過failovertimeout時, 則故障轉移失敗。
    4) 如果c) 階段執行時間超過了failover-timeout(不包含複製時間),則故障轉移失敗。 注意即使超過了這個時間, Sentinel節點也會最終配置從節點去同步最新的主節點。
    
-- 7. auth-pass
sentinel auth-pass <master-name> <password>
# 如果Sentinel監控的主節點配置了密碼, sentinel auth-pass配置通過新增主節點的密碼, 防止Sentinel節點對主節點無法監控    

-- 8. notification-script
sentinel notification-script <master-name> <script-path>
# 在故障轉移期間, 當一些警告級別的Sentinel事件發生(指重要事件, 例如-sdown: 客觀下線、 -odown: 主觀下線) 時, 會觸發對應路徑的指令碼, 並向指令碼傳送相應的事件引數。

-- 9. client-reconfig-script
# 在故障轉移結束後, 會觸發對應路徑的指令碼, 並向指令碼傳送故障轉移結果的相關引數
監控多套環境配置模板
# 結構
sentinel monitor [master name] [master ip] [master port] [quorum]
sentinel down-after-milliseconds [master name] 60000
sentinel failover-timeout [master name] 180000
sentinel parallel-syncs [master name] 1


# 舉例
# master-business-1
sentinel monitor master-business-1 10.10.xx.1 6379 2
sentinel down-after-milliseconds master-business-1 60000
sentinel failover-timeout master-business-1 180000
sentinel parallel-syncs master-business-1 1
# master-business-2
sentinel monitor master-business-2 10.16.xx.2 6380 2
sentinel down-after-milliseconds master-business-2 10000
sentinel failover-timeout master-business-2 180000
sentinel parallel-syncs master-business-2 1

Sentinel引數調整
-- 動態調整引數,並立刻更新配置檔案,set 命令只對當前節點生效
[root@progs redis5]# rcli -h 127.0.0.1 -p 26380
127.0.0.1:26380> sentinel set mymaster quorum 3
OK
127.0.0.1:26380> sentinel set mymaster quorum 2
OK
127.0.0.1:26380>
引數 樣例
quorum sentinel set mymaster quorum 2
down-after-milliseconds sentinel set mymaster down-after-milliseconds 30000
failover-timeout sentinel set mymaster failover-timeout 360000
parallel-syncs sentinel set mymaster parallel-syncs 2
notification-script sentinel set mymaster notification-script /opt/xx.sh
client-reconfig-script sentinel set mymaster client-reconfig-script /opt/yy.sh
auth-pass sentinel set mymaster auth-pass masterPassword
API 管理
-- 1. sentinel masters: 展示所有被監控的主節點狀態以及相關的統計資訊
127.0.0.1:26380> sentinel masters
1)  1) "name"
    2) "mymaster"
    3) "ip"
    4) "127.0.0.1"
    5) "port"
    6) "6379"
......

-- 2. sentinel master <master name> : 展示指定<master name>的主節點狀態以及相關的統計資訊
127.0.0.1:26380> sentinel master mymaster
 1) "name"
 2) "mymaster"
 3) "ip"
 4) "127.0.0.1"
 5) "port"
 6) "6379"


-- 3. sentinel slves <master name> : 展示指定<master name>的從節點狀態以及相關的統計資訊
127.0.0.1:26380> sentinel slaves mymaster
1)  1) "name"
    2) "192.168.10.181:6380"
    3) "ip"
    4) "192.168.10.181"
    5) "port"
    6) "6380"
    ......
2)  1) "name"
    2) "192.168.10.181:6381"
    3) "ip"
    4) "192.168.10.181"
    5) "port"
    6) "6381"

 -- 4. sentinel sentinels <master name> : 展示指定<master name>的Sentinel節點集合(不包含當前Sentinel節點)
 127.0.0.1:26380> sentinel sentinels mymaster
1)  1) "name"
    2) "46ca78c18e06a7f1d9a79df2dddef8626bd50c61"
    3) "ip"
    4) "127.0.0.1"
    5) "port"
    6) "26381"
    ...
2)  1) "name"
    2) "07b1e48cc7722621f1bebe64f2f84c9e03d6fbdd"
    3) "ip"
    4) "127.0.0.1"
    5) "port"
    6) "26379"

  -- 5. sentinel get-master-addr-by-name <master name>:返回指定<master name>主節點的IP地址和埠
  127.0.0.1:26380> sentinel get-master-addr-by-name mymaster
1) "127.0.0.1"
2) "6379"
127.0.0.1:26380>

-- 6. sentinel reset <pattern> : 
# 當前Sentinel節點對符合<pattern>(萬用字元風格) 主節點的配置進行重置, 包含清除主節點的相關狀態(例如故障轉移) , 重新發現從節點和Sentinel節點

-- 7. sentinel failover <master name>: 對指定<master name>主節點進行強制故障轉移(沒有和其他Sentinel節點“協商”) , 當故障轉移完成後, 其他Sentinel節點按照故障轉移的結果更新自身配置

-- 8. sentinel ckquorum <master name> : 檢測當前可達的Sentinel節點總數是否達到<quorum>的個數。
127.0.0.1:26380> sentinel ckquorum mymaster
OK 3 usable Sentinels. Quorum and failover authorization can be reached
127.0.0.1:26380> 

-- 9. sentinel flushconfig : 將Sentinel節點的配置強制刷到磁碟上

-- 10. sentinel remove <master name> : 取消當前Sentinel節點對於指定<master name>主節點的監控

-- 11. sentinel monitor <master name> <ip> <port> <quorum>: 

-- 12. sentinel set <master name>: 動態修改Sentinel節點配置選項

-- 13. sentinel is-master-down-by-addr: Sentinel節點之間用來交換對主節點是否下線的判斷
故障轉移模擬實驗
模擬主節點宕機
-- 1. kill 主節點程序
[root@progs redis5]# ps -ef|grep redis-
root      10045      1  0 Nov18 ?        00:10:28 /ups/app/redis5/bin/redis-server 192.168.10.181:6379
root      27112      1  0 08:25 ?        00:01:05 ./bin/redis-server 192.168.10.181:6380
root      27124      1  0 08:26 ?        00:01:06 ./bin/redis-server 192.168.10.181:6381
root      30149      1  0 09:13 ?        00:03:01 ./bin/redis-sentinel *:26379 [sentinel]
root      31029      1  0 09:26 ?        00:02:59 ./bin/redis-sentinel *:26380 [sentinel]
root      31037      1  0 09:26 ?        00:02:58 ./bin/redis-sentinel *:26381 [sentinel]
root      36698  29590  0 10:56 pts/1    00:00:00 /ups/app/redis5/bin/redis-cli -h 127.0.0.1 -p 26380
root      49862  25666  0 14:25 pts/4    00:00:00 /ups/app/redis5/bin/redis-cli -p 6379
root      52957  26762  0 15:14 pts/0    00:00:00 grep --color=auto redis-
[root@progs redis5]# kill -9 10045
[root@progs redis5]# ps -ef|grep redis-
root      27112      1  0 08:25 ?        00:01:05 ./bin/redis-server 192.168.10.181:6380
root      27124      1  0 08:26 ?        00:01:06 ./bin/redis-server 192.168.10.181:6381
root      30149      1  0 09:13 ?        00:03:01 ./bin/redis-sentinel *:26379 [sentinel]
root      31029      1  0 09:26 ?        00:02:59 ./bin/redis-sentinel *:26380 [sentinel]
root      31037      1  0 09:26 ?        00:02:58 ./bin/redis-sentinel *:26381 [sentinel]
root      36698  29590  0 10:56 pts/1    00:00:00 /ups/app/redis5/bin/redis-cli -h 127.0.0.1 -p 26380
root      49862  25666  0 14:25 pts/4    00:00:00 /ups/app/redis5/bin/redis-cli -p 6379
root      52987  26762  0 15:14 pts/0    00:00:00 grep --color=auto redis-
[root@progs redis5]# date
Thu Nov 21 15:14:59 CST 2019
[root@progs redis5]#

-- 2. 6380 節點
  (15:14:44: 發現主節點失聯)
27112:S 21 Nov 2019 08:25:59.126 * Background AOF rewrite finished successfully
27112:S 21 Nov 2019 15:14:44.053 # Connection with master lost.
27112:S 21 Nov 2019 15:14:44.053 * Caching the disconnected master state.
27112:S 21 Nov 2019 15:14:44.584 * Connecting to MASTER 127.0.0.1:6379
27112:S 21 Nov 2019 15:14:44.584 * MASTER <-> REPLICA sync started
27112:S 21 Nov 2019 15:14:44.584 # Error condition on socket for SYNC: Connection refused
27112:S 21 Nov 2019 15:14:45.589 * Connecting to MASTER 127.0.0.1:6379
27112:S 21 Nov 2019 15:14:45.590 * MASTER <-> REPLICA sync started
27112:S 21 Nov 2019 15:14:45.590 # Error condition on socket for SYNC: Connection refused

  (15:15:14: 接到Sentinel節點的命令: 清理原來快取的主節點狀態, Sentinel節點將6380節點晉升為主節點, 並重寫配置)
27112:S 21 Nov 2019 15:15:13.756 # Error condition on socket for SYNC: Connection refused
27112:M 21 Nov 2019 15:15:14.396 # Setting secondary replication ID to 913a1700ed78f16e5bdfef2e46726b49f0b3432c, valid up to offset: 4293056. New replication ID is 0be0eab2c4b1150b42d1d3d66ab87fb3ef27b08d
27112:M 21 Nov 2019 15:15:14.396 * Discarding previously cached master state.
27112:M 21 Nov 2019 15:15:14.396 * MASTER MODE enabled (user request from 'id=10 addr=192.168.10.181:11474 fd=13 name=sentinel-46ca78c1-cmd age=20902 idle=0 flags=x db=0 sub=0 psub=0 multi=3 qbuf=154 qbuf-free=32614 obl=36 oll=0 omem=0 events=r cmd=exec')
27112:M 21 Nov 2019 15:15:14.401 # CONFIG REWRITE executed with success.

  ( 15:15:14: 6381節點發來了複製請求)
27112:M 21 Nov 2019 15:15:14.463 * Replica 192.168.10.181:6381 asks for synchronization
27112:M 21 Nov 2019 15:15:14.463 * Partial resynchronization request from 192.168.10.181:6381 accepted. Sending 166 bytes of backlog starting from offset 4293056.

-- 3. 6381 節點
  (15:14:44: 發現主節點失聯)
27124:S 21 Nov 2019 08:26:04.904 * Background AOF rewrite finished successfully
27124:S 21 Nov 2019 15:14:44.053 # Connection with master lost.
27124:S 21 Nov 2019 15:14:44.053 * Caching the disconnected master state.
27124:S 21 Nov 2019 15:14:44.284 * Connecting to MASTER 127.0.0.1:6379
27124:S 21 Nov 2019 15:14:44.284 * MASTER <-> REPLICA sync started
27124:S 21 Nov 2019 15:14:44.284 # Error condition on socket for SYNC: Connection refused

  (15:15:14: 接到Sentinel節點的命令,  讓它去複製新的主節點)
27124:S 21 Nov 2019 15:15:13.452 # Error condition on socket for SYNC: Connection refused
27124:S 21 Nov 2019 15:15:14.452 * REPLICAOF 192.168.10.181:6380 enabled (user request from 'id=10 addr=192.168.10.181:39479 fd=13 name=sentinel-46ca78c1-cmd age=20902 idle=0 flags=x db=0 sub=0 psub=0 multi=3 qbuf=297 qbuf-free=32471 obl=36 oll=0 omem=0 events=r cmd=exec')
27124:S 21 Nov 2019 15:15:14.459 # CONFIG REWRITE executed with success.

  (15:15:14: 向新的主節點(6380節點) 發起複製操作)
27124:S 21 Nov 2019 15:15:14.459 * Connecting to MASTER 192.168.10.181:6380
27124:S 21 Nov 2019 15:15:14.460 * MASTER <-> REPLICA sync started
27124:S 21 Nov 2019 15:15:14.460 * Non blocking connect for SYNC fired the event.
27124:S 21 Nov 2019 15:15:14.461 * Master replied to PING, replication can continue...
27124:S 21 Nov 2019 15:15:14.462 * Trying a partial resynchronization (request 913a1700ed78f16e5bdfef2e46726b49f0b3432c:4293056).
27124:S 21 Nov 2019 15:15:14.464 * Successful partial resynchronization with master.
27124:S 21 Nov 2019 15:15:14.464 # Master replication ID changed to 0be0eab2c4b1150b42d1d3d66ab87fb3ef27b08d
27124:S 21 Nov 2019 15:15:14.464 * MASTER <-> REPLICA sync: Master accepted a Partial Resynchronization.

-- 4. sentinel-1
  (15:15:14.118: 主觀下線, Sentinel節點更新自己的配置紀元(new-epoch))
30149:X 21 Nov 2019 15:14:43.851 * +sentinel-address-switch master mymaster 127.0.0.1 6379 ip 192.168.10.181 port 26381 for 46ca78c18e06a7f1d9a79df2dddef8626bd50c61
30149:X 21 Nov 2019 15:15:14.118 # +sdown master mymaster 127.0.0.1 6379
30149:X 21 Nov 2019 15:15:14.187 # +new-epoch 1
  (投票給)
30149:X 21 Nov 2019 15:15:14.188 # +vote-for-leader 46ca78c18e06a7f1d9a79df2dddef8626bd50c61 1
30149:X 21 Nov 2019 15:15:14.452 # +config-update-from sentinel 46ca78c18e06a7f1d9a79df2dddef8626bd50c61 192.168.10.181 26381 @ mymaster 127.0.0.1 6379
  (6380 成為主節點,發現2個從節點, 然後6379主觀下線)
30149:X 21 Nov 2019 15:15:14.452 # +switch-master mymaster 127.0.0.1 6379 192.168.10.181 6380
30149:X 21 Nov 2019 15:15:14.453 * +slave slave 192.168.10.181:6381 192.168.10.181 6381 @ mymaster 192.168.10.181 6380
30149:X 21 Nov 2019 15:15:14.453 * +slave slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 192.168.10.181 6380
30149:X 21 Nov 2019 15:15:44.484 # +sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 192.168.10.181 6380

-- 5. sentinel-2
31029:X 21 Nov 2019 15:15:14.150 # +sdown master mymaster 127.0.0.1 6379
31029:X 21 Nov 2019 15:15:14.187 # +new-epoch 1
31029:X 21 Nov 2019 15:15:14.188 # +vote-for-leader 46ca78c18e06a7f1d9a79df2dddef8626bd50c61 1
31029:X 21 Nov 2019 15:15:14.213 # +odown master mymaster 127.0.0.1 6379 #quorum 3/2
31029:X 21 Nov 2019 15:15:14.213 # Next failover delay: I will not start a failover before Thu Nov 21 15:21:14 2019
31029:X 21 Nov 2019 15:15:14.455 # +config-update-from sentinel 46ca78c18e06a7f1d9a79df2dddef8626bd50c61 192.168.10.181 26381 @ mymaster 127.0.0.1 6379
31029:X 21 Nov 2019 15:15:14.455 # +switch-master mymaster 127.0.0.1 6379 192.168.10.181 6380
31029:X 21 Nov 2019 15:15:14.456 * +slave slave 192.168.10.181:6381 192.168.10.181 6381 @ mymaster 192.168.10.181 6380
31029:X 21 Nov 2019 15:15:14.456 * +slave slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 192.168.10.181 6380
31029:X 21 Nov 2019 15:15:44.478 # +sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 192.168.10.181 6380

-- 6. sentinel-3
  (15:15:14: 達到客觀下線條件)
31037:X 21 Nov 2019 15:15:14.119 # +sdown master mymaster 127.0.0.1 6379
31037:X 21 Nov 2019 15:15:14.185 # +odown master mymaster 127.0.0.1 6379 #quorum 2/2
31037:X 21 Nov 2019 15:15:14.185 # +new-epoch 1
  (sentinel-3 被選為領導者)
31037:X 21 Nov 2019 15:15:14.185 # +try-failover master mymaster 127.0.0.1 6379
31037:X 21 Nov 2019 15:15:14.186 # +vote-for-leader 46ca78c18e06a7f1d9a79df2dddef8626bd50c61 1
31037:X 21 Nov 2019 15:15:14.188 # 07b1e48cc7722621f1bebe64f2f84c9e03d6fbdd voted for 46ca78c18e06a7f1d9a79df2dddef8626bd50c61 1
31037:X 21 Nov 2019 15:15:14.188 # acbcd245e4e1aac9ae7a6527218bd5295209a9a2 voted for 46ca78c18e06a7f1d9a79df2dddef8626bd50c61 1
31037:X 21 Nov 2019 15:15:14.249 # +elected-leader master mymaster 127.0.0.1 6379
  (尋找合適的從節點作為新的主節點 6380)
31037:X 21 Nov 2019 15:15:14.249 # +failover-state-select-slave master mymaster 127.0.0.1 6379
31037:X 21 Nov 2019 15:15:14.332 # +selected-slave slave 192.168.10.181:6380 192.168.10.181 6380 @ mymaster 127.0.0.1 6379
  (命令6380節點執行slaveof no one, 使其成為主節點)
31037:X 21 Nov 2019 15:15:14.333 * +failover-state-send-slaveof-noone slave 192.168.10.181:6380 192.168.10.181 6380 @ mymaster 127.0.0.1 6379
  (等待6380節點晉升為主節點)
31037:X 21 Nov 2019 15:15:14.395 * +failover-state-wait-promotion slave 192.168.10.181:6380 192.168.10.181 6380 @ mymaster 127.0.0.1 6379
  (確認6380節點已經晉升為主節點)
31037:X 21 Nov 2019 15:15:14.403 # +promoted-slave slave 192.168.10.181:6380 192.168.10.181 6380 @ mymaster 127.0.0.1 6379
  (故障轉移進入重新配置從節點階段)
31037:X 21 Nov 2019 15:15:14.403 # +failover-state-reconf-slaves master mymaster 127.0.0.1 6379
  (命令6381節點複製新的主節點)
31037:X 21 Nov 2019 15:15:14.452 * +slave-reconf-sent slave 192.168.10.181:6381 192.168.10.181 6381 @ mymaster 127.0.0.1 6379
  (正在重新配置成為6380節點的從節點, 但是同步過程尚未完成)
31037:X 21 Nov 2019 15:15:14.772 * +slave-reconf-inprog slave 192.168.10.181:6381 192.168.10.181 6381 @ mymaster 127.0.0.1 6379
  (6381節點完成對6380節點的同步)
31037:X 21 Nov 2019 15:15:14.772 * +slave-reconf-done slave 192.168.10.181:6381 192.168.10.181 6381 @ mymaster 127.0.0.1 6379
  (故障轉移順利完成)
31037:X 21 Nov 2019 15:15:14.849 # +failover-end master mymaster 127.0.0.1 6379
  (故障轉移成功後, 釋出主節點的切換訊息)
31037:X 21 Nov 2019 15:15:14.849 # +switch-master mymaster 127.0.0.1 6379 192.168.10.181 6380
31037:X 21 Nov 2019 15:15:14.850 * +slave slave 192.168.10.181:6381 192.168.10.181 6381 @ mymaster 192.168.10.181 6380
31037:X 21 Nov 2019 15:15:14.850 * +slave slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 192.168.10.181 6380
31037:X 21 Nov 2019 15:15:44.867 # +sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 192.168.10.181 6380

主動切換

-- 6380
[root@progs ~]# rcli -p 6380
127.0.0.1:6380> config set slave-priority 0
OK
127.0.0.1:6380> config rewrite
OK
127.0.0.1:6380> exit

[root@progs ~]# rcli -p 6381
127.0.0.1:6381> config set slave-priority 0
OK
127.0.0.1:6381> config rewrite
OK
127.0.0.1:6381> exit

-- failover
[root@progs logs]# rcli -p 26379 
127.0.0.1:26379> sentinel failover mymaster
OK
127.0.0.1:26379> 

-- 檢查
for i in 6379 6380 6381; do 
    echo -e $i
    rcli -p $i info replication|grep role
    rcli -p $i config get slave-priority
done
Sentinel釋出訂閱

5.3 叢集(cluster)

Redis Cluster是Redis的分散式解決方案, 在3.0版本正式推出, 有效地解決了Redis分散式方面的需求。Redis3.0版本之前,通過Redis Sentinel(哨兵)來實現高可用(HA)。

Redis Cluster 叢集節點最小配置 6 個節點以上(3主3從),其中主節點提供讀寫操作,從節點作為備用節點,不提供請求,只作為故障轉移使用。

Redis Cluster 採用虛擬槽分割槽,所有的鍵根據雜湊函式對映到 0~16383 個整數槽內,每個節點負責維護一部分槽以及槽所對映的鍵值資料。

環境部署

手動敲命令部署Cluster叢集
-- 1. 配置6個節點
cd /ups/app/redis5/config
cp redis-cluster-6379.conf redis-cluster-6380.conf 
cp redis-cluster-6379.conf redis-cluster-6381.conf 
cp redis-cluster-6379.conf redis-cluster-6382.conf 
cp redis-cluster-6379.conf redis-cluster-6383.conf 
cp redis-cluster-6379.conf redis-cluster-6384.conf 
cp redis-cluster-6379.conf redis-cluster-6385.conf 
cp redis-cluster-6379.conf redis-cluster-6386.conf

sed -i 's/6379/6384/g' redis-cluster-6384.conf

-- 2. 啟動
/ups/app/redis5/bin/redis-server /ups/app/redis5/config/redis-cluster-6379.conf
/ups/app/redis5/bin/redis-server /ups/app/redis5/config/redis-cluster-6380.conf
/ups/app/redis5/bin/redis-server /ups/app/redis5/config/redis-cluster-6381.conf
/ups/app/redis5/bin/redis-server /ups/app/redis5/config/redis-cluster-6382.conf
/ups/app/redis5/bin/redis-server /ups/app/redis5/config/redis-cluster-6383.conf
/ups/app/redis5/bin/redis-server /ups/app/redis5/config/redis-cluster-6384.conf

-- 3. 建立節點握手
[root@progs redisdata]# rcli -p 6379
127.0.0.1:6379> cluster nodes
02a7b79fc1a4848b05b8369616a2f6ba7327dc02 :6379@16379 myself,master - 0 0 0 connected
127.0.0.1:6379> 
127.0.0.1:6379> 
127.0.0.1:6379> 
127.0.0.1:6379> cluster meet 127.0.0.1 6380
OK
127.0.0.1:6379> cluster nodes
3a551c30fce6f8455728a4cc88ac05ad5a15fc41 127.0.0.1:6380@16380 master - 0 1574407049306 0 connected
02a7b79fc1a4848b05b8369616a2f6ba7327dc02 127.0.0.1:6379@16379 myself,master - 0 0 1 connected
127.0.0.1:6379> cluster meet 127.0.0.1 6381
OK
127.0.0.1:6379> cluster meet 127.0.0.1 6382
OK
127.0.0.1:6379> cluster meet 127.0.0.1 6383
OK
127.0.0.1:6379> cluster meet 127.0.0.1 6384
OK
127.0.0.1:6379> cluster nodes
3a551c30fce6f8455728a4cc88ac05ad5a15fc41 127.0.0.1:6380@16380 master - 0 1574407299822 2 connected
eacdc8ec0f52b13a188a39cd40aa3272f32d3852 127.0.0.1:6382@16382 master - 0 1574407301835 0 connected
4b6d3dbc64661d57564fadd25fdb38bcc12401e5 127.0.0.1:6383@16383 master - 0 1574407299000 5 connected
763c88dfe985e3946ce829eba5b3e32535f322f6 127.0.0.1:6381@16381 master - 0 1574407299000 3 connected
02a7b79fc1a4848b05b8369616a2f6ba7327dc02 127.0.0.1:6379@16379 myself,master - 0 1574407300000 1 connected
052722f4e34429e5958617d8f2c72172983cfb4a 127.0.0.1:6384@16384 master - 0 1574407301000 4 connected
127.0.0.1:6379> cluster info
cluster_state:fail
cluster_slots_assigned:0
cluster_slots_ok:0
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:0
cluster_current_epoch:5
cluster_my_epoch:1
cluster_stats_messages_ping_sent:303
cluster_stats_messages_pong_sent:297
cluster_stats_messages_meet_sent:5
cluster_stats_messages_sent:605
cluster_stats_messages_ping_received:297
cluster_stats_messages_pong_received:308
cluster_stats_messages_received:605
127.0.0.1:6379> 

-- 4. 分配槽
rcli -h 127.0.0.1 -p 6379 cluster addslots {0..5461}
rcli -h 127.0.0.1 -p 6380 cluster addslots {5462..10922}
rcli -h 127.0.0.1 -p 6381 cluster addslots {10923..16383}

[root@progs ~]# rcli -h 127.0.0.1 -p 6379 cluster addslots {0..5461}
OK
[root@progs ~]#

127.0.0.1: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
cluster_size:3
cluster_current_epoch:5
cluster_my_epoch:1
cluster_stats_messages_ping_sent:728
cluster_stats_messages_pong_sent:695
cluster_stats_messages_meet_sent:5
cluster_stats_messages_sent:1428
cluster_stats_messages_ping_received:695
cluster_stats_messages_pong_received:733
cluster_stats_messages_received:1428
127.0.0.1:6379> 
127.0.0.1:6379> cluster nodes
3a551c30fce6f8455728a4cc88ac05ad5a15fc41 127.0.0.1:6380@16380 master - 0 1574407820950 2 connected 5462-10922
eacdc8ec0f52b13a188a39cd40aa3272f32d3852 127.0.0.1:6382@16382 master - 0 1574407821000 0 connected
4b6d3dbc64661d57564fadd25fdb38bcc12401e5 127.0.0.1:6383@16383 master - 0 1574407823969 5 connected
763c88dfe985e3946ce829eba5b3e32535f322f6 127.0.0.1:6381@16381 master - 0 1574407821956 3 connected 10923-16383
02a7b79fc1a4848b05b8369616a2f6ba7327dc02 127.0.0.1:6379@16379 myself,master - 0 1574407818000 1 connected 0-5461
052722f4e34429e5958617d8f2c72172983cfb4a 127.0.0.1:6384@16384 master - 0 1574407822962 4 connected
127.0.0.1:6379>

-- 5. 配置從節點
127.0.0.1:6382>cluster replicate 02a7b79fc1a4848b05b8369616a2f6ba7327dc02
127.0.0.1:6383>cluster replicate 3a551c30fce6f8455728a4cc88ac05ad5a15fc41
127.0.0.1:6384>cluster replicate 763c88dfe985e3946ce829eba5b3e32535f322f6

[root@progs ~]# rcli -p 6382 cluster replicate 02a7b79fc1a4848b05b8369616a2f6ba7327dc02
OK
[root@progs ~]# rcli -p 6383 cluster replicate 3a551c30fce6f8455728a4cc88ac05ad5a15fc41
OK
[root@progs ~]# rcli -p 6384 cluster replicate 763c88dfe985e3946ce829eba5b3e32535f322f6
OK
[root@progs ~]#

-- 檢查從節點及其槽
127.0.0.1:6379> cluster nodes
3a551c30fce6f8455728a4cc88ac05ad5a15fc41 127.0.0.1:6380@16380 master - 0 1574408081000 2 connected 5462-10922
eacdc8ec0f52b13a188a39cd40aa3272f32d3852 127.0.0.1:6382@16382 slave 02a7b79fc1a4848b05b8369616a2f6ba7327dc02 0 1574408082000 1 connected
4b6d3dbc64661d57564fadd25fdb38bcc12401e5 127.0.0.1:6383@16383 slave 3a551c30fce6f8455728a4cc88ac05ad5a15fc41 0 1574408081000 5 connected
763c88dfe985e3946ce829eba5b3e32535f322f6 127.0.0.1:6381@16381 master - 0 1574408080459 3 connected 10923-16383
02a7b79fc1a4848b05b8369616a2f6ba7327dc02 127.0.0.1:6379@16379 myself,master - 0 1574408079000 1 connected 0-5461
052722f4e34429e5958617d8f2c72172983cfb4a 127.0.0.1:6384@16384 slave 763c88dfe985e3946ce829eba5b3e32535f322f6 0 1574408082474 4 connected
127.0.0.1:6379>
API工具部署
redis-trib.rb工具快捷部署(適用於redis 5版本之前)
-- 1. 安裝Ruby
curl -C - -O https://cache.ruby-lang.org/pub/ruby/2.6/ruby-2.6.5.tar.gz

tar xf ruby-2.6.5tar.gz
./configure --prefix=/ups/app/ruby
make && make install

OR
yum -y install ruby

-- 2. 安裝外掛
/*
curl -C - -O https://rubygems.org/rubygems/rubygems-3.0.6.tgz
tar xf rubygems-3.0.6.tgz
cd rubygems-3.0.6
 ruby setup.rb
*/
export PATH=${PATH}:/ups/app/ruby/bin
curl -C - -O https://rubygems.org/downloads/redis-4.1.3.gem
gem install -l redis-4.1.3.gem
gem list redis gem

-- 安裝redis-trib.rb
cp /ups/soft/redis-5.0.5/src/redis-trib.rb /ups/app/redis5/bin/
[root@progs bin]# ./redis-trib.rb --help
WARNING: redis-trib.rb is not longer available!
You should use redis-cli instead.


-- 3. 準備配置
cd /ups/app/redis5/config
cp redis-cluster-6379.conf redis-cluster-6380.conf 
cp redis-cluster-6379.conf redis-cluster-6381.conf 
cp redis-cluster-6379.conf redis-cluster-6382.conf 
cp redis-cluster-6379.conf redis-cluster-6383.conf 
cp redis-cluster-6379.conf redis-cluster-6384.conf 
cp redis-cluster-6379.conf redis-cluster-6385.conf 
cp redis-cluster-6379.conf redis-cluster-6386.conf

sed -i 's/6379/6384/g' redis-cluster-6384.conf

/ups/app/redis5/bin/redis-server /ups/app/redis5/config/redis-cluster-6379.conf
/ups/app/redis5/bin/redis-server /ups/app/redis5/config/redis-cluster-6380.conf
/ups/app/redis5/bin/redis-server /ups/app/redis5/config/redis-cluster-6381.conf
/ups/app/redis5/bin/redis-server /ups/app/redis5/config/redis-cluster-6382.conf
/ups/app/redis5/bin/redis-server /ups/app/redis5/config/redis-cluster-6383.conf
/ups/app/redis5/bin/redis-server /ups/app/redis5/config/redis-cluster-6384.conf

-- 4. 建立叢集
redis-trib.rb create --replicas 1 127.0.0.1:6481 127.0.0.1:6482 127.0.0.1:6483 127.0.0.1:6484 127.0.0.1:6485 127.0.0.1:6486
# --replicas引數指定叢集中每個主節點配備幾個從節點

-- 5. 檢查完整性
redis-trib.rb check 127.0.0.1:6379

redis-cli工具部署(適用於redis5及以上)
-- 1. 準備配置檔案和啟動服務
cd /ups/app/redis5/config
cp redis-cluster-6379.conf redis-cluster-6380.conf 
cp redis-cluster-6379.conf redis-cluster-6381.conf 
cp redis-cluster-6379.conf redis-cluster-6382.conf 
cp redis-cluster-6379.conf redis-cluster-6383.conf 
cp redis-cluster-6379.conf redis-cluster-6384.conf 
cp redis-cluster-6379.conf redis-cluster-6385.conf 
cp redis-cluster-6379.conf redis-cluster-6386.conf

sed -i 's/6379/6384/g' redis-cluster-6384.conf

/ups/app/redis5/bin/redis-server /ups/app/redis5/config/redis-cluster-6379.conf
/ups/app/redis5/bin/redis-server /ups/app/redis5/config/redis-cluster-6380.conf
/ups/app/redis5/bin/redis-server /ups/app/redis5/config/redis-cluster-6381.conf
/ups/app/redis5/bin/redis-server /ups/app/redis5/config/redis-cluster-6382.conf
/ups/app/redis5/bin/redis-server /ups/app/redis5/config/redis-cluster-6383.conf
/ups/app/redis5/bin/redis-server /ups/app/redis5/config/redis-cluster-6384.conf

-- 2. 建立叢集
redis-cli --cluster create --cluster-replicas 1 127.0.0.1:6379 127.0.0.1:6380 127.0.0.1:6381 127.0.0.1:6382 127.0.0.1:6383 127.0.0.1:6384

-- 3. 驗證
rcli -c -p 6379 cluster nodes
rcli -c -p 6379 cluster info

叢集擴容

手動擴容叢集
-- 啟動孤兒節點
/ups/app/redis5/bin/redis-server /ups/app/redis5/config/redis-cluster-6385.conf
/ups/app/redis5/bin/redis-server /ups/app/redis5/config/redis-cluster-6386.conf

-- 2. 加入叢集
[root@progs ~]# rcli -p 6379
127.0.0.1:6379> cluster meet 127.0.0.1 6385
OK
127.0.0.1:6379> cluster meet 127.0.0.1 6386
OK
127.0.0.1:6379> cluster nodes
cd64d0f790fb0c568fe48f458fc9bf18c51662a5 127.0.0.1:6385@16385 master - 0 1574641619000 0 connected
3a551c30fce6f8455728a4cc88ac05ad5a15fc41 127.0.0.1:6380@16380 master - 0 1574641618000 2 connected 5462-10922
3bbb9a93de8cff52a813ef754cd8229da05d2e12 127.0.0.1:6386@16386 master - 0 1574641620564 6 connected
eacdc8ec0f52b13a188a39cd40aa3272f32d3852 127.0.0.1:6382@16382 slave 02a7b79fc1a4848b05b8369616a2f6ba7327dc02 0 1574641617143 1 connected
4b6d3dbc64661d57564fadd25fdb38bcc12401e5 127.0.0.1:6383@16383 slave 3a551c30fce6f8455728a4cc88ac05ad5a15fc41 0 1574641617000 5 connected
763c88dfe985e3946ce829eba5b3e32535f322f6 127.0.0.1:6381@16381 master - 0 1574641619156 3 connected 10923-16383
02a7b79fc1a4848b05b8369616a2f6ba7327dc02 127.0.0.1:6379@16379 myself,master - 0 1574641617000 1 connected 0-5461
052722f4e34429e5958617d8f2c72172983cfb4a 127.0.0.1:6384@16384 slave 763c88dfe985e3946ce829eba5b3e32535f322f6 0 1574641620162 4 connected
127.0.0.1:6379>

/*
# 準備資料
127.0.0.1:6379> SET key:{test}:555 value:test:555
127.0.0.1:6380> SET key:{test}:666 value:test:666
OK
127.0.0.1:6380> SET key:{test}:777 value:test:777
OK
127.0.0.1:6380> CLUSTER KEYSLOT key:{test}:555
*/

-- 3. 遷移槽和資料(資料遷移過程是逐個槽進行的, 每個槽資料遷移的流程如下)
- 3.1 目標節點匯入4096槽資料 (cluster setslot {slot} importing {sourceNodeId})
rcli -p 6385
cluster setslot 4096 importing 02a7b79fc1a4848b05b8369616a2f6ba7327dc02 
cluster nodes

[root@progs ~]# rcli -p 6385
127.0.0.1:6385> cluster setslot 4096 importing 02a7b79fc1a4848b05b8369616a2f6ba7327dc02 
OK
127.0.0.1:6385> cluster nodes
763c88dfe985e3946ce829eba5b3e32535f322f6 127.0.0.1:6381@16381 master - 0 1574642222000 3 connected 10923-16383
3a551c30fce6f8455728a4cc88ac05ad5a15fc41 127.0.0.1:6380@16380 master - 0 1574642223000 2 connected 5462-10922
4b6d3dbc64661d57564fadd25fdb38bcc12401e5 127.0.0.1:6383@16383 slave 3a551c30fce6f8455728a4cc88ac05ad5a15fc41 0 1574642222000 2 connected
eacdc8ec0f52b13a188a39cd40aa3272f32d3852 127.0.0.1:6382@16382 slave 02a7b79fc1a4848b05b8369616a2f6ba7327dc02 0 1574642224884 1 connected
cd64d0f790fb0c568fe48f458fc9bf18c51662a5 127.0.0.1:6385@16385 myself,master - 0 1574642223000 0 connected [4096-<-02a7b79fc1a4848b05b8369616a2f6ba7327dc02]
3bbb9a93de8cff52a813ef754cd8229da05d2e12 127.0.0.1:6386@16386 master - 0 1574642225890 6 connected
052722f4e34429e5958617d8f2c72172983cfb4a 127.0.0.1:6384@16384 slave 763c88dfe985e3946ce829eba5b3e32535f322f6 0 1574642223000 3 connected
02a7b79fc1a4848b05b8369616a2f6ba7327dc02 127.0.0.1:6379@16379 master - 0 1574642224000 1 connected 0-5461
127.0.0.1:6385>

- 3.2 對源節點發送命令,準備遷移出槽的資料 (cluster setslot {slot} migrating {targetNodeId})
rcli -p 6379
cluster setslot 4096 migrating cd64d0f790fb0c568fe48f458fc9bf18c51662a5

127.0.0.1:6379> cluster setslot 4096 migrating cd64d0f790fb0c568fe48f458fc9bf18c51662a5
OK
127.0.0.1:6379> cluster nodes
cd64d0f790fb0c568fe48f458fc9bf18c51662a5 127.0.0.1:6385@16385 master - 0 1574642298533 0 connected
3a551c30fce6f8455728a4cc88ac05ad5a15fc41 127.0.0.1:6380@16380 master - 0 1574642295000 2 connected 5462-10922
3bbb9a93de8cff52a813ef754cd8229da05d2e12 127.0.0.1:6386@16386 master - 0 1574642295515 6 connected
eacdc8ec0f52b13a188a39cd40aa3272f32d3852 127.0.0.1:6382@16382 slave 02a7b79fc1a4848b05b8369616a2f6ba7327dc02 0 1574642297529 1 connected
4b6d3dbc64661d57564fadd25fdb38bcc12401e5 127.0.0.1:6383@16383 slave 3a551c30fce6f8455728a4cc88ac05ad5a15fc41 0 1574642294000 5 connected
763c88dfe985e3946ce829eba5b3e32535f322f6 127.0.0.1:6381@16381 master - 0 1574642296520 3 connected 10923-16383
02a7b79fc1a4848b05b8369616a2f6ba7327dc02 127.0.0.1:6379@16379 myself,master - 0 1574642296000 1 connected 0-5461 [4096->-cd64d0f790fb0c568fe48f458fc9bf18c51662a5]
052722f4e34429e5958617d8f2c72172983cfb4a 127.0.0.1:6384@16384 slave 763c88dfe985e3946ce829eba5b3e32535f322f6 0 1574642297000 4 connected
127.0.0.1:6379> 

- 3.3 源節點迴圈執行 (cluster getkeysinslot {slot} {count})命令, 獲取count個屬於槽{slot}的鍵
cluster getkeysinslot 4096 100

- 3.4 在源節點上執行(migrate {targetIp} {targetPort} "" 0 {timeout} keys {keys...})命令, 把獲取的鍵通過流水線(pipeline) 機制批量遷移到目標節點
migrate 127.0.0.1 6385 "" 0 5000 keys key:test:5028

- 3.5 重複3和4步驟,直到槽下所有的鍵值資料遷移到目標節點

- 3.6 向叢集內所有主節點發送cluster setslot {slot} node {targetNodeId}命令, 通知槽分配給目標節點
cluster setslot 4096 node cd64d0f790fb0c568fe48f458fc9bf18c51662a5

[root@progs config]# rcli -p 6379 cluster setslot 4096 node cd64d0f790fb0c568fe48f458fc9bf18c51662a5
OK
[root@progs config]# rcli -p 6380 cluster setslot 4096 node cd64d0f790fb0c568fe48f458fc9bf18c51662a5
OK
[root@progs config]# rcli -p 6381 cluster setslot 4096 node cd64d0f790fb0c568fe48f458fc9bf18c51662a5
OK
[root@progs config]# rcli -p 6385 cluster setslot 4096 node cd64d0f790fb0c568fe48f458fc9bf18c51662a5
OK
[root@progs config]# rcli -p 6379 cluster nodes
cd64d0f790fb0c568fe48f458fc9bf18c51662a5 127.0.0.1:6385@16385 master - 0 1574642672950 7 connected 4096
3a551c30fce6f8455728a4cc88ac05ad5a15fc41 127.0.0.1:6380@16380 master - 0 1574642669928 2 connected 5462-10922
3bbb9a93de8cff52a813ef754cd8229da05d2e12 127.0.0.1:6386@16386 master - 0 1574642667916 6 connected
eacdc8ec0f52b13a188a39cd40aa3272f32d3852 127.0.0.1:6382@16382 slave 02a7b79fc1a4848b05b8369616a2f6ba7327dc02 0 1574642670000 1 connected
4b6d3dbc64661d57564fadd25fdb38bcc12401e5 127.0.0.1:6383@16383 slave 3a551c30fce6f8455728a4cc88ac05ad5a15fc41 0 1574642670936 5 connected
763c88dfe985e3946ce829eba5b3e32535f322f6 127.0.0.1:6381@16381 master - 0 1574642670000 3 connected 10923-16383
02a7b79fc1a4848b05b8369616a2f6ba7327dc02 127.0.0.1:6379@16379 myself,master - 0 1574642672000 1 connected 0-4095 4097-5461
052722f4e34429e5958617d8f2c72172983cfb4a 127.0.0.1:6384@16384 slave 763c88dfe985e3946ce829eba5b3e32535f322f6 0 1574642671944 4 connected
[root@progs config]# 

-- 4. 新增從節點(cluster replicate {targetMasterNodeId})
rcli -p 6386 cluster replicate cd64d0f790fb0c568fe48f458fc9bf18c51662a5

[root@progs config]# rcli -p 6386 cluster replicate cd64d0f790fb0c568fe48f458fc9bf18c51662a5
OK
[root@progs config]# rcli -p 6379 cluster nodes
cd64d0f790fb0c568fe48f458fc9bf18c51662a5 127.0.0.1:6385@16385 master - 0 1574642826000 7 connected 4096
3a551c30fce6f8455728a4cc88ac05ad5a15fc41 127.0.0.1:6380@16380 master - 0 1574642823000 2 connected 5462-10922
3bbb9a93de8cff52a813ef754cd8229da05d2e12 127.0.0.1:6386@16386 slave cd64d0f790fb0c568fe48f458fc9bf18c51662a5 0 1574642827996 7 connected
eacdc8ec0f52b13a188a39cd40aa3272f32d3852 127.0.0.1:6382@16382 slave 02a7b79fc1a4848b05b8369616a2f6ba7327dc02 0 1574642826989 1 connected
4b6d3dbc64661d57564fadd25fdb38bcc12401e5 127.0.0.1:6383@16383 slave 3a551c30fce6f8455728a4cc88ac05ad5a15fc41 0 1574642827000 5 connected
763c88dfe985e3946ce829eba5b3e32535f322f6 127.0.0.1:6381@16381 master - 0 1574642828000 3 connected 10923-16383
02a7b79fc1a4848b05b8369616a2f6ba7327dc02 127.0.0.1:6379@16379 myself,master - 0 1574642824000 1 connected 0-4095 4097-5461
052722f4e34429e5958617d8f2c72172983cfb4a 127.0.0.1:6384@16384 slave 763c88dfe985e3946ce829eba5b3e32535f322f6 0 1574642829001 4 connected
[root@progs config]#
叢集水平伸縮(redis5 )
# 用法
[root@progs config]# rcli --cluster help
Cluster Manager Commands:
  create         host1:port1 ... hostN:portN
                 --cluster-replicas <arg>
  check          host:port
                 --cluster-search-multiple-owners
  info           host:port
  fix            host:port
                 --cluster-search-multiple-owners
  reshard        host:port
                 --cluster-from <arg>
                 --cluster-to <arg>
                 --cluster-slots <arg>
                 --cluster-yes
                 --cluster-timeout <arg>
                 --cluster-pipeline <arg>
                 --cluster-replace
  rebalance      host:port
                 --cluster-weight <node1=w1...nodeN=wN>
                 --cluster-use-empty-masters
                 --cluster-timeout <arg>
                 --cluster-simulate
                 --cluster-pipeline <arg>
                 --cluster-threshold <arg>
                 --cluster-replace
  add-node       new_host:new_port existing_host:existing_port
                 --cluster-slave
                 --cluster-master-id <arg>
  del-node       host:port node_id
  call           host:port command arg arg .. arg
  set-timeout    host:port milliseconds
  import         host:port
                 --cluster-from <arg>
                 --cluster-copy
                 --cluster-replace
  help           

For check, fix, reshard, del-node, set-timeout you can specify the host and port of any working node in the cluster.

# 操作
-- 1. 加節點
redis-cli --cluster add-node 127.0.0.1:6385 127.0.0.1:6379
redis-cli --cluster add-node 127.0.0.1:6386 127.0.0.1:6379

-- 2. 分配hash槽 (redis-cli --cluster reshard 127.0.0.1:6379)
[root@progs config]# rcli --cluster reshard 127.0.0.1:6379
>>> Performing Cluster Check (using node 127.0.0.1:6379)
M: 02a7b79fc1a4848b05b8369616a2f6ba7327dc02 127.0.0.1:6379
   slots:[0-4095],[4097-5461] (5461 slots) master
   1 additional replica(s)
M: cd64d0f790fb0c568fe48f458fc9bf18c51662a5 127.0.0.1:6385
   slots:[4096] (1 slots) master
   1 additional replica(s)
M: 3a551c30fce6f8455728a4cc88ac05ad5a15fc41 127.0.0.1:6380
   slots:[5462-10922] (5461 slots) master
   1 additional replica(s)
S: 3bbb9a93de8cff52a813ef754cd8229da05d2e12 127.0.0.1:6386
   slots: (0 slots) slave
   replicates cd64d0f790fb0c568fe48f458fc9bf18c51662a5
S: eacdc8ec0f52b13a188a39cd40aa3272f32d3852 127.0.0.1:6382
   slots: (0 slots) slave
   replicates 02a7b79fc1a4848b05b8369616a2f6ba7327dc02
S: 4b6d3dbc64661d57564fadd25fdb38bcc12401e5 127.0.0.1:6383
   slots: (0 slots) slave
   replicates 3a551c30fce6f8455728a4cc88ac05ad5a15fc41
M: 763c88dfe985e3946ce829eba5b3e32535f322f6 127.0.0.1:6381
   slots:[10923-16383] (5461 slots) master
   1 additional replica(s)
S: 052722f4e34429e5958617d8f2c72172983cfb4a 127.0.0.1:6384
   slots: (0 slots) slave
   replicates 763c88dfe985e3946ce829eba5b3e32535f322f6
[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)? 4096                             <<<<<<<<<<<< 分配多少個槽給新節點6385
What is the receiving node ID? cd64d0f790fb0c568fe48f458fc9bf18c51662a5                <<<<<<<<<<<< 接收節點的ID(6385)
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                                                                    <<<<<<<<<<<< all表示是所有服務節點

Ready to move 4096 slots.
  Source nodes:
    M: 02a7b79fc1a4848b05b8369616a2f6ba7327dc02 127.0.0.1:6379
       slots:[0-4095],[4097-5461] (5461 slots) master
       1 additional replica(s)
    M: 3a551c30fce6f8455728a4cc88ac05ad5a15fc41 127.0.0.1:6380
       slots:[5462-10922] (5461 slots) master
       1 additional replica(s)
    M: 763c88dfe985e3946ce829eba5b3e32535f322f6 127.0.0.1:6381
       slots:[10923-16383] (5461 slots) master
       1 additional replica(s)
  Destination node:
    M: cd64d0f790fb0c568fe48f458fc9bf18c51662a5 127.0.0.1:6385
       slots:[4096] (1 slots) master
       1 additional replica(s)
  Resharding plan:
    Moving slot 0 from 02a7b79fc1a4848b05b8369616a2f6ba7327dc02
    Moving slot 1 from 02a7b79fc1a4848b05b8369616a2f6ba7327dc02
    Moving slot 2 from 02a7b79fc1a4848b05b8369616a2f6ba7327dc02
    ...
    ...
    Moving slot 12287 from 763c88dfe985e3946ce829eba5b3e32535f322f6
Do you want to proceed with the proposed reshard plan (yes/no)? yes

-- 3. 設定從節點(cluster replicate {targetMasterNodeId})
rcli -p 6386 cluster replicate cd64d0f790fb0c568fe48f458fc9bf18c51662a5

-- 4. 檢視節點情況
[root@progs config]# rcli -c -p 6379 cluster nodes
cd64d0f790fb0c568fe48f458fc9bf18c51662a5 127.0.0.1:6385@16385 master - 0 1574646742000 7 connected 0-1365 4096 5462-6826 10923-12287
3a551c30fce6f8455728a4cc88ac05ad5a15fc41 127.0.0.1:6380@16380 master - 0 1574646742000 2 connected 6827-10922
3bbb9a93de8cff52a813ef754cd8229da05d2e12 127.0.0.1:6386@16386 slave cd64d0f790fb0c568fe48f458fc9bf18c51662a5 0 1574646739000 7 connected
eacdc8ec0f52b13a188a39cd40aa3272f32d3852 127.0.0.1:6382@16382 slave 02a7b79fc1a4848b05b8369616a2f6ba7327dc02 0 1574646740986 1 connected
4b6d3dbc64661d57564fadd25fdb38bcc12401e5 127.0.0.1:6383@16383 slave 3a551c30fce6f8455728a4cc88ac05ad5a15fc41 0 1574646737000 5 connected
763c88dfe985e3946ce829eba5b3e32535f322f6 127.0.0.1:6381@16381 master - 0 1574646743002 3 connected 12288-16383
02a7b79fc1a4848b05b8369616a2f6ba7327dc02 127.0.0.1:6379@16379 myself,master - 0 1574646739000 1 connected 1366-4095 4097-5461
052722f4e34429e5958617d8f2c72172983cfb4a 127.0.0.1:6384@16384 slave 763c88dfe985e3946ce829eba5b3e32535f322f6 0 1574646742000 4 connected
[root@progs config]#
叢集收縮(redis5)
-- 1. 刪除從節點 (6386) rcli --cluster del-node 127.0.0.1:6386 3bbb9a93de8cff52a813ef754cd8229da05d2e12

[root@progs config]# rcli --cluster del-node 127.0.0.1:6386 3bbb9a93de8cff52a813ef754cd8229da05d2e12
>>> Removing node 3bbb9a93de8cff52a813ef754cd8229da05d2e12 from cluster 127.0.0.1:6386
>>> Sending CLUSTER FORGET messages to the cluster...
>>> SHUTDOWN the node.
[root@progs config]# rcli -c -p 6379 cluster nodes
cd64d0f790fb0c568fe48f458fc9bf18c51662a5 127.0.0.1:6385@16385 master - 0 1574647032000 7 connected 0-1365 4096 5462-6826 10923-12287
3a551c30fce6f8455728a4cc88ac05ad5a15fc41 127.0.0.1:6380@16380 master - 0 1574647033000 2 connected 6827-10922
eacdc8ec0f52b13a188a39cd40aa3272f32d3852 127.0.0.1:6382@16382 slave 02a7b79fc1a4848b05b8369616a2f6ba7327dc02 0 1574647034841 1 connected
4b6d3dbc64661d57564fadd25fdb38bcc12401e5 127.0.0.1:6383@16383 slave 3a551c30fce6f8455728a4cc88ac05ad5a15fc41 0 1574647033000 5 connected
763c88dfe985e3946ce829eba5b3e32535f322f6 127.0.0.1:6381@16381 master - 0 1574647033837 3 connected 12288-16383
02a7b79fc1a4848b05b8369616a2f6ba7327dc02 127.0.0.1:6379@16379 myself,master - 0 1574647032000 1 connected 1366-4095 4097-5461
052722f4e34429e5958617d8f2c72172983cfb4a 127.0.0.1:6384@16384 slave 763c88dfe985e3946ce829eba5b3e32535f322f6 0 1574647032000 4 connected
[root@progs config]#

-- 2. 對主節點重新分片 ( redis-cli --cluster reshard 127.0.0.1:6385)
[root@progs config]# rcli --cluster reshard 127.0.0.1:6385
>>> Performing Cluster Check (using node 127.0.0.1:6385)
M: cd64d0f790fb0c568fe48f458fc9bf18c51662a5 127.0.0.1:6385
   slots:[0-1365],[4096],[5462-6826],[10923-12287] (4097 slots) master
M: 763c88dfe985e3946ce829eba5b3e32535f322f6 127.0.0.1:6381
   slots:[12288-16383] (4096 slots) master
   1 additional replica(s)
M: 3a551c30fce6f8455728a4cc88ac05ad5a15fc41 127.0.0.1:6380
   slots:[6827-10922] (4096 slots) master
   1 additional replica(s)
S: 4b6d3dbc64661d57564fadd25fdb38bcc12401e5 127.0.0.1:6383
   slots: (0 slots) slave
   replicates 3a551c30fce6f8455728a4cc88ac05ad5a15fc41
S: eacdc8ec0f52b13a188a39cd40aa3272f32d3852 127.0.0.1:6382
   slots: (0 slots) slave
   replicates 02a7b79fc1a4848b05b8369616a2f6ba7327dc02
S: 052722f4e34429e5958617d8f2c72172983cfb4a 127.0.0.1:6384
   slots: (0 slots) slave
   replicates 763c88dfe985e3946ce829eba5b3e32535f322f6
M: 02a7b79fc1a4848b05b8369616a2f6ba7327dc02 127.0.0.1:6379
   slots:[1366-4095],[4097-5461] (4095 slots) master
   1 additional replica(s)
[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)? 4097
What is the receiving node ID? 02a7b79fc1a4848b05b8369616a2f6ba7327dc02
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: cd64d0f790fb0c568fe48f458fc9bf18c51662a5
Source node #2: done
...
    Moving slot 12286 from cd64d0f790fb0c568fe48f458fc9bf18c51662a5
    Moving slot 12287 from cd64d0f790fb0c568fe48f458fc9bf18c51662a5
Do you want to proceed with the proposed reshard plan (yes/no)? yes
...
[root@progs config]#

[root@progs config]# rcli -c -p 6379 cluster nodes
cd64d0f790fb0c568fe48f458fc9bf18c51662a5 127.0.0.1:6385@16385 master - 0 1574648077000 7 connected
3a551c30fce6f8455728a4cc88ac05ad5a15fc41 127.0.0.1:6380@16380 master - 0 1574648078000 2 connected 6827-10922
eacdc8ec0f52b13a188a39cd40aa3272f32d3852 127.0.0.1:6382@16382 slave 02a7b79fc1a4848b05b8369616a2f6ba7327dc02 0 1574648079611 8 connected
4b6d3dbc64661d57564fadd25fdb38bcc12401e5 127.0.0.1:6383@16383 slave 3a551c30fce6f8455728a4cc88ac05ad5a15fc41 0 1574648077000 5 connected
763c88dfe985e3946ce829eba5b3e32535f322f6 127.0.0.1:6381@16381 master - 0 1574648080618 3 connected 12288-16383
02a7b79fc1a4848b05b8369616a2f6ba7327dc02 127.0.0.1:6379@16379 myself,master - 0 1574648076000 8 connected 0-6826 10923-12287
052722f4e34429e5958617d8f2c72172983cfb4a 127.0.0.1:6384@16384 slave 763c88dfe985e3946ce829eba5b3e32535f322f6 0 1574648079000 4 connected
[root@progs config]# 

-- 3. 刪除節點(6385)
rcli --cluster del-node 127.0.0.1:6385 cd64d0f790fb0c568fe48f458fc9bf18c51662a5

[root@progs config]# rcli --cluster del-node 127.0.0.1:6385 cd64d0f790fb0c568fe48f458fc9bf18c51662a5
>>> Removing node cd64d0f790fb0c568fe48f458fc9bf18c51662a5 from cluster 127.0.0.1:6385
>>> Sending CLUSTER FORGET messages to the cluster...
>>> SHUTDOWN the node.
[root@progs config]# rcli -c -p 6379 cluster nodes
3a551c30fce6f8455728a4cc88ac05ad5a15fc41 127.0.0.1:6380@16380 master - 0 1574648207000 2 connected 6827-10922
eacdc8ec0f52b13a188a39cd40aa3272f32d3852 127.0.0.1:6382@16382 slave 02a7b79fc1a4848b05b8369616a2f6ba7327dc02 0 1574648206452 8 connected
4b6d3dbc64661d57564fadd25fdb38bcc12401e5 127.0.0.1:6383@16383 slave 3a551c30fce6f8455728a4cc88ac05ad5a15fc41 0 1574648209000 5 connected
763c88dfe985e3946ce829eba5b3e32535f322f6 127.0.0.1:6381@16381 master - 0 1574648209474 3 connected 12288-16383
02a7b79fc1a4848b05b8369616a2f6ba7327dc02 127.0.0.1:6379@16379 myself,master - 0 1574648208000 8 connected 0-6826 10923-12287
052722f4e34429e5958617d8f2c72172983cfb4a 127.0.0.1:6384@16384 slave 763c88dfe985e3946ce829eba5b3e32535f322f6 0 1574648207459 4 connected
[root@progs config]#

-- 4. 資料平衡
[root@progs config]# rcli --cluster rebalance  127.0.0.1:6379
>>> Performing Cluster Check (using node 127.0.0.1:6379)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
>>> Rebalancing across 3 nodes. Total weight = 3.00
Moving 1366 slots from 127.0.0.1:6379 to 127.0.0.1:6380
################################################################################################################################################
Moving 1365 slots from 127.0.0.1:6379 to 127.0.0.1:6381
################################################################################################################################################--
[root@progs config]# 
[root@progs config]# rcli -c -p 6379 cluster nodes
3a551c30fce6f8455728a4cc88ac05ad5a15fc41 127.0.0.1:6380@16380 master - 0 1574650327091 9 connected 0-1365 6827-10922
eacdc8ec0f52b13a188a39cd40aa3272f32d3852 127.0.0.1:6382@16382 slave 02a7b79fc1a4848b05b8369616a2f6ba7327dc02 0 1574650324000 8 connected
4b6d3dbc64661d57564fadd25fdb38bcc12401e5 127.0.0.1:6383@16383 slave 3a551c30fce6f8455728a4cc88ac05ad5a15fc41 0 1574650328097 9 connected
763c88dfe985e3946ce829eba5b3e32535f322f6 127.0.0.1:6381@16381 master - 0 1574650326083 10 connected 1366-2730 12288-16383
02a7b79fc1a4848b05b8369616a2f6ba7327dc02 127.0.0.1:6379@16379 myself,master - 0 1574650325000 8 connected 2731-6826 10923-12287
052722f4e34429e5958617d8f2c72172983cfb4a 127.0.0.1:6384@16384 slave 763c88dfe985e3946ce829eba5b3e32535f322f6 0 1574650325075 10 connected
[root@progs config]#

故障轉移

-- 1. failover
[root@progs config]# rcli -c -p 6379 cluster nodes
3a551c30fce6f8455728a4cc88ac05ad5a15fc41 127.0.0.1:6380@16380 master - 0 1574650946000 9 connected 0-1365 6827-10922
eacdc8ec0f52b13a188a39cd40aa3272f32d3852 127.0.0.1:6382@16382 slave 02a7b79fc1a4848b05b8369616a2f6ba7327dc02 0 1574650947235 8 connected
4b6d3dbc64661d57564fadd25fdb38bcc12401e5 127.0.0.1:6383@16383 slave 3a551c30fce6f8455728a4cc88ac05ad5a15fc41 0 1574650945222 9 connected
763c88dfe985e3946ce829eba5b3e32535f322f6 127.0.0.1:6381@16381 master - 0 1574650945000 10 connected 1366-2730 12288-16383
02a7b79fc1a4848b05b8369616a2f6ba7327dc02 127.0.0.1:6379@16379 myself,master - 0 1574650946000 8 connected 2731-6826 10923-12287
052722f4e34429e5958617d8f2c72172983cfb4a 127.0.0.1:6384@16384 slave 763c88dfe985e3946ce829eba5b3e32535f322f6 0 1574650946228 10 connected
[root@progs config]# rcli -p 6382 cluster failover
OK
[root@progs config]# rcli -c -p 6379 cluster nodes
3a551c30fce6f8455728a4cc88ac05ad5a15fc41 127.0.0.1:6380@16380 master - 0 1574650966371 9 connected 0-1365 6827-10922
eacdc8ec0f52b13a188a39cd40aa3272f32d3852 127.0.0.1:6382@16382 master - 0 1574650964360 11 connected 2731-6826 10923-12287
4b6d3dbc64661d57564fadd25fdb38bcc12401e5 127.0.0.1:6383@16383 slave 3a551c30fce6f8455728a4cc88ac05ad5a15fc41 0 1574650964000 9 connected
763c88dfe985e3946ce829eba5b3e32535f322f6 127.0.0.1:6381@16381 master - 0 1574650964000 10 connected 1366-2730 12288-16383
02a7b79fc1a4848b05b8369616a2f6ba7327dc02 127.0.0.1:6379@16379 myself,slave eacdc8ec0f52b13a188a39cd40aa3272f32d3852 0 1574650963000 8 connected
052722f4e34429e5958617d8f2c72172983cfb4a 127.0.0.1:6384@16384 slave 763c88dfe985e3946ce829eba5b3e32535f322f6 0 1574650965365 10 connected
[root@progs config]#