redis簡書

概述redis

Redis是一個開源的,基於內存的結構化數據存儲媒介,能夠做爲數據庫、緩存服務或消息服務使用。
Redis支持多種數據結構,包括字符串、哈希表、鏈表、集合、有序集合、位圖、Hyperloglogs等。
Redis具有LRU淘汰、事務實現、以及不一樣級別的硬盤持久化等能力,而且支持副本集和經過Redis Sentinel實現的高可用方案,同時還支持經過Redis Cluster實現的數據自動分片能力。算法

Redis的主要功能都基於單線程模型實現,也就是說Redis使用一個線程來服務全部的客戶端請求,同時Redis採用了非阻塞式IO,並精細地優化各類命令的算法時間複雜度,這些信息意味着:數據庫

Redis是線程安全的(由於只有一個線程),其全部操做都是原子的,不會因併發產生數據異常
Redis的速度很是快(由於使用非阻塞式IO,且大部分命令的算法時間複雜度都是O(1))
使用高耗時的Redis命令是很危險的,會佔用惟一的一個線程的大量處理時間,致使全部的請求都被拖慢。(例如時間複雜度爲O(N)的KEYS命令,嚴格禁止在生產環境中使用)數組


Redis的數據結構和相關經常使用命令緩存

本節中將介紹Redis支持的主要數據結構,以及相關的經常使用Redis命令。本節只對Redis命令進行扼要的介紹,且只列出了較經常使用的命令。若是想要了解完整的Redis命令集,或瞭解某個命令的詳細使用方法,請參考官方文檔:https://redis.io/commands安全

Key性能優化

Redis採用Key-Value型的基本數據結構,任何二進制序列均可以做爲Redis的Key使用(例如普通的字符串或一張JPEG圖片)
關於Key的一些注意事項:網絡

不要使用過長的Key。例如使用一個1024字節的key就不是一個好主意,不只會消耗更多的內存,還會致使查找的效率下降
Key短到缺失了可讀性也是很差的,例如"u1000flw"比起"user:1000:followers"來講,節省了寥寥的存儲空間,卻引起了可讀性和可維護性上的麻煩
最好使用統一的規範來設計Key,好比"object-type:id:attr",以這一規範設計出的Key多是"user:1000"或"comment:1234:reply-to"
Redis容許的最大Key長度是512MB(對Value的長度限制也是512MB)數據結構

其餘經常使用命令架構

EXISTS:判斷指定的key是否存在,返回1表明存在,0表明不存在,時間複雜度O(1)
DEL:刪除指定的key及其對應的value,時間複雜度O(N),N爲刪除的key數量
EXPIRE/PEXPIRE:爲一個key設置有效期,單位爲秒或毫秒,時間複雜度O(1)
TTL/PTTL:返回一個key剩餘的有效時間,單位爲秒或毫秒,時間複雜度O(1)
RENAME/RENAMENX:將key重命名爲newkey。使用RENAME時,若是newkey已經存在,其值會被覆蓋;使用RENAMENX時,若是newkey已經存在,則不會進行任何操做,時間複雜度O(1)
TYPE:返回指定key的類型,string, list, set, zset, hash。時間複雜度O(1)
CONFIG GET:得到Redis某配置項的當前值,可使用*通配符,時間複雜度O(1)
CONFIG SET:爲Redis某個配置項設置新值,時間複雜度O(1)
CONFIG REWRITE:讓Redis從新加載redis.conf中的配置
數據持久化

Redis提供了將數據按期自動持久化至硬盤的能力,包括RDB和AOF兩種方案,兩種方案分別有其長處和短板,能夠配合起來同時運行,確保數據的穩定性。

必須使用數據持久化嗎?

Redis的數據持久化機制是能夠關閉的。若是你只把Redis做爲緩存服務使用,Redis中存儲的全部數據都不是該數據的主體而僅僅是同步過來的備份,那麼能夠關閉Redis的數據持久化機制。
但一般來講,仍然建議至少開啓RDB方式的數據持久化,由於:

