精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

我從未見過的牛逼解說方式!Redis五種數(shù)據(jù)結(jié)構(gòu),看一遍就懂了

新聞 數(shù)據(jù)庫 Redis
Redis是基于c語言編寫的開源非關(guān)系型內(nèi)存數(shù)據(jù)庫,可以用作數(shù)據(jù)庫、緩存、消息中間件,這么優(yōu)秀的東西客定要一點一點的吃透它。

 我從未見過的牛逼解說方式!Redis五種數(shù)據(jù)結(jié)構(gòu),看一遍就懂了

前言

Redis是基于c語言編寫的開源非關(guān)系型內(nèi)存數(shù)據(jù)庫,可以用作數(shù)據(jù)庫、緩存、消息中間件,這么優(yōu)秀的東西客定要一點一點的吃透它。

關(guān)于Redis的文章之前也寫過兩篇,閱讀量和讀者的反映都還可以,其中第一篇是Redis的緩存三大問題[。

第二篇是Redis的內(nèi)存管理和淘汰策略[]。

這是關(guān)于Redis的第三篇文章,主要講解Redis的五種數(shù)據(jù)結(jié)構(gòu)詳解,包括這五種的數(shù)據(jù)結(jié)構(gòu)的底層原理實現(xiàn)。

理論肯定是要用于實踐的,因此最重要的還是實戰(zhàn)部分,也就是這里還會講解五種數(shù)據(jù)結(jié)構(gòu)的應用場景。

話不多說,我們直接進入主題,很多人都知道Redis的五種數(shù)據(jù)結(jié)構(gòu)包括以下五種:

  1. String:字符串類型
  2. List:列表類型
  3. Set:無序集合類型
  4. ZSet:有序集合類型
  5. Hash:哈希表類型

但是作為一名優(yōu)秀的程序員可能不能只停留在只會用著五種類型進行crud工作,還是得深入了解這五種數(shù)據(jù)結(jié)構(gòu)的底層原理。

Redis核心對象

在Redis中有一個「核心的對象」叫做redisObject ,是用來表示所有的key和value的,用redisObject結(jié)構(gòu)體來表示String、Hash、List、Set、ZSet五種數(shù)據(jù)類型。

redisObject的源代碼在redis.h中,使用c語言寫的,感興趣的可以自行查看,關(guān)于redisObject我這里畫了一張圖,表示redisObject的結(jié)構(gòu)如下所示:

我從未見過的牛逼解說方式!Redis五種數(shù)據(jù)結(jié)構(gòu),看一遍就懂了

在redisObject中「type表示屬于哪種數(shù)據(jù)類型,encoding表示該數(shù)據(jù)的存儲方式」,也就是底層的實現(xiàn)的該數(shù)據(jù)類型的數(shù)據(jù)結(jié)構(gòu)。因此這篇文章具體介紹的也是encoding對應的部分。

那么encoding中的存儲類型又分別表示什么意思呢?具體數(shù)據(jù)類型所表示的含義,如下圖所示:

我從未見過的牛逼解說方式!Redis五種數(shù)據(jù)結(jié)構(gòu),看一遍就懂了

可能看完這圖,還是覺得一臉懵。不慌,會進行五種數(shù)據(jù)結(jié)構(gòu)的詳細介紹,這張圖只是讓你找到每種中數(shù)據(jù)結(jié)構(gòu)對應的儲存類型有哪些,大概腦子里有個印象。

舉一個簡單的例子,你在Redis中設(shè)置一個字符串key 234,然后查看這個字符串的存儲類型就會看到為int類型,非整數(shù)型的使用的是embstr儲存類型,具體操作如下圖所示:

我從未見過的牛逼解說方式!Redis五種數(shù)據(jù)結(jié)構(gòu),看一遍就懂了

String類型

String是Redis最基本的數(shù)據(jù)類型,上面的簡介中也說到Redis是用c語言開發(fā)的。但是Redis中的字符串和c語言中的字符串類型卻是有明顯的區(qū)別。

String類型的數(shù)據(jù)結(jié)構(gòu)存儲方式有三種int、raw、embstr。那么這三種存儲方式有什么區(qū)別呢?

int

Redis中規(guī)定假如存儲的是「整數(shù)型值」,比如set num 123這樣的類型,就會使用 int的存儲方式進行存儲,在redisObject的「ptr屬性」中就會保存該值。

我從未見過的牛逼解說方式!Redis五種數(shù)據(jù)結(jié)構(gòu),看一遍就懂了

SDS

假如存儲的「字符串是一個字符串值并且長度大于32個字節(jié)」就會使用SDS(simple dynamic string)方式進行存儲,并且encoding設(shè)置為raw;若是「字符串長度小于等于32個字節(jié)」就會將encoding改為embstr來保存字符串。

SDS稱為「簡單動態(tài)字符串」,對于SDS中的定義在Redis的源碼中有的三個屬性int len、int free、char buf[]。

len保存了字符串的長度,free表示buf數(shù)組中未使用的字節(jié)數(shù)量,buf數(shù)組則是保存字符串的每一個字符元素。

因此當你在Redsi中存儲一個字符串Hello時,根據(jù)Redis的源代碼的描述可以畫出SDS的形式的redisObject結(jié)構(gòu)圖如下圖所示:

我從未見過的牛逼解說方式!Redis五種數(shù)據(jù)結(jié)構(gòu),看一遍就懂了

SDS與c語言字符串對比

Redis使用SDS作為存儲字符串的類型肯定是有自己的優(yōu)勢,SDS與c語言的字符串相比,SDS對c語言的字符串做了自己的設(shè)計和優(yōu)化,具體優(yōu)勢有以下幾點:

(1)c語言中的字符串并不會記錄自己的長度,因此「每次獲取字符串的長度都會遍歷得到,時間的復雜度是O(n)」,而Redis中獲取字符串只要讀取len的值就可,時間復雜度變?yōu)镺(1)。

