掌握這些Redis知識點,面試官一定覺得你很NB(干貨 | 建議珍藏)

是數(shù)據(jù)結構而非類型

很多文章都會說誊垢,redis支持5種常用的?數(shù)據(jù)類型?掉弛,這其實是存在很大的歧義。redis里存的都是二進制數(shù)據(jù)喂走,其實就是字節(jié)數(shù)組(byte[])殃饿,這些字節(jié)數(shù)據(jù)是沒有數(shù)據(jù)類型的,只有把它們按照合理的格式解碼后芋肠,可以變成一個字符串乎芳,整數(shù)或?qū)ο螅藭r才具有數(shù)據(jù)類型。

這一點必須要記住奈惑。所以任何東西只要能轉(zhuǎn)化成字節(jié)數(shù)組(byte[])的吭净,都可以存到redis里。管你是字符串携取、數(shù)字攒钳、對象、圖片雷滋、聲音不撑、視頻、還是文件晤斩,只要變成byte數(shù)組焕檬。

因此redis里的String指的并不是字符串,它其實表示的是一種最簡單的數(shù)據(jù)結構澳泵,即一個key只能對應一個value实愚。這里的key和value都是byte數(shù)組,只不過key一般是由一個字符串轉(zhuǎn)換成的byte數(shù)組兔辅,value則根據(jù)實際需要而定腊敲。

在特定情況下,對value也會有一些要求维苔,比如要進行自增或自減操作碰辅,那value對應的byte數(shù)組必須要能被解碼成一個數(shù)字才行,否則會報錯介时。

那么List這種數(shù)據(jù)結構没宾,其實表示一個key可以對應多個value,且value之間是有先后順序的沸柔,value值可以重復循衰。

Set這種數(shù)據(jù)結構,表示一個key可以對應多個value褐澎,且value之間是沒有先后順序的会钝,value值也不可以重復。

Hash這種數(shù)據(jù)結構乱凿,表示一個key可以對應多個key-value對顽素,此時這些key-value對之間的先后順序一般意義不大,這是一個按照名稱語義來訪問的數(shù)據(jù)結構徒蟆,而非位置語義胁出。

Sorted Set這種數(shù)據(jù)結構,表示一個key可以對應多個value段审,value之間是有大小排序的全蝶,value值不可以重復。每個value都和一個浮點數(shù)相關聯(lián),該浮點數(shù)叫score抑淫。元素排序規(guī)則是:先按score排序绷落,再按value排序。

相信現(xiàn)在你對這5種數(shù)據(jù)結構有了更清晰的認識始苇,那它們的對應命令對你來說就是小case了砌烁。

集群帶來的問題與解決思路

集群帶來的好處是顯而易見的,比如容量增加催式、處理能力增強函喉,還可以按需要進行動態(tài)的擴容、縮容荣月。但同時也會引入一些新的問題管呵,至少會有下面這兩個。

一是數(shù)據(jù)分配:存數(shù)據(jù)時應該放到哪個節(jié)點上哺窄,取數(shù)據(jù)時應該去哪個節(jié)點上找捐下。二是數(shù)據(jù)移動:集群擴容,新增加節(jié)點時萌业,該節(jié)點上的數(shù)據(jù)從何處來坷襟;集群縮容,要剔除節(jié)點時生年,該節(jié)點上的數(shù)據(jù)往何處去啤握。

上面這兩個問題有一個共同點就是,如何去描述和存儲數(shù)據(jù)與節(jié)點的映射關系晶框。又因為數(shù)據(jù)的位置是由key決定的,所以問題就演變?yōu)槿绾谓⑵鸶鱾€key和集群所有節(jié)點的關聯(lián)關系懂从。

集群的節(jié)點是相對固定和少數(shù)的授段,雖然有增加節(jié)點和剔除節(jié)點。但集群里存儲的key番甩,則是完全隨機侵贵、沒有規(guī)律、不可預測缘薛、數(shù)量龐多窍育,還非常瑣碎宴胧。

這就好比一所大學和它的所有學生之間的關系漱抓。如果大學和學生直接掛鉤的話,一定會比較混亂∷∑耄現(xiàn)實是它們之間又加入了好幾層乞娄,首先有院系,其次有專業(yè),再者有年級仪或,最后還有班級确镊。經(jīng)過這四層映射之后,關系就清爽很多了范删。

