Redis基礎金矛、高級特性與性能調(diào)優(yōu)

本文將從Redis的基本特性入手,通過講述Redis的數(shù)據(jù)結(jié)構(gòu)和主要命令對Redis的基本能力進行直觀介紹勺届。之后概覽Redis提供的高級能力驶俊,并在部署、維護免姿、性能調(diào)優(yōu)等多個方面進行更深入的介紹和指導饼酿。
本文適合使用Redis的普通開發(fā)人員,以及對Redis進行選型胚膊、架構(gòu)設計和性能調(diào)優(yōu)的架構(gòu)設計人員故俐。

目錄
概述
Redis的數(shù)據(jù)結(jié)構(gòu)和相關常用命令
數(shù)據(jù)持久化
內(nèi)存管理與數(shù)據(jù)淘汰機制
Pipelining
事務與Scripting
Redis性能調(diào)優(yōu)
主從復制與集群分片
Redis Java客戶端的選擇

概述
Redis是一個開源的,基于內(nèi)存的結(jié)構(gòu)化數(shù)據(jù)存儲媒介紊婉,可以作為數(shù)據(jù)庫药版、緩存服務或消息服務使用。
Redis支持多種數(shù)據(jù)結(jié)構(gòu)喻犁,包括字符串槽片、哈希表、鏈表肢础、集合还栓、有序集合、位圖乔妈、Hyperloglogs等蝙云。
Redis具備LRU淘汰、事務實現(xiàn)路召、以及不同級別的硬盤持久化等能力勃刨,并且支持副本集和通過Redis Sentinel實現(xiàn)的高可用方案,同時還支持通過Redis Cluster實現(xiàn)的數(shù)據(jù)自動分片能力股淡。
Redis的主要功能都基于單線程模型實現(xiàn)身隐,也就是說Redis使用一個線程來服務所有的客戶端請求,同時Redis采用了非阻塞式IO唯灵,并精細地優(yōu)化各種命令的算法時間復雜度贾铝,這些信息意味著:
Redis是線程安全的(因為只有一個線程),其所有操作都是原子的埠帕,不會因并發(fā)產(chǎn)生數(shù)據(jù)異常
Redis的速度非彻缚快(因為使用非阻塞式IO,且大部分命令的算法時間復雜度都是O(1))
使用高耗時的Redis命令是很危險的敛瓷,會占用唯一的一個線程的大量處理時間叁巨,導致所有的請求都被拖慢。(例如時間復雜度為O(N)的KEYS命令呐籽,嚴格禁止在生產(chǎn)環(huán)境中使用)

Redis的數(shù)據(jù)結(jié)構(gòu)和相關常用命令
本節(jié)中將介紹Redis支持的主要數(shù)據(jù)結(jié)構(gòu)锋勺,以及相關的常用Redis命令蚀瘸。本節(jié)只對Redis命令進行扼要的介紹,且只列出了較常用的命令庶橱。如果想要了解完整的Redis命令集贮勃,或了解某個命令的詳細使用方法,請參考官方文檔:https://redis.io/commands

Key
Redis采用Key-Value型的基本數(shù)據(jù)結(jié)構(gòu)苏章,任何二進制序列都可以作為Redis的Key使用(例如普通的字符串或一張JPEG圖片)
關于Key的一些注意事項:
不要使用過長的Key寂嘉。例如使用一個1024字節(jié)的key就不是一個好主意,不僅會消耗更多的內(nèi)存布近,還會導致查找的效率降低
Key短到缺失了可讀性也是不好的垫释,例如"u1000flw"比起"user:1000:followers"來說丝格,節(jié)省了寥寥的存儲空間撑瞧,卻引發(fā)了可讀性和可維護性上的麻煩
最好使用統(tǒng)一的規(guī)范來設計Key,比如"object-type:id:attr"显蝌,以這一規(guī)范設計出的Key可能是"user:1000"或"comment:1234:reply-to"
Redis允許的最大Key長度是512MB(對Value的長度限制也是512MB)

String
String是Redis的基礎數(shù)據(jù)類型预伺,Redis沒有Int、Float曼尊、Boolean等數(shù)據(jù)類型的概念酬诀,所有的基本類型在Redis中都以String體現(xiàn)。
與String相關的常用命令:
SET:為一個key設置value骆撇,可以配合EX/PX參數(shù)指定key的有效期瞒御,通過NX/XX參數(shù)針對key是否存在的情況進行區(qū)別操作,時間復雜度O(1)
GET:獲取某個key對應的value神郊,時間復雜度O(1)
GETSET:為一個key設置value肴裙,并返回該key的原value,時間復雜度O(1)
MSET:為多個key設置value涌乳,時間復雜度O(N)
MSETNX:同MSET蜻懦,如果指定的key中有任意一個已存在,則不進行任何操作夕晓,時間復雜度O(N)
MGET:獲取多個key對應的value宛乃,時間復雜度O(N)
上文提到過,Redis的基本數(shù)據(jù)類型只有String蒸辆,但Redis可以把String作為整型或浮點型數(shù)字來使用征炼,主要體現(xiàn)在INCR、DECR類的命令上:
INCR:將key對應的value值自增1躬贡,并返回自增后的值谆奥。只對可以轉(zhuǎn)換為整型的String數(shù)據(jù)起作用。時間復雜度O(1)
INCRBY:將key對應的value值自增指定的整型數(shù)值逗宜,并返回自增后的值雄右。只對可以轉(zhuǎn)換為整型的String數(shù)據(jù)起作用空骚。時間復雜度O(1)
DECR/DECRBY:同INCR/INCRBY,自增改為自減擂仍。
INCR/DECR系列命令要求操作的value類型為String囤屹,并可以轉(zhuǎn)換為64位帶符號的整型數(shù)字,否則會返回錯誤逢渔。
也就是說肋坚,進行INCR/DECR系列命令的value,必須在[-2^63 ~ 2^63 - 1]范圍內(nèi)肃廓。
前文提到過智厌,Redis采用單線程模型,天然是線程安全的盲赊,這使得INCR/DECR命令可以非常便利的實現(xiàn)高并發(fā)場景下的精確控制铣鹏。

例1:庫存控制
在高并發(fā)場景下實現(xiàn)庫存余量的精準校驗,確保不出現(xiàn)超賣的情況哀蘑。
設置庫存總量:
SET inv:remain "100"
庫存扣減+余量校驗:

DECR inv:remain
當DECR命令返回值大于等于0時诚卸,說明庫存余量校驗通過,如果返回小于0的值绘迁,則說明庫存已耗盡合溺。
假設同時有300個并發(fā)請求進行庫存扣減,Redis能夠確保這300個請求分別得到99到-200的返回值缀台,每個請求得到的返回值都是唯一的棠赛,絕對不會找出現(xiàn)兩個請求得到一樣的返回值的情況。

