0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Redis的數(shù)據(jù)結(jié)構(gòu)和主要命令對Redis的基本能力進(jìn)行直觀介紹

電子工程師 ? 來源:未知 ? 作者:李倩 ? 2018-11-05 17:57 ? 次閱讀

本文將從Redis的基本特性入手,通過講述Redis的數(shù)據(jù)結(jié)構(gòu)和主要命令對Redis的基本能力進(jìn)行直觀介紹。之后概覽Redis提供的高級能力,并在部署、維護(hù)、性能調(diào)優(yōu)等多個方面進(jìn)行更深入的介紹和指導(dǎo)。

本文適合使用Redis的普通開發(fā)人員,以及對Redis進(jìn)行選型、架構(gòu)設(shè)計和性能調(diào)優(yōu)的架構(gòu)設(shè)計人員。

概述

Redis是一個開源的,基于內(nèi)存的結(jié)構(gòu)化數(shù)據(jù)存儲媒介,可以作為數(shù)據(jù)庫、緩存服務(wù)或消息服務(wù)使用。

Redis支持多種數(shù)據(jù)結(jié)構(gòu),包括字符串、哈希表、鏈表、集合、有序集合、位圖、Hyperloglogs等。

Redis具備LRU淘汰、事務(wù)實現(xiàn)、以及不同級別的硬盤持久化等能力,并且支持副本集和通過Redis Sentinel實現(xiàn)的高可用方案,同時還支持通過Redis Cluster實現(xiàn)的數(shù)據(jù)自動分片能力。

Redis的主要功能都基于單線程模型實現(xiàn),也就是說Redis使用一個線程來服務(wù)所有的客戶端請求,同時Redis采用了非阻塞式IO,并精細(xì)地優(yōu)化各種命令的算法時間復(fù)雜度,這些信息意味著:

Redis是線程安全的(因為只有一個線程),其所有操作都是原子的,不會因并發(fā)產(chǎn)生數(shù)據(jù)異常

Redis的速度非??欤ㄒ驗槭褂梅亲枞絀O,且大部分命令的算法時間復(fù)雜度都是O(1))

使用高耗時的Redis命令是很危險的,會占用唯一的一個線程的大量處理時間,導(dǎo)致所有的請求都被拖慢。(例如時間復(fù)雜度為O(N)的KEYS命令,嚴(yán)格禁止在生產(chǎn)環(huán)境中使用)

Redis的數(shù)據(jù)結(jié)構(gòu)和相關(guān)常用命令

本節(jié)中將介紹Redis支持的主要數(shù)據(jù)結(jié)構(gòu),以及相關(guān)的常用Redis命令。本節(jié)只對Redis命令進(jìn)行扼要的介紹,且只列出了較常用的命令。如果想要了解完整的Redis命令集,或了解某個命令的詳細(xì)使用方法,請參考官方文檔:

https://redis.io/commands

Key

Redis采用Key-Value型的基本數(shù)據(jù)結(jié)構(gòu),任何二進(jìn)制序列都可以作為Redis的Key使用(例如普通的字符串或一張JPEG圖片)

關(guān)于Key的一些注意事項:

不要使用過長的Key。例如使用一個1024字節(jié)的key就不是一個好主意,不僅會消耗更多的內(nèi)存,還會導(dǎo)致查找的效率降低

Key短到缺失了可讀性也是不好的,例如”u1000flw”比起”user:1000:followers”來說,節(jié)省了寥寥的存儲空間,卻引發(fā)了可讀性和可維護(hù)性上的麻煩

最好使用統(tǒng)一的規(guī)范來設(shè)計Key,比如”object-type:id:attr”,以這一規(guī)范設(shè)計出的Key可能是”user:1000″或”comment:1234:reply-to”

Redis允許的最大Key長度是512MB(對Value的長度限制也是512MB)

String

String是Redis的基礎(chǔ)數(shù)據(jù)類型,Redis沒有Int、Float、Boolean等數(shù)據(jù)類型的概念,所有的基本類型在Redis中都以String體現(xiàn)。

與String相關(guān)的常用命令:

SET:為一個key設(shè)置value,可以配合EX/PX參數(shù)指定key的有效期,通過NX/XX參數(shù)針對key是否存在的情況進(jìn)行區(qū)別操作,時間復(fù)雜度O(1)

GET:獲取某個key對應(yīng)的value,時間復(fù)雜度O(1)

GETSET:為一個key設(shè)置value,并返回該key的原value,時間復(fù)雜度O(1)

MSET:為多個key設(shè)置value,時間復(fù)雜度O(N)

MSETNX:同MSET,如果指定的key中有任意一個已存在,則不進(jìn)行任何操作,時間復(fù)雜度O(N)

MGET:獲取多個key對應(yīng)的value,時間復(fù)雜度O(N)

上文提到過,Redis的基本數(shù)據(jù)類型只有String,但Redis可以把String作為整型或浮點(diǎn)型數(shù)字來使用,主要體現(xiàn)在INCR、DECR類的命令上:

INCR:將key對應(yīng)的value值自增1,并返回自增后的值。只對可以轉(zhuǎn)換為整型的String數(shù)據(jù)起作用。時間復(fù)雜度O(1)

INCRBY:將key對應(yīng)的value值自增指定的整型數(shù)值,并返回自增后的值。只對可以轉(zhuǎn)換為整型的String數(shù)據(jù)起作用。時間復(fù)雜度O(1)

DECR/DECRBY:同INCR/INCRBY,自增改為自減。

INCR/DECR系列命令要求操作的value類型為String,并可以轉(zhuǎn)換為64位帶符號的整型數(shù)字,否則會返回錯誤。

也就是說,進(jìn)行INCR/DECR系列命令的value,必須在[-2^63 ~ 2^63 – 1]范圍內(nèi)。

前文提到過,Redis采用單線程模型,天然是線程安全的,這使得INCR/DECR命令可以非常便利的實現(xiàn)高并發(fā)場景下的精確控制。

例1:庫存控制

在高并發(fā)場景下實現(xiàn)庫存余量的精準(zhǔn)校驗,確保不出現(xiàn)超賣的情況。

設(shè)置庫存總量:

SET inv:remain"100"

庫存扣減+余量校驗:

DECR inv:remain

當(dāng)DECR命令返回值大于等于0時,說明庫存余量校驗通過,如果返回小于0的值,則說明庫存已耗盡。

假設(shè)同時有300個并發(fā)請求進(jìn)行庫存扣減,Redis能夠確保這300個請求分別得到99到-200的返回值,每個請求得到的返回值都是唯一的,絕對不會找出現(xiàn)兩個請求得到一樣的返回值的情況。

例2:自增序列生成

實現(xiàn)類似于RDBMS的Sequence功能,生成一系列唯一的序列號

設(shè)置序列起始值:

SET sequence"10000"

獲取一個序列值:

INCR sequence

直接將返回值作為序列使用即可。

獲取一批(如100個)序列值:

INCRBY sequence100

假設(shè)返回值為N,那么[N – 99 ~ N]的數(shù)值都是可用的序列值。

當(dāng)多個客戶端同時向Redis申請自增序列時,Redis能夠確保每個客戶端得到的序列值或序列范圍都是全局唯一的,絕對不會出現(xiàn)不同客戶端得到了重復(fù)的序列值的情況。

List