RDB方式的持久化幾乎不損耗Redis自己的性能,在進行RDB持久化時,Redis主進程惟一須要作的事情就是fork出一個子進程,全部持久化工做都由子進程完成
Redis不管由於什麼緣由crash掉以後,重啓時可以自動恢復到上一次RDB快照中記錄的數據。這省去了手工從其餘數據源(如DB)同步數據的過程,並且要比其餘任何的數據恢復方式都要快
如今硬盤那麼大,真的不缺那一點地方
RDB

採用RDB持久方式,Redis會按期保存數據快照至一個rbd文件中,並在啓動時自動加載rdb文件,恢復以前保存的數據。能夠在配置文件中配置Redis進行快照保存的時機:

save [seconds] [changes]
意爲在[seconds]秒內若是發生了[changes]次數據修改,則進行一次RDB快照保存,例如

save 60 100
會讓Redis每60秒檢查一次數據變動狀況,若是發生了100次或以上的數據變動,則進行RDB快照保存。
能夠配置多條save指令,讓Redis執行多級的快照保存策略。
Redis默認開啓RDB快照,默認的RDB策略以下:

save 900 1
save 300 10
save 60 10000
也能夠經過BGSAVE命令手工觸發RDB快照保存。

RDB的優勢:

對性能影響最小。如前文所述,Redis在保存RDB快照時會fork出子進程進行,幾乎不影響Redis處理客戶端請求的效率。
每次快照會生成一個完整的數據快照文件,因此能夠輔以其餘手段保存多個時間點的快照(例如把天天0點的快照備份至其餘存儲媒介中),做爲很是可靠的災難恢復手段。
使用RDB文件進行數據恢復比使用AOF要快不少。
RDB的缺點:

快照是按期生成的,因此在Redis crash時或多或少會丟失一部分數據。
若是數據集很是大且CPU不夠強(好比單核CPU),Redis在fork子進程時可能會消耗相對較長的時間(長至1秒),影響這期間的客戶端請求。

AOF

採用AOF持久方式時,Redis會把每個寫請求都記錄在一個日誌文件裏。在Redis重啓時,會把AOF文件中記錄的全部寫操做順序執行一遍,確保數據恢復到最新。

AOF默認是關閉的,如要開啓,進行以下配置:

appendonly yes
AOF提供了三種fsync配置,always/everysec/no,經過配置項[appendfsync]指定:

appendfsync no:不進行fsync,將flush文件的時機交給OS決定,速度最快
appendfsync always:每寫入一條日誌就進行一次fsync操做,數據安全性最高,但速度最慢
appendfsync everysec:折中的作法,交由後臺線程每秒fsync一次
隨着AOF不斷地記錄寫操做日誌,一定會出現一些無用的日誌,例如某個時間點執行了命令SET key1 "abc",在以後某個時間點又執行了SET key1 "bcd",那麼第一條命令很顯然是沒有用的。大量的無用日誌會讓AOF文件過大,也會讓數據恢復的時間過長。
因此Redis提供了AOF rewrite功能,能夠重寫AOF文件,只保留可以把數據恢復到最新狀態的最小寫操做集。
AOF rewrite能夠經過BGREWRITEAOF命令觸發,也能夠配置Redis按期自動進行:

auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
上面兩行配置的含義是,Redis在每次AOF rewrite時,會記錄完成rewrite後的AOF日誌大小,當AOF日誌大小在該基礎上增加了100%後,自動進行AOF rewrite。同時若是增加的大小沒有達到64mb,則不會進行rewrite。

AOF的優勢:

最安全,在啓用appendfsync always時,任何已寫入的數據都不會丟失,使用在啓用appendfsync everysec也至多隻會丟失1秒的數據。
AOF文件在發生斷電等問題時也不會損壞,即便出現了某條日誌只寫入了一半的狀況,也可使用redis-check-aof工具輕鬆修復。
AOF文件易讀,可修改,在進行了某些錯誤的數據清除操做後,只要AOF文件沒有rewrite,就能夠把AOF文件備份出來,把錯誤的命令刪除,而後恢復數據。
AOF的缺點:

AOF文件一般比RDB文件更大
性能消耗比RDB高
數據恢復速度比RDB慢

內存管理與數據淘汰機制

最大內存設置

