redis數(shù)據(jù)庫泳桦,完全基于內存景用,且其內部數(shù)據(jù)類型豐富盗迟,性能也非常出色
redis中的集合插入分zet和set兩種坤邪,zset是有序的,而set是無序的罚缕,由于redis中的集合都是以hashcode的形式實現(xiàn)的艇纺,所以他添加,刪除邮弹,查找的時間復雜度和空間復雜度都是o(1)黔衡,集合中允許插入的最大數(shù)據(jù)量為(2^32-1)的數(shù)據(jù)量, Redis 有序集合和集合一樣也是 string 類型元素的集合,且不允許重復的成員腌乡。不同的是每個元素都會關聯(lián)一個 double 類型的分數(shù)盟劫。redis 正是通過分數(shù)來為集合中的成員進行從小到大的排序。有序集合的成員是唯一的,但分數(shù)(score)卻可以重復与纽。集合中最大的成員數(shù)為(2^32 - 1)侣签,而有序集合的添加和刪除塘装,會基于插入項的大小進行判定,如果是大于64字節(jié)硝岗,就同時使用了hash和skiplist兩種設計實現(xiàn)氢哮,這樣會大大優(yōu)化查找的性能,添加和刪除都需要修改skiplist型檀,所以復雜度為O(log(n)),而如果僅僅是查找元素的話可以直接使用hash听盖,其復雜度為O(1) 胀溺,其他的range操作復雜度一般為O(log(n)),如果是小于64字節(jié)的且元素數(shù)量小于128個的(可通過redis的配置項(zset-max-ziplist-entries 和 zset-max-ziplist-value )進行修改)皆看,則使用了ziplist編碼仓坞,否則會自動使用skiplist編碼
ziplist:
ziplist 編碼的 Zset 使用緊挨在一起的壓縮列表節(jié)點來保存。ziplist 內的集合元素按 score 從小到大排序腰吟,其實質是一個雙向鏈表无埃。雖然元素是按 score 有序排序的, 但對 ziplist 的節(jié)點指針只能線性地移動毛雇,所以在 REDIS_ENCODING_ZIPLIST 編碼的 Zset 中嫉称, 查找某個給定元素的復雜度O(N)
skiplist:
skiplist 編碼的 Zset 底層為一個被稱為 zset 的結構體答姥,這個結構體中包含一個字典和一個跳躍表辖众。
跳表(skipList)是一種隨機化的數(shù)據(jù)結構冗荸,基于并聯(lián)的鏈表刷钢,實現(xiàn)簡單泽铛,插入直秆、刪除邑狸、查找的復雜度均為O(logN)诡必。簡單說來跳表也是鏈表的一種蒿赢,只不過它在鏈表的基礎上增加了跳躍功能润樱,正是這個跳躍的功能,使得在查找元素時羡棵,跳表能夠提供O(logN)的時間復雜度
跳表除了第一層鏈表中的具體節(jié)點之外壹若,還存在若干層稀疏的鏈表,這些鏈表里面的指針故意跳過了一些節(jié)點(而且越高層的鏈表跳過的節(jié)點越多)晾腔。這就使得我們在查找數(shù)據(jù)的時候能夠先在高層的鏈表中進行查找舌稀,然后逐層降低,最終降到第1層鏈表來精確地確定數(shù)據(jù)位置灼擂,這樣提升了查詢的效率
- 跳表中的每個節(jié)點都會有若干個指針壁查,每個節(jié)點肯定都有第1層指針(每個節(jié)點都在第1層鏈表里)
- 如果一個節(jié)點有第i層(i>=1)指針(即節(jié)點已經(jīng)在第1層到第i層鏈表中),那么它有第(i+1)層指針的概率為p
- 節(jié)點最大的層數(shù)不允許超過一個最大值剔应,記為MaxLevel
在redis中這兩個值是允許設置的睡腿,默認概率為1/4语御,節(jié)點層數(shù)最大值為32
SKIPLIST和平衡樹還有hashcode的對比:
- skiplist和各種平衡樹(如AVL、紅黑樹等)的元素是有序排列的席怪,而哈希表不是有序的应闯。因此,在哈希表上只能做單個key的查找挂捻,不適宜做范圍查找碉纺。所謂范圍查找,指的是查找那些大小在指定的兩個值之間的所有節(jié)點
- skiplist在做范圍查找的時候刻撒,平衡樹比skiplist操作要復雜骨田。在平衡樹上,我們找到指定范圍的小值之后声怔,還需要以中序遍歷的順序繼續(xù)尋找其它不超過大值的節(jié)點态贤。如果不對平衡樹進行一定的改造,這里的中序遍歷并不容易實現(xiàn)醋火。而在skiplist上進行范圍查找就非常簡單悠汽,只需要在找到小值之后,對第1層鏈表進行若干步的遍歷就可以實現(xiàn)芥驳,從算法邏輯上skiplist比平衡樹要簡單得多
- 平衡樹的插入和刪除操作可能引發(fā)子樹的調整柿冲,邏輯復雜,而skiplist的插入和刪除只需要修改相鄰節(jié)點的指針晚树,操作簡單又快速
- 從內存占用上來說姻采,skiplist比平衡樹更靈活一些。一般來說爵憎,平衡樹每個節(jié)點包含2個指針(分別指向左右子樹)慨亲,而skiplist每個節(jié)點包含的指針數(shù)目平均為1/(1-p),具體取決于參數(shù)p的大小宝鼓。如果像Redis里的實現(xiàn)一樣刑棵,取p=1/4,那么平均每個節(jié)點包含1.33個指針愚铡,比平衡樹更有優(yōu)勢
- 查找單個key蛉签,skiplist和平衡樹的時間復雜度都為O(log n),大體相當沥寥;而哈希表在保持較低的哈希值沖突概率的前提下碍舍,查找時間復雜度接近O(1),性能更高一些邑雅。所以我們平常使用的各種Map或dictionary結構片橡,大都是基于哈希表實現(xiàn)的
zsetOps.removeRangeByScore
移除有序集 key 中,所有 score 值介于 min 和 max 之間(包括等于 min 或 max )的成員淮野。 min 和 max 可以是 -inf 和 +inf 捧书,這樣一來吹泡,你就可以在不知道有序集的最低和最高 score 值的情況下,使用 ZRANGEBYSCORE 這類命令经瓷。 默認情況下爆哑,區(qū)間的取值使用閉區(qū)間 (小于等于或大于等于),你也可以通過給參數(shù)前增加 "(" 符號來使用可選的開區(qū)間 (小于或大于)舆吮。例如"(5"代表不包括5
zsetOps.intersectAndStore(key1,key2,key3)
取key1和key3的交集放入key2中