這其實是一個非常重要的結論蕾域,這個世界上沒有什么問題是不能通過加入一層來解決的。如果有到旦,那就再加入一層旨巷。計算機里也是這樣的。

redis在數(shù)據(jù)和節(jié)點之間又加入了一層厢绝,把這層稱為槽(slot)契沫,因該槽主要和哈希有關,又叫哈希槽昔汉。

最后變成了懈万,節(jié)點上放的是槽,槽里放的是數(shù)據(jù)靶病。槽解決的是粒度問題会通,相當于把粒度變大了,這樣便于數(shù)據(jù)移動娄周。哈希解決的是映射問題涕侈,使用key的哈希值來計算所在的槽,便于數(shù)據(jù)分配煤辨。

可以這樣來理解裳涛,你的學習桌子上堆滿了書,亂的很众辨,想找到某本書非常困難端三。于是你買了幾個大的收納箱,把這些書按照書名的長度放入不同的收納箱鹃彻,然后把這些收納箱放到桌子上郊闯。

這樣就變成了,桌子上是收納箱蛛株,收納箱里是書籍团赁。這樣書籍移動很方便,搬起一個箱子就走了谨履。尋找書籍也很方便欢摄,只要數(shù)一數(shù)書名的長度,去對應的箱子里找就行了屉符。

其實我們也沒做什么剧浸,只是買了幾個箱子锹引,按照某種規(guī)則把書裝入箱子。就這么簡單的舉動唆香,就徹底改變了原來一盤散沙的狀況嫌变。是不是有點小小的神奇呢。

一個集群只能有16384個槽躬它,編號0-16383腾啥。這些槽會分配給集群中的所有主節(jié)點,分配策略沒有要求冯吓√却可以指定哪些編號的槽分配給哪個主節(jié)點。集群會記錄節(jié)點和槽的對應關系组贺。

接下來就需要對key求哈希值凸舵,然后對16384取余,余數(shù)是幾key就落入對應的槽里失尖。?slot = CRC16(key) % 16384?啊奄。

以槽為單位移動數(shù)據(jù),因為槽的數(shù)目是固定的掀潮,處理起來比較容易菇夸,這樣數(shù)據(jù)移動問題就解決了。

使用哈希函數(shù)計算出key的哈希值仪吧,這樣就可以算出它對應的槽庄新,然后利用集群存儲的槽和節(jié)點的映射關系查詢出槽所在的節(jié)點,于是數(shù)據(jù)和節(jié)點就映射起來了薯鼠,這樣數(shù)據(jù)分配問題就解決了择诈。

我想說的是,一般的人只會去學習各種技術出皇,高手更在乎如何跳出技術吭从,尋求一種解決方案或思路方向,順著這個方向走下去恶迈,八九不離十能找到你想要的答案。

集群對命令操作的取舍

客戶端只要和集群中的一個節(jié)點建立鏈接后谱醇,就可以獲取到整個集群的所有節(jié)點信息暇仲。此外還會獲取所有哈希槽和節(jié)點的對應關系信息,這些信息數(shù)據(jù)都會在客戶端緩存起來副渴,因為這些信息相當有用奈附。

客戶端可以向任何節(jié)點發(fā)送請求,那么拿到一個key后到底該向哪個節(jié)點發(fā)請求呢煮剧?其實就是把集群里的那套key和節(jié)點的映射關系理論搬到客戶端來就行了斥滤。

所以客戶端需要實現(xiàn)一個和集群端一樣的哈希函數(shù)将鸵,先計算出key的哈希值,然后再對16384取余佑颇,這樣就找到了該key對應的哈希槽顶掉,利用客戶端緩存的槽和節(jié)點的對應關系信息,就可以找到該key對應的節(jié)點了挑胸。

接下來發(fā)送請求就可以了痒筒。還可以把key和節(jié)點的映射關系緩存起來,下次再請求該key時茬贵,直接就拿到了它對應的節(jié)點簿透,不用再計算一遍了。

理論和現(xiàn)實總是有差距的解藻,集群已經(jīng)發(fā)生了變化老充,客戶端的緩存還沒來得及更新∶螅肯定會出現(xiàn)拿到一個key向?qū)墓?jié)點發(fā)請求啡浊,其實這個key已經(jīng)不在那個節(jié)點上了。此時這個節(jié)點應該怎么辦路狮?

