在任何一種編程語言裏,字符串String
都是最基礎的數據結構, 那你有想過Redis
中存儲一個字符串都進行了哪些操做嘛?java
在Redis
中String
是能夠修改的,稱爲動態字符串
(Simple Dynamic String
簡稱SDS
)(快拿小本本記名詞,要考的),說是字符串但它的內部結構更像是一個ArrayList
,內部維護着一個字節數組,而且在其內部預分配了必定的空間,以減小內存的頻繁分配。golang
Redis
的內存分配機制是這樣:redis
這樣既保證了內存空間夠用,還不至於形成內存的浪費,字符串最大長度爲512MB
.。 編程
上圖就是字符串的基本結構,其中content
裏面保存的是字符串內容,0x\0
做爲結束字符不會被計算len
中。數組
分析一下字符串的數據結構bash
struct SDS{
T capacity; //數組容量
T len; //實際長度
byte flages; //標誌位,低三位表示類型
byte[] content; //數組內容
}
複製代碼
capacity
和len
兩個屬性都是泛型,爲何不直接用int類型
?由於Redis
內部有不少優化方案,爲更合理的使用內存,不一樣長度的字符串採用不一樣的數據類型表示,且在建立字符串的時候len
會和capacity
同樣大,不產生冗餘的空間,因此String
值能夠是字符串、數字(整數、浮點數) 或者 二進制。網絡
一、應用場景:數據結構
存儲key-value鍵值對,這個比較簡單不細說了編程語言
二、字符串(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 建立,不然返回0
incr [key] 若是value爲整數 可用 incr命令每次自增1
incrby [key] [number] 使用incrby命令對整數值 進行增長 number
複製代碼
Redis
中的list
和Java
中的LinkedList
很像,底層都是一種鏈表結構,list
的插入和刪除操做很是快,時間複雜度爲 0(1),不像數組結構插入、刪除操做須要移動數據。
像歸像,可是redis
中的list
底層可不是一個雙向鏈表那麼簡單。
當數據量較少的時候它的底層存儲結構爲一塊連續內存,稱之爲ziplist(壓縮列表)
,它將全部的元素緊挨着一塊兒存儲,分配的是一塊連續的內存;當數據量較多的時候將會變成quicklist(快速鏈表)
結構。
可單純的鏈表也是有缺陷的,鏈表的先後指針prev
和next
會佔用較多的內存,會比較浪費空間,並且會加劇內存的碎片化。在redis 3.2以後就都改用ziplist+鏈表
的混合結構,稱之爲quicklist(快速鏈表)
。
下面具體介紹下兩種鏈表
先看一下ziplist
的數據結構,
struct ziplist<T>{
int32 zlbytes; //壓縮列表佔用字節數
int32 zltail_offset; //最後一個元素距離起始位置的偏移量,用於快速定位到最後一個節點
int16 zllength; //元素個數
T[] entries; //元素內容
int8 zlend; //結束位 0xFF
}
複製代碼
int32 zlbytes
: 壓縮列表佔用字節數 int32 zltail_offset
: 最後一個元素距離起始位置的偏移量,用於快速定位到最後一個節點
`int16 zllength`:元素個數
`T[] entries`:元素內容
`int8 zlend`:結束位 0xFF
複製代碼
壓縮列表爲了支持雙向遍歷,因此纔會有ztail_offset
這個字段,用來快速定位到最後一 個元素,而後倒着遍歷
entry
的數據結構:
struct entry{
int<var> prevlen; //前一個 entry 的長度
int<var> encoding; //元素類型編碼
optional byte[] content; //元素內容
}
複製代碼
entry
它的prevlen
字段表示前一個entry
的字節長度,當壓縮列表倒着遍歷時,須要經過這 個字段來快速定位到下一個元素的位置。
一、應用場景:
因爲list它是一個按照插入順序排序的列表,因此應用場景相對還較多的,例如:
lpop
和rpush
(或者反過來,lpush
和rpop
)能實現隊列的功能lpush
命令和lrange
命令能實現最新列表的功能,每次經過lpush
命令往列表裏插入新的元素,而後經過lrange
命令讀取最新的元素列表。二、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 表示刪除所有相同的元素
(PS: index 表明元素下標,index 能夠爲負數, index= 表示倒數第一個元素,同理 index=-2 表示倒數第二 個元素。)
lindex [key] [index] 獲取list指定下標的元素 (須要遍歷,時間複雜度爲O(n))
lrange [key] [start_index] [end_index] 獲取list 區間內的全部元素 (時間複雜度爲 O(n))
ltrim [key] [start_index] [end_index] 保留區間內的元素,其餘元素刪除(時間複雜度爲 O(n))
複製代碼
Redis
中的Hash
和 Java的HashMap
更加類似,都是數組+鏈表
的結構,當發生 hash 碰撞時將會把元素追加到鏈表上,值得注意的是在Redis
的Hash
中value
只能是字符串.
hset books java "Effective java" (integer) 1
hset books golang "concurrency in go" (integer) 1
hget books java "Effective java"
hset user age 17 (integer) 1
hincrby user age 1 #單個 key 能夠進行計數 和 incr 命令基本一致 (integer) 18
複製代碼
Hash
和String
均可以用來存儲用戶信息 ,但不一樣的是Hash
能夠對用戶信息的每一個字段單獨存儲;String
存的是用戶所有信息通過序列化後的字符串,若是想要修改某個用戶字段必須將用戶信息字符串所有查詢出來,解析成相應的用戶信息對象,修改完後在序列化成字符串存入。而 hash能夠只對某個字段修改,從而節約網絡流量,不過hash內存佔用要大於String
,這是hash
的缺點。
一、應用場景:
hset [key] [field] [value]
命令, 能夠實現以用戶Id
,商品Id
爲field
,商品數量爲value
,剛好構成了購物車的3個要素。hash
類型的(key, field, value)
的結構與對象的(對象id, 屬性, 值)
的結構類似,也能夠用來存儲對象。二、hash經常使用的操做命令:
hset [key] [field] [value] 新建字段信息
hget [key] [field] 獲取字段信息
hdel [key] [field] 刪除字段
hlen [key] 保存的字段個數
hgetall [key] 獲取指定key 字典裏的全部字段和值 (字段信息過多,會致使慢查詢 慎用:親身經歷 曾經用過這個這個指令致使線上服務故障)
hmset [key] [field1] [value1] [field2] [value2] ...... 批量建立
hincr [key] [field] 對字段值自增
hincrby [key] [field] [number] 對字段值增長number
複製代碼
Redis
中的set
和Java
中的HashSet
有些相似,它內部的鍵值對是無序的、惟一 的。它的內部實現至關於一個特殊的字典,字典中全部的value都是一個值 NULL。當集合中最後一個元素被移除以後,數據結構被自動刪除,內存被回收。
一、應用場景:
sinter
命令能夠得到A和B兩個用戶的共同好友; 2)sismember
命令能夠判斷A是不是B的好友; 3)scard
命令能夠獲取好友數量; 4) 關注時,smove
命令能夠將B從A的粉絲集合轉移到A的好友集合srandmember
命令則能夠從中隨機獲取幾個。二、set的經常使用命令:
sadd [key] [value] 向指定key的set中添加元素
smembers [key] 獲取指定key 集合中的全部元素
sismember [key] [value] 判斷集合中是否存在某個value
scard [key] 獲取集合的長度
spop [key] 彈出一個元素
srem [key] [value] 刪除指定元素
複製代碼
zset
也叫SortedSet
一方面它是個set
,保證了內部 value 的惟一性,另方面它能夠給每一個 value 賦予一個score
,表明這個value的排序權重。它的內部實現用的是一種叫做「跳躍列表
」的數據結構。
一、應用場景:
zset
能夠用作排行榜,可是和list
不一樣的是zset
它可以實現動態的排序,例如: 能夠用來存儲粉絲列表,value 值是粉絲的用戶 ID,score 是關注時間,咱們能夠對粉絲列表按關注時間進行排序。
zset
還能夠用來存儲學生的成績,value
值是學生的 ID,score
是他的考試成績。 咱們對成績按分數進行排序就能夠獲得他的名次。
二、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
複製代碼