Redis的List是鏈表型的數(shù)據(jù)結(jié)構(gòu),可以使用LPUSH/RPUSH/LPOP/RPOP等命令在List的兩端執(zhí)行插入元素和彈出元素的操作。雖然List也支持在特定index上插入和讀取元素的功能,但其時間復(fù)雜度較高(O(N)),應(yīng)小心使用。

與List相關(guān)的常用命令:

LPUSH:向指定List的左側(cè)(即頭部)插入1個或多個元素,返回插入后的List長度。時間復(fù)雜度O(N),N為插入元素的數(shù)量

RPUSH:同LPUSH,向指定List的右側(cè)(即尾部)插入1或多個元素

LPOP:從指定List的左側(cè)(即頭部)移除一個元素并返回,時間復(fù)雜度O(1)

RPOP:同LPOP,從指定List的右側(cè)(即尾部)移除1個元素并返回

LPUSHX/RPUSHX:與LPUSH/RPUSH類似,區(qū)別在于,LPUSHX/RPUSHX操作的key如果不存在,則不會進(jìn)行任何操作

LLEN:返回指定List的長度,時間復(fù)雜度O(1)

LRANGE:返回指定List中指定范圍的元素(雙端包含,即LRANGE key 0 10會返回11個元素),時間復(fù)雜度O(N)。應(yīng)盡可能控制一次獲取的元素數(shù)量,一次獲取過大范圍的List元素會導(dǎo)致延遲,同時對長度不可預(yù)知的List,避免使用LRANGE key 0 -1這樣的完整遍歷操作。

應(yīng)謹(jǐn)慎使用的List相關(guān)命令:

LINDEX:返回指定List指定index上的元素,如果index越界,返回nil。index數(shù)值是回環(huán)的,即-1代表List最后一個位置,-2代表List倒數(shù)第二個位置。時間復(fù)雜度O(N)

LSET:將指定List指定index上的元素設(shè)置為value,如果index越界則返回錯誤,時間復(fù)雜度O(N),如果操作的是頭/尾部的元素,則時間復(fù)雜度為O(1)

LINSERT:向指定List中指定元素之前/之后插入一個新元素,并返回操作后的List長度。如果指定的元素不存在,返回-1。如果指定key不存在,不會進(jìn)行任何操作,時間復(fù)雜度O(N)

由于Redis的List是鏈表結(jié)構(gòu)的,上述的三個命令的算法效率較低,需要對List進(jìn)行遍歷,命令的耗時無法預(yù)估,在List長度大的情況下耗時會明顯增加,應(yīng)謹(jǐn)慎使用。

換句話說,Redis的List實際是設(shè)計來用于實現(xiàn)隊列,而不是用于實現(xiàn)類似ArrayList這樣的列表的。如果你不是想要實現(xiàn)一個雙端出入的隊列,那么請盡量不要使用Redis的List數(shù)據(jù)結(jié)構(gòu)。

為了更好支持隊列的特性,Redis還提供了一系列阻塞式的操作命令,如BLPOP/BRPOP等,能夠?qū)崿F(xiàn)類似于BlockingQueue的能力,即在List為空時,阻塞該連接,直到List中有對象可以出隊時再返回。針對阻塞類的命令,此處不做詳細(xì)探討,請參考官方文檔(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對應(yīng)對象的field即可。

Hash的優(yōu)點(diǎn)包括:

可以實現(xiàn)二元查找,如”查找ID為1000的用戶的年齡”

比起將整個對象序列化后作為String存儲的方法,Hash能夠有效地減少網(wǎng)絡(luò)傳輸?shù)南?/p>

當(dāng)使用Hash維護(hù)一個集合時,提供了比List效率高得多的隨機(jī)訪問命令

與Hash相關(guān)的常用命令:

HSET:將key對應(yīng)的Hash中的field設(shè)置為value。如果該Hash不存在,會自動創(chuàng)建一個。時間復(fù)雜度O(1)

HGET:返回指定Hash中field字段的值,時間復(fù)雜度O(1)

HMSET/HMGET:同HSET和HGET,可以批量操作同一個key下的多個field,時間復(fù)雜度:O(N),N為一次操作的field數(shù)量

HSETNX:同HSET,但如field已經(jīng)存在,HSETNX不會進(jìn)行任何操作,時間復(fù)雜度O(1)

HEXISTS:判斷指定Hash中field是否存在,存在返回1,不存在返回0,時間復(fù)雜度O(1)

HDEL:刪除指定Hash中的field(1個或多個),時間復(fù)雜度:O(N),N為操作的field數(shù)量

HINCRBY:同INCRBY命令,對指定Hash中的一個field進(jìn)行INCRBY,時間復(fù)雜度O(1)

應(yīng)謹(jǐn)慎使用的Hash相關(guān)命令:

HGETALL:返回指定Hash中所有的field-value對。返回結(jié)果為數(shù)組,數(shù)組中field和value交替出現(xiàn)。時間復(fù)雜度O(N)

HKEYS/HVALS:返回指定Hash中所有的field/value,時間復(fù)雜度O(N)

上述三個命令都會對Hash進(jìn)行完整遍歷,Hash中的field數(shù)量與命令的耗時線性相關(guān),對于尺寸不可預(yù)知的Hash,應(yīng)嚴(yán)格避免使用上面三個命令,而改為使用HSCAN命令進(jìn)行游標(biāo)式的遍歷,具體請見

https://redis.io/commands/scan

Set

Redis Set是無序的,不可重復(fù)的String集合。

與Set相關(guān)的常用命令:

SADD:向指定Set中添加1個或多個member,如果指定Set不存在,會自動創(chuàng)建一個。時間復(fù)雜度O(N),N為添加的member個數(shù)

SREM:從指定Set中移除1個或多個member,時間復(fù)雜度O(N),N為移除的member個數(shù)

SRANDMEMBER:從指定Set中隨機(jī)返回1個或多個member,時間復(fù)雜度O(N),N為返回的member個數(shù)

SPOP:從指定Set中隨機(jī)移除并返回count個member,時間復(fù)雜度O(N),N為移除的member個數(shù)

SCARD:返回指定Set中的member個數(shù),時間復(fù)雜度O(1)

SISMEMBER:判斷指定的value是否存在于指定Set中,時間復(fù)雜度O(1)

SMOVE:將指定member從一個Set移至另一個Set

慎用的Set相關(guān)命令:

SMEMBERS:返回指定Hash中所有的member,時間復(fù)雜度O(N)

SUNION/SUNIONSTORE:計算多個Set的并集并返回/存儲至另一個Set中,時間復(fù)雜度O(N),N為參與計算的所有集合的總member數(shù)

SINTER/SINTERSTORE:計算多個Set的交集并返回/存儲至另一個Set中,時間復(fù)雜度O(N),N為參與計算的所有集合的總member數(shù)

SDIFF/SDIFFSTORE:計算1個Set與1或多個Set的差集并返回/存儲至另一個Set中,時間復(fù)雜度O(N),N為參與計算的所有集合的總member數(shù)

上述幾個命令涉及的計算量大,應(yīng)謹(jǐn)慎使用,特別是在參與計算的Set尺寸不可知的情況下,應(yīng)嚴(yán)格避免使用。可以考慮通過SSCAN命令遍歷獲取相關(guān)Set的全部member(具體請見https://redis.io/commands/scan),如果需要做并集/交集/差集計算,可以在客戶端進(jìn)行,或在不服務(wù)實時查詢請求的Slave上進(jìn)行。

Sorted Set

Redis Sorted Set是有序的、不可重復(fù)的String集合。Sorted Set中的每個元素都需要指派一個分?jǐn)?shù)(score),Sorted Set會根據(jù)score對元素進(jìn)行升序排序。如果多個member擁有相同的score,則以字典序進(jìn)行升序排序。

Sorted Set非常適合用于實現(xiàn)排名。

Sorted Set的主要命令:

ZADD:向指定Sorted Set中添加1個或多個member,時間復(fù)雜度O(Mlog(N)),M為添加的member數(shù)量,N為Sorted Set中的member數(shù)量

ZREM:從指定Sorted Set中刪除1個或多個member,時間復(fù)雜度O(Mlog(N)),M為刪除的member數(shù)量,N為Sorted Set中的member數(shù)量

ZCOUNT:返回指定Sorted Set中指定score范圍內(nèi)的member數(shù)量,時間復(fù)雜度:O(log(N))

ZCARD:返回指定Sorted Set中的member數(shù)量,時間復(fù)雜度O(1)

ZSCORE:返回指定Sorted Set中指定member的score,時間復(fù)雜度O(1)

ZRANK/ZREVRANK:返回指定member在Sorted Set中的排名,ZRANK返回按升序排序的排名,ZREVRANK則返回按降序排序的排名。時間復(fù)雜度O(log(N))

ZINCRBY:同INCRBY,對指定Sorted Set中的指定member的score進(jìn)行自增,時間復(fù)雜度O(log(N))

慎用的Sorted Set相關(guān)命令:

ZRANGE/ZREVRANGE:返回指定Sorted Set中指定排名范圍內(nèi)的所有member,ZRANGE為按score升序排序,ZREVRANGE為按score降序排序,時間復(fù)雜度O(log(N)+M),M為本次返回的member數(shù)

ZRANGEBYSCORE/ZREVRANGEBYSCORE:返回指定Sorted Set中指定score范圍內(nèi)的所有member,返回結(jié)果以升序/降序排序,min和max可以指定為-inf和+inf,代表返回所有的member。時間復(fù)雜度O(log(N)+M)

ZREMRANGEBYRANK/ZREMRANGEBYSCORE:移除Sorted Set中指定排名范圍/指定score范圍內(nèi)的所有member。時間復(fù)雜度O(log(N)+M)

上述幾個命令,應(yīng)盡量避免傳遞[0 -1]或[-inf +inf]這樣的參數(shù),來對Sorted Set做一次性的完整遍歷,特別是在Sorted Set的尺寸不可預(yù)知的情況下??梢酝ㄟ^ZSCAN命令來進(jìn)行游標(biāo)式的遍歷(具體請見https://redis.io/commands/scan),或通過LIMIT參數(shù)來限制返回member的數(shù)量(適用于ZRANGEBYSCORE和ZREVRANGEBYSCORE命令),以實現(xiàn)游標(biāo)式的遍歷。

Bitmap和HyperLogLog

Redis的這兩種數(shù)據(jù)結(jié)構(gòu)相較之前的并不常用,在本文中只做簡要介紹,如想要詳細(xì)了解這兩種數(shù)據(jù)結(jié)構(gòu)與其相關(guān)的命令,請參考官方文檔

https://redis.io/topics/data-types-intro中的相關(guān)章節(jié)

Bitmap在Redis中不是一種實際的數(shù)據(jù)類型,而是一種將String作為Bitmap使用的方法??梢岳斫鉃閷tring轉(zhuǎn)換為bit數(shù)組。使用Bitmap來存儲true/false類型的簡單數(shù)據(jù)極為節(jié)省空間。

HyperLogLogs是一種主要用于數(shù)量統(tǒng)計的數(shù)據(jù)結(jié)構(gòu),它和Set類似,維護(hù)一個不可重復(fù)的String集合,但是HyperLogLogs并不維護(hù)具體的member內(nèi)容,只維護(hù)member的個數(shù)。也就是說,HyperLogLogs只能用于計算一個集合中不重復(fù)的元素數(shù)量,所以它比Set要節(jié)省很多內(nèi)存空間。

其他常用命令

EXISTS:判斷指定的key是否存在,返回1代表存在,0代表不存在,時間復(fù)雜度O(1)

DEL:刪除指定的key及其對應(yīng)的value,時間復(fù)雜度O(N),N為刪除的key數(shù)量

EXPIRE/PEXPIRE:為一個key設(shè)置有效期,單位為秒或毫秒,時間復(fù)雜度O(1)

TTL/PTTL:返回一個key剩余的有效時間,單位為秒或毫秒,時間復(fù)雜度O(1)

RENAME/RENAMENX:將key重命名為newkey。使用RENAME時,如果newkey已經(jīng)存在,其值會被覆蓋;使用RENAMENX時,如果newkey已經(jīng)存在,則不會進(jìn)行任何操作,時間復(fù)雜度O(1)

TYPE:返回指定key的類型,string, list, set, zset, hash。時間復(fù)雜度O(1)

CONFIG GET:獲得Redis某配置項的當(dāng)前值,可以使用*通配符,時間復(fù)雜度O(1)

CONFIG SET:為Redis某個配置項設(shè)置新值,時間復(fù)雜度O(1)

CONFIG REWRITE:讓Redis重新加載redis.conf中的配置

數(shù)據(jù)持久化

Redis提供了將數(shù)據(jù)定期自動持久化至硬盤的能力,包括RDB和AOF兩種方案,兩種方案分別有其長處和短板,可以配合起來同時運(yùn)行,確保數(shù)據(jù)的穩(wěn)定性。

必須使用數(shù)據(jù)持久化嗎?

Redis的數(shù)據(jù)持久化機(jī)制是可以關(guān)閉的。如果你只把Redis作為緩存服務(wù)使用,Redis中存儲的所有數(shù)據(jù)都不是該數(shù)據(jù)的主體而僅僅是同步過來的備份,那么可以關(guān)閉Redis的數(shù)據(jù)持久化機(jī)制。

但通常來說,仍然建議至少開啟RDB方式的數(shù)據(jù)持久化,因為:

RDB方式的持久化幾乎不損耗Redis本身的性能,在進(jìn)行RDB持久化時,Redis主進(jìn)程唯一需要做的事情就是fork出一個子進(jìn)程,所有持久化工作都由子進(jìn)程完成

Redis無論因為什么原因crash掉之后,重啟時能夠自動恢復(fù)到上一次RDB快照中記錄的數(shù)據(jù)。這省去了手工從其他數(shù)據(jù)源(如DB)同步數(shù)據(jù)的過程,而且要比其他任何的數(shù)據(jù)恢復(fù)方式都要快

現(xiàn)在硬盤那么大,真的不缺那一點(diǎn)地方

RDB

采用RDB持久方式,Redis會定期保存數(shù)據(jù)快照至一個rbd文件中,并在啟動時自動加載rdb文件,恢復(fù)之前保存的數(shù)據(jù)??梢栽谂渲梦募信渲肦edis進(jìn)行快照保存的時機(jī):

save[seconds][changes]

意為在[seconds]秒內(nèi)如果發(fā)生了[changes]次數(shù)據(jù)修改,則進(jìn)行一次RDB快照保存,例如

save60100

會讓Redis每60秒檢查一次數(shù)據(jù)變更情況,如果發(fā)生了100次或以上的數(shù)據(jù)變更,則進(jìn)行RDB快照保存。

可以配置多條save指令,讓Redis執(zhí)行多級的快照保存策略。

Redis默認(rèn)開啟RDB快照,默認(rèn)的RDB策略如下:

save9001

save30010

save6010000

也可以通過BGSAVE命令手工觸發(fā)RDB快照保存。

RDB的優(yōu)點(diǎn):

對性能影響最小。如前文所述,Redis在保存RDB快照時會fork出子進(jìn)程進(jìn)行,幾乎不影響Redis處理客戶端請求的效率。

每次快照會生成一個完整的數(shù)據(jù)快照文件,所以可以輔以其他手段保存多個時間點(diǎn)的快照(例如把每天0點(diǎn)的快照備份至其他存儲媒介中),作為非??煽康臑?zāi)難恢復(fù)手段。

