redis 是一個(gè)開源的,基于內(nèi)存的結(jié)構(gòu)化數(shù)據(jù)存儲(chǔ)媒介,可以作為數(shù)據(jù)庫、緩存服務(wù)或消息服務(wù)使用。
Redis 支持多種數(shù)據(jù)結(jié)構(gòu),包括字符串、哈希表、鏈表、集合、有序集合、位圖、Hyperloglogs 等。
Redis 具備 LRU 淘汰、事務(wù)實(shí)現(xiàn)、以及不同級(jí)別的硬盤持久化等能力。
支持副本集和通過 Redis Sentinel 實(shí)現(xiàn)的高可用方案,同時(shí)還支持通過 Redis Cluster 實(shí)現(xiàn)的數(shù)據(jù)自動(dòng)分片能力。
Redis 的主要功能都基于單線程模型實(shí)現(xiàn),也就是說 Redis 使用一個(gè)線程來服務(wù)所有的客戶端請求,同時(shí) Redis 采用了非阻塞式 IO,并精細(xì)地優(yōu)化各種命令的算法時(shí)間復(fù)雜度。
這些信息意味著:
Redis 是線程安全的(因?yàn)橹挥幸粋€(gè)線程),其所有操作都是原子的,不會(huì)因并發(fā)產(chǎn)生數(shù)據(jù)異常。
Redis 的速度非常快(因?yàn)槭褂梅亲枞?IO,且大部分命令的算法時(shí)間復(fù)雜度都是 O(1))。
使用高耗時(shí)的 Redis 命令是很危險(xiǎn)的,會(huì)占用唯一的一個(gè)線程的大量處理時(shí)間,導(dǎo)致所有的請求都被拖慢。(例如時(shí)間復(fù)雜度為 O(N) 的 KEYS 命令,嚴(yán)格禁止在生產(chǎn)環(huán)境中使用)
常用命令一:Key
Redis 采用 Key-Value 型的基本數(shù)據(jù)結(jié)構(gòu),任何二進(jìn)制序列都可以作為 Redis 的 Key 使用(例如普通的字符串或一張 JPEG 圖片)
關(guān)于 Key 的一些注意事項(xiàng):
不要使用過長的 Key。
例如使用一個(gè) 1024 字節(jié)的 key 就不是一個(gè)好主意,不僅會(huì)消耗更多的內(nèi)存,還會(huì)導(dǎo)致查找的效率降低。
Key 短到缺失了可讀性也是不好的。
例如”u1000flw” 比起”user:1000:followers” 來說,節(jié)省了寥寥的存儲(chǔ)空間,卻引發(fā)了可讀性和可維護(hù)性上的麻煩。
最好使用統(tǒng)一的規(guī)范來設(shè)計(jì) Key,比如”object-type:id:attr”,以這一規(guī)范設(shè)計(jì)出的 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:為一個(gè) key 設(shè)置 value,可以配合 EX/PX 參數(shù)指定 key 的有效期,通過 NX/XX 參數(shù)針對 key 是否存在的情況進(jìn)行區(qū)別操作,時(shí)間復(fù)雜度 O(1)
GET:獲取某個(gè) key 對應(yīng)的 value,時(shí)間復(fù)雜度 O(1)
GETSET:為一個(gè) key 設(shè)置 value,并返回該 key 的原 value,時(shí)間復(fù)雜度 O(1)
MSET:為多個(gè) key 設(shè)置 value,時(shí)間復(fù)雜度 O(N)
MSETNX:同 MSET,如果指定的 key 中有任意一個(gè)已存在,則不進(jìn)行任何操作,時(shí)間復(fù)雜度 O(N)
MGET:獲取多個(gè) key 對應(yīng)的 value,時(shí)間復(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ù)起作用。時(shí)間復(fù)雜度 O(1)
INCRBY:將 key 對應(yīng)的 value 值自增指定的整型數(shù)值,并返回自增后的值。
只對可以轉(zhuǎn)換為整型的 String 數(shù)據(jù)起作用。時(shí)間復(fù)雜度 O(1)
DECR/DECRBY:同 INCR/INCRBY,自增改為自減。
INCR/DECR 系列命令要求操作的 value 類型為 String,并可以轉(zhuǎn)換為 64 位帶符號(hào)的整型數(shù)字,否則會(huì)返回錯(cuò)誤。
也就是說,進(jìn)行 INCR/DECR 系列命令的 value,必須在 [-2^63 ~ 2^63 - 1] 范圍內(nèi)。
Redis 采用單線程模型,天然是線程安全的,這使得 INCR/DECR 命令可以非常便利的實(shí)現(xiàn)高并發(fā)場景下的精確控制。
例 1:庫存控制
在高并發(fā)場景下實(shí)現(xiàn)庫存余量的精準(zhǔn)校驗(yàn),確保不出現(xiàn)超賣的情況。
設(shè)置庫存總量:
SET inv:remain "100"
庫存扣減 + 余量校驗(yàn):
DECR inv:remain
當(dāng) DECR 命令返回值大于等于 0 時(shí),說明庫存余量校驗(yàn)通過;
如果返回小于 0 的值,則說明庫存已耗盡。
假設(shè)同時(shí)有 300 個(gè)并發(fā)請求進(jìn)行庫存扣減,Redis 能夠確保這 300 個(gè)請求分別得到 99 到 - 200 的返回值,每個(gè)請求得到的返回值都是唯一的。
絕對不會(huì)找出現(xiàn)兩個(gè)請求得到一樣的返回值的情況。
例 2:自增序列生成
實(shí)現(xiàn)類似于 RDBMS 的 Sequence 功能,生成一系列唯一的序列號(hào)
設(shè)置序列起始值:
SET sequence "10000"
獲取一個(gè)序列值:
INCR sequence
直接將返回值作為序列使用即可。
獲取一批(如 100 個(gè))序列值:
INCRBY sequence 100
假設(shè)返回值為 N,那么 [N - 99 ~ N] 的數(shù)值都是可用的序列值。
當(dāng)多個(gè)客戶端同時(shí)向 Redis 申請自增序列時(shí),Redis 能夠確保每個(gè)客戶端得到的序列值或序列范圍都是全局唯一的。
絕對不會(huì)出現(xiàn)不同客戶端得到了重復(fù)的序列值的情況。
常用命令三:List
Redis 的 List 是鏈表型的數(shù)據(jù)結(jié)構(gòu),可以使用 LPUSH/RPUSH/LPOP/RPOP 等命令在 List 的兩端執(zhí)行插入元素和彈出元素的操作。
雖然 List 也支持在特定 index 上插入和讀取元素的功能,但其時(shí)間復(fù)雜度較高(O(N)),應(yīng)小心使用。
與 List 相關(guān)的常用命令:
LPUSH:向指定 List 的左側(cè)(即頭部)插入 1 個(gè)或多個(gè)元素,返回插入后的 List 長度。時(shí)間復(fù)雜度 O(N),N 為插入元素的數(shù)量
RPUSH:同 LPUSH,向指定 List 的右側(cè)(即尾部)插入 1 或多個(gè)元素
LPOP:從指定 List 的左側(cè)(即頭部)移除一個(gè)元素并返回,時(shí)間復(fù)雜度 O(1)
RPOP:同 LPOP,從指定 List 的右側(cè)(即尾部)移除 1 個(gè)元素并返回
LPUSHX/RPUSHX:與 LPUSH/RPUSH 類似,區(qū)別在于,
LPUSHX/RPUSHX 操作的 key 如果不存在,則不會(huì)進(jìn)行任何操作
LLEN:返回指定 List 的長度,時(shí)間復(fù)雜度 O(1)
LRANGE:返回指定 List 中指定范圍的元素(雙端包含,即 LRANGE key 0 10 會(huì)返回 11 個(gè)元素),時(shí)間復(fù)雜度 O(N)。
應(yīng)盡可能控制一次獲取的元素?cái)?shù)量,一次獲取過大范圍的 List 元素會(huì)導(dǎo)致延遲。
同時(shí)對長度不可預(yù)知的 List,避免使用 LRANGE key 0 -1 這樣的完整遍歷操作。
應(yīng)謹(jǐn)慎使用的 List 相關(guān)命令:
LINDEX:返回指定 List 指定 index 上的元素,如果 index 越界,返回 nil。
index 數(shù)值是回環(huán)的,即 - 1 代表 List 最后一個(gè)位置,-2 代表 List 倒數(shù)第二個(gè)位置。時(shí)間復(fù)雜度 O(N)
LSET:將指定 List 指定 index 上的元素設(shè)置為 value,如果 index 越界則返回錯(cuò)誤,時(shí)間復(fù)雜度 O(N)。
如果操作的是頭 / 尾部的元素,則時(shí)間復(fù)雜度為 O(1)
LINSERT:向指定 List 中指定元素之前 / 之后插入一個(gè)新元素,并返回操作后的 List 長度。
如果指定的元素不存在,返回 - 1。如果指定 key 不存在,不會(huì)進(jìn)行任何操作,時(shí)間復(fù)雜度 O(N)
由于 Redis 的 List 是鏈表結(jié)構(gòu)的,上述的三個(gè)命令的算法效率較低,需要對 List 進(jìn)行遍歷。
命令的耗時(shí)無法預(yù)估,在 List 長度大的情況下耗時(shí)會(huì)明顯增加,應(yīng)謹(jǐn)慎使用。
換句話說,Redis 的 List 實(shí)際是設(shè)計(jì)來用于實(shí)現(xiàn)隊(duì)列,而不是用于實(shí)現(xiàn)類似 ArrayList 這樣的列表的。
如果你不是想要實(shí)現(xiàn)一個(gè)雙端出入的隊(duì)列,那么請盡量不要使用 Redis 的 List 數(shù)據(jù)結(jié)構(gòu)。
為了更好支持隊(duì)列的特性,Redis 還提供了一系列阻塞式的操作命令,如 BLPOP/BRPOP 等,能夠?qū)崿F(xiàn)類似于 BlockingQueue 的能力。
即在 List 為空時(shí),阻塞該連接,直到 List 中有對象可以出隊(duì)時(shí)再返回。
常用命令四: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)包括:
可以實(shí)現(xiàn)二元查找,如” 查找 ID 為 1000 的用戶的年齡”
比起將整個(gè)對象序列化后作為 String 存儲(chǔ)的方法,Hash 能夠有效地減少網(wǎng)絡(luò)傳輸?shù)南摹?/p>
當(dāng)使用 Hash 維護(hù)一個(gè)集合時(shí),提供了比 List 效率高得多的隨機(jī)訪問命令
與 Hash 相關(guān)的常用命令:
HSET:將 key 對應(yīng)的 Hash 中的 field 設(shè)置為 value。如果該 Hash 不存在,會(huì)自動(dòng)創(chuàng)建一個(gè)。時(shí)間復(fù)雜度 O(1)
HGET:返回指定 Hash 中 field 字段的值,時(shí)間復(fù)雜度 O(1)
HMSET/HMGET:同 HSET 和 HGET,可以批量操作同一個(gè) key 下的多個(gè) field,時(shí)間復(fù)雜度:O(N),N 為一次操作的 field 數(shù)量
HSETNX:同 HSET,但如 field 已經(jīng)存在,HSETNX 不會(huì)進(jìn)行任何操作,時(shí)間復(fù)雜度 O(1)
HEXISTS:判斷指定 Hash 中 field 是否存在,存在返回 1,不存在返回 0,時(shí)間復(fù)雜度 O(1)
HDEL:刪除指定 Hash 中的 field(1 個(gè)或多個(gè)),時(shí)間復(fù)雜度:O(N),N 為操作的 field 數(shù)量
HINCRBY:同 INCRBY 命令,對指定 Hash 中的一個(gè) field 進(jìn)行 INCRBY,時(shí)間復(fù)雜度 O(1)
應(yīng)謹(jǐn)慎使用的 Hash 相關(guān)命令:
HGETALL:返回指定 Hash 中所有的 field-value 對。
返回結(jié)果為數(shù)組,數(shù)組中 field 和 value 交替出現(xiàn)。時(shí)間復(fù)雜度 O(N)
HKEYS/HVALS:返回指定 Hash 中所有的 field/value,時(shí)間復(fù)雜度 O(N)
上述三個(gè)命令都會(huì)對 Hash 進(jìn)行完整遍歷,Hash 中的 field 數(shù)量與命令的耗時(shí)線性相關(guān),對于尺寸不可預(yù)知的 Hash,應(yīng)嚴(yán)格避免使用上面三個(gè)命令,而改為使用 HSCAN 命令進(jìn)行游標(biāo)式的遍歷。
常用命令五:Set
Redis Set 是無序的,不可重復(fù)的 String 集合。
與 Set 相關(guān)的常用命令:
SADD:向指定 Set 中添加 1 個(gè)或多個(gè) member,如果指定 Set 不存在,會(huì)自動(dòng)創(chuàng)建一個(gè)。時(shí)間復(fù)雜度 O(N),N 為添加的 member 個(gè)數(shù)
SREM:從指定 Set 中移除 1 個(gè)或多個(gè) member,時(shí)間復(fù)雜度 O(N),N 為移除的 member 個(gè)數(shù)
SRANDMEMBER:從指定 Set 中隨機(jī)返回 1 個(gè)或多個(gè) member,時(shí)間復(fù)雜度 O(N),N 為返回的 member 個(gè)數(shù)
SPOP:從指定 Set 中隨機(jī)移除并返回 count 個(gè) member,時(shí)間復(fù)雜度 O(N),N 為移除的 member 個(gè)數(shù)
SCARD:返回指定 Set 中的 member 個(gè)數(shù),時(shí)間復(fù)雜度 O(1)
SISMEMBER:判斷指定的 value 是否存在于指定 Set 中,時(shí)間復(fù)雜度 O(1)
SMOVE:將指定 member 從一個(gè) Set 移至另一個(gè) Set
慎用的 Set 相關(guān)命令:
SMEMBERS:返回指定 Hash 中所有的 member,時(shí)間復(fù)雜度 O(N)
SUNION/SUNIONSTORE:計(jì)算多個(gè) Set 的并集并返回 / 存儲(chǔ)至另一個(gè) Set中,時(shí)間復(fù)雜度 O(N),N 為參與計(jì)算的所有集合的總 member 數(shù)。
SINTER/SINTERSTORE:計(jì)算多個(gè) Set 的交集并返回 / 存儲(chǔ)至另一個(gè) Set 中,時(shí)間復(fù)雜度 O(N),N 為參與計(jì)算的所有集合的總 member 數(shù)。
SDIFF/SDIFFSTORE:計(jì)算 1 個(gè) Set 與 1 或多個(gè) Set 的差集并返回 / 存儲(chǔ)至另一個(gè) Set 中,時(shí)間復(fù)雜度 O(N),N 為參與計(jì)算的所有集合的總 member 數(shù)。
上述幾個(gè)命令涉及的計(jì)算量大,應(yīng)謹(jǐn)慎使用,特別是在參與計(jì)算的 Set 尺寸不可知的情況下,應(yīng)嚴(yán)格避免使用。
可以考慮通過 SSCAN 命令遍歷獲取相關(guān) Set 的全部 member。
如果需要做并集 / 交集 / 差集計(jì)算,可以在客戶端進(jìn)行,或在不服務(wù)實(shí)時(shí)查詢請求的 Slave 上進(jìn)行。
常用命令六:Sorted Set
Redis Sorted Set 是有序的、不可重復(fù)的 String 集合。
Sorted Set 中的每個(gè)元素都需要指派一個(gè)分?jǐn)?shù) (score),Sorted Set 會(huì)根據(jù) score 對元素進(jìn)行升序排序。
如果多個(gè) member 擁有相同的 score,則以字典序進(jìn)行升序排序。
Sorted Set 非常適合用于實(shí)現(xiàn)排名。
Sorted Set 的主要命令:
ZADD:向指定 Sorted Set 中添加 1 個(gè)或多個(gè) member,時(shí)間復(fù)雜度 O(Mlog(N)),M 為添加的 member 數(shù)量,N 為 Sorted Set 中的 member 數(shù)量
ZREM:從指定 Sorted Set 中刪除 1 個(gè)或多個(gè) member,時(shí)間復(fù)雜度 O(Mlog(N)),M 為刪除的 member 數(shù)量,N 為 Sorted Set 中的 member 數(shù)量
ZCOUNT:返回指定 Sorted Set 中指定 score 范圍內(nèi)的 member 數(shù)量,時(shí)間復(fù)雜度:O(log(N))
ZCARD:返回指定 Sorted Set 中的 member 數(shù)量,時(shí)間復(fù)雜度 O(1)
ZSCORE:返回指定 Sorted Set 中指定 member 的 score,時(shí)間復(fù)雜度 O(1)
ZRANK/ZREVRANK:返回指定 member 在 Sorted Set 中的排名,ZRANK 返回按升序排序的排名,ZREVRANK 則返回按降序排序的排名。
時(shí)間復(fù)雜度 O(log(N))
ZINCRBY:同 INCRBY,對指定 Sorted Set 中的指定 member 的 score 進(jìn)行自增,時(shí)間復(fù)雜度 O(log(N))
慎用的 Sorted Set 相關(guān)命令:
ZRANGE/ZREVRANGE:返回指定 Sorted Set 中指定排名范圍內(nèi)的所有 member。
ZRANGE 為按 score 升序排序,ZREVRANGE 為按 score 降序排序,時(shí)間復(fù)雜度
O(log(N)+M),M 為本次返回的 member 數(shù)
ZRANGEBYSCORE/ZREVRANGEBYSCORE:返回指定 Sorted Set 中指定 score 范圍內(nèi)的所有 member,返回結(jié)果以升序 / 降序排序。
min 和 max 可以指定為 - inf 和 + inf,代表返回所有的 member。時(shí)間復(fù)雜度 O(log(N)+M)
ZREMRANGEBYRANK/ZREMRANGEBYSCORE:移除 Sorted Set 中指定排名范圍 / 指定 score 范圍內(nèi)的所有 member。時(shí)間復(fù)雜度 O(log(N)+M)
上述幾個(gè)命令,應(yīng)盡量避免傳遞 [0 -1] 或 [-inf +inf] 這樣的參數(shù),來對 Sorted Set 做一次性的完整遍歷,特別是在 Sorted Set 的尺寸不可預(yù)知的情況下。
可以通過 ZSCAN 命令來進(jìn)行游標(biāo)式的遍歷,或通過 LIMIT 參數(shù)來限制返回 member 的數(shù)量(適用于 ZRANGEBYSCORE 和 ZREVRANGEBYSCORE 命令),以實(shí)現(xiàn)游標(biāo)式的遍歷。
常用命令七:Bitmap 和 HyperLogLog
Redis 的這兩種數(shù)據(jù)結(jié)構(gòu)相較之前的并不常用,在 Redis 中不是一種實(shí)際的數(shù)據(jù)類型,而是一種將 String 作為 Bitmap 使用的方法。
可以理解為將 String 轉(zhuǎn)換為 bit 數(shù)組。使用 Bitmap 來存儲(chǔ) true/false 類型的簡單數(shù)據(jù)極為節(jié)省空間。
HyperLogLogs 是一種主要用于數(shù)量統(tǒng)計(jì)的數(shù)據(jù)結(jié)構(gòu),它和 Set 類似,維護(hù)一個(gè)不可重復(fù)的 String 集合。
但是 HyperLogLogs 并不維護(hù)具體的 member 內(nèi)容,只維護(hù) member 的個(gè)數(shù)。
也就是說,HyperLogLogs 只能用于計(jì)算一個(gè)集合中不重復(fù)的元素?cái)?shù)量,所以它比 Set 要節(jié)省很多內(nèi)存空間。
其他常用命令
EXISTS:判斷指定的 key 是否存在,返回 1 代表存在,0 代表不存在,時(shí)間復(fù)雜度 O(1)
DEL:刪除指定的 key 及其對應(yīng)的 value,時(shí)間復(fù)雜度 O(N),N 為刪除的 key 數(shù)量
EXPIRE/PEXPIRE:為一個(gè) key 設(shè)置有效期,單位為秒或毫秒,時(shí)間復(fù)雜度 O(1)
TTL/PTTL:返回一個(gè) key 剩余的有效時(shí)間,單位為秒或毫秒,時(shí)間復(fù)雜度 O(1)
RENAME/RENAMENX:將 key 重命名為 newkey。使用 RENAME 時(shí),如果 newkey 已經(jīng)存在,其值會(huì)被覆蓋;使用 RENAMENX 時(shí),如果 newkey 已經(jīng)存在,則不會(huì)進(jìn)行任何操作,時(shí)間復(fù)雜度 O(1)
TYPE:返回指定 key 的類型,string, list, set, zset, hash。時(shí)間復(fù)雜度 O(1)
CONFIG GET:獲得 Redis 某配置項(xiàng)的當(dāng)前值,可以使用 * 通配符,時(shí)間復(fù)雜度 O(1)
CONFIG SET:為 Redis 某個(gè)配置項(xiàng)設(shè)置新值,時(shí)間復(fù)雜度 O(1)
CONFIG REWRITE:讓 Redis 重新加載 redis.conf 中的配置
來源:kelgon