(2)「c語言」中兩個字符串拼接,若是沒有分配足夠長度的內(nèi)存空間就「會出現(xiàn)緩沖區(qū)溢出的情況」;而「SDS」會先根據(jù)len屬性判斷空間是否滿足要求,若是空間不夠,就會進行相應的空間擴展,所以「不會出現(xiàn)緩沖區(qū)溢出的情況」

(3)SDS還提供「空間預分配」「惰性空間釋放」兩種策略。在為字符串分配空間時,分配的空間比實際要多,這樣就能「減少連續(xù)的執(zhí)行字符串增長帶來內(nèi)存重新分配的次數(shù)」

當字符串被縮短的時候,SDS也不會立即回收不適用的空間,而是通過free屬性將不使用的空間記錄下來,等后面使用的時候再釋放。

具體的空間預分配原則是:「當修改字符串后的長度len小于1MB,就會預分配和len一樣長度的空間,即len=free;若是len大于1MB,free分配的空間大小就為1MB」

(4)SDS是二進制安全的,除了可以儲存字符串以外還可以儲存二進制文件(如圖片、音頻,視頻等文件的二進制數(shù)據(jù));而c語言中的字符串是以空字符串作為結(jié)束符,一些圖片中含有結(jié)束符,因此不是二進制安全的。

為了方便易懂,做了一個c語言的字符串和SDS進行對比的表格,如下所示:

c語言字符串 SDS 獲取長度的時間復雜度為O(n) 獲取長度的時間復雜度為O(1) 不是二進制安全的 是二進制安全的 只能保存字符串 還可以保存二進制數(shù)據(jù) n次增長字符串必然會帶來n次的內(nèi)存分配 n次增長字符串內(nèi)存分配的次數(shù)<=n

String類型應用

說到這里我相信很多人可以說已經(jīng)精通Redis的String類型了,但是純理論的精通,理論還是得應用實踐,上面說到String可以用來存儲圖片,現(xiàn)在就以圖片存儲作為案例實現(xiàn)。

