hashmap应用场景_Redis 5种数据结构 及使用场景分析
也當(dāng)過面試官,面試過不少應(yīng)聘者,因?yàn)槭俏易约赫腥俗约河?#xff0c;所以我不會(huì)看應(yīng)聘者造火箭的技術(shù)有多牛比,只看擰螺絲的手藝瓷不瓷實(shí)。畢竟以后是一個(gè)整體,拖了大家后腿團(tuán)隊(duì)都很難受。
面試的題目一般也不會(huì)太難,就像問Redis,我只是想確認(rèn)他真正用過就夠了。Redis 5種基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)和簡單操作要知道,最基本的要求,如果這個(gè)時(shí)候他會(huì)說出每種數(shù)據(jù)結(jié)構(gòu)大致的應(yīng)用場景,那么這一定是加分的,起碼要比那些只會(huì)說出幾種數(shù)據(jù)結(jié)構(gòu)后,在那干瞪眼等我問下一個(gè)問題的強(qiáng)很多,千萬別冷場。
Redis基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)有哪些?
一、String(字符串)
在任何一種編程語言里,字符串String都是最基礎(chǔ)的數(shù)據(jù)結(jié)構(gòu), 那你有想過Redis中存儲一個(gè)字符串都進(jìn)行了哪些操作嘛?
在Redis中String是可以修改的,稱為動(dòng)態(tài)字符串(Simple Dynamic String 簡稱 SDS)(快拿小本本記名詞,要考的),說是字符串但它的內(nèi)部結(jié)構(gòu)更像是一個(gè) ArrayList,內(nèi)部維護(hù)著一個(gè)字節(jié)數(shù)組,并且在其內(nèi)部預(yù)分配了一定的空間,以減少內(nèi)存的頻繁分配。
Redis的內(nèi)存分配機(jī)制是這樣:
- 當(dāng)字符串的長度小于 1MB時(shí),每次擴(kuò)容都是加倍現(xiàn)有的空間。
- 如果字符串長度超過 1MB時(shí),每次擴(kuò)容時(shí)只會(huì)擴(kuò)展 1MB 的空間。
這樣既保證了內(nèi)存空間夠用,還不至于造成內(nèi)存的浪費(fèi),字符串最大長度為 512MB.。
以上圖片源自網(wǎng)絡(luò),如有侵權(quán)聯(lián)系刪除上圖就是字符串的基本結(jié)構(gòu),其中 content 里面保存的是字符串內(nèi)容,0x0作為結(jié)束字符不會(huì)被計(jì)算len中。
分析一下字符串的數(shù)據(jù)結(jié)構(gòu)
struct SDS{T capacity; //數(shù)組容量T len; //實(shí)際長度byte flages; //標(biāo)志位,低三位表示類型byte[] content; //數(shù)組內(nèi)容 }capacity 和 len兩個(gè)屬性都是泛型,為什么不直接用int類型?因?yàn)镽edis內(nèi)部有很多優(yōu)化方案,為更合理的使用內(nèi)存,不同長度的字符串采用不同的數(shù)據(jù)類型表示,且在創(chuàng)建字符串的時(shí)候 len 會(huì)和 capacity 一樣大,不產(chǎn)生冗余的空間,所以String值可以是字符串、數(shù)字(整數(shù)、浮點(diǎn)數(shù)) 或者 二進(jìn)制。
1、應(yīng)用場景:
存儲key-value鍵值對,這個(gè)比較簡單不細(xì)說了
2、字符串(String)常用的命令:
set [key] [value] 給指定key設(shè)置值(set 可覆蓋老的值)get [key] 獲取指定key 的值del [key] 刪除指定keyexists [key] 判斷是否存在指定keymset [key1] [value1] [key2] [value2] ...... 批量存鍵值對mget [key1] [key2] ...... 批量取keyexpire [key] [time] 給指定key 設(shè)置過期時(shí)間 單位秒setex [key] [time] [value] 等價(jià)于 set + expire 命令組合setnx [key] [value] 如果key不存在則set 創(chuàng)建,否則返回0incr [key] 如果value為整數(shù) 可用 incr命令每次自增1incrby [key] [number] 使用incrby命令對整數(shù)值 進(jìn)行增加 number二、list(列表)
Redis中的list和Java中的LinkedList很像,底層都是一種鏈表結(jié)構(gòu), list的插入和刪除操作非常快,時(shí)間復(fù)雜度為 0(1),不像數(shù)組結(jié)構(gòu)插入、刪除操作需要移動(dòng)數(shù)據(jù)。
像歸像,但是redis中的list底層可不是一個(gè)雙向鏈表那么簡單。
當(dāng)數(shù)據(jù)量較少的時(shí)候它的底層存儲結(jié)構(gòu)為一塊連續(xù)內(nèi)存,稱之為ziplist(壓縮列表),它將所有的元素緊挨著一起存儲,分配的是一塊連續(xù)的內(nèi)存;當(dāng)數(shù)據(jù)量較多的時(shí)候?qū)?huì)變成quicklist(快速鏈表)結(jié)構(gòu)。
可單純的鏈表也是有缺陷的,鏈表的前后指針 prev 和 next 會(huì)占用較多的內(nèi)存,會(huì)比較浪費(fèi)空間,而且會(huì)加重內(nèi)存的碎片化。在redis 3.2之后就都改用ziplist+鏈表的混合結(jié)構(gòu),稱之為 quicklist(快速鏈表)。
下面具體介紹下兩種鏈表
ziplist(壓縮列表)
先看一下ziplist的數(shù)據(jù)結(jié)構(gòu),
struct ziplist<T>{int32 zlbytes; //壓縮列表占用字節(jié)數(shù)int32 zltail_offset; //最后一個(gè)元素距離起始位置的偏移量,用于快速定位到最后一個(gè)節(jié)點(diǎn)int16 zllength; //元素個(gè)數(shù)T[] entries; //元素內(nèi)容int8 zlend; //結(jié)束位 0xFF }int32 zlbytes: 壓縮列表占用字節(jié)數(shù)int32 zltail_offset: 最后一個(gè)元素距離起始位置的偏移量,用于快速定位到最后一個(gè)節(jié)點(diǎn)
`int16 zllength`:元素個(gè)數(shù) `T[] entries`:元素內(nèi)容 `int8 zlend`:結(jié)束位 0xFF壓縮列表為了支持雙向遍歷,所以才會(huì)有 ztail_offset 這個(gè)字段,用來快速定位到最后一
個(gè)元素,然后倒著遍歷
entry的數(shù)據(jù)結(jié)構(gòu):
struct entry{int<var> prevlen; //前一個(gè) entry 的長度int<var> encoding; //元素類型編碼optional byte[] content; //元素內(nèi)容 }entry它的 prevlen 字段表示前一個(gè) entry 的字節(jié)長度,當(dāng)壓縮列表倒著遍歷時(shí),需要通過這
個(gè)字段來快速定位到下一個(gè)元素的位置。
1、應(yīng)用場景:
由于list它是一個(gè)按照插入順序排序的列表,所以應(yīng)用場景相對還較多的,例如:
- 消息隊(duì)列:lpop和rpush(或者反過來,lpush和rpop)能實(shí)現(xiàn)隊(duì)列的功能
- 朋友圈的點(diǎn)贊列表、評論列表、排行榜:lpush命令和lrange命令能實(shí)現(xiàn)最新列表的功能,每次通過lpush命令往列表里插入新的元素,然后通過lrange命令讀取最新的元素列表。
2、list操作的常用命名:
rpush [key] [value1] [value2] ...... 鏈表右側(cè)插入rpop [key] 移除右側(cè)列表頭元素,并返回該元素lpop [key] 移除左側(cè)列表頭元素,并返回該元素llen [key] 返回該列表的元素個(gè)數(shù)lrem [key] [count] [value] 刪除列表中與value相等的元素,count是刪除的個(gè)數(shù)。 count>0 表示從左側(cè)開始查找,刪除count個(gè)元素,count<0 表示從右側(cè)開始查找,刪除count個(gè)相同元素,count=0 表示刪除全部相同的元素(PS: index 代表元素下標(biāo),index 可以為負(fù)數(shù), index= 表示倒數(shù)第一個(gè)元素,同理 index=-2 表示倒數(shù)第二 個(gè)元素。)lindex [key] [index] 獲取list指定下標(biāo)的元素 (需要遍歷,時(shí)間復(fù)雜度為O(n))lrange [key] [start_index] [end_index] 獲取list 區(qū)間內(nèi)的所有元素 (時(shí)間復(fù)雜度為 O(n))ltrim [key] [start_index] [end_index] 保留區(qū)間內(nèi)的元素,其他元素刪除(時(shí)間復(fù)雜度為 O(n))三、hash (字典)
Redis 中的 Hash和 Java的 HashMap 更加相似,都是數(shù)組+鏈表的結(jié)構(gòu),當(dāng)發(fā)生 hash 碰撞時(shí)將會(huì)把元素追加到鏈表上,值得注意的是在 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 #單個(gè) key 可以進(jìn)行計(jì)數(shù) 和 incr 命令基本一致 (integer) 18Hash 和String都可以用來存儲用戶信息 ,但不同的是Hash可以對用戶信息的每個(gè)字段單獨(dú)存儲;String存的是用戶全部信息經(jīng)過序列化后的字符串,如果想要修改某個(gè)用戶字段必須將用戶信息字符串全部查詢出來,解析成相應(yīng)的用戶信息對象,修改完后在序列化成字符串存入。而 hash可以只對某個(gè)字段修改,從而節(jié)約網(wǎng)絡(luò)流量,不過hash內(nèi)存占用要大于 String,這是 hash 的缺點(diǎn)。
1、應(yīng)用場景:
- 購物車:hset [key] [field] [value] 命令, 可以實(shí)現(xiàn)以用戶Id,商品Id為field,商品數(shù)量為value,恰好構(gòu)成了購物車的3個(gè)要素。
- 存儲對象:hash類型的(key, field, value)的結(jié)構(gòu)與對象的(對象id, 屬性, 值)的結(jié)構(gòu)相似,也可以用來存儲對象。
2、hash常用的操作命令:
hset [key] [field] [value] 新建字段信息hget [key] [field] 獲取字段信息hdel [key] [field] 刪除字段hlen [key] 保存的字段個(gè)數(shù)hgetall [key] 獲取指定key 字典里的所有字段和值 (字段信息過多,會(huì)導(dǎo)致慢查詢 慎用:親身經(jīng)歷 曾經(jīng)用過這個(gè)這個(gè)指令導(dǎo)致線上服務(wù)故障)hmset [key] [field1] [value1] [field2] [value2] ...... 批量創(chuàng)建hincr [key] [field] 對字段值自增hincrby [key] [field] [number] 對字段值增加number四、set(集合)
Redis 中的 set和Java中的HashSet 有些類似,它內(nèi)部的鍵值對是無序的、唯一 的。它的內(nèi)部實(shí)現(xiàn)相當(dāng)于一個(gè)特殊的字典,字典中所有的value都是一個(gè)值 NULL。當(dāng)集合中最后一個(gè)元素被移除之后,數(shù)據(jù)結(jié)構(gòu)被自動(dòng)刪除,內(nèi)存被回收。
1、應(yīng)用場景:
- 好友、關(guān)注、粉絲、感興趣的人集合:
1) sinter命令可以獲得A和B兩個(gè)用戶的共同好友;
2) sismember命令可以判斷A是否是B的好友;
3) scard命令可以獲取好友數(shù)量;
4) 關(guān)注時(shí),smove命令可以將B從A的粉絲集合轉(zhuǎn)移到A的好友集合 - 首頁展示隨機(jī):美團(tuán)首頁有很多推薦商家,但是并不能全部展示,set類型適合存放所有需要展示的內(nèi)容,而srandmember命令則可以從中隨機(jī)獲取幾個(gè)。
- 存儲某活動(dòng)中中獎(jiǎng)的用戶ID ,因?yàn)橛腥ブ毓δ?#xff0c;可以保證同一個(gè)用戶不會(huì)中獎(jiǎng)兩次。
2、set的常用命令:
sadd [key] [value] 向指定key的set中添加元素smembers [key] 獲取指定key 集合中的所有元素sismember [key] [value] 判斷集合中是否存在某個(gè)valuescard [key] 獲取集合的長度spop [key] 彈出一個(gè)元素srem [key] [value] 刪除指定元素五、zset(有序集合)
zset也叫SortedSet一方面它是個(gè) set ,保證了內(nèi)部 value 的唯一性,另方面它可以給每個(gè) value 賦予一個(gè)score,代表這個(gè)value的排序權(quán)重。它的內(nèi)部實(shí)現(xiàn)用的是一種叫作“跳躍列表”的數(shù)據(jù)結(jié)構(gòu)。
1、應(yīng)用場景:
zset 可以用做排行榜,但是和list不同的是zset它能夠?qū)崿F(xiàn)動(dòng)態(tài)的排序,例如: 可以用來存儲粉絲列表,value 值是粉絲的用戶 ID,score 是關(guān)注時(shí)間,我們可以對粉絲列表按關(guān)注時(shí)間進(jìn)行排序。
zset 還可以用來存儲學(xué)生的成績, value 值是學(xué)生的 ID, score 是他的考試成績。 我們對成績按分?jǐn)?shù)進(jìn)行排序就可以得到他的名次。
2、zset有序集合的常用操作命令:
zadd [key] [score] [value] 向指定key的集合中增加元素zrange [key] [start_index] [end_index] 獲取下標(biāo)范圍內(nèi)的元素列表,按score 排序輸出zrevrange [key] [start_index] [end_index] 獲取范圍內(nèi)的元素列表 ,按score排序 逆序輸出zcard [key] 獲取集合列表的元素個(gè)數(shù)zrank [key] [value] 獲取元素再集合中的排名zrangebyscore [key] [score1] [score2] 輸出score范圍內(nèi)的元素列表zrem [key] [value] 刪除元素zscore [key] [value] 獲取元素的score總結(jié)
本文很多概念都一帶而過了,只是給大家粗略的講述一下Redis五種基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)和應(yīng)用場景,旨在給小伙伴們一個(gè)面試備題的方向
來源:思否
作者:程序員內(nèi)點(diǎn)事
原文:https://segmentfault.com/a/1190000022800471
總結(jié)
以上是生活随笔為你收集整理的hashmap应用场景_Redis 5种数据结构 及使用场景分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 谷粒商城高级篇资料_一文搞定剑指offe
- 下一篇: linux cmake编译源码,linu