這個節(jié)點可以去key實際所在的節(jié)點上拿到數(shù)據(jù)再返回給客戶端虫啥,也可以直接告訴客戶端key已經(jīng)不在我這里了,同時附上key現(xiàn)在所在的節(jié)點信息奄妨,讓客戶端再去請求一次涂籽,類似于HTTP的302重定向。

這其實是個選擇問題砸抛,也是個哲學問題评雌。結果就是redis集群選擇了后者。因此直焙,節(jié)點只處理自己擁有的key景东,對于不擁有的key將返回重定向錯誤,即?-MOVED key 127.0.0.1:6381?奔誓,客戶端重新向這個新節(jié)點發(fā)送請求斤吐。

所以說選擇是一種哲學,也是個智慧厨喂。稍后再談這個問題和措。先來看看另一個情況,和這個問題有些相同點蜕煌。

redis有一種命令可以一次帶多個key派阱,如MGET,我把這些稱為多key命令斜纪。這個多key命令的請求被發(fā)送到一個節(jié)點上贫母,這里有一個潛在的問題文兑,不知道大家有沒有想到,就是這個命令里的多個key一定都位于那同一個節(jié)點上嗎腺劣?

就分為兩種情況了绿贞,如果多個key不在同一個節(jié)點上,此時節(jié)點只能返回重定向錯誤了誓酒,但是多個key完全可能位于多個不同的節(jié)點上樟蠕,此時返回的重定向錯誤就會非常亂,所以redis集群選擇不支持此種情況靠柑。

如果多個key位于同一個節(jié)點上呢寨辩,理論上是沒有問題的,redis集群是否支持就和redis的版本有關系了歼冰,具體使用時自己測試一下就行了靡狞。

在這個過程中我們發(fā)現(xiàn)了一件頗有意義的事情,就是讓一組相關的key映射到同一個節(jié)點上是非常有必要的隔嫡,這樣可以提高效率甸怕,通過多key命令一次獲取多個值。

那么問題來了腮恩,如何給這些key起名字才能讓他們落到同一個節(jié)點上梢杭,難不成都要先計算個哈希值,再取個余數(shù)秸滴,太麻煩了吧武契。當然不是這樣了,redis已經(jīng)幫我們想好了荡含。

可以來簡單推理下咒唆,要想讓兩個key位于同一個節(jié)點上,它們的哈希值必須要一樣释液。要想哈希值一樣全释,傳入哈希函數(shù)的字符串必須一樣。那我們只能傳進去兩個一模一樣的字符串了误债,那不就變成同一個key了浸船,后面的會覆蓋前面的數(shù)據(jù)。

這里的問題是我們都是拿整個key去計算哈希值寝蹈,這就導致key和參與計算哈希值的字符串耦合了糟袁,需要將它們解耦才行,就是key和參與計算哈希值的字符串有關但是又不一樣躺盛。

redis基于這個原理為我們提供了方案,叫做key哈希標簽形帮。先看例子槽惫,{?user1000?}.following周叮,{user1000?}.followers,相信你已經(jīng)看出了門道界斜,就是僅使用Key中的位于?{?和?}?間的字符串參與計算哈希值仿耽。

這樣可以保證哈希值相同,落到相同的節(jié)點上各薇。但是key又是不同的项贺,不會互相覆蓋。使用哈希標簽把一組相關的key關聯(lián)了起來峭判,問題就這樣被輕松愉快地解決了开缎。

相信你已經(jīng)發(fā)現(xiàn)了,要解決問題靠的是巧妙的奇思妙想林螃,而不是非要用牛逼的技術牛逼的算法奕删。這就是小強,小而強大疗认。

最后再來談選擇的哲學完残。redis的核心就是以最快的速度進行常用數(shù)據(jù)結構的key/value存取,以及圍繞這些數(shù)據(jù)結構的運算横漏。對于與核心無關的或會拖累核心的都選擇弱化處理或不處理谨设,這樣做是為了保證核心的簡單、快速和穩(wěn)定缎浇。

其實就是在廣度和深度面前扎拣,redis選擇了深度。所以節(jié)點不去處理自己不擁有的key华畏,集群不去支持多key命令鹏秋。這樣一方面可以快速地響應客戶端,另一方面可以避免在集群內(nèi)部有大量的數(shù)據(jù)傳輸與合并亡笑。

單線程模型

redis集群的每個節(jié)點里只有一個線程負責接受和執(zhí)行所有客戶端發(fā)送的請求侣夷。技術上使用多路復用I/O,使用Linux的epoll函數(shù)仑乌,這樣一個線程就可以管理很多socket連接百拓。

除此之外,選擇單線程還有以下這些原因:

1晰甚、redis都是對內(nèi)存的操作衙传,速度極快(10W+QPS)

2、整體的時間主要都是消耗在了網(wǎng)絡的傳輸上

3厕九、如果使用了多線程蓖捶,則需要多線程同步,這樣實現(xiàn)起來會變的復雜

4扁远、線程的加鎖時間甚至都超過了對內(nèi)存操作的時間

5俊鱼、多線程上下文頻繁的切換需要消耗更多的CPU時間

6刻像、還有就是單線程天然支持原子操作,而且單線程的代碼寫起來更簡單

事務

事務大家都知道并闲,就是把多個操作捆綁在一起细睡,要么都執(zhí)行(成功了),要么一個也不執(zhí)行(回滾了)帝火。redis也是支持事務的溜徙,但可能和你想要的不太一樣,一起來看看吧犀填。

redis的事務可以分為兩步蠢壹,定義事務和執(zhí)行事務。使用multi命令開啟一個事務宏浩,然后把要執(zhí)行的所有命令都依次排上去知残。這就定義好了一個事務。此時使用exec命令來執(zhí)行這個事務比庄,或使用discard命令來放棄這個事務求妹。

你可能希望在你的事務開始前,你關心的key不想被別人操作佳窑,那么可以使用watch命令來監(jiān)視這些key制恍,如果開始執(zhí)行前這些key被其它命令操作了則會取消事務的。也可以使用unwatch命令來取消對這些key的監(jiān)視神凑。

redis事務具有以下特點:

1净神、如果開始執(zhí)行事務前出錯,則所有命令都不執(zhí)行

2溉委、一旦開始鹃唯,則保證所有命令一次性按順序執(zhí)行完而不被打斷

3、如果執(zhí)行過程中遇到錯誤瓣喊,會繼續(xù)執(zhí)行下去坡慌,不會停止的

4、對于執(zhí)行過程中遇到錯誤藻三,是不會進行回滾的

看完這些洪橘,真想問一句話,你這能叫事務嗎棵帽?很顯然熄求,這并不是我們通常認為的事務,因為它連原子性都保證不了逗概。保證不了原子性是因為redis不支持回滾弟晚,不過它也給出了不支持的理由。

不支持回滾的理由:

1、redis認為卿城,失敗都是由命令使用不當造成

2淑履、redis這樣做,是為了保持內(nèi)部實現(xiàn)簡單快速

3藻雪、redis還認為,回滾并不能解決所有問題

哈哈狸吞,這就是霸王條款勉耀,因此,好像使用redis事務的不太多

管道

客戶端和集群的交互過程是串行化阻塞式的蹋偏,即客戶端發(fā)送了一個命令后必須等到響應回來后才能發(fā)第二個命令便斥,這一來一回就是一個往返時間。?如果你有很多的命令威始,都這樣一個一個的來進行枢纠,會變得很慢。

redis提供了一種管道技術黎棠,可以讓客戶端一次發(fā)送多個命令晋渺,期間不需要等待服務器端的響應,等所有的命令都發(fā)完了脓斩,再依次接收這些命令的全部響應木西。?這就極大地節(jié)省了許多時間,提升了效率随静。

聰明的你是不是意識到了另外一個問題八千,多個命令就是多個key啊,這不就是上面提到的多key操作嘛燎猛,那么問題來了恋捆,你如何保證這多個key都是同一個節(jié)點上的啊,哈哈重绷,redis集群又放棄了對管道的支持沸停。

不過可以在客戶端模擬實現(xiàn),就是使用多個連接往多個節(jié)點同時發(fā)送命令论寨,然后等待所有的節(jié)點都返回了響應星立,再把它們按照發(fā)送命令的順序整理好,返回給用戶代碼葬凳。?哎呀绰垂,好麻煩呀。

協(xié)議