(1)首先要把上傳得圖片進行編碼,這里寫了一個工具類把圖片處理成了Base64得編碼形式,具體得實現(xiàn)代碼如下:

  1. /**  
  2. * 將圖片內(nèi)容處理成Base64編碼格式  
  3. * @param file  
  4. * @return  
  5. */  
  6. public static String encodeImg(MultipartFile file) {  
  7. byte[] imgBytes = null;  
  8. try {  
  9. imgBytes = file.getBytes();  
  10. catch (IOException e) {  
  11. e.printStackTrace();  
  12. }  
  13. BASE64Encoder encoder = new BASE64Encoder();  
  14. return imgBytes==null?null:encoder.encode(imgBytes );  

復制代碼 

(2)第二步就是把處理后的圖片字符串格式存儲進Redis中,實現(xiàn)得代碼如下所示: 

  1. /**  
  2. * Redis存儲圖片  
  3. * @param file  
  4. * @return  
  5. */  
  6. public void uploadImageServiceImpl(MultipartFile image) {  
  7. String imgId = UUID.randomUUID().toString();  
  8. String imgStr= ImageUtils.encodeImg(image);  
  9. redisUtils.set(imgId , imgStr);  
  10. // 后續(xù)操作可以把imgId存進數(shù)據(jù)庫對應的字段,如果需要從redis中取出,只要獲取到這個字段后從redis中取出即可。  
  11. }  

這樣就是實現(xiàn)了圖片得二進制存儲,當然String類型得數(shù)據(jù)結(jié)構(gòu)得應用也還有常規(guī)計數(shù):「統(tǒng)計微博數(shù)、統(tǒng)計粉絲數(shù)」等。

Hash類型

Hash對象的實現(xiàn)方式有兩種分別是ziplist、hashtable,其中hashtable的存儲方式key是String類型的,value也是以key value的形式進行存儲。

字典類型的底層就是hashtable實現(xiàn)的,明白了字典的底層實現(xiàn)原理也就是明白了hashtable的實現(xiàn)原理,hashtable的實現(xiàn)原理可以于HashMap的是底層原理相類比。

字典

兩者在新增時都會通過key計算出數(shù)組下標,不同的是計算法方式不同,HashMap中是以hash函數(shù)的方式,而hashtable中計算出hash值后,還要通過sizemask 屬性和哈希值再次得到數(shù)組下標。

我們知道hash表最大的問題就是hash沖突,為了解決hash沖突,假如hashtable中不同的key通過計算得到同一個index,就會形成單向鏈表(「鏈地址法」),如下圖所示:

我從未見過的牛逼解說方式!Redis五種數(shù)據(jù)結(jié)構(gòu),看一遍就懂了

rehash

在字典的底層實現(xiàn)中,value對象以每一個dictEntry的對象進行存儲,當hash表中的存放的鍵值對不斷的增加或者減少時,需要對hash表進行一個擴展或者收縮。

這里就會和HashMap一樣也會就進行rehash操作,進行重新散列排布。從上圖中可以看到有ht[0]和ht[1]兩個對象,先來看看對象中的屬性是干嘛用的。

在hash表結(jié)構(gòu)定義中有四個屬性分別是dictEntry **table、unsigned long size、unsigned long sizemask、unsigned long used,分別表示的含義就是「哈希表數(shù)組、hash表大小、用于計算索引值,總是等于size-1、hash表中已有的節(jié)點數(shù)」

ht[0]是用來最開始存儲數(shù)據(jù)的,當要進行擴展或者收縮時,ht[0]的大小就決定了ht[1]的大小,ht[0]中的所有的鍵值對就會重新散列到ht[1]中。

擴展操作:ht[1]擴展的大小是比當前 ht[0].used 值的二倍大的第一個 2 的整數(shù)冪;收縮操作:ht[0].used 的第一個大于等于的 2 的整數(shù)冪。

當ht[0]上的所有的鍵值對都rehash到ht[1]中,會重新計算所有的數(shù)組下標值,當數(shù)據(jù)遷移完后ht[0]就會被釋放,然后將ht[1]改為ht[0],并新創(chuàng)建ht[1],為下一次的擴展和收縮做準備。

漸進式rehash

假如在rehash的過程中數(shù)據(jù)量非常大,Redis不是一次性把全部數(shù)據(jù)rehash成功,這樣會導致Redis對外服務停止,Redis內(nèi)部為了處理這種情況采用「漸進式的rehash」

Redis將所有的rehash的操作分成多步進行,直到都rehash完成,具體的實現(xiàn)與對象中的rehashindex屬性相關(guān),「若是rehashindex 表示為-1表示沒有rehash操作」

當rehash操作開始時會將該值改成0,在漸進式rehash的過程「更新、刪除、查詢會在ht[0]和ht[1]中都進行」,比如更新一個值先更新ht[0],然后再更新ht[1]。

而新增操作直接就新增到ht[1]表中,ht[0]不會新增任何的數(shù)據(jù),這樣保證「ht[0]只減不增,直到最后的某一個時刻變成空表」,這樣rehash操作完成。

上面就是字典的底層hashtable的實現(xiàn)原理,說完了hashtable的實現(xiàn)原理,我們再來看看Hash數(shù)據(jù)結(jié)構(gòu)的兩一種存儲方式「ziplist(壓縮列表)」

ziplist

壓縮列表(ziplist)是一組連續(xù)內(nèi)存塊組成的順序的數(shù)據(jù)結(jié)構(gòu),壓縮列表能夠節(jié)省空間,壓縮列表中使用多個節(jié)點來存儲數(shù)據(jù)。

壓縮列表是列表鍵和哈希鍵底層實現(xiàn)的原理之一,「壓縮列表并不是以某種壓縮算法進行壓縮存儲數(shù)據(jù),而是它表示一組連續(xù)的內(nèi)存空間的使用,節(jié)省空間」,壓縮列表的內(nèi)存結(jié)構(gòu)圖如下:

我從未見過的牛逼解說方式!Redis五種數(shù)據(jù)結(jié)構(gòu),看一遍就懂了

壓縮列表中每一個節(jié)點表示的含義如下所示:

zlbytes:4個字節(jié)的大小,記錄壓縮列表占用內(nèi)存的字節(jié)數(shù)。

  1. zltail:4個字節(jié)大小,記錄表尾節(jié)點距離起始地址的偏移量,用于快速定位到尾節(jié)點的地址。
  2. zllen:2個字節(jié)的大小,記錄壓縮列表中的節(jié)點數(shù)。
  3. entry:表示列表中的每一個節(jié)點。
  4. zlend:表示壓縮列表的特殊結(jié)束符號'0xFF'。

再壓縮列表中每一個entry節(jié)點又有三部分組成,包括previous_entry_ength、encoding、content。

  1. previous_entry_ength表示前一個節(jié)點entry的長度,可用于計算前一個節(jié)點的其實地址,因為他們的地址是連續(xù)的。
  2. encoding:這里保存的是content的內(nèi)容類型和長度。
  3. content:content保存的是每一個節(jié)點的內(nèi)容。
我從未見過的牛逼解說方式!Redis五種數(shù)據(jù)結(jié)構(gòu),看一遍就懂了

說到這里相信大家已經(jīng)都hash這種數(shù)據(jù)結(jié)構(gòu)已經(jīng)非常了解,若是第一次接觸Redis五種基本數(shù)據(jù)結(jié)構(gòu)的底層實現(xiàn)的話,建議多看幾遍,下面來說一說hash的應用場景。

應用場景

哈希表相對于String類型存儲信息更加直觀,擦歐總更加方便,經(jīng)常會用來做用戶數(shù)據(jù)的管理,存儲用戶的信息。

hash也可以用作高并發(fā)場景下使用Redis生成唯一的id。下面我們就以這兩種場景用作案例編碼實現(xiàn)。

存儲用戶數(shù)據(jù)

第一個場景比如我們要儲存用戶信息,一般使用用戶的ID作為key值,保持唯一性,用戶的其他信息(地址、年齡、生日、電話號碼等)作為value值存儲。

若是傳統(tǒng)的實現(xiàn)就是將用戶的信息封裝成為一個對象,通過序列化存儲數(shù)據(jù),當需要獲取用戶信息的時候,就會通過反序列化得到用戶信息。

我從未見過的牛逼解說方式!Redis五種數(shù)據(jù)結(jié)構(gòu),看一遍就懂了

但是這樣必然會造成序列化和反序列化的性能的開銷,并且若是只修改其中的一個屬性值,就需要把整個對象序列化出來,操作的動作太大,造成不必要的性能開銷。

若是使用Redis的hash來存儲用戶數(shù)據(jù),就會將原來的value值又看成了一個k v形式的存儲容器,這樣就不會帶來序列化的性能開銷的問題。

我從未見過的牛逼解說方式!Redis五種數(shù)據(jù)結(jié)構(gòu),看一遍就懂了

分布式生成唯一ID

第二個場景就是生成分布式的唯一ID,這個場景下就是把redis封裝成了一個工具類進行實現(xiàn),實現(xiàn)的代碼如下: 

  1. // offset表示的是id的遞增梯度值  
  2. public Long getId(String key,String hashKey,Long offset) throws BusinessException{  
  3. try {  
  4. if (null == offset) {  
  5. offset=1L;  
  6. }  
  7. // 生成唯一id  
  8. return redisUtil.increment(key, hashKey, offset);  
  9. catch (Exception e) {  
  10. //若是出現(xiàn)異常就是用uuid來生成唯一的id值  
  11. int randNo=UUID.randomUUID().toString().hashCode();  
  12. if (randNo < 0) {  
  13. randNo=-randNo;  
  14. }  
  15. return Long.valueOf(String.format("%16d", randNo));  
  16. }  
  17. }  

List類型

Redis中的列表在3.2之前的版本是使用ziplist和linkedlist進行實現(xiàn)的。在3.2之后的版本就是引入了quicklist。

ziplist壓縮列表上面已經(jīng)講過了,我們來看看linkedlist和quicklist的結(jié)構(gòu)是怎么樣的。

linkedlist是一個雙向鏈表,他和普通的鏈表一樣都是由指向前后節(jié)點的指針。插入、修改、更新的時間復雜度尾O(1),但是查詢的時間復雜度確實O(n)。

linkedlist和quicklist的底層實現(xiàn)是采用鏈表進行實現(xiàn),在c語言中并沒有內(nèi)置的鏈表這種數(shù)據(jù)結(jié)構(gòu),Redis實現(xiàn)了自己的鏈表結(jié)構(gòu)。

我從未見過的牛逼解說方式!Redis五種數(shù)據(jù)結(jié)構(gòu),看一遍就懂了

Redis中鏈表的特性:

  1. 每一個節(jié)點都有指向前一個節(jié)點和后一個節(jié)點的指針。
  2. 頭節(jié)點和尾節(jié)點的prev和next指針指向為null,所以鏈表是無環(huán)的。
  3. 鏈表有自己長度的信息,獲取長度的時間復雜度為O(1)。

Redis中List的實現(xiàn)比較簡單,下面我們就來看看它的應用場景。

應用場景

Redis中的列表可以實現(xiàn)「阻塞隊列」,結(jié)合lpush和brpop命令就可以實現(xiàn)。生產(chǎn)者使用lupsh從列表的左側(cè)插入元素,消費者使用brpop命令從隊列的右側(cè)獲取元素進行消費。

(1)首先配置redis的配置,為了方便我就直接放在application.yml配置文件中,實際中可以把redis的配置文件放在一個redis.properties文件單獨放置,具體配置如下:

  1. spring  
  2. redis:  
  3. host: 127.0.0.1  
  4. port: 6379  
  5. password: user  
  6. timeout: 0  
  7. database: 2  
  8. pool:  
  9. max-active: 100  
  10. max-idle: 10  
  11. min-idle: 0  
  12. max-wait: 100000  

(2)第二步創(chuàng)建redis的配置類,叫做RedisConfig,并標注上@Configuration注解,表明他是一個配置類。

  1. @Configuration  
  2. public class RedisConfiguration {  
  3. @Value("{spring.redis.port}")  
  4. private int port;  
  5. @Value("{spring.redis.pool.max-active}")  
  6. private int maxActive;  
  7. @Value("{spring.redis.pool.min-idle}")  
  8. private int minIdle;  
  9. @Value("{spring.redis.database}")  
  10. private int database;  
  11. @Value("${spring.redis.timeout}")  
  12. private int timeout;  
  13. @Bean  
  14. public JedisPoolConfig getRedisConfiguration(){  
  15. JedisPoolConfig jedisPoolConfig= new JedisPoolConfig();  
  16. jedisPoolConfig.setMaxTotal(maxActive);  
  17. jedisPoolConfig.setMaxIdle(maxIdle);  
  18. jedisPoolConfig.setMinIdle(minIdle);  
  19. jedisPoolConfig.setMaxWaitMillis(maxWait);  
  20. return jedisPoolConfig;  
  21. }  
  22. @Bean  
  23. public JedisConnectionFactory getConnectionFactory() {  
  24. JedisConnectionFactory factory = new JedisConnectionFactory();  
  25. factory.setHostName(host);  
  26. factory.setPort(port);  
  27. factory.setPassword(password);  
  28. factory.setDatabase(database);  
  29. JedisPoolConfig jedisPoolConfig= getRedisConfiguration();  
  30. factory.setPoolConfig(jedisPoolConfig);  
  31. return factory;  
  32. }  
  33.  
  34. @Bean  
  35. public RedisTemplate<?, ?> getRedisTemplate() {  
  36. JedisConnectionFactory factory = getConnectionFactory();  
  37. RedisTemplate<?, ?> redisTemplate = new StringRedisTemplate(factory);  
  38. return redisTemplate;  
  39. }  
  40. }  

(3)第三步就是創(chuàng)建Redis的工具類RedisUtil,自從學了面向?qū)ο蠛螅拖矚g把一些通用的東西拆成工具類,好像一個一個零件,需要的時候,就把它組裝起來。

  1. @Component  
  2. public class RedisUtil {  
  3. @Autowired  
  4. private RedisTemplate<String, Object> redisTemplate;  
  5. /**  
  6.  
  7. 存消息到消息隊列中  
  8. @param key 鍵  
  9. @param value 值  
  10. @return  
  11. */  
  12. public boolean lPushMessage(String key, Object value) {  
  13. try {  
  14. redisTemplate.opsForList().leftPush(key, value);  
  15. return true;  
  16. catch (Exception e) {  
  17. e.printStackTrace();  
  18. return false;  
  19. }  
  20. }  
  21.  
  22. /**  
  23.  
  24. 從消息隊列中彈出消息 - <rpop:非阻塞式>  
  25. @param key 鍵  
  26. @return  
  27. */  
  28. public Object rPopMessage(String key) {  
  29. try {  
  30. return redisTemplate.opsForList().rightPop(key);  
  31. catch (Exception e) {  
  32. e.printStackTrace();  
  33. return null;  
  34. }  
  35. }  
  36.  
  37. /**  
  38.  
  39. 查看消息  
  40. @param key 鍵  
  41. @param start 開始  
  42. @param end 結(jié)束 0 到 -1代表所有值  
  43. 復制代碼@return  
  44. */  
  45. public List<Object> getMessage(String key, long start, long end) {  
  46. try {  
  47. return redisTemplate.opsForList().range(key, start, end);  
  48. catch (Exception e) {  
  49. e.printStackTrace();  
  50. return null;  
  51. }  
  52. }  

這樣就完成了Redis消息隊列工具類的創(chuàng)建,在后面的代碼中就可以直接使用。

Set集合

Redis中列表和集合都可以用來存儲字符串,但是「Set是不可重復的集合,而List列表可以存儲相同的字符串」,Set集合是無序的這個和后面講的ZSet有序集合相對。

Set的底層實現(xiàn)是「ht和intset」,ht(哈希表)前面已經(jīng)詳細了解過,下面我們來看看inset類型的存儲結(jié)構(gòu)。

inset也叫做整數(shù)集合,用于保存整數(shù)值的數(shù)據(jù)結(jié)構(gòu)類型,它可以保存int16_t、int32_t 或者int64_t 的整數(shù)值。

在整數(shù)集合中,有三個屬性值encoding、length、contents[],分別表示編碼方式、整數(shù)集合的長度、以及元素內(nèi)容,length就是記錄contents里面的大小。

在整數(shù)集合新增元素的時候,若是超出了原集合的長度大小,就會對集合進行升級,具體的升級過程如下:

  1. 首先擴展底層數(shù)組的大小,并且數(shù)組的類型為新元素的類型。
  2. 然后將原來的數(shù)組中的元素轉(zhuǎn)為新元素的類型,并放到擴展后數(shù)組對應的位置。
  3. 整數(shù)集合升級后就不會再降級,編碼會一直保持升級后的狀態(tài)。

應用場景

Set集合的應用場景可以用來「去重、抽獎、共同好友、二度好友」等業(yè)務類型。接下來模擬一個添加好友的案例實現(xiàn):

  1. @RequestMapping(value = "/addFriend", method = RequestMethod.POST)  
  2. public Long addFriend(User user, String friend) {  
  3. String currentKey = null;  
  4. // 判斷是否是當前用戶的好友  
  5. if (AppContext.getCurrentUser().getId().equals(user.getId)) {  
  6. currentKey = user.getId.toString();  
  7. }  
  8. //若是返回0則表示不是該用戶好友  
  9. return currentKey==null?0l:setOperations.add(currentKey, friend);  
  10. }  


假如兩個用戶A和B都是用上上面的這個接口添加了很多的自己的好友,那么有一個需求就是要實現(xiàn)獲取A和B的共同好友,那么可以進行如下操作:

  1. public Set intersectFriend(User userA, User userB) {  
  2. return setOperations.intersect(userA.getId.toString(), userB.getId.toString());  
  3. }  

舉一反三,還可以實現(xiàn)A用戶自己的好友,或者B用戶自己的好友等,都可以進行實現(xiàn)。

ZSet集合

ZSet是有序集合,從上面的圖中可以看到ZSet的底層實現(xiàn)是ziplist和skiplist實現(xiàn)的,ziplist上面已經(jīng)詳細講過,這里來講解skiplist的結(jié)構(gòu)實現(xiàn)。

skiplist也叫做「跳躍表」,跳躍表是一種有序的數(shù)據(jù)結(jié)構(gòu),它通過每一個節(jié)點維持多個指向其它節(jié)點的指針,從而達到快速訪問的目的。

skiplist由如下幾個特點:

  1. 有很多層組成,由上到下節(jié)點數(shù)逐漸密集,最上層的節(jié)點最稀疏,跨度也最大。
  2. 每一層都是一個有序鏈表,只掃包含兩個節(jié)點,頭節(jié)點和尾節(jié)點。
  3. 每一層的每一個每一個節(jié)點都含有指向同一層下一個節(jié)點和下一層同一個位置節(jié)點的指針。
  4. 如果一個節(jié)點在某一層出現(xiàn),那么該以下的所有鏈表同一個位置都會出現(xiàn)該節(jié)點。

具體實現(xiàn)的結(jié)構(gòu)圖如下所示:

我從未見過的牛逼解說方式!Redis五種數(shù)據(jù)結(jié)構(gòu),看一遍就懂了

在跳躍表的結(jié)構(gòu)中有head和tail表示指向頭節(jié)點和尾節(jié)點的指針,能后快速的實現(xiàn)定位。level表示層數(shù),len表示跳躍表的長度,BW表示后退指針,在從尾向前遍歷的時候使用。

BW下面還有兩個值分別表示分值(score)和成員對象(各個節(jié)點保存的成員對象)。

跳躍表的實現(xiàn)中,除了最底層的一層保存的是原始鏈表的完整數(shù)據(jù),上層的節(jié)點數(shù)會越來越少,并且跨度會越來越大。

跳躍表的上面層就相當于索引層,都是為了找到最后的數(shù)據(jù)而服務的,數(shù)據(jù)量越大,條表所體現(xiàn)的查詢的效率就越高,和平衡樹的查詢效率相差無幾。

應用場景

因為ZSet是有序的集合,因此ZSet在實現(xiàn)排序類型的業(yè)務是比較常見的,比如在首頁推薦10個最熱門的帖子,也就是閱讀量由高到低,排行榜的實現(xiàn)等業(yè)務。

下面就選用獲取排行榜前前10名的選手作為案例實現(xiàn),實現(xiàn)的代碼如下所示:

  1. @Autowired  
  2. private RedisTemplate redisTemplate;  
  3. /**  
  4. * 獲取前10排名  
  5. * @return  
  6. */  
  7. public static List<levelVO > getZset(String key, long baseNum, LevelService levelService){  
  8. ZSetOperations<Serializable, Object> operations = redisTemplate.opsForZSet();  
  9. // 根據(jù)score分數(shù)值獲取前10名的數(shù)據(jù)  
  10. Set<ZSetOperations.TypedTuple<Object>> set = operations.reverseRangeWithScores(key,0,9);  
  11. List<LevelVO> list= new ArrayList<LevelVO>();  
  12. int i=1;  
  13. for (ZSetOperations.TypedTuple<Object> o:set){  
  14. int uid = (int) o.getValue();  
  15. LevelCache levelCache = levelService.getLevelCache(uid);  
  16. LevelVO levelVO = levelCache.getLevelVO();  
  17. long score = (o.getScore().longValue() - baseNum + levelVO .getCtime())/CommonUtil.multiplier;  
  18. levelVO .setScore(score);  
  19. levelVO .setRank(i);  
  20. list.add( levelVO );  
  21. i++;  
  22. }  
  23. return list;  
  24. }  

以上的代碼實現(xiàn)大致邏輯就是根據(jù)score分數(shù)值獲取前10名的數(shù)據(jù),然后封裝成lawyerVO對象的列表進行返回。

到這里我們已經(jīng)精通Redis的五種基本數(shù)據(jù)類型了,又可以去和面試官扯皮了,扯不過就跑路吧,或者這篇文章多看幾遍,相信對你總是有好處的。

 

責任編輯:張燕妮 來源: 今日頭條
相關(guān)推薦

2018-09-05 09:33:41

DevOps轉(zhuǎn)型指標

2023-09-12 07:31:45

HashMap線程

2021-03-02 10:11:13

MySQL死鎖數(shù)據(jù)庫

2021-08-12 10:36:18

order byMySQL數(shù)據(jù)庫

2019-05-13 09:25:07

大數(shù)據(jù)數(shù)據(jù)分析隱私

2021-06-15 07:15:15

Oracle底層explain

2022-01-17 20:59:37

開發(fā)group by思路

2025-02-13 09:06:27

2021-12-01 07:26:13

IO模型異步

2024-03-26 07:59:32

IO模型多路復用

2024-03-12 08:20:57

零拷貝存儲開發(fā)

2021-10-07 20:12:03

MVCC事務原理

2025-01-13 06:10:00

2025-05-13 08:05:00

Redis數(shù)據(jù)類型數(shù)據(jù)庫

2025-03-27 00:04:33

AIChatGPT生成模型

2020-02-09 17:30:54

反轉(zhuǎn)鏈表程序員節(jié)點

2024-11-18 09:10:00

2025-05-16 07:56:55

Vue Router架構(gòu)前端

2019-10-29 08:59:16

Redis底層數(shù)據(jù)

2022-08-26 10:41:03

指針C語言
點贊
收藏

51CTO技術(shù)棧公眾號

日韩一级淫片| 国产欧美久久久久| 久久久噜噜噜www成人网| 亚洲专区第一页| 九九久久电影| 亚洲网友自拍偷拍| 久热精品视频在线观看| 一区二区三区国产在线| 老牛影视av一区二区在线观看| 91麻豆国产福利精品| 国产毛片久久| 妖精视频在线观看免费 | 国产精品久久久久久久av电影| www.四虎成人| 麻豆传媒视频在线观看免费| 性一交一乱一精一晶| 久久99久久久精品欧美| 91蜜桃网址入口| 国产精品狠色婷| 久久免费在线观看视频| 亚洲成人黄色| 色狠狠色噜噜噜综合网| 日韩电影天堂视频一区二区| 日韩手机在线视频| 一区二区三区四区在线看| 黄色91在线观看| 精品无人区一区二区三区竹菊| 免费无遮挡无码永久在线观看视频| 成人午夜一级| 中文字幕制服丝袜成人av| 国产精品美女视频网站| 国产不卡在线观看视频| 开心激情综合| 精品国产一区二区三区久久久蜜月 | 欧美少妇一区二区三区| 99久久免费国产精精品| 亚洲成人最新网站| 日韩精品一区在线| 国产69精品久久久久999小说| 日韩一区免费视频| 亚洲精品韩国| 亚洲人免费视频| 一区二区三区免费播放| 欧美18一19xxx性| 国产日韩欧美综合一区| 国产日韩欧美成人| 青娱乐在线视频免费观看| 精品国产一区二区三区四区 | 中文字幕免费在线观看视频一区| 国产不卡视频在线| 91麻豆免费视频网站| 国产精品白浆| 欧美在线啊v一区| 毛片av在线播放| 男生女生差差差的视频在线观看| 日本麻豆一区二区三区视频| 美日韩精品视频免费看| 51调教丨国产调教视频| 色综合视频一区二区三区44| 亚洲成人激情av| 亚洲.欧美.日本.国产综合在线| 国产伦精品一区二区三区四区| 欧美日本中文| 国产一区二区三区精品久久久| 亚洲国产欧美91| 亚洲天堂网一区二区| 久久男人天堂| 国产视频不卡一区| 欧美在线一区二区三区四区| 一区二区日韩在线观看| 亚洲深夜av| 4438全国亚洲精品在线观看视频| 国产小视频你懂的| 日韩三区视频| 欧美一级生活片| 日本中文字幕片| 黑人巨大精品欧美一区二区桃花岛| 亚洲视频免费看| 久久波多野结衣| 91tv国产成人福利| 国产成a人亚洲| 国产日本欧美一区二区三区| 国产精品无码天天爽视频| 国产精品一区二区果冻传媒| 国产97在线亚洲| 久久网中文字幕| 亚洲美女毛片| 国产精品99免视看9| 国产一区二区视频免费观看| 国产麻豆精品在线| 国产视频一区二区三区四区| www.激情五月| 久色婷婷小香蕉久久| 日本三级久久久| 91久久精品无码一区二区| 国产成人综合自拍| 91麻豆国产精品| 亚洲图片欧美在线| 国产河南妇女毛片精品久久久| 精品国产乱码久久久久久蜜柚| 国产大片在线免费观看| 久久综合成人精品亚洲另类欧美 | 视频在线这里都是精品| 久久综合五月天婷婷伊人| 色之综合天天综合色天天棕色| 综合久久2019| 成人欧美一区二区三区小说| 欧洲一区二区在线| 中文字幕中文字幕在线十八区| 精品久久久久久久久中文字幕 | 成人国产精品入口免费视频| 精品久久久久久久久久久久包黑料| 午夜视频在线观| 日韩福利影视| 国产偷国产偷亚洲清高网站| 久久精品综合视频| 五月激情久久久| 精品国产美女在线| 黄色片网站在线播放| 1024精品一区二区三区| 亚洲a在线观看| 性一交一乱一精一晶| 国产精品色哟哟| 亚洲一区二区不卡视频| 在线视频三区| 成人免费一区二区三区视频| 欧美 激情 在线| caoporn成人| 日韩av网站电影| 香蕉视频黄色在线观看| 国产不卡一二三区| 最近2019中文免费高清视频观看www99| 手机看片国产日韩| 久久久久99| 成人久久久久久| 亚洲男人天堂久久| 91麻豆精品一区二区三区| 国产精品视频网站在线观看| 国产不卡精品在线| 精品国产电影一区二区| 久久福利免费视频| 捆绑调教一区二区三区| 亚洲啪啪av| 日本色护士高潮视频在线观看 | 中文字幕一区二| 亚洲欧美激情网| **国产精品| 日韩av中文字幕在线| 久视频在线观看| 成人性生交大片免费看中文| 快播亚洲色图| 97超碰国产一区二区三区| 欧美日韩中文字幕在线视频| av网站在线不卡| 自拍偷拍一区| 国产成人欧美在线观看| 国产中文字幕在线看| 亚洲美女屁股眼交| 成年人免费在线播放| 亚洲欧美成人vr| 日本一区二区不卡| 国产鲁鲁视频在线观看免费| 在线观看三级视频欧美| 中文字幕99页| 欧美国产一级| 欧美在线视频一区| 99热这里只有精品在线观看| 久久中文娱乐网| 日韩精品一区二区三区不卡| 日韩成人在线看| 欧美激情精品久久久久久变态| 中文字幕日本视频| 91在线你懂得| 欧美乱做爰xxxⅹ久久久| 视频免费一区二区| 欧美一级bbbbb性bbbb喷潮片| 欧美日本网站| 欧美日韩国产一级片| 国产精品亚洲无码| 在线国产精品一区| 91青青草免费在线看| 成人午夜在线观看视频| 欧美日韩一二三四五区| 亚洲av午夜精品一区二区三区| 精品国产一级毛片| 成人在线国产精品| 成年网站在线视频网站| 日韩一区二区在线观看视频| 丁香花五月婷婷| 亚欧美中日韩视频| 国产日韩二区| 亚洲mmav| 国产亚洲免费的视频看| 99久久精品国产一区二区成人| 亚洲6080在线| 污污免费在线观看| 亚洲黄色大片| 天天久久人人| 国产精品久久久久久久久久白浆| 国产成人高潮免费观看精品| 成人免费网址| 日韩一区二区影院| 欧美一区免费看| 久久久99精品久久| 精品免费国产一区二区| 欧美+亚洲+精品+三区| 91精品国产高清久久久久久91裸体| 91av久久| 精品亚洲男同gayvideo网站| 国产精品人人妻人人爽| 欧美午夜片在线免费观看| 三级在线观看免费大全| 国产校园另类小说区| caopor在线| 精品一区二区久久久| 狼狼综合久久久久综合网| 日韩中文字幕| 国产欧美中文字幕| 午夜日韩成人影院| 一区二区三区 在线观看视| 性高潮视频在线观看| 亚洲成人综合网站| 日韩欧美123区| 国产人伦精品一区二区| 在线观看av网页| 亚洲一区中文| 精品少妇人妻av免费久久洗澡| 久久精品国产亚洲blacked| 91夜夜未满十八勿入爽爽影院| 黄瓜视频成人app免费| 欧美尤物巨大精品爽| av在线不卡免费| 久久久久久久久久久av| 精品亚洲综合| 日韩精品999| 免费av一级片| 亚洲成人网av| 国产一卡二卡三卡| 亚洲日本在线视频观看| 国产精品理论在线| 成人一区二区三区在线观看| 国产aⅴ爽av久久久久| 国产一区日韩一区| 欧美午夜欧美| 国产精品视频一区视频二区| 国产精品久久久一区| 成人免费福利| 国产精品九九久久久久久久| 婷婷综合六月| 国产91精品在线播放| 69堂精品视频在线播放| 欧美丰满老妇厨房牲生活| 污污的网站在线看| 久久久久久久久久婷婷| 123区在线| 91tv亚洲精品香蕉国产一区7ujn| 五月婷婷在线观看| 亚洲黄色片网站| 亚洲一级特黄毛片| 欧美喷潮久久久xxxxx| 欧美片一区二区| 亚洲精品欧美激情| 久久婷婷一区二区| 欧美日韩亚洲视频| 一区二区三区在线免费观看视频 | 国产伦精品一区二区三区视频青涩 | 亚洲高清乱码| 婷婷久久国产对白刺激五月99| 久久香蕉视频网站| 日韩五码在线| 亚洲中文字幕久久精品无码喷水| 亚洲欧美网站在线观看| 欧美日韩精品一区| 日韩88av| 欧美日韩一区综合| 波多野结衣在线观看一区二区三区| 国产精品视频在线免费观看| 日韩城人网站| 成人在线资源网址| 97久久中文字幕| 成人免费91在线看| 少妇精品久久久一区二区| 亚洲欧洲另类精品久久综合| 欧美日韩p片| 看av免费毛片手机播放| 欧美日韩免费| 国产精品人人妻人人爽人人牛| 久久精品国产一区二区三区免费看| 国产探花一区二区三区| 久久欧美肥婆一二区| 免费看日本毛片| 秋霞午夜av一区二区三区| 成人啪啪18免费游戏链接| 久久精品夜夜夜夜久久| 国产suv一区二区三区| 国产欧美一区二区精品性色| 午夜免费激情视频| 一区二区三区四区视频精品免费 | 91精品国产三级| 91女厕偷拍女厕偷拍高清| 三上悠亚在线观看视频| 国产精品乱码一区二三区小蝌蚪| 全程偷拍露脸中年夫妇| 91精品办公室少妇高潮对白| 高潮毛片7777777毛片| 中文字幕一精品亚洲无线一区| av女优在线| 91精品国产91久久久久久不卡| 欧美风情在线视频| 久久96国产精品久久99软件| 亚洲天堂免费| 8x8x最新地址| 337p粉嫩大胆噜噜噜噜噜91av| 中文字幕影音先锋| 欧美日韩成人在线| 欧洲综合视频| 国产一区二区三区视频免费| 美女航空一级毛片在线播放| 欧美激情videoshd| 91tv亚洲精品香蕉国产一区| 国产一区在线免费| 欧美成人69av| 日韩欧美亚洲另类| 欧美激情中文不卡| 国产伦精品一区二区三区视频网站| 精品欧美一区二区久久| 久久77777| 久久久久久久影院| 经典三级久久| 精品国产91亚洲一区二区三区www| 91精品国产成人观看| 男人搞女人网站| 久久精品一区二区三区不卡| 国产精品suv一区二区三区| 在线观看成人小视频| 日本一卡二卡四卡精品| 91精品国产色综合久久不卡98| 亚洲一区二区电影| 欧美在线播放一区| 国产精品一国产精品k频道56| 亚洲一区二区在线免费| 国产亚洲欧美在线| 国产精品久久久久久人| 亚洲欧美日韩天堂| 国产在线1区| 91久久精品国产| 无需播放器亚洲| 欧美激情第四页| 一区二区三区在线不卡| wwwav在线播放| 国内精品一区二区三区四区| 欧美日韩在线精品一区二区三区激情综合 | 久久视频国产精品免费视频在线 | 日韩综合小视频| 亚洲视频在线不卡| 亚洲男人的天堂网| 国产成人免费看一级大黄| 久久99久久99精品免观看粉嫩 | 成人国产精品一区| 亚洲一级淫片| 亚洲美女高潮久久久| 亚洲成人tv网| 久草在线网址| 国产欧美一区二区三区久久人妖| 亚洲欧洲中文字幕| 男女一区二区三区| 日本韩国欧美一区| 秋霞午夜在线观看| 成人综合电影| 天堂久久久久va久久久久| 国产不卡在线观看视频| 日韩一区二区不卡| 两个人看的在线视频www| 蜜桃传媒视频麻豆一区| 美女www一区二区| 黑鬼狂亚洲人videos| 日韩国产中文字幕| 国产91欧美| 男女啪啪免费观看| 久久久综合网站| 国产精品九九九九| 91精品国产91久久久久久| 日韩欧美1区| 中文字幕在线观看91| 欧美性色综合网| 日本精品600av| 日韩精品欧美在线| 成人一区在线看| 亚洲精品国产欧美在线观看| 久久99热精品这里久久精品| 欧洲vs亚洲vs国产| 自拍日韩亚洲一区在线| 久久久精品国产免大香伊 | 国产精品一区二区视频| 国产微拍精品一区| 久久综合伊人77777尤物| 亚洲品质自拍| 色哟哟在线观看视频| 欧美在线观看一区二区| 丁香花电影在线观看完整版|