Redis基礎知識(二)

Redis事務

redis中的事務是一組命令的集合,事務中的命令要麼所有執行,要麼都不執行,Redis 經過 MULTI 、DISCARD 、EXEC 和 WATCH
四個命令來實現事務功能,multi表示事物的開啓,exec表示事物的執行,exec執行後返回事務執行的結果,discard表示放棄事務執行,清空事務隊列中已有的全部命令並退出隊列,watch用於監視給定的鍵,若是鍵被其餘客戶端修改,將不會執行事務。node

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set key 1
QUEUED
127.0.0.1:6379> get key
QUEUED
127.0.0.1:6379> exec
1) OK
2) "1"

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set key1 1
QUEUED
127.0.0.1:6379> discard
OK
127.0.0.1:6379> get key1
(nil)

這裏我在另外一個客戶端修改了被監視的key,致使在這個客戶端事務沒有執行python

127.0.0.1:6379> set key 1
OK
127.0.0.1:6379> watch key 
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incr key  
QUEUED
127.0.0.1:6379> incr key  #客戶端2
(integer) 2
127.0.0.1:6379> exec
(nil)
127.0.0.1:6379> get key
"2"

因爲事務在執行時會獨佔服務器,因此儘可能避免在事務中執行過多命令,以避免服務器阻塞redis

Redis Pipline

redis是一個cs模式的tcp server,使用和http相似的請求響應協議。一個client能夠經過一個socket鏈接發起多個請求命令。每一個請求命令發出後client一般會阻塞並等待redis服務處理,redis處理完後請求命令後會將結果經過響應報文返回給client。若是網絡延遲較大,那將會花費太多的時間,redis提供了pipline能夠解決這個問題,redis能夠在pipline中發送多個消息而無需等待每一個消息的答覆的過程。
這裏我使用python的redis庫寫了個demo來演示使用pipline的效果算法

from redis import Redis
import time
conn=Redis(host="60.205.177.100",port="6379")

def usepipline():
    start_time=time.time()
    pipline=conn.pipeline()
    for i in range(300):
        pipline.incr("key")
    pipline.execute()
    print("usepipline:",time.time()-start_time)
def withoutpipline():
    start_time=time.time()
    for i in range(300):
        conn.incr("key1")
    print("withoutpipline:",time.time()-start_time)
usepipline()
withoutpipline()

響應結果數據庫

usepipline: 1.2412519454956055
withoutpipline: 7.2261717319488525

能夠看到使用pipline效果是很明顯的vim

Redis發佈訂閱模式

Redis經過PUBLISH 、SUBSCRIBE 等命令實現了訂閱與發佈模式,發佈者能夠向多個頻道發佈消息,訂閱者能夠訂閱多個頻道,固然一個頻道也能夠有多個訂閱者,發佈者和訂閱者的這種分離能夠容許更大的可伸縮性和更動態的網絡拓撲。
Redis基礎知識(二) 安全

命令

向頻道發送消息

publish channel message

例如

返回的是接收到消息的訂閱者數量服務器

127.0.0.1:6379> publish CCTV1 worldnews
(integer) 0
127.0.0.1:6379> publish CCTV1 chinanews
(integer) 0

訂閱頻道

subscribe channel [channel ...]

例如

127.0.0.1:6379> subscribe CCTV1 
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "CCTV1"
3) (integer) 1
1) "message"
2) "CCTV1"
3) "chinanews"

退訂頻道

UNSUBSCRIBE [channel [channel ...]]

例如

127.0.0.1:6379> UNSUBSCRIBE CCTV1 CCTV2
1) "unsubscribe"
2) "CCTV1"
3) (integer) 0
4) "unsubscribe"
5) "CCTV2"
6) (integer) 0

訂閱模式

redis支持使用glob的方式來一次訂閱多個頻道網絡

PSUBSCRIBE pattern [pattern ...]

例如

127.0.0.1:6379> publish CCTV2 chinanew
(integer) 1
127.0.0.1:6379> publish CCTV1 worldnews
(integer) 1
127.0.0.1:6379> PSUBSCRIBE CCTV*
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "CCTV*"
3) (integer) 1
1) "pmessage"
2) "CCTV*"
3) "CCTV2"
4) "chinanews"
1) "pmessage"
2) "CCTV*"
3) "CCTV2"
4) "chinanew"

退訂模式

PUNSUBSCRIBE [pattern [pattern ...]]

例如

127.0.0.1:6379> PUNSUBSCRIBE CCTV*
1) "punsubscribe"
2) "CCTV*"
3) (integer) 0

python demo

redis_pub.py

from redis import Redis
import time
conn=Redis(host="60.205.177.100",port="6379")
def publish():  
    while True:       
        conn.publish("CCTV3","test")

redis_sub.py