簡單了解下redis的協(xié)議火焰,知道redis的數(shù)據(jù)傳輸格式劲装。

發(fā)送請求的協(xié)議:

*?參數(shù)個數(shù)?CRLF?$?參數(shù)1的字節(jié)數(shù)?CRLF?參數(shù)1的數(shù)據(jù)?CRLF...?$?參數(shù)N的字節(jié)數(shù)?CRLF?參數(shù)N的數(shù)據(jù)?CRLF

例如,SET name lixinjie,實際發(fā)送的數(shù)據(jù)是:

*?3?\r\n?$?3?\r\n?SET?\r\n?$?4?\r\n?name?\r\n?$?8?\r\n?lixinjie?\r\n

接受響應的協(xié)議:

單行回復占业,第一個字節(jié)是

+

錯誤消息绒怨,第一個字節(jié)是?-

整型數(shù)字,第一個字節(jié)是?:

批量回復谦疾,第一個字節(jié)是?$

多個批量回復南蹂,第一個字節(jié)是?*

例如,

+?OK?\r\n

-?ERR Operation against?\r\n

:?1000?\r\n

$?6?\r\n?foobar?\r\n

*?2?\r\n?$?3?\r\n?foo?\r\n?$?3?\r\n?bar?\r\n

可見redis的協(xié)議設計的非常簡單念恍。

和大家的分享就到這吧六剥!有收獲,需要Java資料的可以關注小編峰伙。私信“架構”可以獲取本人整理的一份java架構進階資料疗疟。

?

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市瞳氓,隨后出現(xiàn)的幾起案子策彤,更是在濱河造成了極大的恐慌,老刑警劉巖匣摘,帶你破解...
    沈念sama閱讀 222,252評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件店诗,死亡現(xiàn)場離奇詭異,居然都是意外死亡恋沃,警方通過查閱死者的電腦和手機必搞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來囊咏,“玉大人恕洲,你說我怎么就攤上這事∶犯睿” “怎么了霜第?”我有些...
    開封第一講書人閱讀 168,814評論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長户辞。 經(jīng)常有香客問我泌类,道長,這世上最難降的妖魔是什么底燎? 我笑而不...
    開封第一講書人閱讀 59,869評論 1 299
  • 正文 為了忘掉前任刃榨,我火速辦了婚禮,結果婚禮上双仍,老公的妹妹穿的比我還像新娘枢希。我一直安慰自己,他們只是感情好朱沃,可當我...
    茶點故事閱讀 68,888評論 6 398
  • 文/花漫 我一把揭開白布苞轿。 她就那樣靜靜地躺著茅诱,像睡著了一般。 火紅的嫁衣襯著肌膚如雪搬卒。 梳的紋絲不亂的頭發(fā)上瑟俭,一...
    開封第一講書人閱讀 52,475評論 1 312
  • 那天,我揣著相機與錄音契邀,去河邊找鬼摆寄。 笑死,一個胖子當著我的面吹牛坯门,可吹牛的內(nèi)容都是我干的椭迎。 我是一名探鬼主播,決...
    沈念sama閱讀 41,010評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼田盈,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了缴阎?” 一聲冷哼從身側(cè)響起允瞧,我...
    開封第一講書人閱讀 39,924評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蛮拔,沒想到半個月后述暂,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,469評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡建炫,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,552評論 3 342
  • 正文 我和宋清朗相戀三年畦韭,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片肛跌。...
    茶點故事閱讀 40,680評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡艺配,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出衍慎,到底是詐尸還是另有隱情转唉,我是刑警寧澤,帶...
    沈念sama閱讀 36,362評論 5 351
  • 正文 年R本政府宣布稳捆,位于F島的核電站赠法,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏乔夯。R本人自食惡果不足惜砖织,卻給世界環(huán)境...
    茶點故事閱讀 42,037評論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望末荐。 院中可真熱鬧侧纯,春花似錦、人聲如沸鞠评。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至聋涨,卻和暖如春晾浴,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背牍白。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評論 1 274
  • 我被黑心中介騙來泰國打工绣张, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留院崇,地道東北人。 一個月前我還...
    沈念sama閱讀 49,099評論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像牵囤,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子烙常,可洞房花燭夜當晚...
    茶點故事閱讀 45,691評論 2 361

推薦閱讀更多精彩內(nèi)容