使用RDB文件進(jìn)行數(shù)據(jù)恢復(fù)比使用AOF要快很多。

RDB的缺點(diǎn):

快照是定期生成的,所以在Redis crash時或多或少會丟失一部分?jǐn)?shù)據(jù)。

如果數(shù)據(jù)集非常大且CPU不夠強(qiáng)(比如單核CPU),Redis在fork子進(jìn)程時可能會消耗相對較長的時間(長至1秒),影響這期間的客戶端請求。

AOF

采用AOF持久方式時,Redis會把每一個寫請求都記錄在一個日志文件里。在Redis重啟時,會把AOF文件中記錄的所有寫操作順序執(zhí)行一遍,確保數(shù)據(jù)恢復(fù)到最新。

AOF默認(rèn)是關(guān)閉的,如要開啟,進(jìn)行如下配置:

appendonly yes

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

appendfsync no:不進(jìn)行fsync,將flush文件的時機(jī)交給OS決定,速度最快

appendfsync always:每寫入一條日志就進(jìn)行一次fsync操作,數(shù)據(jù)安全性最高,但速度最慢

appendfsync everysec:折中的做法,交由后臺線程每秒fsync一次

隨著AOF不斷地記錄寫操作日志,必定會出現(xiàn)一些無用的日志,例如某個時間點(diǎn)執(zhí)行了命令SET key1 “abc”,在之后某個時間點(diǎn)又執(zhí)行了SET key1 “bcd”,那么第一條命令很顯然是沒有用的。大量的無用日志會讓AOF文件過大,也會讓數(shù)據(jù)恢復(fù)的時間過長。

所以Redis提供了AOF rewrite功能,可以重寫AOF文件,只保留能夠把數(shù)據(jù)恢復(fù)到最新狀態(tài)的最小寫操作集。

AOF rewrite可以通過BGREWRITEAOF命令觸發(fā),也可以配置Redis定期自動進(jìn)行:

auto-aof-rewrite-percentage100

auto-aof-rewrite-min-size64mb

上面兩行配置的含義是,Redis在每次AOF rewrite時,會記錄完成rewrite后的AOF日志大小,當(dāng)AOF日志大小在該基礎(chǔ)上增長了100%后,自動進(jìn)行AOF rewrite。同時如果增長的大小沒有達(dá)到64mb,則不會進(jìn)行rewrite。

AOF的優(yōu)點(diǎn):

最安全,在啟用appendfsync always時,任何已寫入的數(shù)據(jù)都不會丟失,使用在啟用appendfsync everysec也至多只會丟失1秒的數(shù)據(jù)。

AOF文件在發(fā)生斷電等問題時也不會損壞,即使出現(xiàn)了某條日志只寫入了一半的情況,也可以使用redis-check-aof工具輕松修復(fù)。

AOF文件易讀,可修改,在進(jìn)行了某些錯誤的數(shù)據(jù)清除操作后,只要AOF文件沒有rewrite,就可以把AOF文件備份出來,把錯誤的命令刪除,然后恢復(fù)數(shù)據(jù)。

AOF的缺點(diǎn):

AOF文件通常比RDB文件更大

性能消耗比RDB高

數(shù)據(jù)恢復(fù)速度比RDB慢

內(nèi)存管理與數(shù)據(jù)淘汰機(jī)制

最大內(nèi)存設(shè)置

默認(rèn)情況下,在32位OS中,Redis最大使用3GB的內(nèi)存,在64位OS中則沒有限制。

在使用Redis時,應(yīng)該對數(shù)據(jù)占用的最大空間有一個基本準(zhǔn)確的預(yù)估,并為Redis設(shè)定最大使用的內(nèi)存。否則在64位OS中Redis會無限制地占用內(nèi)存(當(dāng)物理內(nèi)存被占滿后會使用swap空間),容易引發(fā)各種各樣的問題。

通過如下配置控制Redis使用的最大內(nèi)存:

maxmemory100mb

在內(nèi)存占用達(dá)到了maxmemory后,再向Redis寫入數(shù)據(jù)時,Redis會:

根據(jù)配置的數(shù)據(jù)淘汰策略嘗試淘汰數(shù)據(jù),釋放空間

如果沒有數(shù)據(jù)可以淘汰,或者沒有配置數(shù)據(jù)淘汰策略,那么Redis會對所有寫請求返回錯誤,但讀請求仍然可以正常執(zhí)行

在為Redis設(shè)置maxmemory時,需要注意:

如果采用了Redis的主從同步,主節(jié)點(diǎn)向從節(jié)點(diǎn)同步數(shù)據(jù)時,會占用掉一部分內(nèi)存空間,如果maxmemory過于接近主機(jī)的可用內(nèi)存,導(dǎo)致數(shù)據(jù)同步時內(nèi)存不足。所以設(shè)置的maxmemory不要過于接近主機(jī)可用的內(nèi)存,留出一部分預(yù)留用作主從同步。

數(shù)據(jù)淘汰機(jī)制

Redis提供了5種數(shù)據(jù)淘汰策略:

volatile-lru:使用LRU算法進(jìn)行數(shù)據(jù)淘汰(淘汰上次使用時間最早的,且使用次數(shù)最少的key),只淘汰設(shè)定了有效期的key

allkeys-lru:使用LRU算法進(jìn)行數(shù)據(jù)淘汰,所有的key都可以被淘汰

volatile-random:隨機(jī)淘汰數(shù)據(jù),只淘汰設(shè)定了有效期的key

allkeys-random:隨機(jī)淘汰數(shù)據(jù),所有的key都可以被淘汰

volatile-ttl:淘汰剩余有效期最短的key

最好為Redis指定一種有效的數(shù)據(jù)淘汰策略以配合maxmemory設(shè)置,避免在內(nèi)存使用滿后發(fā)生寫入失敗的情況。

一般來說,推薦使用的策略是volatile-lru,并辨識Redis中保存的數(shù)據(jù)的重要性。對于那些重要的,絕對不能丟棄的數(shù)據(jù)(如配置類數(shù)據(jù)等),應(yīng)不設(shè)置有效期,這樣Redis就永遠(yuǎn)不會淘汰這些數(shù)據(jù)。對于那些相對不是那么重要的,并且能夠熱加載的數(shù)據(jù)(比如緩存最近登錄的用戶信息,當(dāng)在Redis中找不到時,程序會去DB中讀取),可以設(shè)置上有效期,這樣在內(nèi)存不夠時Redis就會淘汰這部分?jǐn)?shù)據(jù)。

配置方法:

maxmemory-policy volatile-lru #默認(rèn)是noeviction,即不進(jìn)行數(shù)據(jù)淘汰

Pipelining

Pipelining

Redis提供許多批量操作的命令,如MSET/MGET/HMSET/HMGET等等,這些命令存在的意義是減少維護(hù)網(wǎng)絡(luò)連接和傳輸數(shù)據(jù)所消耗的資源和時間。

