Redis 五種基礎數據結構


字符串

和java中的ArrayList的實現類似。
動態字符串實現,采用預分配冗余空間的方式來減少內存的頻繁分配。
擴容:字符串長度小於len的時候,擴容都是加倍現有的空間。如果字符串長度超過1MB,擴容一次增加1MB,字符串的最大長度為512MB。
字符串由多個字節組成,每個字節又由8bit組成,如此可以將字符串看很很多bit的組合,這便是bitmap位圖的數據結構。

鍵值對

相當於字典的key和value,支持簡單的增刪改查操作。

set name hello
get name
exists name
del name

批量鍵值對

可以對多個字符串進行批量讀寫

mget name1 name2 name2
mset name1 aaa name2 bbb name3 ccc

過期和set命令擴展

可以對key設置過期時間,到時間會自動刪除,這個功能用來控制緩存的失效時間。

set name aaa
get name
expire name 5
setex name 5 aaa
get name

setnx name aaa # 如果name不存在就執行set創建,如果name已經存在,那么創建不成功

計數

如果value值是一個整數,還可以對它進行自增操作。自增有范圍,為signed long的最大值和最小值之間,如果超過了這個范圍,Redis會報錯。

set age 30
incr age

list(列表)

Redis的列表相當於java里面的LinkedList
列表中的每個元素都使用雙向指針,串起來可以同時支持前向后向遍歷。
當列表彈出了最后一個元素之后,該數據結構被自東刪除,內存被回收。

Redis的列表結構常用來做 異步隊列使用。 將需要延后處理的任務結構體序列化成字符串,塞進Redis列表,另一個線程從這個列表中輪詢數據進行處理。

右邊進左邊出: 隊列

隊列是先進先出的數據結構,常用消息排隊和異步邏輯處理,它會確保元素的訪問順序性。

rpush books python java golang 
llen books
lpop books

右邊進右邊出: 棧

棧是先進后出的數據結構,根隊列正好相反。拿Redis的列表數據結構來做棧棧使用的業務場景並不多見。

rpush books python java golang
rpop books

慢操作

lindex 相當於java鏈表的get(int index)方法,它需要對鏈表進行遍歷,性能隨着參數index增大而變差。
ltrim和字面上的含義不一樣,叫lretain更合適一些,因為ltrim的兩個參數start_index和end_index定義了一個區間,在這個區間內的值,ltrim要保留,區間之外的砍掉。
我們可以通過ltrim來實現一個定長的鏈表。
index可以為負數,index = -1 表示倒數第一個元素。

rpush books python java golang 
lindex books 1
lrange books 0 -1
ltrim books 1 -1

快速列表

Redis底層存儲的不是一個簡單的linkedList,而是稱為“快速鏈表” (quicklist)的一個結構。
首先在列表元素較少的情況下,會使用一塊連續的內存存儲,這個結構是ziplist,即壓縮列表。
它將所有的元素彼此緊挨着一起存儲,分配的是一塊連續的內存。
當數據量比較多的時候才會改成quicklist。因為普通的鏈表需要的附加指針空間太大,會浪費空間,還會加重內存的碎片化。
所以Redis將鏈表和ziplist結合起來組成了quicklist,也就是將多個ziplist使用雙向指針串起來使用。
quicklist既滿足了快速的插入刪除性能,又不會出現太大的空間冗余。
image

hash(字典)

Redis的字典相當於java語言里面的HashMap,內部存儲了很多鍵盤值對,實現結構上和java的HashMap是一樣的。是“數組+鏈表”的二維結構。

不同的是,Redis的字典的值只能是字符串,另外它們進行rehash的方式不一樣,因為java的HashMap在字典很大時,rehash是個耗時的操作,需要一次性全部rehash。
Redis為了追求高性能,不能阻塞服務,采用了漸進式rehash策略。

漸進式rehash會在rehash的同時,保留新舊兩個hash結構。
查詢的同時會同時查詢兩個hash結構,然后在后續的定時任務以及hash操作指令中,循序漸進的將舊hash的內容一點點遷移到新的hash結構中。當搬遷完成了,就會使用新的hash結構取而代之。

image

當ht[0]移除了最后一個元素之后,該數據結構被自動刪除,內存被回收。

