【面試】吃透了這些Redis知識點闰靴,面試官一定覺得你很NB(干貨 | 建議珍藏)](轉(zhuǎn))

【面試】吃透了這些Redis知識點彪笼,面試官一定覺得你很NB(干貨 | 建議珍藏)]

是數(shù)據(jù)結(jié)構(gòu)而非類型

很多文章都會說,redis支持5種常用的數(shù)據(jù)類型蚂且,這其實是存在很大的歧義配猫。redis里存的都是二進(jìn)制數(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ù)結(jié)構(gòu),即一個key只能對應(yīng)一個value列吼。這里的key和value都是byte數(shù)組幽崩,只不過key一般是由一個字符串轉(zhuǎn)換成的byte數(shù)組,value則根據(jù)實際需要而定寞钥。

在特定情況下慌申,對value也會有一些要求,比如要進(jìn)行自增或自減操作理郑,那value對應(yīng)的byte數(shù)組必須要能被解碼成一個數(shù)字才行蹄溉,否則會報錯。

那么List這種數(shù)據(jù)結(jié)構(gòu)您炉,其實表示一個key可以對應(yīng)多個value柒爵,且value之間是有先后順序的,value值可以重復(fù)赚爵。

Set這種數(shù)據(jù)結(jié)構(gòu)棉胀,表示一個key可以對應(yīng)多個value,且value之間是沒有先后順序的冀膝,value值也不可以重復(fù)唁奢。

Hash這種數(shù)據(jù)結(jié)構(gòu),表示一個key可以對應(yīng)多個key-value對窝剖,此時這些key-value對之間的先后順序一般意義不大麻掸,這是一個按照名稱語義來訪問的數(shù)據(jù)結(jié)構(gòu),而非位置語義赐纱。

Sorted Set這種數(shù)據(jù)結(jié)構(gòu)脊奋,表示一個key可以對應(yīng)多個value,value之間是有大小排序的千所,value值不可以重復(fù)狂魔。每個value都和一個浮點數(shù)相關(guān)聯(lián),該浮點數(shù)叫score淫痰。元素排序規(guī)則是:先按score排序最楷,再按value排序。

相信現(xiàn)在你對這5種數(shù)據(jù)結(jié)構(gòu)有了更清晰的認(rèn)識待错,那它們的對應(yīng)命令對你來說就是小case了籽孙。

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

集群帶來的好處是顯而易見的,比如容量增加火俄、處理能力增強(qiáng)犯建,還可以按需要進(jìn)行動態(tài)的擴(kuò)容、縮容瓜客。但同時也會引入一些新的問題适瓦,至少會有下面這兩個竿开。

一是數(shù)據(jù)分配:存數(shù)據(jù)時應(yīng)該放到哪個節(jié)點上,取數(shù)據(jù)時應(yīng)該去哪個節(jié)點上找玻熙。二是數(shù)據(jù)移動:集群擴(kuò)容否彩,新增加節(jié)點時,該節(jié)點上的數(shù)據(jù)從何處來嗦随;集群縮容列荔,要剔除節(jié)點時,該節(jié)點上的數(shù)據(jù)往何處去枚尼。

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

集群的節(jié)點是相對固定和少數(shù)的霎奢,雖然有增加節(jié)點和剔除節(jié)點另玖。但集群里存儲的key,則是完全隨機(jī)梳毙、沒有規(guī)律唤殴、不可預(yù)測、數(shù)量龐多到腥,還非扯涫牛瑣碎。

這就好比一所大學(xué)和它的所有學(xué)生之間的關(guān)系乡范。如果大學(xué)和學(xué)生直接掛鉤的話配名,一定會比較混亂。現(xiàn)實是它們之間又加入了好幾層晋辆,首先有院系渠脉,其次有專業(yè),再者有年級瓶佳,最后還有班級芋膘。經(jīng)過這四層映射之后,關(guān)系就清爽很多了霸饲。

這其實是一個非常重要的結(jié)論为朋,這個世界上沒有什么問題是不能通過加入一層來解決的。如果有厚脉,那就再加入一層习寸。計算機(jī)里也是這樣的。

redis在數(shù)據(jù)和節(jié)點之間又加入了一層傻工,把這層稱為槽(slot)霞溪,因該槽主要和哈希有關(guān)孵滞,又叫哈希槽。

最后變成了鸯匹,節(jié)點上放的是槽坊饶,槽里放的是數(shù)據(jù)。槽解決的是粒度問題忽你,相當(dāng)于把粒度變大了幼东,這樣便于數(shù)據(jù)移動。哈希解決的是映射問題科雳,使用key的哈希值來計算所在的槽根蟹,便于數(shù)據(jù)分配。