例如連續(xù)使用5次SET命令設(shè)置5個不同的key,比起使用一次MSET命令設(shè)置5個不同的key,效果是一樣的,但前者會消耗更多的RTT(Round Trip Time)時長,永遠(yuǎn)應(yīng)優(yōu)先使用后者。

然而,如果客戶端要連續(xù)執(zhí)行的多次操作無法通過Redis命令組合在一起,例如:

SETa"abc"

INCRb

HSETcname"hi"

此時便可以使用Redis提供的pipelining功能來實現(xiàn)在一次交互中執(zhí)行多條命令。

使用pipelining時,只需要從客戶端一次向Redis發(fā)送多條命令(以rn)分隔,Redis就會依次執(zhí)行這些命令,并且把每個命令的返回按順序組裝在一起一次返回,比如:

$(printf"PINGrnPINGrnPINGrn";sleep1) | nc localhost6379

+PONG

+PONG

+PONG

大部分的Redis客戶端都對Pipelining提供支持,所以開發(fā)者通常并不需要自己手工拼裝命令列表。

Pipelining的局限性

Pipelining只能用于執(zhí)行連續(xù)且無相關(guān)性的命令,當(dāng)某個命令的生成需要依賴于前一個命令的返回時,就無法使用Pipelining了。

通過Scripting功能,可以規(guī)避這一局限性

事務(wù)與Scripting

Pipelining能夠讓Redis在一次交互中處理多條命令,然而在一些場景下,我們可能需要在此基礎(chǔ)上確保這一組命令是連續(xù)執(zhí)行的。

比如獲取當(dāng)前累計的PV數(shù)并將其清0

> GET vCount

12384

> SET vCount0

OK

如果在GET和SET命令之間插進(jìn)來一個INCR vCount,就會使客戶端拿到的vCount不準(zhǔn)確。

Redis的事務(wù)可以確保復(fù)數(shù)命令執(zhí)行時的原子性。也就是說Redis能夠保證:一個事務(wù)中的一組命令是絕對連續(xù)執(zhí)行的,在這些命令執(zhí)行完成之前,絕對不會有來自于其他連接的其他命令插進(jìn)去執(zhí)行。

通過MULTI和EXEC命令來把這兩個命令加入一個事務(wù)中:

> MULTI

OK

> GET vCount

QUEUED

> SET vCount0

QUEUED

> EXEC

1)12384

2)OK

Redis在接收到MULTI命令后便會開啟一個事務(wù),這之后的所有讀寫命令都會保存在隊列中但并不執(zhí)行,直到接收到EXEC命令后,Redis會把隊列中的所有命令連續(xù)順序執(zhí)行,并以數(shù)組形式返回每個命令的返回結(jié)果。

可以使用DISCARD命令放棄當(dāng)前的事務(wù),將保存的命令隊列清空。

需要注意的是,Redis事務(wù)不支持回滾:

如果一個事務(wù)中的命令出現(xiàn)了語法錯誤,大部分客戶端驅(qū)動會返回錯誤,2.6.5版本以上的Redis也會在執(zhí)行EXEC時檢查隊列中的命令是否存在語法錯誤,如果存在,則會自動放棄事務(wù)并返回錯誤。

但如果一個事務(wù)中的命令有非語法類的錯誤(比如對String執(zhí)行HSET操作),無論客戶端驅(qū)動還是Redis都無法在真正執(zhí)行這條命令之前發(fā)現(xiàn),所以事務(wù)中的所有命令仍然會被依次執(zhí)行。在這種情況下,會出現(xiàn)一個事務(wù)中部分命令成功部分命令失敗的情況,然而與RDBMS不同,Redis不提供事務(wù)回滾的功能,所以只能通過其他方法進(jìn)行數(shù)據(jù)的回滾。

通過事務(wù)實現(xiàn)CAS

Redis提供了WATCH命令與事務(wù)搭配使用,實現(xiàn)CAS樂觀鎖的機(jī)制。

假設(shè)要實現(xiàn)將某個商品的狀態(tài)改為已售:

if(exec(HGET stock:1001state) == "in stock")

exec(HSET stock:1001state"sold");

這一偽代碼執(zhí)行時,無法確保并發(fā)安全性,有可能多個客戶端都獲取到了”in stock”的狀態(tài),導(dǎo)致一個庫存被售賣多次。

使用WATCH命令和事務(wù)可以解決這一問題:

exec(WATCH stock:1001);

if(exec(HGET stock:1001state) == "in stock"){

exec(MULTI);

exec(HSET stock:1001state"sold");

exec(EXEC);

}

WATCH的機(jī)制是:在事務(wù)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之間密集的讀/寫交互放在服務(wù)端進(jìn)行,避免過多的數(shù)據(jù)交互,提升性能。

Scripting功能是作為事務(wù)功能的替代者誕生的,事務(wù)提供的所有能力Scripting都可以做到。Redis官方推薦使用LUA Script來代替事務(wù),前者的效率和便利性都超過了事務(wù)。

關(guān)于Scripting的具體使用,本文不做詳細(xì)介紹,請參考官方文檔

https://redis.io/commands/eval

Redis性能調(diào)優(yōu)

盡管Redis是一個非??焖俚膬?nèi)存數(shù)據(jù)存儲媒介,也并不代表Redis不會產(chǎn)生性能問題。

前文中提到過,Redis采用單線程模型,所有的命令都是由一個線程串行執(zhí)行的,所以當(dāng)某個命令執(zhí)行耗時較長時,會拖慢其后的所有命令,這使得Redis對每個任務(wù)的執(zhí)行效率更加敏感。

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

最初的也是最重要的,確保沒有讓Redis執(zhí)行耗時長的命令

使用pipelining將連續(xù)執(zhí)行的命令組合執(zhí)行

操作系統(tǒng)的Transparent huge pages功能必須關(guān)閉:

echo never > /sys/kernel/mm/transparent_hugepage/enabled

如果在虛擬機(jī)中運(yùn)行Redis,可能天然就有虛擬機(jī)環(huán)境帶來的固有延遲。可以通過./redis-cli –intrinsic-latency 100命令查看固有延遲。同時如果對Redis的性能有較高要求的話,應(yīng)盡可能在物理機(jī)上直接部署Redis。

檢查數(shù)據(jù)持久化策略

考慮引入讀寫分離機(jī)制

長耗時命令

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

通常來說,O(1)的命令是安全的,O(N)命令在使用時需要注意,如果N的數(shù)量級不可預(yù)知,則應(yīng)避免使用。例如對一個field數(shù)未知的Hash數(shù)據(jù)執(zhí)行HGETALL/HKEYS/HVALS命令,通常來說這些命令執(zhí)行的很快,但如果這個Hash中的field數(shù)量極多,耗時就會成倍增長。

又如使用SUNION對兩個Set執(zhí)行Union操作,或使用SORT對List/Set執(zhí)行排序操作等時,都應(yīng)該嚴(yán)加注意。

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

不要把List當(dāng)做列表使用,僅當(dāng)做隊列來使用

通過機(jī)制嚴(yán)格控制Hash、Set、Sorted Set的大小

可能的話,將排序、并集、交集等操作放在客戶端執(zhí)行

絕對禁止使用KEYS命令

避免一次性遍歷集合類型的所有成員,而應(yīng)使用SCAN類的命令進(jìn)行分批的,游標(biāo)式的遍歷