from redis import Redis
import time
conn=Redis(host="60.205.177.100",port="6379")
def subscribe():
    subscribe=conn.pubsub()
    subscribe.subscribe('CCTV3')
    message=subscribe.parse_response()
    print(message)

Redis複製

雖然已經有了aof和rdb作持久化了,可是爲了防止單點故障,這就須要複製多個數據副原本保證數據安全運維

redis複製的基本特徵

  • Redis使用異步複製,並以每秒一次的頻率向主服務器確認複製進度。
  • 一個主服務器能夠有多個從服務器。從服務器也能夠有多個從服務器,從服務器還能夠以相似級聯的結構鏈接到其餘從服務器。
  • Redis複製在主服務器端無阻塞。這意味着當一個或多個副本執行初始同步或部分從新同步時,主服務器將繼續處理查詢。
  • 複製在從服務器基本上也沒有阻塞。當副本執行初始同步時,若是配置了replica-serve-stale-data參數爲yes,則從服務器可使用數據集的舊版本處理查詢。或者配置replica-serve-stale-data參數爲no,在複製鏈接斷開時,向客戶端發送一個錯誤,可是,在初始同步以後,從服務器刪除舊版本數據集並載入新版本數據集的那段時間內, 鏈接請求會被阻塞。
  • 複製功能能夠單純地用於數據冗餘(data redundancy), 也能夠經過讓多個從服務器處理只讀命令請求來提高擴展性(scalability)
  • 可使用複製來避免讓主數據庫執行持久化,而是複製到從服務器以後由從服務器進行持久化,可是最好仍是開啓主服務器的持久化,由於從新啓動的主服務器將以空數據集開始,若是從服務器嘗試與其同步,致使從服務器的數據也將被清空。
  • 缺點也很明顯,Redis主從模式仍具備單點風險,一旦主服務器掛掉將沒法進行寫數據操做

redis複製原理

徹底同步

主服務器後臺發送RDB文件給從服務器。從服務器接收rdb數據期間,主服務器會將新數據保存到複製客戶端緩衝區,當從服務器接收完rdb文件後,將其保存在磁盤上,而後將其加載到內存中。加載完rdb文件後,若是開啓aof,從服務器會進行重寫操做。主服務器會把緩衝區的新數據發送給從服務器

部分同步

當主從鏈接中斷後,從服務器使用psync命令向主服務器發送上次複製的偏移量,以及記錄的masterID,若是上次複製的偏移量仍存在主服務器的緩衝區中,而且masterID與主服務器的masterID一致,將會從緩衝區中上次斷開的位置開始增量複製,不然將會發生徹底同步

psync命令

psync masterID offset

當從服務器與主服務器創建鏈接時,會判斷是否是初次複製,若是是的話,將會發送psync ? -1進行徹底同步,若是不是的話,會發送psync masterID offset嘗試部分同步,若是發送的masterID與主服務器一致且offset存在於主服務器的複製緩衝區中,將進行部分同步,不然將會進行徹底同步
首先咱們查看主服務器的masterID和offset

Redis基礎知識(二)

而後將masterID和offset複製到從服務器,期間我在主服務器寫了兩條數據
Redis基礎知識(二)

能夠看到從服務器已經有了主服務器的數據

redis複製配置

因爲主服務器設置了密碼,須要在從服務器中指定註定主服務器的密碼
vim /etc/redis/6379.conf

masterauth 123456

因爲個人redis版本是5.0,因此同步的命令較之前的命令不太同樣,但也兼容之前的同步命令

第一種方法:

配置文件修改
vim /etc/redis/6379.conf

replicaof masterip port   #5.0版本支持的命令
或者
slaveof masterip port

第二種方法:

在redis客戶端執行命令,在客戶端執行的同步命令將在重啓後失效

replicaof masterip port   #5.0版本支持的命令
或者
slaveof masterip port

配置好以後,主從的masterID信息是同樣的,這是主服務器的信息
Redis基礎知識(二)

這是從服務器的信息
Redis基礎知識(二)

主從同步後,從服務器將不能寫入數據
Redis基礎知識(二)

當咱們斷開當前數據庫的鏈接,與另外一臺主服務器創建主從同步後,masterid也發生變化,將從新進行徹底同步
Redis基礎知識(二)

Redis哨兵

在redis主從模式下,一旦主服務器宕機,須要人工進行干預將某個從服務器轉換爲主服務器,還須要去監視redis的狀態,費事費力,還會形成一段時間內服務不可用,對於某些應用場景,這種處理方法並不可取,redis在2.8版本開始提供了RedisSentinel工具,經過心跳檢測的方式監視多個主服務器以及它們屬下的全部從服務器,並在某個主服務器下線時自動對其實施故障轉移,Sentinel將選擇一個從服務器並將其提高爲主服務器,其餘剩餘的從服務器實例將自動從新配置爲使用新的主服務器。
Redis基礎知識(二)