默認狀況下,在32位OS中,Redis最大使用3GB的內存,在64位OS中則沒有限制。

在使用Redis時,應該對數據佔用的最大空間有一個基本準確的預估,併爲Redis設定最大使用的內存。不然在64位OS中Redis會無限制地佔用內存(當物理內存被佔滿後會使用swap空間),容易引起各類各樣的問題。

經過以下配置控制Redis使用的最大內存:

maxmemory 100mb
在內存佔用達到了maxmemory後,再向Redis寫入數據時,Redis會:

根據配置的數據淘汰策略嘗試淘汰數據,釋放空間
若是沒有數據能夠淘汰,或者沒有配置數據淘汰策略,那麼Redis會對全部寫請求返回錯誤,但讀請求仍然能夠正常執行
在爲Redis設置maxmemory時,須要注意:

數據淘汰機制

Redis提供了5種數據淘汰策略:

volatile-lru:使用LRU算法進行數據淘汰(淘汰上次使用時間最先的,且使用次數最少的key),只淘汰設定了有效期的key
allkeys-lru:使用LRU算法進行數據淘汰,全部的key均可以被淘汰
volatile-random:隨機淘汰數據,只淘汰設定了有效期的key
allkeys-random:隨機淘汰數據,全部的key均可以被淘汰
volatile-ttl:淘汰剩餘有效期最短的key
最好爲Redis指定一種有效的數據淘汰策略以配合maxmemory設置,避免在內存使用滿後發生寫入失敗的狀況。

通常來講,推薦使用的策略是volatile-lru,並辨識Redis中保存的數據的重要性。對於那些重要的,絕對不能丟棄的數據(如配置類數據等),應不設置有效期,這樣Redis就永遠不會淘汰這些數據。對於那些相對不是那麼重要的,而且可以熱加載的數據(好比緩存最近登陸的用戶信息,當在Redis中找不到時,程序會去DB中讀取),能夠設置上有效期,這樣在內存不夠時Redis就會淘汰這部分數據。

配置方法:

maxmemory-policy volatile-lru   #默認是noeviction,即不進行數據淘汰

Pipelining

Pipelining

Redis提供許多批量操做的命令,如MSET/MGET/HMSET/HMGET等等,這些命令存在的意義是減小維護網絡鏈接和傳輸數據所消耗的資源和時間。
例如連續使用5次SET命令設置5個不一樣的key,比起使用一次MSET命令設置5個不一樣的key,效果是同樣的,但前者會消耗更多的RTT(Round Trip Time)時長,永遠應優先使用後者。

然而,若是客戶端要連續執行的屢次操做沒法經過Redis命令組合在一塊兒,例如:

SET a "abc"
INCR b
HSET c name "hi"
此時即可以使用Redis提供的pipelining功能來實如今一次交互中執行多條命令。
使用pipelining時,只須要從客戶端一次向Redis發送多條命令(以\r\n)分隔,Redis就會依次執行這些命令,而且把每一個命令的返回按順序組裝在一塊兒一次返回,好比:

$ (printf "PING\r\nPING\r\nPING\r\n"; sleep 1) | nc localhost 6379
+PONG
+PONG
+PONG
大部分的Redis客戶端都對Pipelining提供支持,因此開發者一般並不須要本身手工拼裝命令列表。

Pipelining的侷限性

Pipelining只能用於執行連續且無相關性的命令,當某個命令的生成須要依賴於前一個命令的返回時,就沒法使用Pipelining了。

經過Scripting功能,能夠規避這一侷限性

事務與Scripting

Pipelining可以讓Redis在一次交互中處理多條命令,然而在一些場景下,咱們可能須要在此基礎上確保這一組命令是連續執行的。

好比獲取當前累計的PV數並將其清0

> GET vCount
12384
> SET vCount 0
OK
若是在GET和SET命令之間插進來一個INCR vCount,就會使客戶端拿到的vCount不許確。

Redis的事務能夠確保複數命令執行時的原子性。也就是說Redis可以保證:一個事務中的一組命令是絕對連續執行的,在這些命令執行完成以前,絕對不會有來自於其餘鏈接的其餘命令插進去執行。