Redis提供了SCAN命令,可以對Redis中存儲的所有key進(jìn)行游標(biāo)式的遍歷,避免使用KEYS命令帶來的性能問題。同時還有SSCAN/HSCAN/ZSCAN等命令,分別用于對Set/Hash/Sorted Set中的元素進(jìn)行游標(biāo)式遍歷。SCAN類命令的使用請參考官方文檔:

https://redis.io/commands/scan

Redis提供了Slow Log功能,可以自動記錄耗時較長的命令。相關(guān)的配置參數(shù)有兩個:

slowlog-log-slower-than xxxms#執(zhí)行時間慢于xxx毫秒的命令計入Slow Log

slowlog-max-len xxx#Slow Log的長度,即最大紀(jì)錄多少條Slow Log

使用SLOWLOG GET [number]命令,可以輸出最近進(jìn)入Slow Log的number條命令。

使用SLOWLOG RESET命令,可以重置Slow Log

網(wǎng)絡(luò)引發(fā)的延遲

盡可能使用長連接或連接池,避免頻繁創(chuàng)建銷毀連接

客戶端進(jìn)行的批量數(shù)據(jù)操作,應(yīng)使用Pipeline特性在一次交互中完成。具體請參照本文的Pipelining章節(jié)

數(shù)據(jù)持久化引發(fā)的延遲

Redis的數(shù)據(jù)持久化工作本身就會帶來延遲,需要根據(jù)數(shù)據(jù)的安全級別和性能要求制定合理的持久化策略:

AOF + fsync always的設(shè)置雖然能夠絕對確保數(shù)據(jù)安全,但每個操作都會觸發(fā)一次fsync,會對Redis的性能有比較明顯的影響

AOF + fsync every second是比較好的折中方案,每秒fsync一次

AOF + fsync never會提供AOF持久化方案下的最優(yōu)性能

使用RDB持久化通常會提供比使用AOF更高的性能,但需要注意RDB的策略配置

每一次RDB快照和AOF Rewrite都需要Redis主進(jìn)程進(jìn)行fork操作。fork操作本身可能會產(chǎn)生較高的耗時,與CPU和Redis占用的內(nèi)存大小有關(guān)。根據(jù)具體的情況合理配置RDB快照和AOF Rewrite時機(jī),避免過于頻繁的fork帶來的延遲

Redis在fork子進(jìn)程時需要將內(nèi)存分頁表拷貝至子進(jìn)程,以占用了24GB內(nèi)存的Redis實例為例,共需要拷貝24GB / 4kB * 8 = 48MB的數(shù)據(jù)。在使用單Xeon 2.27Ghz的物理機(jī)上,這一fork操作耗時216ms。

可以通過INFO命令返回的latest_fork_usec字段查看上一次fork操作的耗時(微秒)

Swap引發(fā)的延遲

當(dāng)Linux將Redis所用的內(nèi)存分頁移至swap空間時,將會阻塞Redis進(jìn)程,導(dǎo)致Redis出現(xiàn)不正常的延遲。Swap通常在物理內(nèi)存不足或一些進(jìn)程在進(jìn)行大量I/O操作時發(fā)生,應(yīng)盡可能避免上述兩種情況的出現(xiàn)。

/proc//smaps文件中會保存進(jìn)程的swap記錄,通過查看這個文件,能夠判斷Redis的延遲是否由Swap產(chǎn)生。如果這個文件中記錄了較大的Swap size,則說明延遲很有可能是Swap造成的。

數(shù)據(jù)淘汰引發(fā)的延遲

當(dāng)同一秒內(nèi)有大量key過期時,也會引發(fā)Redis的延遲。在使用時應(yīng)盡量將key的失效時間錯開。

引入讀寫分離機(jī)制

Redis的主從復(fù)制能力可以實現(xiàn)一主多從的多節(jié)點(diǎn)架構(gòu),在這一架構(gòu)下,主節(jié)點(diǎn)接收所有寫請求,并將數(shù)據(jù)同步給多個從節(jié)點(diǎn)。

在這一基礎(chǔ)上,我們可以讓從節(jié)點(diǎn)提供對實時性要求不高的讀請求服務(wù),以減小主節(jié)點(diǎn)的壓力。

尤其是針對一些使用了長耗時命令的統(tǒng)計類任務(wù),完全可以指定在一個或多個從節(jié)點(diǎn)上執(zhí)行,避免這些長耗時命令影響其他請求的響應(yīng)。

關(guān)于讀寫分離的具體說明,請參見后續(xù)章節(jié)

主從復(fù)制與集群分片

主從復(fù)制

Redis支持一主多從的主從復(fù)制架構(gòu)。一個Master實例負(fù)責(zé)處理所有的寫請求,Master將寫操作同步至所有Slave。

借助Redis的主從復(fù)制,可以實現(xiàn)讀寫分離和高可用:

實時性要求不是特別高的讀請求,可以在Slave上完成,提升效率。特別是一些周期性執(zhí)行的統(tǒng)計任務(wù),這些任務(wù)可能需要執(zhí)行一些長耗時的Redis命令,可以專門規(guī)劃出1個或幾個Slave用于服務(wù)這些統(tǒng)計任務(wù)

借助Redis Sentinel可以實現(xiàn)高可用,當(dāng)Master crash后,Redis Sentinel能夠自動將一個Slave晉升為Master,繼續(xù)提供服務(wù)

啟用主從復(fù)制非常簡單,只需要配置多個Redis實例,在作為Slave的Redis實例中配置:

slaveof192.168.1.16379#指定Master的IP和端口

當(dāng)Slave啟動后,會從Master進(jìn)行一次冷啟動數(shù)據(jù)同步,由Master觸發(fā)BGSAVE生成RDB文件推送給Slave進(jìn)行導(dǎo)入,導(dǎo)入完成后Master再將增量數(shù)據(jù)通過Redis Protocol同步給Slave。之后主從之間的數(shù)據(jù)便一直以Redis Protocol進(jìn)行同步

使用Sentinel做自動failover

Redis的主從復(fù)制功能本身只是做數(shù)據(jù)同步,并不提供監(jiān)控和自動failover能力,要通過主從復(fù)制功能來實現(xiàn)Redis的高可用,還需要引入一個組件:Redis Sentinel

Redis Sentinel是Redis官方開發(fā)的監(jiān)控組件,可以監(jiān)控Redis實例的狀態(tài),通過Master節(jié)點(diǎn)自動發(fā)現(xiàn)Slave節(jié)點(diǎn),并在監(jiān)測到Master節(jié)點(diǎn)失效時選舉出一個新的Master,并向所有Redis實例推送新的主從配置。

Redis Sentinel需要至少部署3個實例才能形成選舉關(guān)系。

關(guān)鍵配置:

另外需要注意的是,Redis Sentinel實現(xiàn)的自動failover不是在同一個IP和端口上完成的,也就是說自動failover產(chǎn)生的新Master提供服務(wù)的IP和端口與之前的Master是不一樣的,所以要實現(xiàn)HA,還要求客戶端必須支持Sentinel,能夠與Sentinel交互獲得新Master的信息才行。

集群分片

為何要做集群分片:

Redis中存儲的數(shù)據(jù)量大,一臺主機(jī)的物理內(nèi)存已經(jīng)無法容納

Redis的寫請求并發(fā)量大,一個Redis實例以無法承載

當(dāng)上述兩個問題出現(xiàn)時,就必須要對Redis進(jìn)行分片了。

