日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
Redis為什么快?你只知道單線程和基于內(nèi)存?抱歉我不能給你offer...

面試場(chǎng)景

面試官:Redis有哪些數(shù)據(jù)類型?

我:String,List,set,zset,hash

面試官:沒(méi)了?

我:哦哦哦,還有HyperLogLog,bitMap,GeoHash,BloomFilter

面試官:就這?回家等通知吧。

前言

我敢肯定,第一個(gè)回答,100%的人都能說(shuō)上來(lái),但是第二個(gè)回答能回答上來(lái)的人可能就不多了,但是這也不是我今天探討的話題。

我就從我自己的去面試的回答思路,以及作為一個(gè)面試官他想聽(tīng)到的標(biāo)準(zhǔn)答案來(lái)給大家出一期,Redis基礎(chǔ)類型的文章(系列文章),寫(xiě)這個(gè)的時(shí)候我還是很有心得的,不知道大家有多少人跟我最開(kāi)始一樣,面試官問(wèn)有哪些類型,就回答出那五種就結(jié)束了,如果你是這樣的可以在評(píng)論區(qū)留言,讓我看看有多少人是這樣的。

但是,一場(chǎng)面試少說(shuō)都是半小時(shí)起步上不封頂,你這樣一句話就回答了這么重要的五個(gè)知識(shí)點(diǎn),這個(gè)結(jié)果是你想要的么?是面試官想要的么?

我再問(wèn)你一個(gè)問(wèn)題,你可能就懵逼了:String在Redis底層是怎么存儲(chǔ)的?這些數(shù)據(jù)類型在Redis中是怎么存放的?Redis快的原因就只有單線程和基于內(nèi)存么?

寶貝,觸及知識(shí)盲區(qū)沒(méi)?不慌,我以前也是這樣的,我以為我背出那五種就完事了,結(jié)果被面試官安排了一波,后面我苦心修煉,總算是好了一點(diǎn),現(xiàn)在對(duì)緩存也是非常熟悉了,你不會(huì)沒(méi)事,有我嘛,乖。

正文

Redis是C語(yǔ)言開(kāi)發(fā)的,C語(yǔ)言自己就有字符類型,但是Redis卻沒(méi)直接采用C語(yǔ)言的字符串類型,而是自己構(gòu)建了動(dòng)態(tài)字符串(SDS)的抽象類型。

就好比這樣的一個(gè)命令,其實(shí)我是在Redis創(chuàng)建了兩個(gè)SDS,一個(gè)是名為aobing的Key SDS,另一個(gè)是名為cool的Value SDS,就算是字符類型的List,也是由很多的SDS構(gòu)成的Key和Value罷了。

SDS在Redis中除了用作字符串,還用作緩沖區(qū)(buffer),那到這里大家都還是有點(diǎn)疑惑的,C語(yǔ)言的字符串不好么為啥用SDS?SDS長(zhǎng)啥樣?有什么優(yōu)點(diǎn)呢?

為此我去找到了Redis的源碼,可以看到SDS值的結(jié)果大概是這樣的,源碼的在GitHub上是開(kāi)源的大家一搜就有了。

 
 
 
 
  1. struct sdshdr{  
  2.     int len;  
  3.     int free;  
  4.     char buf[];  

回到最初的問(wèn)題,為什么Redis用了自己新開(kāi)發(fā)的SDS,而不用C語(yǔ)言的字符串?那好我們?nèi)タ纯此麄兊膮^(qū)別。

SDS與C字符串的區(qū)別

  1. 計(jì)數(shù)方式不同

C語(yǔ)言對(duì)字符串長(zhǎng)度的統(tǒng)計(jì),就完全來(lái)自遍歷,從頭遍歷到末尾,直到發(fā)現(xiàn)空字符就停止,以此統(tǒng)計(jì)出字符串的長(zhǎng)度,這樣獲取長(zhǎng)度的時(shí)間復(fù)雜度來(lái)說(shuō)是0(n),大概就像下面這樣:

但是這樣的計(jì)數(shù)方式會(huì)留下隱患,所以Redis沒(méi)有采用C的字符串,我后面會(huì)提到。

而Redis我在上面已經(jīng)給大家看過(guò)結(jié)構(gòu)了,他自己本身就保存了長(zhǎng)度的信息,所以我們獲取長(zhǎng)度的時(shí)間復(fù)雜度為0(1),是不是發(fā)現(xiàn)了Redis快的一點(diǎn)小細(xì)節(jié)了?還沒(méi)完,不止這些。

  1. 杜絕緩沖區(qū)溢出

字符串拼接是我們經(jīng)常做的操作,在C和Redis中一樣,也是很常見(jiàn)的操作,但是問(wèn)題就來(lái)了,C是不記錄字符串長(zhǎng)度的,一旦我們調(diào)用了拼接的函數(shù),如果沒(méi)有提前計(jì)算好內(nèi)存,是會(huì)產(chǎn)生緩存區(qū)溢出的。

比如本來(lái)字符串長(zhǎng)這樣:

你現(xiàn)在需要在后面拼接 ,但是你沒(méi)計(jì)算好內(nèi)存,結(jié)果就可能這樣了:

這是你要的結(jié)果么?很顯然,不是,你的結(jié)果意外的被修改了,這要是放在線上的系統(tǒng),這不是完了?那Redis是怎么避免這樣的情況的?

我們都知道,他結(jié)構(gòu)存儲(chǔ)了當(dāng)前長(zhǎng)度,還有free未使用的長(zhǎng)度,那簡(jiǎn)單呀,你現(xiàn)在做了拼接操作,我去判斷一些是否可以放得下,如果長(zhǎng)度夠就直接執(zhí)行,如果不夠,那我就進(jìn)行擴(kuò)容。

這些大家在Redis源碼里面都是可以看到對(duì)應(yīng)的API的,后面我就不一一貼源碼了,有興趣的可以自己去看一波,需要一點(diǎn)C語(yǔ)言的基礎(chǔ)。

  1.  減少修改字符串時(shí)帶來(lái)的內(nèi)存重分配次數(shù)

C語(yǔ)言字符串底層也是一個(gè)數(shù)組,每次創(chuàng)建的時(shí)候就創(chuàng)建一個(gè)N+1長(zhǎng)度的字符,多的那個(gè)1,就是為了保存空字符的,這個(gè)空字符也是個(gè)坑,但是不是這個(gè)環(huán)節(jié)探討的內(nèi)容。

Redis是個(gè)高速緩存數(shù)據(jù)庫(kù),如果我們需要對(duì)字符串進(jìn)行頻繁的拼接和截?cái)嗖僮?,如果我們?xiě)代碼忘記了重新分配內(nèi)存,就可能造成緩沖區(qū)溢出,以及內(nèi)存泄露。

內(nèi)存分配算法很耗時(shí),且不說(shuō)你會(huì)不會(huì)忘記重新分配內(nèi)存,就算你全部記得,對(duì)于一個(gè)高速緩存數(shù)據(jù)庫(kù)來(lái)說(shuō),這樣的開(kāi)銷也是我們應(yīng)該要避免的。

Redis為了避免C字符串這樣的缺陷,就分別采用了兩種解決方案,去達(dá)到性能最大化,空間利用最大化:

  •  空間預(yù)分配:當(dāng)我們對(duì)SDS進(jìn)行擴(kuò)展操作的時(shí)候,Redis會(huì)為SDS分配好內(nèi)存,并且根據(jù)特定的公式,分配多余的free空間,還有多余的1byte空間(這1byte也是為了存空字符),這樣就可以避免我們連續(xù)執(zhí)行字符串添加所帶來(lái)的內(nèi)存分配消耗。

比如現(xiàn)在有這樣的一個(gè)字符:

我們調(diào)用了拼接函數(shù),字符串邊長(zhǎng)了,Redis還會(huì)根據(jù)算法計(jì)算出一個(gè)free值給他備用:

我們?cè)倮^續(xù)拼接,你會(huì)發(fā)現(xiàn),備用的free用上了,省去了這次的內(nèi)存重分配:

  •  惰性空間釋放:剛才提到了會(huì)預(yù)分配多余的空間,很多小伙伴會(huì)擔(dān)心帶來(lái)內(nèi)存的泄露或者浪費(fèi),別擔(dān)心,Redis大佬一樣幫我們想到了,當(dāng)我們執(zhí)行完一個(gè)字符串縮減的操作,redis并不會(huì)馬上收回我們的空間,因?yàn)榭梢灶A(yù)防你繼續(xù)添加的操作,這樣可以減少分配空間帶來(lái)的消耗,但是當(dāng)你再次操作還是沒(méi)用到多余空間的時(shí)候,Redis也還是會(huì)收回對(duì)于的空間,防止內(nèi)存的浪費(fèi)的。

還是一樣的字符串:

當(dāng)我們調(diào)用了刪減的函數(shù),并不會(huì)馬上釋放掉free空間:

如果我們需要繼續(xù)添加這個(gè)空間就能用上了,減少了內(nèi)存的重分配,如果空間不需要了,調(diào)用函數(shù)刪掉就好了:

  1. 二進(jìn)制安全

仔細(xì)看的仔肯定看到上面我不止一次提到了空字符也就是’0‘,C語(yǔ)言是判斷空字符去判斷一個(gè)字符的長(zhǎng)度的,但是有很多數(shù)據(jù)結(jié)構(gòu)經(jīng)常會(huì)穿插空字符在中間,比如圖片,音頻,視頻,壓縮文件的二進(jìn)制數(shù)據(jù),就比如下面這個(gè)單詞,他只能識(shí)別前面的 不能識(shí)別后面的字符,那對(duì)于我們開(kāi)發(fā)者而言,這樣的結(jié)果顯然不是我們想要的對(duì)不對(duì)。

Redis就不存在這個(gè)問(wèn)題了,他不是保存了字符串的長(zhǎng)度嘛,他不判斷空字符,他就判斷長(zhǎng)度對(duì)不對(duì)就好了,所以redis也經(jīng)常被我們拿來(lái)保存各種二進(jìn)制數(shù)據(jù),我反正是用的很high,經(jīng)常用來(lái)保存小文件的二進(jìn)制。

資料參考:Redis設(shè)計(jì)與實(shí)現(xiàn)

總結(jié)

大家是不是發(fā)現(xiàn),一個(gè)小小的SDS居然有這么多道理在這?

以前就知道Redis快,最多說(shuō)個(gè)Redis是單線程的,說(shuō)個(gè)多路IO復(fù)用,說(shuō)個(gè)基于內(nèi)存的操作就完了,現(xiàn)在是不是還可以展開(kāi)說(shuō)說(shuō)了?

本文是系列文的第一章,后續(xù)會(huì)陸續(xù)更新的,不知道這樣的類型大家是否喜歡。

大家一同去面試,一樣的問(wèn)題,就是有人能過(guò),有人不能過(guò),大家經(jīng)常歸咎于自己學(xué)歷,自己過(guò)往經(jīng)歷的原因,但是你可以問(wèn)一下自己,底層的細(xì)節(jié)字節(jié)是否有深究呢?細(xì)節(jié)往往才是最重要的,也是最少人知道的,如何和別的仔拉開(kāi)差距拿到offer,我想就是這樣些細(xì)節(jié)決定的吧,背誰(shuí)不會(huì)呢?


標(biāo)題名稱:Redis為什么快?你只知道單線程和基于內(nèi)存?抱歉我不能給你offer...
文章URL:http://www.dlmjj.cn/article/dhsdgid.html