經過MULTI和EXEC命令來把這兩個命令加入一個事務中:

> MULTI
OK
> GET vCount
QUEUED
> SET vCount 0
QUEUED
> EXEC
1) 12384
2) OK
Redis在接收到MULTI命令後便會開啓一個事務,這以後的全部讀寫命令都會保存在隊列中但並不執行,直到接收到EXEC命令後,Redis會把隊列中的全部命令連續順序執行,並以數組形式返回每一個命令的返回結果。

可使用DISCARD命令放棄當前的事務,將保存的命令隊列清空。

須要注意的是,Redis事務不支持回滾:
若是一個事務中的命令出現了語法錯誤,大部分客戶端驅動會返回錯誤,2.6.5版本以上的Redis也會在執行EXEC時檢查隊列中的命令是否存在語法錯誤,若是存在,則會自動放棄事務並返回錯誤。
但若是一個事務中的命令有非語法類的錯誤(好比對String執行HSET操做),不管客戶端驅動仍是Redis都沒法在真正執行這條命令以前發現,因此事務中的全部命令仍然會被依次執行。在這種狀況下,會出現一個事務中部分命令成功部分命令失敗的狀況,然而與RDBMS不一樣,Redis不提供事務回滾的功能,因此只能經過其餘方法進行數據的回滾。

經過事務實現CAS

Redis提供了WATCH命令與事務搭配使用,實現CAS樂觀鎖的機制。

假設要實現將某個商品的狀態改成已售:

if(exec(HGET stock:1001 state) == "in stock")
    exec(HSET stock:1001 state "sold");
這一僞代碼執行時,沒法確保併發安全性,有可能多個客戶端都獲取到了"in stock"的狀態,致使一個庫存被售賣屢次。

使用WATCH命令和事務能夠解決這一問題:

exec(WATCH stock:1001);
if(exec(HGET stock:1001 state) == "in stock") {
    exec(MULTI);
    exec(HSET stock:1001 state "sold");
    exec(EXEC);
}
WATCH的機制是:在事務EXEC命令執行時,Redis會檢查被WATCH的key,只有被WATCH的key從WATCH起始時至今沒有發生過變動,EXEC纔會被執行。若是WATCH的key在WATCH命令到EXEC命令之間發生過變化,則EXEC命令會返回失敗。

Scripting

經過EVAL與EVALSHA命令,可讓Redis執行LUA腳本。這就相似於RDBMS的存儲過程同樣,能夠把客戶端與Redis之間密集的讀/寫交互放在服務端進行,避免過多的數據交互,提高性能。

Scripting功能是做爲事務功能的替代者誕生的,事務提供的全部能力Scripting均可以作到。Redis官方推薦使用LUA Script來代替事務,前者的效率和便利性都超過了事務。

關於Scripting的具體使用,本文不作詳細介紹,請參考官方文檔 https://redis.io/commands/eval

Redis性能調優

儘管Redis是一個很是快速的內存數據存儲媒介,也並不表明Redis不會產生性能問題。
前文中提到過,Redis採用單線程模型,全部的命令都是由一個線程串行執行的,因此當某個命令執行耗時較長時,會拖慢其後的全部命令,這使得Redis對每一個任務的執行效率更加敏感。

針對Redis的性能優化,主要從下面幾個層面入手:

最初的也是最重要的,確保沒有讓Redis執行耗時長的命令
使用pipelining將連續執行的命令組合執行
操做系統的Transparent huge pages功能必須關閉:
echo never > /sys/kernel/mm/transparent_hugepage/enabled
若是在虛擬機中運行Redis,可能自然就有虛擬機環境帶來的固有延遲。能夠經過./redis-cli --intrinsic-latency 100命令查看固有延遲。同時若是對Redis的性能有較高要求的話,應儘量在物理機上直接部署Redis。
檢查數據持久化策略
考慮引入讀寫分離機制
長耗時命令

Redis絕大多數讀寫命令的時間複雜度都在O(1)到O(N)之間,在文本和官方文檔中均對每一個命令的時間複雜度有說明。