Redis的分片方案有很多種,例如很多Redis的客戶端都自行實現(xiàn)了分片功能,也有向Twemproxy這樣的以代理方式實現(xiàn)的Redis分片方案。然而首選的方案還應(yīng)該是Redis官方在3.0版本中推出的Redis Cluster分片方案。

本文不會對Redis Cluster的具體安裝和部署細(xì)節(jié)進(jìn)行介紹,重點(diǎn)介紹Redis Cluster帶來的好處與弊端。

Redis Cluster的能力

能夠自動將數(shù)據(jù)分散在多個節(jié)點(diǎn)上

當(dāng)訪問的key不在當(dāng)前分片上時,能夠自動將請求轉(zhuǎn)發(fā)至正確的分片

當(dāng)集群中部分節(jié)點(diǎn)失效時仍能提供服務(wù)

其中第三點(diǎn)是基于主從復(fù)制來實現(xiàn)的,Redis Cluster的每個數(shù)據(jù)分片都采用了主從復(fù)制的結(jié)構(gòu),原理和前文所述的主從復(fù)制完全一致,唯一的區(qū)別是省去了Redis Sentinel這一額外的組件,由Redis Cluster負(fù)責(zé)進(jìn)行一個分片內(nèi)部的節(jié)點(diǎn)監(jiān)控和自動failover。

Redis Cluster分片原理

Redis Cluster中共有16384個hash slot,Redis會計算每個key的CRC16,將結(jié)果與16384取模,來決定該key存儲在哪一個hash slot中,同時需要指定Redis Cluster中每個數(shù)據(jù)分片負(fù)責(zé)的Slot數(shù)。Slot的分配在任何時間點(diǎn)都可以進(jìn)行重新分配。

客戶端在對key進(jìn)行讀寫操作時,可以連接Cluster中的任意一個分片,如果操作的key不在此分片負(fù)責(zé)的Slot范圍內(nèi),Redis Cluster會自動將請求重定向到正確的分片上。

hash tags

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

使用Redis Cluster時,pipelining、事務(wù)和LUA Script功能涉及的key必須在同一個數(shù)據(jù)分片上,否則將會返回錯誤。如要在Redis Cluster中使用上述功能,就必須通過hash tags來確保一個pipeline或一個事務(wù)中操作的所有key都位于同一個Slot中。

有一些客戶端(如Redisson)實現(xiàn)了集群化的pipelining操作,可以自動將一個pipeline里的命令按key所在的分片進(jìn)行分組,分別發(fā)到不同的分片上執(zhí)行。但是Redis不支持跨分片的事務(wù),事務(wù)和LUA Script還是必須遵循所有key在一個分片上的規(guī)則要求。

主從復(fù)制 vs 集群分片

在設(shè)計軟件架構(gòu)時,要如何在主從復(fù)制和集群分片兩種部署方案中取舍呢?

從各個方面看,Redis Cluster都是優(yōu)于主從復(fù)制的方案

Redis Cluster能夠解決單節(jié)點(diǎn)上數(shù)據(jù)量過大的問題

Redis Cluster能夠解決單節(jié)點(diǎn)訪問壓力過大的問題

Redis Cluster包含了主從復(fù)制的能力

那是不是代表Redis Cluster永遠(yuǎn)是優(yōu)于主從復(fù)制的選擇呢?

并不是。

軟件架構(gòu)永遠(yuǎn)不是越復(fù)雜越好,復(fù)雜的架構(gòu)在帶來顯著好處的同時,一定也會帶來相應(yīng)的弊端。采用Redis Cluster的弊端包括:

維護(hù)難度增加。在使用Redis Cluster時,需要維護(hù)的Redis實例數(shù)倍增,需要監(jiān)控的主機(jī)數(shù)量也相應(yīng)增加,數(shù)據(jù)備份/持久化的復(fù)雜度也會增加。同時在進(jìn)行分片的增減操作時,還需要進(jìn)行reshard操作,遠(yuǎn)比主從模式下增加一個Slave的復(fù)雜度要高。

客戶端資源消耗增加。當(dāng)客戶端使用連接池時,需要為每一個數(shù)據(jù)分片維護(hù)一個連接池,客戶端同時需要保持的連接數(shù)成倍增多,加大了客戶端本身和操作系統(tǒng)資源的消耗。

性能優(yōu)化難度增加。你可能需要在多個分片上查看Slow Log和Swap日志才能定位性能問題。

事務(wù)和LUA Script的使用成本增加。在Redis Cluster中使用事務(wù)和LUA Script特性有嚴(yán)格的限制條件,事務(wù)和Script中操作的key必須位于同一個分片上,這就使得在開發(fā)時必須對相應(yīng)場景下涉及的key進(jìn)行額外的規(guī)劃和規(guī)范要求。如果應(yīng)用的場景中大量涉及事務(wù)和Script的使用,如何在保證這兩個功能的正常運(yùn)作前提下把數(shù)據(jù)平均分到多個數(shù)據(jù)分片中就會成為難點(diǎn)。

所以說,在主從復(fù)制和集群分片兩個方案中做出選擇時,應(yīng)該從應(yīng)用軟件的功能特性、數(shù)據(jù)和訪問量級、未來發(fā)展規(guī)劃等方面綜合考慮,只在確實有必要引入數(shù)據(jù)分片時再使用Redis Cluster。

下面是一些建議:

需要在Redis中存儲的數(shù)據(jù)有多大?未來2年內(nèi)可能發(fā)展為多大?這些數(shù)據(jù)是否都需要長期保存?是否可以使用LRU算法進(jìn)行非熱點(diǎn)數(shù)據(jù)的淘汰?綜合考慮前面幾個因素,評估出Redis需要使用的物理內(nèi)存。

用于部署Redis的主機(jī)物理內(nèi)存有多大?有多少可以分配給Redis使用?對比(1)中的內(nèi)存需求評估,是否足夠用?