哨兵功能

  • 監控。Sentinel會不斷檢查主從節點是否按預期工做。
  • 通知。Sentinel經過API通知客戶端出現問題的節點
  • 自動故障轉移。當主服務器不能正常工做時,Sentinel能夠進行故障轉移操做,在該操做中將從服務器升級爲主服務器,將其餘從服務器改成複製新主服務器。
  • 配置提供程序。客戶端在初始化時,經過鏈接到Sentinels,獲取當前主服務器的節點地址。

哨兵搭建

環境準備

192.168.179.131:6379 master
192.168.179.132:6379 slave
192.168.179.134:6379 slave
192.168.179.131:26379 sentinel
192.168.179.132:26379 sentinel
192.168.179.134:26379 sentinel

主從節點配置

#192.168.179.131
port 6379
daemonize yes
logfile /var/log/redis_6379.log
dbfilename dump.rdb

#192.168.179.132
port 6379
daemonize yes
logfile /var/log/redis_6379.log
dbfilename dump.rdb
replicaof 192.168.179.131 6379

#192.168.179.134
port 6379
daemonize yes
logfile /var/log/redis_6379.log
dbfilename dump.rdb
replicaof 192.168.179.131 6379

測試主從複製是否配置成功

Redis基礎知識(二)

哨兵節點配置

三個哨兵節點相同配置,最後一句配置的意思是監控192.168.179.131這個主節點,節點名稱是mymaster,而且至少須要兩個哨兵節點贊成才能斷定主節點故障並進行自動遷移

port 26379
daemonize yes
sentinel monitor mymaster 192.168.179.131 6379 2

啓動哨兵節點

如下兩條命令啓動均可以,能夠看到哨兵已經啓動成功

./src/redis-sentinel sentinel.conf
./src/redis-server sentinel.conf --sentinel

Redis基礎知識(二)

故障轉移測試

這裏我將192.168.179.131的redis給關掉,查看從節點的複製信息,能夠看到redis的主節點已經切換爲192.168.179.132
Redis基礎知識(二)

將192.168.179.131上面的redis再次啓動,查看節點狀態已經變爲從節點了
Redis基礎知識(二)

哨兵管理命令

  • info sentinel:獲取監控的全部主節點的基本信息
  • sentinel masters:獲取全部被監視主節點的信息
  • sentinel master <master-name>:獲取指定主節點的詳細信息
  • sentinel slaves <master-name>:獲取指定主節點的從節點的詳細信息
  • sentinel sentinels <master-name>:獲取指定主節點的哨兵節點的詳細信息
  • sentinel get-master-addr-by-name mymaster:獲取主節點的IP地址和端口
  • sentinel is-master-down-by-addr:哨兵節點之間能夠經過該命令詢問主節點是否下線,從而對是否客觀下線作出判斷
  • sentinel failover <master-name>: 強制對主節點進行故障轉移
  • sentinel ckquorum: 檢查可用的哨兵數量
  • sentinel flushconfig: 強制寫入配置文件
  • sentinel remove <master-name>:取消對指定主節點的監視

Redis集羣模式

上面也說到,Redis主從模式中若是主服務器宕掉將沒法進行寫操做,即便哨兵模式提供了Redis的高可用,但面對數據量比較大的場景,Redis單點就不太能知足這個要求了
Redis 集羣是一個分佈式、容錯的 Redis 實現, 集羣可使用的功能是普通單機 Redis 所能使用的功能的一個子集。
Redis 集羣是Redis的分佈式實現,集羣中不存在中心節點或者代理節點, 集羣的其中一個主要設計目標是達到線性可擴展性。集羣的容錯功能是經過使用主節點和從節點兩種角色的節點來實現的:主節點和從節點使用徹底相同的服務器實現, 它們的功能也徹底同樣, 但從節點一般僅用於替換失效的主節點。若是不須要保證「先寫入,後讀取」操做的一致性, 那麼可使用從節點來執行只讀查詢。
Redis 集羣不像單機 Redis 那樣支持多數據庫功能, 集羣只使用默認的 0 號數據庫, 而且不能使用 SELECT index 命令。

Redis集羣中節點的工做內容

  • 保存鍵值對數據。
  • 記錄集羣的狀態,包括鍵到正確節點的映射。
  • 自動發現其餘節點,識別工做不正常的節點,並從節點中選舉出新的主節點。

鍵分佈模型

Redis 集羣的鍵空間被分割爲 16384 個槽(slot), 集羣的最大節點數量也是 16384 個。當一個集羣處於「穩定」狀態時, 集羣每一個哈希槽都不會進行移動,當須要添加一個節點的時候,只須要將其餘節點的某些哈希槽轉移到新節點上,當須要刪除一個節點的時候,就把此節點的哈希槽轉移到其餘節點上就能夠了。一個主節點能夠有任意多個從節點, 這些從節點用於在主節點發生網絡斷線或者節點失效時, 對主節點進行替換。