一般來講,O(1)的命令是安全的,O(N)命令在使用時須要注意,若是N的數量級不可預知,則應避免使用。例如對一個field數未知的Hash數據執行HGETALL/HKEYS/HVALS命令,一般來講這些命令執行的很快,但若是這個Hash中的field數量極多,耗時就會成倍增加。
又如使用SUNION對兩個Set執行Union操做,或使用SORT對List/Set執行排序操做等時,都應該嚴加註意。

避免在使用這些O(N)命令時發生問題主要有幾個辦法:

不要把List當作列表使用,僅當作隊列來使用
經過機制嚴格控制Hash、Set、Sorted Set的大小
可能的話,將排序、並集、交集等操做放在客戶端執行
絕對禁止使用KEYS命令
避免一次性遍歷集合類型的全部成員,而應使用SCAN類的命令進行分批的,遊標式的遍歷
Redis提供了SCAN命令,能夠對Redis中存儲的全部key進行遊標式的遍歷,避免使用KEYS命令帶來的性能問題。同時還有SSCAN/HSCAN/ZSCAN等命令,分別用於對Set/Hash/Sorted Set中的元素進行遊標式遍歷。SCAN類命令的使用請參考官方文檔:https://redis.io/commands/scan

Redis提供了Slow Log功能,能夠自動記錄耗時較長的命令。相關的配置參數有兩個:

slowlog-log-slower-than xxxms  #執行時間慢於xxx毫秒的命令計入Slow Log
slowlog-max-len xxx  #Slow Log的長度,即最大紀錄多少條Slow Log
使用SLOWLOG GET [number]命令,能夠輸出最近進入Slow Log的number條命令。
使用SLOWLOG RESET命令,能夠重置Slow Log

網絡引起的延遲

儘量使用長鏈接或鏈接池,避免頻繁建立銷燬鏈接
客戶端進行的批量數據操做,應使用Pipeline特性在一次交互中完成。具體請參照本文的Pipelining章節
數據持久化引起的延遲

Redis的數據持久化工做自己就會帶來延遲,須要根據數據的安全級別和性能要求制定合理的持久化策略:

AOF + fsync always的設置雖然可以絕對確保數據安全,但每一個操做都會觸發一次fsync,會對Redis的性能有比較明顯的影響
AOF + fsync every second是比較好的折中方案,每秒fsync一次
AOF + fsync never會提供AOF持久化方案下的最優性能
使用RDB持久化一般會提供比使用AOF更高的性能,但須要注意RDB的策略配置
每一次RDB快照和AOF Rewrite都須要Redis主進程進行fork操做。fork操做自己可能會產生較高的耗時,與CPU和Redis佔用的內存大小有關。根據具體的狀況合理配置RDB快照和AOF Rewrite時機,避免過於頻繁的fork帶來的延遲
Redis在fork子進程時須要將內存分頁表拷貝至子進程,以佔用了24GB內存的Redis實例爲例,共須要拷貝24GB / 4kB * 8 = 48MB的數據。在使用單Xeon 2.27Ghz的物理機上,這一fork操做耗時216ms。

能夠經過INFO命令返回的latest_fork_usec字段查看上一次fork操做的耗時(微秒)
Swap引起的延遲

當Linux將Redis所用的內存分頁移至swap空間時,將會阻塞Redis進程,致使Redis出現不正常的延遲。Swap一般在物理內存不足或一些進程在進行大量I/O操做時發生,應儘量避免上述兩種狀況的出現。

/proc/<pid>/smaps文件中會保存進程的swap記錄,經過查看這個文件,可以判斷Redis的延遲是否由Swap產生。若是這個文件中記錄了較大的Swap size,則說明延遲頗有多是Swap形成的。

數據淘汰引起的延遲

當同一秒內有大量key過時時,也會引起Redis的延遲。在使用時應儘可能將key的失效時間錯開。

引入讀寫分離機制

Redis的主從複製能力能夠實現一主多從的多節點架構,在這一架構下,主節點接收全部寫請求,並將數據同步給多個從節點。
在這一基礎上,咱們可讓從節點提供對實時性要求不高的讀請求服務,以減少主節點的壓力。
尤爲是針對一些使用了長耗時命令的統計類任務,徹底能夠指定在一個或多個從節點上執行,避免這些長耗時命令影響其餘請求的響應。