Redis面臨的并發(fā)寫壓力會有多大?在不使用pipelining時,Redis的寫性能可以超過10萬次/秒(更多的benchmark可以參考https://redis.io/topics/benchmarks)

在使用Redis時,是否會使用到pipelining和事務(wù)功能?使用的場景多不多?

綜合上面幾點(diǎn)考慮,如果單臺主機(jī)的可用物理內(nèi)存完全足以支撐對Redis的容量需求,且Redis面臨的并發(fā)寫壓力距離Benchmark值還尚有距離,建議采用主從復(fù)制的架構(gòu),可以省去很多不必要的麻煩。同時,如果應(yīng)用中大量使用pipelining和事務(wù),也建議盡可能選擇主從復(fù)制架構(gòu),可以減少設(shè)計和開發(fā)時的復(fù)雜度。

Redis Java客戶端的選擇

Redis的Java客戶端很多,官方推薦的有三種:Jedis、Redisson和lettuce。

在這里對Jedis和Redisson進(jìn)行對比介紹

Jedis:

輕量,簡潔,便于集成和改造

支持連接池

支持pipelining、事務(wù)、LUA Scripting、Redis Sentinel、Redis Cluster

不支持讀寫分離,需要自己實現(xiàn)

文檔差(真的很差,幾乎沒有……)

Redisson:

基于Netty實現(xiàn),采用非阻塞IO,性能高

支持異步請求

支持連接池

支持pipelining、LUA Scripting、Redis Sentinel、Redis Cluster

不支持事務(wù),官方建議以LUA Scripting代替事務(wù)

支持在Redis Cluster架構(gòu)下使用pipelining

支持讀寫分離,支持讀負(fù)載均衡,在主從復(fù)制和Redis Cluster架構(gòu)下都可以使用

內(nèi)建Tomcat Session Manager,為Tomcat 6/7/8提供了會話共享功能

可以與Spring Session集成,實現(xiàn)基于Redis的會話共享

文檔較豐富,有中文文檔

對于Jedis和Redisson的選擇,同樣應(yīng)遵循前述的原理,盡管Jedis比起Redisson有各種各樣的不足,但也應(yīng)該在需要使用Redisson的高級特性時再選用Redisson,避免造成不必要的程序復(fù)雜度提升。

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

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 數(shù)據(jù)存儲
    +關(guān)注

    關(guān)注

    5

    文章

    947

    瀏覽量

    50759
  • Redis
    +關(guān)注

    關(guān)注

    0

    文章

    368

    瀏覽量

    10780

原文標(biāo)題:Redis 基礎(chǔ)、高級特性與性能調(diào)優(yōu)

文章出處:【微信號:DBDevs,微信公眾號:數(shù)據(jù)分析與開發(fā)】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    Redis Stream應(yīng)用案例

    IRC頻道(channel1),就可以接收所有用戶發(fā)出的消息了。發(fā)出消息時,只需使用發(fā)布命令(publish)命令即可。整個業(yè)務(wù)邏輯非常的清晰簡單,這也是Redis強(qiáng)大和流行的重要原因——提供的功能和
    發(fā)表于 06-26 17:15

    Redis-數(shù)據(jù)結(jié)構(gòu)與對象

    Redis-數(shù)據(jù)結(jié)構(gòu)與對象-對象
    發(fā)表于 06-01 17:28

    通過講述Redis數(shù)據(jù)結(jié)構(gòu)主要命令Redis基本能力進(jìn)行直觀介紹

    本文將從Redis的基本特性入手,通過講述Redis數(shù)據(jù)結(jié)構(gòu)主要命令Redis基本能力
    的頭像 發(fā)表于 01-25 15:41 ?3888次閱讀

    Redis五種常見對象類型的底層數(shù)據(jù)結(jié)構(gòu)

    集合(Zset),我們在日常工作中也會經(jīng)常使用它們。知其然,更要知其所以然,本文將會帶你讀懂這五種常見對象類型的底層數(shù)據(jù)結(jié)構(gòu)。 本文主要內(nèi)容參考自《Redis設(shè)計與實現(xiàn)》 1. 對象類型和編碼
    的頭像 發(fā)表于 11-14 09:50 ?2909次閱讀
    <b class='flag-5'>Redis</b>五種常見對象類型的底層<b class='flag-5'>數(shù)據(jù)結(jié)構(gòu)</b>

    什么是 Redis

    ? — ? 1 ?— 什么是 Redis? Redis(REmote DIctionary Service)是一個開源的鍵值對數(shù)據(jù)庫服務(wù)器。 Redis 更準(zhǔn)確的描述是一個
    的頭像 發(fā)表于 05-22 15:32 ?993次閱讀
    什么是 <b class='flag-5'>Redis</b>

    redis的五種數(shù)據(jù)類型底層數(shù)據(jù)結(jié)構(gòu)

    Redis是一種內(nèi)存數(shù)據(jù)存儲系統(tǒng),支持多種數(shù)據(jù)結(jié)構(gòu)。這些數(shù)據(jù)結(jié)構(gòu)不僅可以滿足常見的存儲需求,還能夠通過其底層數(shù)據(jù)結(jié)構(gòu)提供高效的操作和查詢。以
    的頭像 發(fā)表于 11-16 11:18 ?605次閱讀

    redis集群狀態(tài)查看命令

    Redis集群是一種高可用性的分布式架構(gòu),可以通過多個節(jié)點(diǎn)實現(xiàn)數(shù)據(jù)的復(fù)制和負(fù)載均衡。為了維護(hù)集群的穩(wěn)定性和可靠性,管理員需要監(jiān)控和查看集群的狀態(tài)。下面是詳細(xì)介紹Redis集群狀態(tài)查看
    的頭像 發(fā)表于 12-04 10:44 ?1022次閱讀

    Java redis鎖怎么實現(xiàn)

    在Java中實現(xiàn)Redis鎖涉及到以下幾個方面:Redis的安裝配置、Redis連接池的使用、Redis數(shù)據(jù)結(jié)構(gòu)的選擇、實現(xiàn)分布式鎖的幾種方
    的頭像 發(fā)表于 12-04 10:47 ?910次閱讀

    redis查看集群狀態(tài)命令

    Redis 是一個開源的、內(nèi)存中的數(shù)據(jù)結(jié)構(gòu)存儲系統(tǒng),提供了一系列命令來管理和操作數(shù)據(jù)。在 Redis 中,集群是一個由多個
    的頭像 發(fā)表于 12-04 11:39 ?843次閱讀

    redis查看主從節(jié)點(diǎn)命令

    Redis是一種開源的內(nèi)存數(shù)據(jù)結(jié)構(gòu)存儲系統(tǒng),常被用作數(shù)據(jù)庫、緩存和消息中間件。在Redis中,可以通過一些命令來查看主從節(jié)點(diǎn)的信息,以便
    的頭像 發(fā)表于 12-04 11:44 ?1052次閱讀

    redis hash底層實現(xiàn)原理

    數(shù)據(jù)結(jié)構(gòu)是如何實現(xiàn)的呢?本文將詳細(xì)介紹Redis哈希底層的實現(xiàn)原理。 在Redis中,每個哈希都是由一個類似于字典(Dictionary)的結(jié)構(gòu)
    的頭像 發(fā)表于 12-04 16:27 ?482次閱讀

    redis的原理和使用場景

    Redis(Remote Dictionary Server)是一個開源的、高性能的非關(guān)系型(NoSQL)的鍵值對數(shù)據(jù)庫管理系統(tǒng)。它以其快速讀寫能力和多種數(shù)據(jù)結(jié)構(gòu)支持而聞名,并被廣泛應(yīng)
    的頭像 發(fā)表于 12-04 16:29 ?487次閱讀

    redis容器內(nèi)怎么查看redis日志

    redis是一款流行的開源內(nèi)存數(shù)據(jù)庫,常用于緩存、消息隊列、任務(wù)管理等場景。在使用redis時,了解如何查看redis日志對于排查問題、監(jiān)控性能和分析應(yīng)用程序行為非常重要。在本文中,我
    的頭像 發(fā)表于 12-05 10:10 ?2923次閱讀

    redis數(shù)據(jù)結(jié)構(gòu)的底層實現(xiàn)

    Redis是一種內(nèi)存鍵值數(shù)據(jù)庫,常用于緩存、消息隊列、實時數(shù)據(jù)分析等場景。它的高性能得益于其精心設(shè)計的數(shù)據(jù)結(jié)構(gòu)和底層實現(xiàn)。本文將詳細(xì)介紹
    的頭像 發(fā)表于 12-05 10:14 ?517次閱讀

    redis是關(guān)系型數(shù)據(jù)庫嗎

    Redis不是關(guān)系型數(shù)據(jù)庫,它是一種基于鍵值對的NoSQL數(shù)據(jù)庫。在本文中,我將對Redis進(jìn)行詳細(xì)介紹
    的頭像 發(fā)表于 12-05 10:32 ?1225次閱讀