集羣節點屬性

每一個節點在集羣中都有一個獨一無二的 ID , 該 ID 是一個十六進制表示的 160 位隨機數, 在節點第一次啓動時由 /dev/urandom 生成。
節點會將它的 ID 保存到配置文件, 只要這個配置文件不被刪除, 節點就會一直沿用這個 ID 。
節點 ID 用於標識集羣中的每一個節點。 一個節點能夠改變它的 IP 和端口號, 而不改變節點 ID 。 集羣能夠自動識別出 IP/端口號的變化, 並將這一信息經過 Gossip 協議廣播給其餘節點知道。
如下是每一個節點都有的關聯信息, 而且節點會將這些信息發送給其餘節點:

  • 節點所使用的 IP 地址和 TCP 端口號。
  • 節點的標誌(flags)。
  • 節點負責處理的哈希槽。
  • 節點最近一次使用集羣鏈接發送 PING 數據包(packet)的時間。
  • 節點最近一次在回覆中接收到 PONG 數據包的時間。
  • 集羣將該節點標記爲下線的時間。
  • 該節點的從節點數量。
  • 若是該節點是從節點的話,那麼它會記錄主節點的節點 ID 。 若是這是一個主節點的話,那麼主節點 ID 這一欄的值爲 0000000 。

以上信息的其中一部分能夠經過向集羣中的任意節點(主節點或者從節點均可以)發送 CLUSTER NODES 命令來得到。

集羣數據一致性

Redis集羣是沒法保證數據的強一致性的

  1. 緣由之一是由於Redis集羣是異步複製的,當客戶端向服務端的某個主節點寫數據時,主服務器會先響應客戶端,以後再把寫操做請求發送給從節點,在這過程之中,若是主節點發生了宕機,而從節點尚未收到寫操做的請求,那麼這條數據將會永久丟失,固然能夠經過強制數據庫在回覆客戶端之前刷新數據到磁盤,但這樣會致使性能下降。
  2. 還有一種狀況是網絡分區(network partition)帶來的,當Redis集羣出現網絡分區時,客戶端仍對處於小分區的主節點進行寫操做,當達到集羣的node timeout的時間限制後,處於大分區的那個從節點將會取代處於小分區的主節點稱爲新的主節點,而在node timeout這段時間裏客戶端向主節點寫入的數據將會丟失

節點失效檢測

  1. 當一個節點向另外一個節點發送 PING 命令時, 目標節點未能在node timeout內返回 PING 命令的回覆時, 那麼發送命令的節點會將目標節點標記爲 PFAIL (可能已失效)。
  2. 每次當節點對其餘節點發送 PING 命令的時候, 它都會隨機地廣播三個它所知道的節點的信息, 這些信息裏面的其中一項就是說明節點是否已經被標記爲 PFAIL 或者 FAIL 。
  3. 若是節點已經將某個節點標記爲 PFAIL , 而且集羣中的大部分其餘主節點也認爲那個節點進入了失效狀態, 那麼節點會將那個失效節點的狀態標記爲 FAIL 。
  4. 一旦某個節點被標記爲 FAIL , 關於這個節點已失效的信息就會被廣播到整個集羣, 全部接收到這條信息的節點都會將失效節點標記爲 FAIL 。

集羣搭建

編輯配置文件啓動集羣節點

每一個集羣實例都要開啓,以後重啓redis實例

cluster-enabled yes
cluster-config-file nodes-6379.conf
cluster-node-timeout 15000

鏈接集羣節點並分配槽

./redis-cli --cluster create 192.168.179.131:6379 192.168.179.131:6380 192.168.179.134:6379 192.168.179.134:6380 192.168.179.132:6379 192.168.179.132:6380 --cluster-replicas 1

Redis基礎知識(二)

能夠查看集羣的節點信息,輸入集羣內任意節點地址便可

./redis-cli --cluster check 192.168.179.132:6379

Redis基礎知識(二)

集羣驗證

當咱們set一個key 時,會用CRC16算法來取模獲得所屬的slot,而後將這個key 分到哈希槽區間的節點上,當get數據的時候,也是經過這種方法去對應的節點上獲取數據

./redis-cli -c -h 192.168.179.132 -p 6380
192.168.179.132:6380> set key 1
-> Redirected to slot [12539] located at 192.168.179.132:6379
OK
./redis-cli -c -h 192.168.179.134 -p 6379
192.168.179.134:6379> get key
-> Redirected to slot [12539] located at 192.168.179.132:6379
"1"

歡迎關注我的公號「運維開發故事」
Redis基礎知識(二)

相關文章
相關標籤/搜索