關於讀寫分離的具體說明,請參見後續章節

主從複製與集羣分片

主從複製

Redis支持一主多從的主從複製架構。一個Master實例負責處理全部的寫請求,Master將寫操做同步至全部Slave。
藉助Redis的主從複製,能夠實現讀寫分離和高可用:

實時性要求不是特別高的讀請求,能夠在Slave上完成,提高效率。特別是一些週期性執行的統計任務,這些任務可能須要執行一些長耗時的Redis命令,能夠專門規劃出1個或幾個Slave用於服務這些統計任務
藉助Redis Sentinel能夠實現高可用,當Master crash後,Redis Sentinel可以自動將一個Slave晉升爲Master,繼續提供服務
啓用主從複製很是簡單,只須要配置多個Redis實例,在做爲Slave的Redis實例中配置:

slaveof 192.168.1.1 6379  #指定Master的IP和端口
當Slave啓動後,會從Master進行一次冷啓動數據同步,由Master觸發BGSAVE生成RDB文件推送給Slave進行導入,導入完成後Master再將增量數據經過Redis Protocol同步給Slave。以後主從之間的數據便一直以Redis Protocol進行同步

使用Sentinel作自動failover

Redis的主從複製功能自己只是作數據同步,並不提供監控和自動failover能力,要經過主從複製功能來實現Redis的高可用,還須要引入一個組件:Redis Sentinel

Redis Sentinel是Redis官方開發的監控組件,能夠監控Redis實例的狀態,經過Master節點自動發現Slave節點,並在監測到Master節點失效時選舉出一個新的Master,並向全部Redis實例推送新的主從配置。

Redis Sentinel須要至少部署3個實例才能造成選舉關係。

關鍵配置:

sentinel monitor mymaster 127.0.0.1 6379 2  #Master實例的IP、端口,以及選舉須要的同意票數
sentinel down-after-milliseconds mymaster 60000  #多長時間沒有響應視爲Master失效
sentinel failover-timeout mymaster 180000  #兩次failover嘗試間的間隔時長
sentinel parallel-syncs mymaster 1  #若是有多個Slave,能夠經過此配置指定同時重新Master進行數據同步的Slave數,避免全部Slave同時進行數據同步致使查詢服務也不可用
另外須要注意的是,Redis Sentinel實現的自動failover不是在同一個IP和端口上完成的,也就是說自動failover產生的新Master提供服務的IP和端口與以前的Master是不同的,因此要實現HA,還要求客戶端必須支持Sentinel,可以與Sentinel交互得到新Master的信息才行。

集羣分片

爲什麼要作集羣分片:

Redis中存儲的數據量大,一臺主機的物理內存已經沒法容納
Redis的寫請求併發量大,一個Redis實例以沒法承載
當上述兩個問題出現時,就必需要對Redis進行分片了。
Redis的分片方案有不少種,例如不少Redis的客戶端都自行實現了分片功能,也有向Twemproxy這樣的以代理方式實現的Redis分片方案。然而首選的方案還應該是Redis官方在3.0版本中推出的Redis Cluster分片方案。

本文不會對Redis Cluster的具體安裝和部署細節進行介紹,重點介紹Redis Cluster帶來的好處與弊端。

Redis Cluster的能力

可以自動將數據分散在多個節點上
當訪問的key不在當前分片上時,可以自動將請求轉發至正確的分片
當集羣中部分節點失效時仍能提供服務
其中第三點是基於主從複製來實現的,Redis Cluster的每一個數據分片都採用了主從複製的結構,原理和前文所述的主從複製徹底一致,惟一的區別是省去了Redis Sentinel這一額外的組件,由Redis Cluster負責進行一個分片內部的節點監控和自動failover。

Redis Cluster分片原理

Redis Cluster中共有16384個hash slot,Redis會計算每一個key的CRC16,將結果與16384取模,來決定該key存儲在哪個hash slot中,同時須要指定Redis Cluster中每一個數據分片負責的Slot數。Slot的分配在任什麼時候間點均可以進行從新分配。

客戶端在對key進行讀寫操做時,能夠鏈接Cluster中的任意一個分片,若是操做的key不在此分片負責的Slot範圍內,Redis Cluster會自動將請求重定向到正確的分片上。

hash tags

在基礎的分片原則上,Redis還支持hash tags功能,以hash tags要求的格式明明的key,將會確保進入同一個Slot中。例如:{uiv}user:1000和{uiv}user:1001擁有一樣的hash tag {uiv},會保存在同一個Slot中。

使用Redis Cluster時,pipelining、事務和LUA Script功能涉及的key必須在同一個數據分片上,不然將會返回錯誤。如要在Redis Cluster中使用上述功能,就必須經過hash tags來確保一個pipeline或一個事務中操做的全部key都位於同一個Slot中。

有一些客戶端(如Redisson)實現了集羣化的pipelining操做,能夠自動將一個pipeline裏的命令按key所在的分片進行分組,分別發到不一樣的分片上執行。可是Redis不支持跨分片的事務,事務和LUA Script仍是必須遵循全部key在一個分片上的規則要求。
主從複製 vs 集羣分片

在設計軟件架構時,要如何在主從複製和集羣分片兩種部署方案中取捨呢?

從各個方面看,Redis Cluster都是優於主從複製的方案

Redis Cluster可以解決單節點上數據量過大的問題
Redis Cluster可以解決單節點訪問壓力過大的問題
Redis Cluster包含了主從複製的能力
那是否是表明Redis Cluster永遠是優於主從複製的選擇呢?

並非。

軟件架構永遠不是越複雜越好,複雜的架構在帶來顯著好處的同時,必定也會帶來相應的弊端。採用Redis Cluster的弊端包括:

維護難度增長。在使用Redis Cluster時,須要維護的Redis實例數倍增,須要監控的主機數量也相應增長,數據備份/持久化的複雜度也會增長。同時在進行分片的增減操做時,還須要進行reshard操做,遠比主從模式下增長一個Slave的複雜度要高。
客戶端資源消耗增長。當客戶端使用鏈接池時,須要爲每個數據分片維護一個鏈接池,客戶端同時須要保持的鏈接數成倍增多,加大了客戶端自己和操做系統資源的消耗。
性能優化難度增長。你可能須要在多個分片上查看Slow Log和Swap日誌才能定位性能問題。
事務和LUA Script的使用成本增長。在Redis Cluster中使用事務和LUA Script特性有嚴格的限制條件,事務和Script中操做的key必須位於同一個分片上,這就使得在開發時必須對相應場景下涉及的key進行額外的規劃和規範要求。若是應用的場景中大量涉及事務和Script的使用,如何在保證這兩個功能的正常運做前提下把數據平均分到多個數據分片中就會成爲難點。
因此說,在主從複製和集羣分片兩個方案中作出選擇時,應該從應用軟件的功能特性、數據和訪問量級、將來發展規劃等方面綜合考慮,只在確實有必要引入數據分片時再使用Redis Cluster。
下面是一些建議:

須要在Redis中存儲的數據有多大?將來2年內可能發展爲多大?這些數據是否都須要長期保存?是否可使用LRU算法進行非熱點數據的淘汰?綜合考慮前面幾個因素,評估出Redis須要使用的物理內存。 用於部署Redis的主機物理內存有多大?有多少能夠分配給Redis使用?對比(1)中的內存需求評估,是否足夠用? Redis面臨的併發寫壓力會有多大?在不使用pipelining時,Redis的寫性能能夠超過10萬次/秒(更多的benchmark能夠參考 https://redis.io/topics/benchmarks ) 在使用Redis時,是否會使用到pipelining和事務功能?使用的場景多很少? 綜合上面幾點考慮,若是單臺主機的可用物理內存徹底足以支撐對Redis的容量需求,且Redis面臨的併發寫壓力距離Benchmark值還尚有距離,建議採用主從複製的架構,能夠省去不少沒必要要的麻煩。同時,若是應用中大量使用pipelining和事務,也建議儘量選擇主從複製架構,能夠減小設計和開發時的複雜度。

相關文章
相關標籤/搜索