例2:自增序列生成
實現(xiàn)類似于RDBMS的Sequence功能膛腐,生成一系列唯一的序列號
設置序列起始值:
SET sequence "10000"
獲取一個序列值:
INCR sequence
直接將返回值作為序列使用即可睛约。
獲取一批(如100個)序列值:
INCRBY sequence 100
假設返回值為N,那么[N - 99 ~ N]的數(shù)值都是可用的序列值沧踏。
當多個客戶端同時向Redis申請自增序列時阱高,Redis能夠確保每個客戶端得到的序列值或序列范圍都是全局唯一的,絕對不會出現(xiàn)不同客戶端得到了重復的序列值的情況勇哗。

List
Redis的List是鏈表型的數(shù)據(jù)結(jié)構(gòu)海铆,可以使用LPUSH/RPUSH/LPOP/RPOP等命令在List的兩端執(zhí)行插入元素和彈出元素的操作锤岸。雖然List也支持在特定index上插入和讀取元素的功能刺啦,但其時間復雜度較高(O(N))玛瘸,應小心使用蜕青。
與List相關的常用命令:
LPUSH:向指定List的左側(cè)(即頭部)插入1個或多個元素,返回插入后的List長度糊渊。時間復雜度O(N)市咆,N為插入元素的數(shù)量
RPUSH:同LPUSH,向指定List的右側(cè)(即尾部)插入1或多個元素
LPOP:從指定List的左側(cè)(即頭部)移除一個元素并返回再来,時間復雜度O(1)
RPOP:同LPOP蒙兰,從指定List的右側(cè)(即尾部)移除1個元素并返回
LPUSHX/RPUSHX:與LPUSH/RPUSH類似,區(qū)別在于芒篷,LPUSHX/RPUSHX操作的key如果不存在搜变,則不會進行任何操作
LLEN:返回指定List的長度,時間復雜度O(1)
LRANGE:返回指定List中指定范圍的元素(雙端包含针炉,即LRANGE key 0 10會返回11個元素)挠他,時間復雜度O(N)。應盡可能控制一次獲取的元素數(shù)量篡帕,一次獲取過大范圍的List元素會導致延遲哈街,同時對長度不可預知的List,避免使用LRANGE key 0 -1這樣的完整遍歷操作伸刃。
應謹慎使用的List相關命令:
LINDEX:返回指定List指定index上的元素屯吊,如果index越界,返回nil怔鳖。index數(shù)值是回環(huán)的茉唉,即-1代表List最后一個位置,-2代表List倒數(shù)第二個位置结执。時間復雜度O(N)
LSET:將指定List指定index上的元素設置為value度陆,如果index越界則返回錯誤,時間復雜度O(N)献幔,如果操作的是頭/尾部的元素懂傀,則時間復雜度為O(1)
LINSERT:向指定List中指定元素之前/之后插入一個新元素,并返回操作后的List長度蜡感。如果指定的元素不存在蹬蚁,返回-1。如果指定key不存在铸敏,不會進行任何操作缚忧,時間復雜度O(N)
由于Redis的List是鏈表結(jié)構(gòu)的,上述的三個命令的算法效率較低杈笔,需要對List進行遍歷闪水,命令的耗時無法預估,在List長度大的情況下耗時會明顯增加,應謹慎使用球榆。
換句話說朽肥,Redis的List實際是設計來用于實現(xiàn)隊列,而不是用于實現(xiàn)類似ArrayList這樣的列表的持钉。如果你不是想要實現(xiàn)一個雙端出入的隊列衡招,那么請盡量不要使用Redis的List數(shù)據(jù)結(jié)構(gòu)。
為了更好支持隊列的特性每强,Redis還提供了一系列阻塞式的操作命令始腾,如BLPOP/BRPOP等,能夠?qū)崿F(xiàn)類似于BlockingQueue的能力空执,即在List為空時浪箭,阻塞該連接,直到List中有對象可以出隊時再返回辨绊。針對阻塞類的命令奶栖,此處不做詳細探討,請參考官方文檔(https://redis.io/topics/data-types-intro) 中"Blocking operations on lists"一節(jié)门坷。

Hash
Hash即哈希表宣鄙,Redis的Hash和傳統(tǒng)的哈希表一樣,是一種field-value型的數(shù)據(jù)結(jié)構(gòu)默蚌,可以理解成將HashMap搬入Redis冻晤。
Hash非常適合用于表現(xiàn)對象類型的數(shù)據(jù),用Hash中的field對應對象的field即可敏簿。
Hash的優(yōu)點包括:
可以實現(xiàn)二元查找明也,如"查找ID為1000的用戶的年齡"
比起將整個對象序列化后作為String存儲的方法,Hash能夠有效地減少網(wǎng)絡傳輸?shù)南?br> 當使用Hash維護一個集合時惯裕,提供了比List效率高得多的隨機訪問命令
與Hash相關的常用命令:
HSET:將key對應的Hash中的field設置為value。如果該Hash不存在绣硝,會自動創(chuàng)建一個蜻势。時間復雜度O(1)
HGET:返回指定Hash中field字段的值,時間復雜度O(1)
HMSET/HMGET:同HSET和HGET鹉胖,可以批量操作同一個key下的多個field握玛,時間復雜度:O(N),N為一次操作的field數(shù)量
HSETNX:同HSET甫菠,但如field已經(jīng)存在挠铲,HSETNX不會進行任何操作,時間復雜度O(1)
HEXISTS:判斷指定Hash中field是否存在寂诱,存在返回1拂苹,不存在返回0,時間復雜度O(1)
HDEL:刪除指定Hash中的field(1個或多個)痰洒,時間復雜度:O(N)瓢棒,N為操作的field數(shù)量
HINCRBY:同INCRBY命令浴韭,對指定Hash中的一個field進行INCRBY,時間復雜度O(1)
應謹慎使用的Hash相關命令:
HGETALL:返回指定Hash中所有的field-value對脯宿。返回結(jié)果為數(shù)組念颈,數(shù)組中field和value交替出現(xiàn)。時間復雜度O(N)
HKEYS/HVALS:返回指定Hash中所有的field/value连霉,時間復雜度O(N)
上述三個命令都會對Hash進行完整遍歷榴芳,Hash中的field數(shù)量與命令的耗時線性相關,對于尺寸不可預知的Hash跺撼,應嚴格避免使用上面三個命令翠语,而改為使用HSCAN命令進行游標式的遍歷,具體請見 https://redis.io/commands/scan