可以這樣來理解糟秘,你的學(xué)習(xí)桌子上堆滿了書简逮,亂的很,想找到某本書非常困難尿赚。于是你買了幾個大的收納箱散庶,把這些書按照書名的長度放入不同的收納箱,然后把這些收納箱放到桌子上凌净。

這樣就變成了悲龟,桌子上是收納箱,收納箱里是書籍冰寻。這樣書籍移動很方便须教,搬起一個箱子就走了。尋找書籍也很方便斩芭,只要數(shù)一數(shù)書名的長度轻腺,去對應(yīng)的箱子里找就行了。

其實我們也沒做什么划乖,只是買了幾個箱子贬养,按照某種規(guī)則把書裝入箱子。就這么簡單的舉動琴庵,就徹底改變了原來一盤散沙的狀況误算。是不是有點小小的神奇呢。

一個集群只能有16384個槽迷殿,編號0-16383尉桩。這些槽會分配給集群中的所有主節(jié)點,分配策略沒有要求贪庙≈├纾可以指定哪些編號的槽分配給哪個主節(jié)點。集群會記錄節(jié)點和槽的對應(yīng)關(guān)系止邮。

接下來就需要對key求哈希值这橙,然后對16384取余奏窑,余數(shù)是幾key就落入對應(yīng)的槽里。slot = CRC16(key) % 16384屈扎。

以槽為單位移動數(shù)據(jù)埃唯,因為槽的數(shù)目是固定的,處理起來比較容易鹰晨,這樣數(shù)據(jù)移動問題就解決了墨叛。

使用哈希函數(shù)計算出key的哈希值,這樣就可以算出它對應(yīng)的槽模蜡,然后利用集群存儲的槽和節(jié)點的映射關(guān)系查詢出槽所在的節(jié)點漠趁,于是數(shù)據(jù)和節(jié)點就映射起來了,這樣數(shù)據(jù)分配問題就解決了忍疾。

我想說的是闯传,一般的人只會去學(xué)習(xí)各種技術(shù),高手更在乎如何跳出技術(shù)卤妒,尋求一種解決方案或思路方向甥绿,順著這個方向走下去,八九不離十能找到你想要的答案则披。

集群對命令操作的取舍

客戶端只要和集群中的一個節(jié)點建立鏈接后共缕,就可以獲取到整個集群的所有節(jié)點信息。此外還會獲取所有哈希槽和節(jié)點的對應(yīng)關(guān)系信息士复,這些信息數(shù)據(jù)都會在客戶端緩存起來骄呼,因為這些信息相當(dāng)有用。

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

所以客戶端需要實現(xiàn)一個和集群端一樣的哈希函數(shù)澄峰,先計算出key的哈希值,然后再對16384取余辟犀,這樣就找到了該key對應(yīng)的哈希槽俏竞,利用客戶端緩存的槽和節(jié)點的對應(yīng)關(guān)系信息,就可以找到該key對應(yīng)的節(jié)點了堂竟。

接下來發(fā)送請求就可以了魂毁。還可以把key和節(jié)點的映射關(guān)系緩存起來,下次再請求該key時出嘹,直接就拿到了它對應(yīng)的節(jié)點席楚,不用再計算一遍了。

理論和現(xiàn)實總是有差距的税稼,集群已經(jīng)發(fā)生了變化烦秩,客戶端的緩存還沒來得及更新垮斯。肯定會出現(xiàn)拿到一個key向?qū)?yīng)的節(jié)點發(fā)請求只祠,其實這個key已經(jīng)不在那個節(jié)點上了兜蠕。此時這個節(jié)點應(yīng)該怎么辦?

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

這其實是個選擇問題,也是個哲學(xué)問題岭皂。結(jié)果就是redis集群選擇了后者郊霎。因此,節(jié)點只處理自己擁有的key爷绘,對于不擁有的key將返回重定向錯誤书劝,即-MOVED key 127.0.0.1:6381,客戶端重新向這個新節(jié)點發(fā)送請求土至。

所以說選擇是一種哲學(xué)购对,也是個智慧。稍后再談這個問題陶因。先來看看另一個情況骡苞,和這個問題有些相同點。

redis有一種命令可以一次帶多個key楷扬,如MGET解幽,我把這些稱為多key命令。這個多key命令的請求被發(fā)送到一個節(jié)點上烘苹,這里有一個潛在的問題躲株,不知道大家有沒有想到,就是這個命令里的多個key一定都位于那同一個節(jié)點上嗎镣衡?