hash結構也可以用來存儲用戶信息,與字符串需要一次性全部序列化整個對象不同,hash可以對用戶結構中的每個字段單獨存儲。這樣當我們需要獲取用戶信息的時候可以部分獲取。而以整個字符串的形式去保存用戶信息的話,就只能一次性全部讀取,就會浪費網絡流量。

hash也有缺點,hash結構的存儲要高於單個字符串。 所以如何去選擇需要去權衡。

hset books java "think in java"
hset books glong "cocurrency in go"
hset books python "python cookbook"
hgetall books
hlen books
hget books java
hmset books java "asdads" python "asdada" golong "asdasdasd"

同字符串一樣,hash結構中的單個子key也可以進行計數,它對應的指令是hincrby,和incr的使用方法一樣。

hset user-laoqian age 29
hincrby user-laoqian age 1

set(集合)

Redis的集合相當於Java語言里面的HashSet,它內部的鍵值對是無序的、唯一的。它的內部實現相當於一個特殊的字典,字典中所有的value都是一個值NULL。
當集合中最后一個元素被移除后,數據結構被自動刪除,內部被回收。
set 結構可以用來存儲某活動中中獎的用戶ID,因為有去重功能,可以保證同一個用戶不會中獎兩次。

sadd books python
smembers books
sismemeber books java
scard books # 獲取長度相當於count(), 
spop books 	#彈出

zset(有序列表)

zset可能是Redis提供的最有特色的數據結構。
類似於Java中的SortedSet和HashMap的結合體,一方面它是一個set,保證了內部value的唯一性,另一方面它可以給每個value賦予一個score,代表這個value的排序權重。它的內部實現用的是一種叫做“跳躍鏈表”的數據結構。
image
zset中最后一個value被移除后,數據結構被自動刪除,內部被回收。
zset可以用來存儲粉絲列表,value值是粉絲的用戶ID,score是關注時間。我們可以對粉絲列表按關注時間進行排序。
zset還可以用來存儲學生對成績,value值是學生的ID,score是它的考試成績。我們對成績按分數進行排序就可以得到他的名次。

zadd books 9.0 "think in java"
zadd books 8.9 "java cocurrency"
zrange books 0 -1 # 按score排序列出,參數區間為排名范圍
zrerange books 0 -1 #按score逆序列出,參數區間為排名范圍
zcard books # 相當於count()
zsocre books "java cocurrency" #獲取制定value的score,內部score使用的是double類型進行存儲
zrank books "java cocurrency" # 排名
zrangebyscore books 0 8.91 # 根據分支區間遍歷zset
zrem books "java cocurency" # 刪除value

跳躍鏈表

zset內部的排序功能是通過“跳躍列表”數據結構來實現的,它的結構非常特殊也比較復雜。
因為zset要支持隨機的插入和刪除,所以不使用數組來表示。
需要一個鏈表按照score值進行排序。這意味着當有新的元素需要插入時,要定位到特定位置的插入點,這樣才可以繼續保證李娜表是有序的。通常我們使用二分查找來找到插入點,但是二分查找的對象必須是數組,只有數組才可以支持快速位置定位,鏈表是不能做到的,應該如何?

跳躍列表類似一種層級制:每個團隊一個組長,每個部門從很多組長中選出一個作為部長。
最下面的元素會串起來。然后每隔幾個元素挑選出一個代表,再將這幾個代表使用另一級指針串起來。然后再這些代表里挑出二級代表,再串起來。
“跳躍列表”之所以“跳躍”,是因為內部的元素可能“身兼數職”,同時處於很多個層級中,可以快速再不同層級之間進行“跳躍”。
image
定位插入點時,先在頂層進行定位,然后下潛到下一級定位,一直下潛到最底層找到合適的位置,將新元素插入。
跳躍鏈表采取一個隨機策略來決定新元素可以兼職到第幾層。

位於L0的概略為100%,兼職L1的概率為50%,L2的概率為25%。
只有極少數的元素可以深入到頂層。列表中的元素越多,能夠深入的層次就越深,元素進入到頂層的可能性就會越大。

過期時間

Redis所有的數據結構都可以設置過期時間,時間到了,Redis會自動刪除相應的對象。過期是以對象為單位的。
如果一個字符串已經設置了過期時間,然后調用set方法修改了它,那么它的過期時間會消失。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM