1. String(字符串)
1.1 簡單介紹
字符串類型是Redis最基礎的數據結構,字符串類型可以是JSON
然爆、XML
甚至是二進制的圖片等數據站粟,但是最大值不能超過512MB
。在 Redis 中施蜜,String 是可以修改的卒蘸,稱為動態(tài)字符串
(Simple Dynamic String
簡稱SDS
)雌隅,說是字符串但它的內部結構更像是一個ArrayList
翻默,內部維護著一個字節(jié)數組,并且在其內部預分配了一定的空間恰起,以減少內存的頻繁分配修械。Redis
的內存分配機制是這樣:
- 當字符串的長度小于 1MB時,每次擴容都是加倍現(xiàn)有的空間检盼。
- 如果字符串長度超過 1MB時肯污,每次擴容時只會擴展 1MB 的空間。
這樣既保證了內存空間夠用吨枉,還不至于造成內存的浪費蹦渣,字符串最大長度為512MB
.。
分析一下字符串的數據結構
struct SDS {
T capacity; //數組容量
T len; //實際長度
byte flages; //標志位,低三位表示類型
byte[] content; //數組內容
}
capacity
和len
兩個屬性都是泛型貌亭,為什么不直接用int類型
柬唯?因為Redis
內部有很多優(yōu)化方案,為更合理的使用內存圃庭,不同長度的字符串采用不同的數據類型表示(int
锄奢、embstr
、raw
)剧腻,且在創(chuàng)建字符串的時候len
會和capacity
一樣大拘央,不產生冗余的空間,所以String
值可以是字符串书在、數字(整數灰伟、浮點數) 或者 二進制。
Redis會根據當前值的類型和長度決定使用哪種內部編碼來實現(xiàn)儒旬。字符串類型的內部編碼有3
種:
- int:8個字節(jié)的長整型栏账。
- embstr:小于等于39個字節(jié)的字符串遏乔。
- raw:大于39個字節(jié)的字符串。
1.2 應用場景
1.2.1 緩存
在 web 服務中发笔,通常使用 MySQL 作為數據庫盟萨,Redis 作為緩存。由于 Redis 具有支撐高并發(fā)的特性了讨,通常能起到加速讀寫和降低后端壓力的作用捻激。web 端的大多數請求都是從 Redis 中獲取的數據,如果 Redis 中沒有需要的數據前计,則會從 MySQL 中去獲取胞谭,并將獲取到的數據寫入 Redis。
1.2.2 計數
Redis 中有一個字符串相關的命令incr key
男杈,incr
命令對值做自增操作丈屹,返回結果分為以下三種情況:
- 值不是整數,返回錯誤
- 值是整數伶棒,返回自增后的結果
- key不存在旺垒,默認鍵為
0
,返回1
比如文章的閱讀量肤无,視頻的播放量等等都會使用 Redis 來計數先蒋,每播放一次,對應的播放量就會加1
宛渐,同時將這些數據異步存儲到數據庫中達到持久化的目的竞漾。
1.2.3 共享 Session
在分布式系統(tǒng)中,用戶的每次請求會訪問到不同的服務器窥翩,這就會導致 session 不同步的問題业岁,假如一個用來獲取用戶信息的請求落在 A 服務器上,獲取到用戶信息后存入 session寇蚊。下一個請求落在 B 服務器上笔时,想要從 session 中獲取用戶信息就不能正常獲取了,因為用戶信息的 session 在服務器 A 上幔荒,為了解決這個問題糊闽,使用 Redis 集中管理這些 session,將 session 存入redis爹梁,使用的時候直接從 Redis 中獲取就可以了右犹。
1.2.4 限速
為了安全考慮,有些網站會對 IP 進行限制姚垃,限制同一 IP 在一定時間內訪問次數不能超過 n 次念链。
1.3 String 常用命令
set [key] [value] 給指定key設置值(set 可覆蓋老的值)
get [key] 獲取指定key 的值
del [key] 刪除指定key
exists [key] 判斷是否存在指定key
mset [key1] [value1] [key2] [value2] ...... 批量存鍵值對
mget [key1] [key2] ...... 批量取key
expire [key] [time] 給指定key 設置過期時間 單位秒
setex [key] [time] [value] 等價于 set + expire 命令組合
setnx [key] [value] 如果key不存在則set 創(chuàng)建,否則返回0
incr [key] 如果value為整數 可用 incr命令每次自增1
incrby [key] [number] 使用incrby命令對整數值 進行增加 number
2. List(列表)
2.1 簡單介紹
Redis 中的List
和Java
中的LinkedList
很像,底層都是一種鏈表結構掂墓,List
的插入和刪除操作非城矗快,時間復雜度為 O(1)君编,不像數組結構插入跨嘉、刪除操作需要移動數據。像歸像吃嘿,但是 Redis 中的List
底層可不是一個雙向鏈表那么簡單祠乃。
當數據量較少的時候它的底層存儲結構為一塊連續(xù)內存,稱之為ziplist(壓縮列表)
兑燥,它將所有的元素緊挨著一起存儲亮瓷,分配的是一塊連續(xù)的內存;當數據量較多的時候將會變成quicklist(快速鏈表)
結構降瞳。
可單純的鏈表也是有缺陷的嘱支,鏈表的前后指針prev
和next
會占用較多的內存,會比較浪費空間挣饥,而且會加重內存的碎片化除师。在 Redis 3.2 之后就都改用ziplist+鏈表
的混合結構,稱之為quicklist(快速鏈表)
亮靴。ziplist
的每個集合元素使用兩個緊挨在一起的壓縮列表節(jié)點來保存馍盟,第一個節(jié)點保存元素的成員于置,第二個元素保存元素的分值茧吊。
2.2 應用場景
由于List
是一個按照插入順序排序的列表,所以應用場景相對還較多的八毯,例如:
2.2.1 消息隊列
列表用來存儲多個有序的字符串搓侄,既然是有序的,那么就滿足消息隊列的特點话速。使用lpush
+rpop
或者rpush
+lpop
實現(xiàn)消息隊列讶踪。除此之外,redis支持阻塞操作泊交,在彈出元素的時候使用阻塞命令來實現(xiàn)阻塞隊列乳讥。
Redis 雖然支持消息隊列的實現(xiàn),但是并不支持 ack廓俭。所以 Redis 實現(xiàn)的消息隊列不能保證消息的可靠性云石,除非自己實現(xiàn)消息確認機制,不過這非常麻煩研乒,所以如果是重要的消息還是推薦使用專門的消息隊列去做汹忠。
2.2.2 棧
由于列表存儲的是有序字符串,滿足隊列的特點,也就能滿足棧先進后出的特點宽菜,使用lpush
+lpop
或者rpush
+rpop
實現(xiàn)棧谣膳。
2.2.3 文章列表
因為列表的元素不但是有序的,而且還支持按照索引范圍獲取元素铅乡。lpush
命令和lrange
命令能實現(xiàn)最新列表的功能继谚,每次通過lpush
命令往列表里插入新的元素,然后通過lrange
命令讀取最新的元素列表阵幸。比如我們可以使用命令lrange key 0 9
分頁獲取文章列表犬庇。
2.3 List 常用命令
rpush [key] [value1] [value2] ...... 鏈表右側插入
rpop [key] 移除右側列表頭元素,并返回該元素
lpop [key] 移除左側列表頭元素侨嘀,并返回該元素
llen [key] 返回該列表的元素個數
lrem [key] [count] [value] 刪除列表中與value相等的元素臭挽,count是刪除的個數。 count>0 表示從左側開始查找咬腕,刪除count個元素欢峰,count<0 表示從右側開始查找,刪除count個相同元素涨共,count=0 表示刪除全部相同的元素
lindex [key] [index] 獲取list指定下標的元素 (需要遍歷纽帖,時間復雜度為O(n)) index 代表元素下標,index 可以為負數举反, index= 表示倒數第一個元素懊直,同理 index=-2 表示倒數第二 個元素。
lrange [key] [start_index] [end_index] 獲取list 區(qū)間內的所有元素(時間復雜度為 O(n))
ltrim [key] [start_index] [end_index] 保留區(qū)間內的元素,其他元素刪除(時間復雜度為 O(n))
3. Hash(字典)
3.1 簡單介紹
Redis 中的Hash
和 Java 的HashMap
更加相似,是數組+鏈表
的結構柿赊,當發(fā)生 hash 碰撞時將會把元素追加到鏈表上狮荔,值得注意的是在Redis
的Hash
中value
只能是字符串。
3.2 使用場景
3.2.1 購物車
hset [key] [field] [value]
命令, 可以實現(xiàn)以用戶Id
,商品Id
為field
,商品數量為value
尝偎,恰好構成了購物車的3個要素。
3.2.2 存儲對象
hash
類型的(key, field, value)
的結構與對象的(對象id, 屬性, 值)
的結構相似鹏控,也可以用來存儲對象致扯,如:
key=JavaUser293847
value={
“id”: 1,
“name”: “SnailClimb”,
“age”: 22,
“l(fā)ocation”: “Wuhan, Hubei”
}
3.3 Hash 常用命令
hset [key] [field] [value] 新建字段信息
hget [key] [field] 獲取字段信息
hdel [key] [field] 刪除字段
hlen [key] 保存的字段個數
hgetall [key] 獲取指定key 字典里的所有字段和值 (字段信息過多,會導致慢查詢 慎用:親身經歷 曾經用過這個這個指令導致線上服務故障)
hmset [key] [field1] [value1] [field2] [value2] ...... 批量創(chuàng)建
hincr [key] [field] 對字段值自增
hincrby [key] [field] [number] 對字段值增加number
4. Set(集合)
4.1 簡單介紹
Redis 中的set
和Java
中的HashSet
有些類似,它內部的鍵值對是無序的当辐、唯一 的抖僵。它的內部實現(xiàn)相當于一個特殊的字典,字典中所有的 value 都是一個值 NULL瀑构。當集合中最后一個元素被移除之后裆针,數據結構被自動刪除刨摩,內存被回收。
4.2 應用場景
- 比如:在在線討論社區(qū)中世吨,可以將一個用戶所有的關注人存在一個集合中澡刹,將其所有粉絲存在一個集合。Redis可以非常方便的實現(xiàn)如共同關注耘婚、共同粉絲罢浇、共同喜好等功能。這個過程也就是求交集的過程沐祷。
- 好友嚷闭、關注、粉絲赖临、感興趣的人集合:
1)sinter
命令可以獲得A和B兩個用戶的共同好友胞锰;
2)sismember
命令可以判斷A是否是B的好友;
3)scard
命令可以獲取好友數量兢榨;- 關注時嗅榕,
smove
命令可以將B從A的粉絲集合轉移到A的好友集合
- 關注時嗅榕,
- 首頁展示隨機:美團首頁有很多推薦商家,但是并不能全部展示吵聪,set類型適合存放所有需要展示的內容凌那,而
srandmember
命令則可以從中隨機獲取幾個。 - 抽獎功能:用戶點擊抽獎按鈕吟逝,參數抽獎帽蝶,將用戶編號放入集合,然后抽獎块攒,分別抽一等獎励稳、二等獎,如果已經抽中一等獎的用戶不能參數抽二等獎則使用
spop key [count]
局蚀,反之使用srandmember key [count]
麦锯。
4.3 Set 常用命令
sadd [key] [value] 向指定key的set中添加元素
smembers [key] 獲取指定key 集合中的所有元素
sismember [key] [value] 判斷集合中是否存在某個value
scard [key] 獲取集合的長度
spop [key] 彈出一個元素
srem [key] [value] 刪除指定元素
5. Zset(有序集合)
5.1 簡單介紹
Zset
也叫SortedSet
一方面它是個set
,保證了內部 value 的唯一性琅绅,另一方面它可以給每個 value 賦予一個score
,代表這個value的排序權重鹅巍。它的內部實現(xiàn)由兩種數據結構支持:ziplist 和 skiplist千扶。
5.1.1 ziplist(壓縮列表)
當 Zset 使用 ziplist 作為存儲結構的時候,每個集合元素使用兩個緊挨在一起的壓縮列表節(jié)點來保存骆捧,第一個節(jié)點保存元素的成員澎羞,第二個元素保存元素的分值。
5.1.2 skiplist(跳躍表)
當 Zset 使用 skiplist 作為存儲結構時敛苇,使用 skiplist 按序保存元素分值妆绞,使用 dict 來保存元素和分值的對應關系顺呕。
跳表是一種基于有序鏈表的擴展,它會維護多個索引鏈表和原鏈表括饶。跳表不斷提升新的關鍵節(jié)點形成新的有序鏈表株茶,是一種空間換時間的思想。在插入元素到跳表時图焰,只需對比新元素的值和關鍵節(jié)點鏈表启盛,這樣就能將對比次數縮減。因為在跳表中技羔,使用空間換時間僵闯,所以會有多層的索引鏈表,每一層索引鏈表都是上一層的一半藤滥,當數據量較大的時候鳖粟,就能夠很輕松地降低鏈表查詢消耗的性能。至于提取關鍵節(jié)點的極限拙绊,是同一層只有兩個節(jié)點牺弹。
跳表插入節(jié)點的流程:
- 新節(jié)點和各層索引節(jié)點逐一比較,確定原鏈表的插入位置时呀,O(logn)张漂。
- 把索引插入到原鏈表,O(1)谨娜。
- 使用拋硬幣的隨機方式航攒,決定新節(jié)點是否提升為上一級索引,O(logn)趴梢。
總體上跳表查找和插入的時間復雜度是 O(logn)漠畜,空間復雜度是 O(n)。
跳表刪除節(jié)點的流程:
- 在索引層找到關鍵節(jié)點進行刪除坞靶,刪除每一層的相同節(jié)點憔狞,O(logn)。
- 如果某一層在刪除節(jié)點之后只剩下一個節(jié)點彰阴,那么刪除這層的鏈表瘾敢,O(logn)。
5.2 Zset 應用場景
5.2.1 排行榜
和list
不同的是Zset
它能夠實現(xiàn)動態(tài)的排序尿这。比如用來存儲粉絲列表簇抵,在線討論社區(qū)項目的關注模塊用到了Zset
,value 為粉絲的用戶 ID射众,score 為關注時間碟摆,這樣我們可以對粉絲列表按關注時間進行排序。
Zset
還可以用來存儲學生的成績叨橱,value
值是學生的 ID典蜕,score
是他的考試成績断盛。 我們對成績按分數進行排序就可以得到他的名次。
5.2.2 延遲消息隊列
在一個下單系統(tǒng)中愉舔,下單后需要在 15 分鐘內進行支付钢猛,如果 15 分鐘未支付則自動取消訂單。將下單后的 15 分鐘后時間作為 score屑宠,訂單作為 value 存入 Redis厢洞,消費者輪詢去消費,如果消費的大于等于這筆記錄的 score典奉,則將這筆記錄移除隊列躺翻,取消訂單。
5.3 Zset 常用命令
zadd [key] [score] [value] 向指定key的集合中增加元素
zrange [key] [start_index] [end_index] 獲取下標范圍內的元素列表卫玖,按score 排序輸出
zrevrange [key] [start_index] [end_index] 獲取范圍內的元素列表 公你,按score排序 逆序輸出
zcard [key] 獲取集合列表的元素個數
zrank [key] [value] 獲取元素再集合中的排名
zrangebyscore [key] [score1] [score2] 輸出score范圍內的元素列表
zrem [key] [value] 刪除元素
zscore [key] [value] 獲取元素的score
6. HyperLogLog
- 常用于計數,它采用一種基數算法假瞬,用于完成獨立總數的統(tǒng)計陕靠。
- 占據空間小,無論統(tǒng)計多少個數據脱茉,只占
12K
的內存空間剪芥。 - 是一種不精確的統(tǒng)計算法,標準誤差為0.81%琴许。
- 比如:在線討論社區(qū)中統(tǒng)計獨立訪客UV税肪,使用當前日期作為 key,IP地址作為 value 存入 HyperLoglog榜田。如果要統(tǒng)計指定日期范圍內的 UV益兄,那么整理該日期范圍內的 key 到一個列表中,然后對這個列表求一個 union箭券,然后統(tǒng)計 HyperLoglog 的 size 即可净捅。
7. Geo
用于支持存儲地理位置信息。
參考: