redis數(shù)據(jù)庫(kù),完全基于內(nèi)存,且其內(nèi)部數(shù)據(jù)類型豐富,性能也非常出色
redis中的集合插入分zet和set兩種,zset是有序的,而set是無(wú)序的,由于redis中的集合都是以hashcode的形式實(shí)現(xiàn)的,所以他添加,刪除,查找的時(shí)間復(fù)雜度和空間復(fù)雜度都是o(1),集合中允許插入的最大數(shù)據(jù)量為(2^32-1)的數(shù)據(jù)量, Redis 有序集合和集合一樣也是 string 類型元素的集合,且不允許重復(fù)的成員。不同的是每個(gè)元素都會(huì)關(guān)聯(lián)一個(gè) double 類型的分?jǐn)?shù)。redis 正是通過分?jǐn)?shù)來(lái)為集合中的成員進(jìn)行從小到大的排序。有序集合的成員是唯一的,但分?jǐn)?shù)(score)卻可以重復(fù)。集合中最大的成員數(shù)為(2^32 - 1),而有序集合的添加和刪除,會(huì)基于插入項(xiàng)的大小進(jìn)行判定,如果是大于64字節(jié),就同時(shí)使用了hash和skiplist兩種設(shè)計(jì)實(shí)現(xiàn),這樣會(huì)大大優(yōu)化查找的性能,添加和刪除都需要修改skiplist,所以復(fù)雜度為O(log(n)),而如果僅僅是查找元素的話可以直接使用hash,其復(fù)雜度為O(1) ,其他的range操作復(fù)雜度一般為O(log(n)),如果是小于64字節(jié)的且元素?cái)?shù)量小于128個(gè)的(可通過redis的配置項(xiàng)(zset-max-ziplist-entries 和 zset-max-ziplist-value )進(jìn)行修改),則使用了ziplist編碼,否則會(huì)自動(dòng)使用skiplist編碼
ziplist:

ziplist 編碼的 Zset 使用緊挨在一起的壓縮列表節(jié)點(diǎn)來(lái)保存。ziplist 內(nèi)的集合元素按 score 從小到大排序,其實(shí)質(zhì)是一個(gè)雙向鏈表。雖然元素是按 score 有序排序的, 但對(duì) ziplist 的節(jié)點(diǎn)指針只能線性地移動(dòng),所以在 REDIS_ENCODING_ZIPLIST 編碼的 Zset 中, 查找某個(gè)給定元素的復(fù)雜度O(N)
skiplist:
skiplist 編碼的 Zset 底層為一個(gè)被稱為 zset 的結(jié)構(gòu)體,這個(gè)結(jié)構(gòu)體中包含一個(gè)字典和一個(gè)跳躍表。
跳表(skipList)是一種隨機(jī)化的數(shù)據(jù)結(jié)構(gòu),基于并聯(lián)的鏈表,實(shí)現(xiàn)簡(jiǎn)單,插入、刪除、查找的復(fù)雜度均為O(logN)。簡(jiǎn)單說來(lái)跳表也是鏈表的一種,只不過它在鏈表的基礎(chǔ)上增加了跳躍功能,正是這個(gè)跳躍的功能,使得在查找元素時(shí),跳表能夠提供O(logN)的時(shí)間復(fù)雜度
跳表除了第一層鏈表中的具體節(jié)點(diǎn)之外,還存在若干層稀疏的鏈表,這些鏈表里面的指針故意跳過了一些節(jié)點(diǎn)(而且越高層的鏈表跳過的節(jié)點(diǎn)越多)。這就使得我們?cè)诓檎覕?shù)據(jù)的時(shí)候能夠先在高層的鏈表中進(jìn)行查找,然后逐層降低,最終降到第1層鏈表來(lái)精確地確定數(shù)據(jù)位置,這樣提升了查詢的效率
- 跳表中的每個(gè)節(jié)點(diǎn)都會(huì)有若干個(gè)指針,每個(gè)節(jié)點(diǎn)肯定都有第1層指針(每個(gè)節(jié)點(diǎn)都在第1層鏈表里)
- 如果一個(gè)節(jié)點(diǎn)有第i層(i>=1)指針(即節(jié)點(diǎn)已經(jīng)在第1層到第i層鏈表中),那么它有第(i+1)層指針的概率為p
- 節(jié)點(diǎn)最大的層數(shù)不允許超過一個(gè)最大值,記為MaxLevel
在redis中這兩個(gè)值是允許設(shè)置的,默認(rèn)概率為1/4,節(jié)點(diǎn)層數(shù)最大值為32
SKIPLIST和平衡樹還有hashcode的對(duì)比:
- skiplist和各種平衡樹(如AVL、紅黑樹等)的元素是有序排列的,而哈希表不是有序的。因此,在哈希表上只能做單個(gè)key的查找,不適宜做范圍查找。所謂范圍查找,指的是查找那些大小在指定的兩個(gè)值之間的所有節(jié)點(diǎn)
- skiplist在做范圍查找的時(shí)候,平衡樹比skiplist操作要復(fù)雜。在平衡樹上,我們找到指定范圍的小值之后,還需要以中序遍歷的順序繼續(xù)尋找其它不超過大值的節(jié)點(diǎn)。如果不對(duì)平衡樹進(jìn)行一定的改造,這里的中序遍歷并不容易實(shí)現(xiàn)。而在skiplist上進(jìn)行范圍查找就非常簡(jiǎn)單,只需要在找到小值之后,對(duì)第1層鏈表進(jìn)行若干步的遍歷就可以實(shí)現(xiàn),從算法邏輯上skiplist比平衡樹要簡(jiǎn)單得多
- 平衡樹的插入和刪除操作可能引發(fā)子樹的調(diào)整,邏輯復(fù)雜,而skiplist的插入和刪除只需要修改相鄰節(jié)點(diǎn)的指針,操作簡(jiǎn)單又快速
- 從內(nèi)存占用上來(lái)說,skiplist比平衡樹更靈活一些。一般來(lái)說,平衡樹每個(gè)節(jié)點(diǎn)包含2個(gè)指針(分別指向左右子樹),而skiplist每個(gè)節(jié)點(diǎn)包含的指針數(shù)目平均為1/(1-p),具體取決于參數(shù)p的大小。如果像Redis里的實(shí)現(xiàn)一樣,取p=1/4,那么平均每個(gè)節(jié)點(diǎn)包含1.33個(gè)指針,比平衡樹更有優(yōu)勢(shì)
- 查找單個(gè)key,skiplist和平衡樹的時(shí)間復(fù)雜度都為O(log n),大體相當(dāng);而哈希表在保持較低的哈希值沖突概率的前提下,查找時(shí)間復(fù)雜度接近O(1),性能更高一些。所以我們平常使用的各種Map或dictionary結(jié)構(gòu),大都是基于哈希表實(shí)現(xiàn)的
zsetOps.removeRangeByScore
移除有序集 key 中,所有 score 值介于 min 和 max 之間(包括等于 min 或 max )的成員。 min 和 max 可以是 -inf 和 +inf ,這樣一來(lái),你就可以在不知道有序集的最低和最高 score 值的情況下,使用 ZRANGEBYSCORE 這類命令。 默認(rèn)情況下,區(qū)間的取值使用閉區(qū)間 (小于等于或大于等于),你也可以通過給參數(shù)前增加 "(" 符號(hào)來(lái)使用可選的開區(qū)間 (小于或大于)。例如"(5"代表不包括5
zsetOps.intersectAndStore(key1,key2,key3)
取key1和key3的交集放入key2中