就分為兩種情況了霜定,如果多個key不在同一個節(jié)點上,此時節(jié)點只能返回重定向錯誤了廊鸥,但是多個key完全可能位于多個不同的節(jié)點上望浩,此時返回的重定向錯誤就會非常亂,所以redis集群選擇不支持此種情況惰说。

如果多個key位于同一個節(jié)點上呢磨德,理論上是沒有問題的,redis集群是否支持就和redis的版本有關(guān)系了吆视,具體使用時自己測試一下就行了剖张。

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

那么問題來了,如何給這些key起名字才能讓他們落到同一個節(jié)點上顾犹,難不成都要先計算個哈希值倒庵,再取個余數(shù),太麻煩了吧炫刷。當(dāng)然不是這樣了擎宝,redis已經(jīng)幫我們想好了。

可以來簡單推理下浑玛,要想讓兩個key位于同一個節(jié)點上绍申,它們的哈希值必須要一樣。要想哈希值一樣顾彰,傳入哈希函數(shù)的字符串必須一樣极阅。那我們只能傳進(jìn)去兩個一模一樣的字符串了,那不就變成同一個key了涨享,后面的會覆蓋前面的數(shù)據(jù)筋搏。

這里的問題是我們都是拿整個key去計算哈希值,這就導(dǎo)致key和參與計算哈希值的字符串耦合了厕隧,需要將它們解耦才行奔脐,就是key和參與計算哈希值的字符串有關(guān)但是又不一樣。

redis基于這個原理為我們提供了方案吁讨,叫做key哈希標(biāo)簽髓迎。先看例子,{user1000}.following建丧,{user1000}.followers排龄,相信你已經(jīng)看出了門道,就是僅使用Key中的位于{和}間的字符串參與計算哈希值茶鹃。

這樣可以保證哈希值相同,落到相同的節(jié)點上艰亮。但是key又是不同的闭翩,不會互相覆蓋。使用哈希標(biāo)簽把一組相關(guān)的key關(guān)聯(lián)了起來迄埃,問題就這樣被輕松愉快地解決了疗韵。

相信你已經(jīng)發(fā)現(xiàn)了,要解決問題靠的是巧妙的奇思妙想侄非,而不是非要用牛逼的技術(shù)牛逼的算法蕉汪。這就是小強(qiáng)流译,小而強(qiáng)大。

最后再來談選擇的哲學(xué)者疤。redis的核心就是以最快的速度進(jìn)行常用數(shù)據(jù)結(jié)構(gòu)的key/value存取福澡,以及圍繞這些數(shù)據(jù)結(jié)構(gòu)的運(yùn)算。對于與核心無關(guān)的或會拖累核心的都選擇弱化處理或不處理驹马,這樣做是為了保證核心的簡單革砸、快速和穩(wěn)定。

其實就是在廣度和深度面前糯累,redis選擇了深度算利。所以節(jié)點不去處理自己不擁有的key,集群不去支持多key命令泳姐。這樣一方面可以快速地響應(yīng)客戶端效拭,另一方面可以避免在集群內(nèi)部有大量的數(shù)據(jù)傳輸與合并。

單線程模型

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

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

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

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

3、如果使用了多線程俯邓,則需要多線程同步骡楼,這樣實現(xiàn)起來會變的復(fù)雜

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

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

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

事務(wù)

事務(wù)大家都知道朦蕴,就是把多個操作捆綁在一起篮条,要么都執(zhí)行(成功了),要么一個也不執(zhí)行(回滾了)吩抓。redis也是支持事務(wù)的涉茧,但可能和你想要的不太一樣,一起來看看吧疹娶。

redis的事務(wù)可以分為兩步伴栓,定義事務(wù)和執(zhí)行事務(wù)。使用multi命令開啟一個事務(wù),然后把要執(zhí)行的所有命令都依次排上去钳垮。這就定義好了一個事務(wù)惑淳。此時使用exec命令來執(zhí)行這個事務(wù),或使用discard命令來放棄這個事務(wù)饺窿。

你可能希望在你的事務(wù)開始前歧焦,你關(guān)心的key不想被別人操作,那么可以使用watch命令來監(jiān)視這些key短荐,如果開始執(zhí)行前這些key被其它命令操作了則會取消事務(wù)的倚舀。也可以使用unwatch命令來取消對這些key的監(jiān)視。

redis事務(wù)具有以下特點:

1忍宋、如果開始執(zhí)行事務(wù)前出錯痕貌,則所有命令都不執(zhí)行

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

3舵稠、如果執(zhí)行過程中遇到錯誤,會繼續(xù)執(zhí)行下去入宦,不會停止的