Set
Redis Set是無序的财边,不可重復的String集合肌括。
與Set相關的常用命令:
SADD:向指定Set中添加1個或多個member,如果指定Set不存在酣难,會自動創(chuàng)建一個谍夭。時間復雜度O(N),N為添加的member個數(shù)
SREM:從指定Set中移除1個或多個member憨募,時間復雜度O(N)紧索,N為移除的member個數(shù)
SRANDMEMBER:從指定Set中隨機返回1個或多個member,時間復雜度O(N)菜谣,N為返回的member個數(shù)
SPOP:從指定Set中隨機移除并返回count個member珠漂,時間復雜度O(N),N為移除的member個數(shù)
SCARD:返回指定Set中的member個數(shù)尾膊,時間復雜度O(1)
SISMEMBER:判斷指定的value是否存在于指定Set中媳危,時間復雜度O(1)
SMOVE:將指定member從一個Set移至另一個Set
慎用的Set相關命令:
SMEMBERS:返回指定Hash中所有的member,時間復雜度O(N)
SUNION/SUNIONSTORE:計算多個Set的并集并返回/存儲至另一個Set中冈敛,時間復雜度O(N)待笑,N為參與計算的所有集合的總member數(shù)
SINTER/SINTERSTORE:計算多個Set的交集并返回/存儲至另一個Set中,時間復雜度O(N)抓谴,N為參與計算的所有集合的總member數(shù)
SDIFF/SDIFFSTORE:計算1個Set與1或多個Set的差集并返回/存儲至另一個Set中暮蹂,時間復雜度O(N),N為參與計算的所有集合的總member數(shù)
上述幾個命令涉及的計算量大癌压,應謹慎使用仰泻,特別是在參與計算的Set尺寸不可知的情況下,應嚴格避免使用滩届〖睿可以考慮通過SSCAN命令遍歷獲取相關Set的全部member(具體請見 https://redis.io/commands/scan ),如果需要做并集/交集/差集計算,可以在客戶端進行浅悉,或在不服務實時查詢請求的Slave上進行趟据。

Sorted Set
Redis Sorted Set是有序的、不可重復的String集合术健。Sorted Set中的每個元素都需要指派一個分數(shù)(score)汹碱,Sorted Set會根據(jù)score對元素進行升序排序。如果多個member擁有相同的score荞估,則以字典序進行升序排序咳促。
Sorted Set非常適合用于實現(xiàn)排名。
Sorted Set的主要命令:
ZADD:向指定Sorted Set中添加1個或多個member勘伺,時間復雜度O(Mlog(N))跪腹,M為添加的member數(shù)量,N為Sorted Set中的member數(shù)量
ZREM:從指定Sorted Set中刪除1個或多個member飞醉,時間復雜度O(Mlog(N))冲茸,M為刪除的member數(shù)量,N為Sorted Set中的member數(shù)量
ZCOUNT:返回指定Sorted Set中指定score范圍內(nèi)的member數(shù)量缅帘,時間復雜度:O(log(N))
ZCARD:返回指定Sorted Set中的member數(shù)量轴术,時間復雜度O(1)
ZSCORE:返回指定Sorted Set中指定member的score,時間復雜度O(1)
ZRANK/ZREVRANK:返回指定member在Sorted Set中的排名钦无,ZRANK返回按升序排序的排名逗栽,ZREVRANK則返回按降序排序的排名。時間復雜度O(log(N))
ZINCRBY:同INCRBY失暂,對指定Sorted Set中的指定member的score進行自增彼宠,時間復雜度O(log(N))
慎用的Sorted Set相關命令:
ZRANGE/ZREVRANGE:返回指定Sorted Set中指定排名范圍內(nèi)的所有member,ZRANGE為按score升序排序弟塞,ZREVRANGE為按score降序排序凭峡,時間復雜度O(log(N)+M),M為本次返回的member數(shù)
ZRANGEBYSCORE/ZREVRANGEBYSCORE:返回指定Sorted Set中指定score范圍內(nèi)的所有member宣肚,返回結(jié)果以升序/降序排序想罕,min和max可以指定為-inf和+inf,代表返回所有的member霉涨。時間復雜度O(log(N)+M)
ZREMRANGEBYRANK/ZREMRANGEBYSCORE:移除Sorted Set中指定排名范圍/指定score范圍內(nèi)的所有member。時間復雜度O(log(N)+M)
上述幾個命令惭适,應盡量避免傳遞[0 -1]或[-inf +inf]這樣的參數(shù)笙瑟,來對Sorted Set做一次性的完整遍歷,特別是在Sorted Set的尺寸不可預知的情況下癞志⊥希可以通過ZSCAN命令來進行游標式的遍歷(具體請見 https://redis.io/commands/scan ),或通過LIMIT參數(shù)來限制返回member的數(shù)量(適用于ZRANGEBYSCORE和ZREVRANGEBYSCORE命令),以實現(xiàn)游標式的遍歷错洁。

Bitmap和HyperLogLog
Redis的這兩種數(shù)據(jù)結(jié)構(gòu)相較之前的并不常用秉宿,在本文中只做簡要介紹,如想要詳細了解這兩種數(shù)據(jù)結(jié)構(gòu)與其相關的命令屯碴,請參考官方文檔https://redis.io/topics/data-types-intro 中的相關章節(jié)
Bitmap在Redis中不是一種實際的數(shù)據(jù)類型描睦,而是一種將String作為Bitmap使用的方法〉级可以理解為將String轉(zhuǎn)換為bit數(shù)組忱叭。使用Bitmap來存儲true/false類型的簡單數(shù)據(jù)極為節(jié)省空間。
HyperLogLogs是一種主要用于數(shù)量統(tǒng)計的數(shù)據(jù)結(jié)構(gòu)今艺,它和Set類似韵丑,維護一個不可重復的String集合,但是HyperLogLogs并不維護具體的member內(nèi)容虚缎,只維護member的個數(shù)撵彻。也就是說,HyperLogLogs只能用于計算一個集合中不重復的元素數(shù)量实牡,所以它比Set要節(jié)省很多內(nèi)存空間陌僵。

其他常用命令
EXISTS:判斷指定的key是否存在,返回1代表存在铲掐,0代表不存在拾弃,時間復雜度O(1)
DEL:刪除指定的key及其對應的value,時間復雜度O(N)摆霉,N為刪除的key數(shù)量
EXPIRE/PEXPIRE:為一個key設置有效期豪椿,單位為秒或毫秒,時間復雜度O(1)
TTL/PTTL:返回一個key剩余的有效時間携栋,單位為秒或毫秒搭盾,時間復雜度O(1)
RENAME/RENAMENX:將key重命名為newkey。使用RENAME時婉支,如果newkey已經(jīng)存在鸯隅,其值會被覆蓋;使用RENAMENX時向挖,如果newkey已經(jīng)存在蝌以,則不會進行任何操作,時間復雜度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中的配置

數(shù)據(jù)持久化
Redis提供了將數(shù)據(jù)定期自動持久化至硬盤的能力奸攻,包括RDB和AOF兩種方案,兩種方案分別有其長處和短板虱痕,可以配合起來同時運行睹耐,確保數(shù)據(jù)的穩(wěn)定性。
必須使用數(shù)據(jù)持久化嗎部翘?
Redis的數(shù)據(jù)持久化機制是可以關閉的硝训。如果你只把Redis作為緩存服務使用,Redis中存儲的所有數(shù)據(jù)都不是該數(shù)據(jù)的主體而僅僅是同步過來的備份略就,那么可以關閉Redis的數(shù)據(jù)持久化機制捎迫。
但通常來說,仍然建議至少開啟RDB方式的數(shù)據(jù)持久化表牢,因為:
RDB方式的持久化幾乎不損耗Redis本身的性能窄绒,在進行RDB持久化時,Redis主進程唯一需要做的事情就是fork出一個子進程崔兴,所有持久化工作都由子進程完成
Redis無論因為什么原因crash掉之后彰导,重啟時能夠自動恢復到上一次RDB快照中記錄的數(shù)據(jù)。這省去了手工從其他數(shù)據(jù)源(如DB)同步數(shù)據(jù)的過程敲茄,而且要比其他任何的數(shù)據(jù)恢復方式都要快
現(xiàn)在硬盤那么大位谋,真的不缺那一點地方

RDB
采用RDB持久方式,Redis會定期保存數(shù)據(jù)快照至一個rbd文件中堰燎,并在啟動時自動加載rdb文件掏父,恢復之前保存的數(shù)據(jù)「鸭簦可以在配置文件中配置Redis進行快照保存的時機:
save [seconds] [changes]
意為在[seconds]秒內(nèi)如果發(fā)生了[changes]次數(shù)據(jù)修改赊淑,則進行一次RDB快照保存,例如

save 60 100
會讓Redis每60秒檢查一次數(shù)據(jù)變更情況仅讽,如果發(fā)生了100次或以上的數(shù)據(jù)變更陶缺,則進行RDB快照保存。
可以配置多條save指令洁灵,讓Redis執(zhí)行多級的快照保存策略饱岸。
Redis默認開啟RDB快照,默認的RDB策略如下:
save 900 1
save 300 10
save 60 10000

也可以通過BGSAVE命令手工觸發(fā)RDB快照保存徽千。
RDB的優(yōu)點:
對性能影響最小苫费。如前文所述,Redis在保存RDB快照時會fork出子進程進行双抽,幾乎不影響Redis處理客戶端請求的效率黍衙。
每次快照會生成一個完整的數(shù)據(jù)快照文件,所以可以輔以其他手段保存多個時間點的快照(例如把每天0點的快照備份至其他存儲媒介中)荠诬,作為非忱欧可靠的災難恢復手段。
使用RDB文件進行數(shù)據(jù)恢復比使用AOF要快很多柑贞。
RDB的缺點:
快照是定期生成的方椎,所以在Redis crash時或多或少會丟失一部分數(shù)據(jù)。
如果數(shù)據(jù)集非常大且CPU不夠強(比如單核CPU)钧嘶,Redis在fork子進程時可能會消耗相對較長的時間(長至1秒)棠众,影響這期間的客戶端請求。

AOF
采用AOF持久方式時有决,Redis會把每一個寫請求都記錄在一個日志文件里闸拿。在Redis重啟時,會把AOF文件中記錄的所有寫操作順序執(zhí)行一遍书幕,確保數(shù)據(jù)恢復到最新新荤。
AOF默認是關閉的,如要開啟台汇,進行如下配置:
appendonly yes
AOF提供了三種fsync配置苛骨,always/everysec/no,通過配置項[appendfsync]指定:
appendfsync no:不進行fsync苟呐,將flush文件的時機交給OS決定痒芝,速度最快
appendfsync always:每寫入一條日志就進行一次fsync操作,數(shù)據(jù)安全性最高牵素,但速度最慢
appendfsync everysec:折中的做法严衬,交由后臺線程每秒fsync一次
隨著AOF不斷地記錄寫操作日志,必定會出現(xiàn)一些無用的日志笆呆,例如某個時間點執(zhí)行了命令SET key1 "abc"请琳,在之后某個時間點又執(zhí)行了SET key1 "bcd",那么第一條命令很顯然是沒有用的腰奋。大量的無用日志會讓AOF文件過大单起,也會讓數(shù)據(jù)恢復的時間過長。
所以Redis提供了AOF rewrite功能劣坊,可以重寫AOF文件嘀倒,只保留能夠把數(shù)據(jù)恢復到最新狀態(tài)的最小寫操作集。
AOF rewrite可以通過BGREWRITEAOF命令觸發(fā)局冰,也可以配置Redis定期自動進行:
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

上面兩行配置的含義是测蘑,Redis在每次AOF rewrite時,會記錄完成rewrite后的AOF日志大小康二,當AOF日志大小在該基礎上增長了100%后碳胳,自動進行AOF rewrite。同時如果增長的大小沒有達到64mb沫勿,則不會進行rewrite挨约。
AOF的優(yōu)點:
最安全味混,在啟用appendfsync always時,任何已寫入的數(shù)據(jù)都不會丟失诫惭,使用在啟用appendfsync everysec也至多只會丟失1秒的數(shù)據(jù)翁锡。
AOF文件在發(fā)生斷電等問題時也不會損壞,即使出現(xiàn)了某條日志只寫入了一半的情況夕土,也可以使用redis-check-aof工具輕松修復馆衔。
AOF文件易讀,可修改怨绣,在進行了某些錯誤的數(shù)據(jù)清除操作后角溃,只要AOF文件沒有rewrite,就可以把AOF文件備份出來篮撑,把錯誤的命令刪除减细,然后恢復數(shù)據(jù)。
AOF的缺點:
AOF文件通常比RDB文件更大
性能消耗比RDB高
數(shù)據(jù)恢復速度比RDB慢

內(nèi)存管理與數(shù)據(jù)淘汰機制
最大內(nèi)存設置
默認情況下咽扇,在32位OS中邪财,Redis最大使用3GB的內(nèi)存,在64位OS中則沒有限制质欲。
在使用Redis時树埠,應該對數(shù)據(jù)占用的最大空間有一個基本準確的預估,并為Redis設定最大使用的內(nèi)存嘶伟。否則在64位OS中Redis會無限制地占用內(nèi)存(當物理內(nèi)存被占滿后會使用swap空間)怎憋,容易引發(fā)各種各樣的問題。
通過如下配置控制Redis使用的最大內(nèi)存:
maxmemory 100mb
在內(nèi)存占用達到了maxmemory后九昧,再向Redis寫入數(shù)據(jù)時绊袋,Redis會:
根據(jù)配置的數(shù)據(jù)淘汰策略嘗試淘汰數(shù)據(jù),釋放空間
如果沒有數(shù)據(jù)可以淘汰铸鹰,或者沒有配置數(shù)據(jù)淘汰策略癌别,那么Redis會對所有寫請求返回錯誤,但讀請求仍然可以正常執(zhí)行
在為Redis設置maxmemory時蹋笼,需要注意:
如果采用了Redis的主從同步展姐,主節(jié)點向從節(jié)點同步數(shù)據(jù)時,會占用掉一部分內(nèi)存空間剖毯,如果maxmemory過于接近主機的可用內(nèi)存圾笨,導致數(shù)據(jù)同步時內(nèi)存不足。所以設置的maxmemory不要過于接近主機可用的內(nèi)存逊谋,留出一部分預留用作主從同步擂达。

數(shù)據(jù)淘汰機制
Redis提供了5種數(shù)據(jù)淘汰策略:
volatile-lru:使用LRU算法進行數(shù)據(jù)淘汰(淘汰上次使用時間最早的,且使用次數(shù)最少的key)胶滋,只淘汰設定了有效期的key
allkeys-lru:使用LRU算法進行數(shù)據(jù)淘汰板鬓,所有的key都可以被淘汰
volatile-random:隨機淘汰數(shù)據(jù)悲敷,只淘汰設定了有效期的key
allkeys-random:隨機淘汰數(shù)據(jù),所有的key都可以被淘汰
volatile-ttl:淘汰剩余有效期最短的key
最好為Redis指定一種有效的數(shù)據(jù)淘汰策略以配合maxmemory設置穗熬,避免在內(nèi)存使用滿后發(fā)生寫入失敗的情況镀迂。
一般來說,推薦使用的策略是volatile-lru唤蔗,并辨識Redis中保存的數(shù)據(jù)的重要性。對于那些重要的窟赏,絕對不能丟棄的數(shù)據(jù)(如配置類數(shù)據(jù)等)妓柜,應不設置有效期,這樣Redis就永遠不會淘汰這些數(shù)據(jù)涯穷。對于那些相對不是那么重要的棍掐,并且能夠熱加載的數(shù)據(jù)(比如緩存最近登錄的用戶信息,當在Redis中找不到時拷况,程序會去DB中讀茸骰汀),可以設置上有效期赚瘦,這樣在內(nèi)存不夠時Redis就會淘汰這部分數(shù)據(jù)粟誓。
配置方法:
maxmemory-policy volatile-lru #默認是noeviction,即不進行數(shù)據(jù)淘汰

Pipelining
Pipelining
Redis提供許多批量操作的命令起意,如MSET/MGET/HMSET/HMGET等等鹰服,這些命令存在的意義是減少維護網(wǎng)絡連接和傳輸數(shù)據(jù)所消耗的資源和時間。
例如連續(xù)使用5次SET命令設置5個不同的key揽咕,比起使用一次MSET命令設置5個不同的key悲酷,效果是一樣的,但前者會消耗更多的RTT(Round Trip Time)時長亲善,永遠應優(yōu)先使用后者设易。
然而,如果客戶端要連續(xù)執(zhí)行的多次操作無法通過Redis命令組合在一起蛹头,例如:
SET a "abc"
INCR b
HSET c name "hi"

此時便可以使用Redis提供的pipelining功能來實現(xiàn)在一次交互中執(zhí)行多條命令顿肺。
使用pipelining時,只需要從客戶端一次向Redis發(fā)送多條命令(以\r\n)分隔掘而,Redis就會依次執(zhí)行這些命令挟冠,并且把每個命令的返回按順序組裝在一起一次返回,比如:
$ (printf "PING\r\nPING\r\nPING\r\n"; sleep 1) | nc localhost 6379
+PONG
+PONG
+PONG

大部分的Redis客戶端都對Pipelining提供支持袍睡,所以開發(fā)者通常并不需要自己手工拼裝命令列表知染。
Pipelining的局限性
Pipelining只能用于執(zhí)行連續(xù)且無相關性的命令,當某個命令的生成需要依賴于前一個命令的返回時斑胜,就無法使用Pipelining了控淡。
通過Scripting功能嫌吠,可以規(guī)避這一局限性
事務與Scripting
Pipelining能夠讓Redis在一次交互中處理多條命令,然而在一些場景下掺炭,我們可能需要在此基礎上確保這一組命令是連續(xù)執(zhí)行的辫诅。
比如獲取當前累計的PV數(shù)并將其清0

GET vCount
12384
SET vCount 0
OK

如果在GET和SET命令之間插進來一個INCR vCount,就會使客戶端拿到的vCount不準確涧狮。
Redis的事務可以確保復數(shù)命令執(zhí)行時的原子性炕矮。也就是說Redis能夠保證:一個事務中的一組命令是絕對連續(xù)執(zhí)行的,在這些命令執(zhí)行完成之前者冤,絕對不會有來自于其他連接的其他命令插進去執(zhí)行肤视。
通過MULTI和EXEC命令來把這兩個命令加入一個事務中:

MULTI
OK
GET vCount
QUEUED
SET vCount 0
QUEUED
EXEC

  1. 12384
  2. OK

Redis在接收到MULTI命令后便會開啟一個事務,這之后的所有讀寫命令都會保存在隊列中但并不執(zhí)行涉枫,直到接收到EXEC命令后邢滑,Redis會把隊列中的所有命令連續(xù)順序執(zhí)行,并以數(shù)組形式返回每個命令的返回結(jié)果愿汰。
可以使用DISCARD命令放棄當前的事務困后,將保存的命令隊列清空。
需要注意的是衬廷,Redis事務不支持回滾:
如果一個事務中的命令出現(xiàn)了語法錯誤摇予,大部分客戶端驅(qū)動會返回錯誤,2.6.5版本以上的Redis也會在執(zhí)行EXEC時檢查隊列中的命令是否存在語法錯誤泵督,如果存在趾盐,則會自動放棄事務并返回錯誤。
但如果一個事務中的命令有非語法類的錯誤(比如對String執(zhí)行HSET操作)小腊,無論客戶端驅(qū)動還是Redis都無法在真正執(zhí)行這條命令之前發(fā)現(xiàn)救鲤,所以事務中的所有命令仍然會被依次執(zhí)行。在這種情況下秩冈,會出現(xiàn)一個事務中部分命令成功部分命令失敗的情況本缠,然而與RDBMS不同,Redis不提供事務回滾的功能入问,所以只能通過其他方法進行數(shù)據(jù)的回滾丹锹。
通過事務實現(xiàn)CAS
Redis提供了WATCH命令與事務搭配使用,實現(xiàn)CAS樂觀鎖的機制芬失。
假設要實現(xiàn)將某個商品的狀態(tài)改為已售:
if(exec(HGET stock:1001 state) == "in stock")
exec(HSET stock:1001 state "sold");

這一偽代碼執(zhí)行時楣黍,無法確保并發(fā)安全性,有可能多個客戶端都獲取到了"in stock"的狀態(tài)棱烂,導致一個庫存被售賣多次租漂。
使用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命令執(zhí)行時,Redis會檢查被WATCH的key,只有被WATCH的key從WATCH起始時至今沒有發(fā)生過變更哩治,EXEC才會被執(zhí)行秃踩。如果WATCH的key在WATCH命令到EXEC命令之間發(fā)生過變化,則EXEC命令會返回失敗业筏。

Scripting
通過EVAL與EVALSHA命令阎肝,可以讓Redis執(zhí)行LUA腳本空入。這就類似于RDBMS的存儲過程一樣,可以把客戶端與Redis之間密集的讀/寫交互放在服務端進行褐捻,避免過多的數(shù)據(jù)交互擂红,提升性能欺旧。
Scripting功能是作為事務功能的替代者誕生的积担,事務提供的所有能力Scripting都可以做到埠居。Redis官方推薦使用LUA Script來代替事務,前者的效率和便利性都超過了事務对碌。
關于Scripting的具體使用,本文不做詳細介紹蒿偎,請參考官方文檔 https://redis.io/commands/eval

Redis性能調(diào)優(yōu)
盡管Redis是一個非承嗝牵快速的內(nèi)存數(shù)據(jù)存儲媒介,也并不代表Redis不會產(chǎn)生性能問題诉位。
前文中提到過骑脱,Redis采用單線程模型,所有的命令都是由一個線程串行執(zhí)行的苍糠,所以當某個命令執(zhí)行耗時較長時叁丧,會拖慢其后的所有命令,這使得Redis對每個任務的執(zhí)行效率更加敏感岳瞭。
針對Redis的性能優(yōu)化拥娄,主要從下面幾個層面入手:
最初的也是最重要的,確保沒有讓Redis執(zhí)行耗時長的命令
使用pipelining將連續(xù)執(zhí)行的命令組合執(zhí)行
操作系統(tǒng)的Transparent huge pages功能必須關閉:
echo never > /sys/kernel/mm/transparent_hugepage/enabled
如果在虛擬機中運行Redis瞳筏,可能天然就有虛擬機環(huán)境帶來的固有延遲稚瘾。可以通過./redis-cli --intrinsic-latency 100命令查看固有延遲姚炕。同時如果對Redis的性能有較高要求的話摊欠,應盡可能在物理機上直接部署Redis。
檢查數(shù)據(jù)持久化策略
考慮引入讀寫分離機制
長耗時命令
Redis絕大多數(shù)讀寫命令的時間復雜度都在O(1)到O(N)之間柱宦,在文本和官方文檔中均對每個命令的時間復雜度有說明些椒。
通常來說,O(1)的命令是安全的掸刊,O(N)命令在使用時需要注意免糕,如果N的數(shù)量級不可預知,則應避免使用。例如對一個field數(shù)未知的Hash數(shù)據(jù)執(zhí)行HGETALL/HKEYS/HVALS命令说墨,通常來說這些命令執(zhí)行的很快骏全,但如果這個Hash中的field數(shù)量極多,耗時就會成倍增長尼斧。
又如使用SUNION對兩個Set執(zhí)行Union操作姜贡,或使用SORT對List/Set執(zhí)行排序操作等時,都應該嚴加注意棺棵。
避免在使用這些O(N)命令時發(fā)生問題主要有幾個辦法:
不要把List當做列表使用楼咳,僅當做隊列來使用
通過機制嚴格控制Hash、Set烛恤、Sorted Set的大小
可能的話母怜,將排序、并集缚柏、交集等操作放在客戶端執(zhí)行
絕對禁止使用KEYS命令
避免一次性遍歷集合類型的所有成員苹熏,而應使用SCAN類的命令進行分批的,游標式的遍歷
Redis提供了SCAN命令币喧,可以對Redis中存儲的所有key進行游標式的遍歷轨域,避免使用KEYS命令帶來的性能問題。同時還有SSCAN/HSCAN/ZSCAN等命令杀餐,分別用于對Set/Hash/Sorted Set中的元素進行游標式遍歷干发。SCAN類命令的使用請參考官方文檔:https://redis.io/commands/scan
Redis提供了Slow Log功能,可以自動記錄耗時較長的命令史翘。相關的配置參數(shù)有兩個:
slowlog-log-slower-than xxxms #執(zhí)行時間慢于xxx毫秒的命令計入Slow Log
slowlog-max-len xxx #Slow Log的長度枉长,即最大紀錄多少條Slow Log

使用SLOWLOG GET [number]命令,可以輸出最近進入Slow Log的number條命令琼讽。
使用SLOWLOG RESET命令必峰,可以重置Slow Log

網(wǎng)絡引發(fā)的延遲
盡可能使用長連接或連接池,避免頻繁創(chuàng)建銷毀連接
客戶端進行的批量數(shù)據(jù)操作跨琳,應使用Pipeline特性在一次交互中完成自点。具體請參照本文的Pipelining章節(jié)

數(shù)據(jù)持久化引發(fā)的延遲
Redis的數(shù)據(jù)持久化工作本身就會帶來延遲,需要根據(jù)數(shù)據(jù)的安全級別和性能要求制定合理的持久化策略:
AOF + fsync always的設置雖然能夠絕對確保數(shù)據(jù)安全脉让,但每個操作都會觸發(fā)一次fsync桂敛,會對Redis的性能有比較明顯的影響
AOF + fsync every second是比較好的折中方案,每秒fsync一次
AOF + fsync never會提供AOF持久化方案下的最優(yōu)性能
使用RDB持久化通常會提供比使用AOF更高的性能溅潜,但需要注意RDB的策略配置
每一次RDB快照和AOF Rewrite都需要Redis主進程進行fork操作术唬。fork操作本身可能會產(chǎn)生較高的耗時,與CPU和Redis占用的內(nèi)存大小有關滚澜。根據(jù)具體的情況合理配置RDB快照和AOF Rewrite時機粗仓,避免過于頻繁的fork帶來的延遲
Redis在fork子進程時需要將內(nèi)存分頁表拷貝至子進程,以占用了24GB內(nèi)存的Redis實例為例,共需要拷貝24GB / 4kB * 8 = 48MB的數(shù)據(jù)借浊。在使用單Xeon 2.27Ghz的物理機上塘淑,這一fork操作耗時216ms。
可以通過INFO命令返回的latest_fork_usec字段查看上一次fork操作的耗時(微秒)

Swap引發(fā)的延遲
當Linux將Redis所用的內(nèi)存分頁移至swap空間時蚂斤,將會阻塞Redis進程存捺,導致Redis出現(xiàn)不正常的延遲。Swap通常在物理內(nèi)存不足或一些進程在進行大量I/O操作時發(fā)生曙蒸,應盡可能避免上述兩種情況的出現(xiàn)捌治。
/proc/<pid>/smaps文件中會保存進程的swap記錄,通過查看這個文件纽窟,能夠判斷Redis的延遲是否由Swap產(chǎn)生肖油。如果這個文件中記錄了較大的Swap size,則說明延遲很有可能是Swap造成的臂港。

數(shù)據(jù)淘汰引發(fā)的延遲
當同一秒內(nèi)有大量key過期時森枪,也會引發(fā)Redis的延遲。在使用時應盡量將key的失效時間錯開审孽。

引入讀寫分離機制
Redis的主從復制能力可以實現(xiàn)一主多從的多節(jié)點架構(gòu)疲恢,在這一架構(gòu)下,主節(jié)點接收所有寫請求瓷胧,并將數(shù)據(jù)同步給多個從節(jié)點。
在這一基礎上棚愤,我們可以讓從節(jié)點提供對實時性要求不高的讀請求服務搓萧,以減小主節(jié)點的壓力。
尤其是針對一些使用了長耗時命令的統(tǒng)計類任務宛畦,完全可以指定在一個或多個從節(jié)點上執(zhí)行瘸洛,避免這些長耗時命令影響其他請求的響應。
關于讀寫分離的具體說明次和,請參見后續(xù)章節(jié)

主從復制與集群分片
主從復制
Redis支持一主多從的主從復制架構(gòu)反肋。一個Master實例負責處理所有的寫請求,Master將寫操作同步至所有Slave踏施。
借助Redis的主從復制石蔗,可以實現(xiàn)讀寫分離和高可用:
實時性要求不是特別高的讀請求,可以在Slave上完成畅形,提升效率养距。特別是一些周期性執(zhí)行的統(tǒng)計任務,這些任務可能需要執(zhí)行一些長耗時的Redis命令日熬,可以專門規(guī)劃出1個或幾個Slave用于服務這些統(tǒng)計任務
借助Redis Sentinel可以實現(xiàn)高可用棍厌,當Master crash后,Redis Sentinel能夠自動將一個Slave晉升為Master,繼續(xù)提供服務
啟用主從復制非常簡單耘纱,只需要配置多個Redis實例敬肚,在作為Slave的Redis實例中配置:
slaveof 192.168.1.1 6379 #指定Master的IP和端口
當Slave啟動后,會從Master進行一次冷啟動數(shù)據(jù)同步束析,由Master觸發(fā)BGSAVE生成RDB文件推送給Slave進行導入艳馒,導入完成后Master再將增量數(shù)據(jù)通過Redis Protocol同步給Slave。之后主從之間的數(shù)據(jù)便一直以Redis Protocol進行同步
使用Sentinel做自動failover
Redis的主從復制功能本身只是做數(shù)據(jù)同步畸陡,并不提供監(jiān)控和自動failover能力鹰溜,要通過主從復制功能來實現(xiàn)Redis的高可用,還需要引入一個組件:Redis Sentinel
Redis Sentinel是Redis官方開發(fā)的監(jiān)控組件丁恭,可以監(jiān)控Redis實例的狀態(tài)曹动,通過Master節(jié)點自動發(fā)現(xiàn)Slave節(jié)點,并在監(jiān)測到Master節(jié)點失效時選舉出一個新的Master牲览,并向所有Redis實例推送新的主從配置墓陈。
Redis Sentinel需要至少部署3個實例才能形成選舉關系。
關鍵配置:
sentinel monitor mymaster 127.0.0.1 6379 2 #Master實例的IP第献、端口贡必,以及選舉需要的贊成票數(shù)
sentinel down-after-milliseconds mymaster 60000 #多長時間沒有響應視為Master失效
sentinel failover-timeout mymaster 180000 #兩次failover嘗試間的間隔時長
sentinel parallel-syncs mymaster 1 #如果有多個Slave,可以通過此配置指定同時從新Master進行數(shù)據(jù)同步的Slave數(shù)庸毫,避免所有Slave同時進行數(shù)據(jù)同步導致查詢服務也不可用
另外需要注意的是仔拟,Redis Sentinel實現(xiàn)的自動failover不是在同一個IP和端口上完成的,也就是說自動failover產(chǎn)生的新Master提供服務的IP和端口與之前的Master是不一樣的飒赃,所以要實現(xiàn)HA利花,還要求客戶端必須支持Sentinel,能夠與Sentinel交互獲得新Master的信息才行载佳。
集群分片
為何要做集群分片:
Redis中存儲的數(shù)據(jù)量大炒事,一臺主機的物理內(nèi)存已經(jīng)無法容納
Redis的寫請求并發(fā)量大,一個Redis實例以無法承載
當上述兩個問題出現(xiàn)時蔫慧,就必須要對Redis進行分片了挠乳。
Redis的分片方案有很多種,例如很多Redis的客戶端都自行實現(xiàn)了分片功能姑躲,也有向Twemproxy這樣的以代理方式實現(xiàn)的Redis分片方案。然而首選的方案還應該是Redis官方在3.0版本中推出的Redis Cluster分片方案肋联。
本文不會對Redis Cluster的具體安裝和部署細節(jié)進行介紹威蕉,重點介紹Redis Cluster帶來的好處與弊端韧涨。

Redis Cluster的能力
能夠自動將數(shù)據(jù)分散在多個節(jié)點上
當訪問的key不在當前分片上時牍戚,能夠自動將請求轉(zhuǎn)發(fā)至正確的分片
當集群中部分節(jié)點失效時仍能提供服務
其中第三點是基于主從復制來實現(xiàn)的,Redis Cluster的每個數(shù)據(jù)分片都采用了主從復制的結(jié)構(gòu)虑粥,原理和前文所述的主從復制完全一致如孝,唯一的區(qū)別是省去了Redis Sentinel這一額外的組件,由Redis Cluster負責進行一個分片內(nèi)部的節(jié)點監(jiān)控和自動failover娩贷。

Redis Cluster分片原理
Redis Cluster中共有16384個hash slot第晰,Redis會計算每個key的CRC16,將結(jié)果與16384取模彬祖,來決定該key存儲在哪一個hash slot中茁瘦,同時需要指定Redis Cluster中每個數(shù)據(jù)分片負責的Slot數(shù)。Slot的分配在任何時間點都可以進行重新分配储笑。
客戶端在對key進行讀寫操作時甜熔,可以連接Cluster中的任意一個分片,如果操作的key不在此分片負責的Slot范圍內(nèi)突倍,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必須在同一個數(shù)據(jù)分片上涂圆,否則將會返回錯誤。如要在Redis Cluster中使用上述功能币叹,就必須通過hash tags來確保一個pipeline或一個事務中操作的所有key都位于同一個Slot中。
有一些客戶端(如Redisson)實現(xiàn)了集群化的pipelining操作模狭,可以自動將一個pipeline里的命令按key所在的分片進行分組颈抚,分別發(fā)到不同的分片上執(zhí)行。但是Redis不支持跨分片的事務嚼鹉,事務和LUA Script還是必須遵循所有key在一個分片上的規(guī)則要求贩汉。

主從復制 vs 集群分片
在設計軟件架構(gòu)時,要如何在主從復制和集群分片兩種部署方案中取舍呢锚赤?
從各個方面看匹舞,Redis Cluster都是優(yōu)于主從復制的方案
Redis Cluster能夠解決單節(jié)點上數(shù)據(jù)量過大的問題
Redis Cluster能夠解決單節(jié)點訪問壓力過大的問題
Redis Cluster包含了主從復制的能力
那是不是代表Redis Cluster永遠是優(yōu)于主從復制的選擇呢?
并不是线脚。
軟件架構(gòu)永遠不是越復雜越好赐稽,復雜的架構(gòu)在帶來顯著好處的同時叫榕,一定也會帶來相應的弊端。采用Redis Cluster的弊端包括:
維護難度增加姊舵。在使用Redis Cluster時晰绎,需要維護的Redis實例數(shù)倍增,需要監(jiān)控的主機數(shù)量也相應增加括丁,數(shù)據(jù)備份/持久化的復雜度也會增加荞下。同時在進行分片的增減操作時,還需要進行reshard操作史飞,遠比主從模式下增加一個Slave的復雜度要高尖昏。
客戶端資源消耗增加。當客戶端使用連接池時构资,需要為每一個數(shù)據(jù)分片維護一個連接池抽诉,客戶端同時需要保持的連接數(shù)成倍增多,加大了客戶端本身和操作系統(tǒng)資源的消耗蚯窥。
性能優(yōu)化難度增加掸鹅。你可能需要在多個分片上查看Slow Log和Swap日志才能定位性能問題。
事務和LUA Script的使用成本增加拦赠。在Redis Cluster中使用事務和LUA Script特性有嚴格的限制條件巍沙,事務和Script中操作的key必須位于同一個分片上,這就使得在開發(fā)時必須對相應場景下涉及的key進行額外的規(guī)劃和規(guī)范要求荷鼠。如果應用的場景中大量涉及事務和Script的使用句携,如何在保證這兩個功能的正常運作前提下把數(shù)據(jù)平均分到多個數(shù)據(jù)分片中就會成為難點。
所以說允乐,在主從復制和集群分片兩個方案中做出選擇時矮嫉,應該從應用軟件的功能特性、數(shù)據(jù)和訪問量級牍疏、未來發(fā)展規(guī)劃等方面綜合考慮蠢笋,只在確實有必要引入數(shù)據(jù)分片時再使用Redis Cluster。
下面是一些建議:
需要在Redis中存儲的數(shù)據(jù)有多大鳞陨?未來2年內(nèi)可能發(fā)展為多大昨寞?這些數(shù)據(jù)是否都需要長期保存?是否可以使用LRU算法進行非熱點數(shù)據(jù)的淘汰厦滤?綜合考慮前面幾個因素援岩,評估出Redis需要使用的物理內(nèi)存。
用于部署Redis的主機物理內(nèi)存有多大掏导?有多少可以分配給Redis使用享怀?對比(1)中的內(nèi)存需求評估,是否足夠用趟咆?
Redis面臨的并發(fā)寫壓力會有多大添瓷?在不使用pipelining時梅屉,Redis的寫性能可以超過10萬次/秒(更多的benchmark可以參考 https://redis.io/topics/benchmarks
在使用Redis時,是否會使用到pipelining和事務功能仰坦?使用的場景多不多履植?
綜合上面幾點考慮,如果單臺主機的可用物理內(nèi)存完全足以支撐對Redis的容量需求悄晃,且Redis面臨的并發(fā)寫壓力距離Benchmark值還尚有距離玫霎,建議采用主從復制的架構(gòu),可以省去很多不必要的麻煩妈橄。同時庶近,如果應用中大量使用pipelining和事務,也建議盡可能選擇主從復制架構(gòu)眷蚓,可以減少設計和開發(fā)時的復雜度鼻种。

Redis Java客戶端的選擇
Redis的Java客戶端很多,官方推薦的有三種:Jedis沙热、Redisson和lettuce叉钥。
在這里對Jedis和Redisson進行對比介紹
Jedis:
輕量,簡潔篙贸,便于集成和改造
支持連接池
支持pipelining投队、事務、LUA Scripting爵川、Redis Sentinel敷鸦、Redis Cluster
不支持讀寫分離,需要自己實現(xiàn)
文檔差(真的很差寝贡,幾乎沒有……)
Redisson:
基于Netty實現(xiàn)扒披,采用非阻塞IO,性能高
支持異步請求
支持連接池
支持pipelining圃泡、LUA Scripting碟案、Redis Sentinel、Redis Cluster
不支持事務颇蜡,官方建議以LUA Scripting代替事務
支持在Redis Cluster架構(gòu)下使用pipelining
支持讀寫分離蟆淀,支持讀負載均衡,在主從復制和Redis Cluster架構(gòu)下都可以使用
內(nèi)建Tomcat Session Manager澡匪,為Tomcat 6/7/8提供了會話共享功能
可以與Spring Session集成,實現(xiàn)基于Redis的會話共享
文檔較豐富褒链,有中文文檔
對于Jedis和Redisson的選擇唁情,同樣應遵循前述的原理,盡管Jedis比起Redisson有各種各樣的不足甫匹,但也應該在需要使用Redisson的高級特性時再選用Redisson甸鸟,避免造成不必要的程序復雜度提升惦费。
Jedis:
github:https://github.com/xetorthio/jedis
文檔:https://github.com/xetorthio/jedis/wiki
Redisson:
github:https://github.com/redisson/redisson
文檔:https://github.com/redisson/redisson/wiki

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市抢韭,隨后出現(xiàn)的幾起案子薪贫,更是在濱河造成了極大的恐慌,老刑警劉巖刻恭,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瞧省,死亡現(xiàn)場離奇詭異,居然都是意外死亡鳍贾,警方通過查閱死者的電腦和手機鞍匾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來骑科,“玉大人橡淑,你說我怎么就攤上這事∨厮” “怎么了梁棠?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長斗埂。 經(jīng)常有香客問我符糊,道長,這世上最難降的妖魔是什么蜜笤? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任濒蒋,我火速辦了婚禮,結(jié)果婚禮上把兔,老公的妹妹穿的比我還像新娘沪伙。我一直安慰自己,他們只是感情好县好,可當我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布围橡。 她就那樣靜靜地躺著,像睡著了一般缕贡。 火紅的嫁衣襯著肌膚如雪翁授。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天晾咪,我揣著相機與錄音收擦,去河邊找鬼。 笑死谍倦,一個胖子當著我的面吹牛塞赂,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播昼蛀,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼宴猾,長吁一口氣:“原來是場噩夢啊……” “哼圆存!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起仇哆,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤沦辙,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后讹剔,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體油讯,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年辟拷,在試婚紗的時候發(fā)現(xiàn)自己被綠了撞羽。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡衫冻,死狀恐怖诀紊,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情隅俘,我是刑警寧澤邻奠,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站为居,受9級特大地震影響碌宴,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蒙畴,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一贰镣、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧膳凝,春花似錦碑隆、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至著淆,卻和暖如春劫狠,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背永部。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工独泞, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人苔埋。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓懦砂,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子孕惜,可洞房花燭夜當晚...
    茶點故事閱讀 44,592評論 2 353

推薦閱讀更多精彩內(nèi)容