4哺徊、對于執(zhí)行過程中遇到錯誤,是不會進(jìn)行回滾的

看完這些乾闰,真想問一句話落追,你這能叫事務(wù)嗎?很顯然涯肩,這并不是我們通常認(rèn)為的事務(wù)轿钠,因為它連原子性都保證不了。保證不了原子性是因為redis不支持回滾病苗,不過它也給出了不支持的理由疗垛。

不支持回滾的理由:

1、redis認(rèn)為硫朦,失敗都是由命令使用不當(dāng)造成

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

3咬展、redis還認(rèn)為泽裳,回滾并不能解決所有問題

哈哈,這就是霸王條款破婆,因此涮总,好像使用redis事務(wù)的不太多

管道

客戶端和集群的交互過程是串行化阻塞式的,即客戶端發(fā)送了一個命令后必須等到響應(yīng)回來后才能發(fā)第二個命令荠割,這一來一回就是一個往返時間妹卿。如果你有很多的命令,都這樣一個一個的來進(jìn)行蔑鹦,會變得很慢夺克。

redis提供了一種管道技術(shù),可以讓客戶端一次發(fā)送多個命令嚎朽,期間不需要等待服務(wù)器端的響應(yīng)铺纽,等所有的命令都發(fā)完了,再依次接收這些命令的全部響應(yīng)哟忍。這就極大地節(jié)省了許多時間狡门,提升了效率。

聰明的你是不是意識到了另外一個問題锅很,多個命令就是多個key啊其馏,這不就是上面提到的多key操作嘛,那么問題來了爆安,你如何保證這多個key都是同一個節(jié)點上的啊叛复,哈哈,redis集群又放棄了對管道的支持扔仓。

不過可以在客戶端模擬實現(xiàn)褐奥,就是使用多個連接往多個節(jié)點同時發(fā)送命令,然后等待所有的節(jié)點都返回了響應(yīng)翘簇,再把它們按照發(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\n3\r\nSET\r\n4\r\nname\r\n$8\r\nlixinjie\r\n

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

單行回復(fù)袖裕,第一個字節(jié)是+

錯誤消息曹抬,第一個字節(jié)是-

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

批量回復(fù)急鳄,第一個字節(jié)是$

多個批量回復(fù)谤民,第一個字節(jié)是*

例如,

+OK\r\n

-ERR Operation against\r\n

:1000\r\n

$6\r\nfoobar\r\n

*2\r\n3\r\nfoo\r\n3\r\nbar\r\n

可見redis的協(xié)議設(shè)計的非常簡單疾宏。

Memo

本文轉(zhuǎn)載至 https://www.cnblogs.com/lixinjie/p/a-key-point-of-redis-in-interview.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末张足,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子坎藐,更是在濱河造成了極大的恐慌为牍,老刑警劉巖哼绑,帶你破解...
    沈念sama閱讀 222,252評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異碉咆,居然都是意外死亡抖韩,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評論 3 399
  • 文/潘曉璐 我一進(jìn)店門疫铜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來茂浮,“玉大人,你說我怎么就攤上這事壳咕∠浚” “怎么了?”我有些...
    開封第一講書人閱讀 168,814評論 0 361
  • 文/不壞的土叔 我叫張陵谓厘,是天一觀的道長幌羞。 經(jīng)常有香客問我,道長竟稳,這世上最難降的妖魔是什么新翎? 我笑而不...
    開封第一講書人閱讀 59,869評論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮住练,結(jié)果婚禮上地啰,老公的妹妹穿的比我還像新娘。我一直安慰自己讲逛,他們只是感情好亏吝,可當(dāng)我...
    茶點故事閱讀 68,888評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著盏混,像睡著了一般蔚鸥。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上许赃,一...
    開封第一講書人閱讀 52,475評論 1 312
  • 那天止喷,我揣著相機(jī)與錄音,去河邊找鬼混聊。 笑死弹谁,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的句喜。 我是一名探鬼主播预愤,決...
    沈念sama閱讀 41,010評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼咳胃!你這毒婦竟也來了植康?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,924評論 0 277
  • 序言:老撾萬榮一對情侶失蹤展懈,失蹤者是張志新(化名)和其女友劉穎销睁,沒想到半個月后供璧,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,469評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡冻记,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,552評論 3 342
  • 正文 我和宋清朗相戀三年嗜傅,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(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
  • 我被黑心中介騙來泰國打工豁跑, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留廉涕,地道東北人。 一個月前我還...
    沈念sama閱讀 49,099評論 3 378
  • 正文 我出身青樓艇拍,卻偏偏與公主長得像火的,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子淑倾,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,691評論 2 361

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