日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

bat 连续读取两行_Redis底层数据结构解析(BAT大厂必问)

發布時間:2024/7/23 数据库 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 bat 连续读取两行_Redis底层数据结构解析(BAT大厂必问) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Redis是一個key-value存儲系統,現在在各種系統中的使用越來越多,大部分情況下是因為其高性能的特性,被當做緩存使用。Redis由于其豐富的數據結構也可以被應用到其他場景。Redis是一個K-V的非關系型數據庫(NoSQL),常見的NoSQL數據庫有:K-V數據庫如Redis、Memcached,列式數據庫如大數據組件HBase,文檔數據庫如mogoDB。Redis應用廣泛,尤其是被作為緩存使用。

Redis的具有很多優勢:

(1)讀寫性能高--100000次/s以上的讀速度,80000次/s以上的寫速度;

(2)K-V,value支持的數據類型很多:字符串(String),隊列(List),哈希(Hash),集合(Sets),有序集合(Sorted Sets)5種不同的數據類型。

(3)原子性,Redis的所有操作都是單線程原子性的。

(4)特性豐富--支持訂閱-發布模式,通知、設置key過期等特性。

(5)在Redis3.0 版本引入了Redis集群,可用于分布式部署。

有需要Redis大廠面試題的小伙伴點擊這里

IT架構師luke:Redis面試題(BAT大廠真題)?zhuanlan.zhihu.com

Redis數據類型及其底層實現方式

Redis是由C語言編寫的。Redis支持5種數據類型,以K-V形式進行存儲,K是String類型的,V支持5種不同的數據類型,分別是:string,list,hash,set,sorted set,每一種數據結構都有其特定的應用場景。從內部實現的角度來看是如何更好的實現這些數據類型。Redis底層數據結構有以下數據類型:簡單動態字符串(SDS),鏈表,字典,跳躍表,整數集合,壓縮列表,對象。接下來,就探討一下Redis是怎么通過這些數據結構來實現value的5種類型的。

簡單動態字符串(simple dynamic string SDS)

String的數據類型是由SDS實現的。Redis并沒有采用C語言的字符串表示,而是自己構建了一種名為SDS的抽象類型,并將SDS作為Redis的默認字符串表示。

redis>SET msg "hello world"

OK

上邊設置key=msg,value=hello world的鍵值對,它們的底層存儲是:鍵(key)是字符串類型,其底層實現是一個保存著“msg”的SDS。值(value)是字符串類型,其底層實現是一個保存著“hello world”的SDS。

注意:SDS除了用于實現字符串類型,還被用作AOF持久化時的緩沖區。

SDS的定義為:

/*

* 保存字符串對象的結構

*/

struct sdshdr {

// buf 中已占用空間的長度

int len;

// buf 中剩余可用空間的長度

int free;

// 數據空間

char buf[];

};

為什么要使用SDS:

我們一定會思考,redis為什么不使用C語言的字符串而是費事搞一個SDS呢,這是因為C語言用N+1的字符數組來表示長度為N的字符串,這樣做在獲取字符串長度,字符串擴展等操作方面效率較低,并且無法滿足redis對字符串在安全性、效率以及功能方面的要求。

獲取字符串長度(SDS O(1))

在C語言字符串中,為了獲取一個字符串的長度,必須遍歷整個字符串,時間復雜度為O(1),而SDS中,有專門用于保存字符串長度的變量,所以可以在O(1)時間內獲得。

防止緩沖區溢出

C字符串,容易導致緩沖區溢出,假設在程序中存在內存緊鄰的字符串s1和s2,s1保存redis,s2保存MongoDB,如下圖:

如果我們現在將s1 的內容修改為redis cluster,但是又忘了重新為s1 分配足夠的空間,這時候就會出現以下問題:

因為s1和s2是緊鄰的,所以原本s2 中的內容已經被S1的內容給占領了,s2 現在為 cluster,而不是“Mongodb”。而Redis中的SDS就杜絕了發生緩沖區溢出的可能性。

當我們需要對一個SDS 進行修改的時候,redis 會在執行拼接操作之前,預先檢查給定SDS 空間是否足夠(free記錄了剩余可用的數據長度),如果不夠,會先拓展SDS 的空間,然后再執行拼接操作。

減少擴展或收縮字符串帶來的內存重分配次數

當字符串進行擴展或收縮時,都會對內存空間進行重新分配。

1. 字符串拼接會產生字符串的內存空間的擴充,在拼接的過程中,原來的字符串的大小很可能小于拼接后的字符串的大小,那么這樣的話,就會導致一旦忘記申請分配空間,就會導致內存的溢出。

2. 字符串在進行收縮的時候,內存空間會相應的收縮,而如果在進行字符串的切割的時候,沒有對內存的空間進行一個重新分配,那么這部分多出來的空間就成為了內存泄露。

比如:字符串"redis",當進行字符串拼接時,將redis+cluster=13,會將SDS的長度修改為13,同時將free也改為13,這意味著進行預分配了,將buffer大小變為了26。這是為了如果再次執行字符串拼接操作,如果拼接的字符串長度<13,就不需要重新進行內存分配了。

通過這種預分配策略,SDS將連續增長N次字符串所需的內存重分配次數從必定N次降低為最多N次。通過惰性空間釋放,SDS 避免了縮短字符串時所需的內存重分配操作,并未將來可能有的增長操作提供了優化。

二進制安全

C 字符串中的字符必須符合某種編碼,并且除了字符串的末尾之外,字符串里面不能包含空字符,否則最先被程序讀入的空字符將被誤認為是字符串結尾,這些限制使得C字符串只能保存文本數據,而不能保存想圖片,音頻,視頻,壓縮文件這樣的二進制數據。

 但是在Redis中,不是靠空字符來判斷字符串的結束的,而是通過len這個屬性。那么,即便是中間出現了空字符對于SDS來說,讀取該字符仍然是可以的。

但是,SDS依然可以兼容部分C字符串函數。

鏈表

鏈表是list的實現方式之一。當list包含了數量較多的元素,或者列表中包含的元素都是比較長的字符串時,Redis會使用鏈表作為實現List的底層實現。此鏈表是雙向鏈表:

typedef struct listNode{

struct listNode *prev;

struct listNode * next;

void * value;

}

一般我們通過操作list來操作鏈表:

typedef struct list{

//表頭節點

listNode * head;

//表尾節點

listNode * tail;

//鏈表長度

unsigned long len;

//節點值復制函數

void *(*dup) (void *ptr);

//節點值釋放函數

void (*free) (void *ptr);

//節點值對比函數

int (*match)(void *ptr, void *key);

}

鏈表結構的特點是可以快速的在表頭和表尾插入和刪除元素,但查找復雜度高,是列表的底層實現之一,也因此列表沒有提供判斷某一元素是否在列表中的借口,因為在鏈表中查找復雜度高。

字典

字典,又稱為符號表(symbol table)、關聯數組(associative array)或映射(map),是一種用于保存鍵值對的抽象數據結構。 

 在字典中,一個鍵(key)可以和一個值(value)進行關聯,字典中的每個鍵都是獨一無二的。在C語言中,并沒有這種數據結構,但是Redis 中構建了自己的字典實現。

redis > SET msg "hello world"

OK

Redis本身的K-V存儲就是利用字典這種數據結構的,另外value類型的哈希表也是通過這個實現的。

哈希表dicy的定義為:

typedef struct dictht {

//哈希表數組

dictEntry **table;

//哈希表大小

unsigned long size;

//哈希表大小掩碼,用于計算索引值

unsigned long sizemask;

//該哈希表已有節點的數量

unsigned long used;

}

我們可以想到對比Java hashMap的實現方式,在dictht中,table數組的類型是:

typeof struct dictEntry{

//鍵

void *key;

//值

union{

void *val;

uint64_tu64;

int64_ts64;

}

struct dictEntry *next;

}

我們存入里面的key 并不是直接的字符串,而是一個hash 值,通過hash 算法,將字符串轉換成對應的hash 值,然后在dictEntry 中找到對應的位置。

這時候我們會發現一個問題,如果出現hash 值相同的情況怎么辦?Redis 采用了鏈地址法來解決hash沖突。這與hashmap的實現類似。

注意:Redis又在dictht的基礎上,又抽象了一層字典dict,其定義為:

typedef struct dict {

// 類型特定函數

dictType *type;

// 私有數據

void *privedata;

// 哈希表

dictht ht[2];

// rehash 索引

in trehashidx;

}

type 屬性 和privdata 屬性是針對不同類型的鍵值對,為創建多態字典而設置的。

ht 屬性是一個包含兩個項(兩個哈希表)的數組,如圖:

解決hash沖突:采用鏈地址法來實現。

擴充Rehash: 隨著對哈希表的不斷操作,哈希表保存的鍵值對會逐漸的發生改變,為了讓哈希表的負載因子維持在一個合理的范圍之內,我們需要對哈希表的大小進行相應的擴展或者壓縮,這時候,我們可以通過 rehash(重新散列)操作來完成。其實現方式和hashmap略有不同,因為dict有兩個hash表dictht,所以它是通過這兩個dictht互相進行轉移的。

比如:

上圖這種情況,代表要進行擴容了,所以就要將ht[0]的數據轉移到ht[1]中。ht[1]創建為2*ht[0].size大小的,如下圖:

將ht[0]釋放,然后將ht[1]設置成ht[0],最后為ht[1]分配一個空白哈希表:

其實上邊的擴容過程和Java 的HashMap具體的擴容實現方式還是挺像的。

漸進式rehash:在實際開發過程中,這個rehash 操作并不是一次性、集中式完成的,而是分多次、漸進式地完成的。采用漸進式rehash 的好處在于它采取分而治之的方式,避免了集中式rehash 帶來的龐大計算量。

漸進式rehash 的詳細步驟:

1、為ht[1] 分配空間,讓字典同時持有ht[0]和ht[1]兩個哈希表

2、在幾點鐘維持一個索引計數器變量rehashidx,并將它的值設置為0,表示rehash 開始

3、在rehash 進行期間,每次對字典執行CRUD操作時,程序除了執行指定的操作以外,還會將ht[0]中的數據rehash 到ht[1]表中,并且將rehashidx加一

4、當ht[0]中所有數據轉移到ht[1]中時,將rehashidx 設置成-1,表示rehash 結束

跳躍表

Redis 只在兩個地方用到了跳躍表,一個是實現有序集合鍵(sorted Sets),另外一個是在集群節點中用作內部數據結構。

其實跳表主要是來替代平衡二叉樹的,比起平衡樹來說,跳表的實現要簡單直觀的多。

跳躍表(skiplist)是一種有序數據結構,它通過在每個節點中維持多個指向其他節點的指針,從而達到快速查找訪問節點的目的。跳躍表是一種隨機化的數據,跳躍表以有序的方式在層次化的鏈表中保存元素,效率和平衡樹媲美 ——查找、刪除、添加等操作都可以在O(logn)期望時間下完成。

Redis 的跳躍表 主要由兩部分組成:zskiplist(鏈表)和zskiplistNode (節點):

typedef struct zskiplistNode{

//層

struct zskiplistLevel{

//前進指針

struct zskiplistNode *forward;

//跨度

unsigned int span;

} level[];

//后退指針

struct zskiplistNode *backward;

//分值

double score;

//成員對象

robj *obj;

}

1、層:level 數組可以包含多個元素,每個元素都包含一個指向其他節點的指針。level數組的每個元素都包含:前進指針:用于指向表尾方向的前進指針,跨度:用于記錄兩個節點之間的距離

2、后退指針:用于從表尾向表頭方向訪問節點

3、分值和成員:跳躍表中的所有節點都按分值從小到大排序(按照這個進行排序的,也就是平衡二叉樹(搜索樹的)的節點大小)。成員對象指向一個字符串,這個字符串對象保存著一個SDS值(實際存儲的值)

typedef struct zskiplist {

//表頭節點和表尾節點

structz skiplistNode *header,*tail;

//表中節點數量

unsigned long length;

//表中層數最大的節點的層數

int level;

}zskiplist;

從結構圖中我們可以清晰的看到,header,tail分別指向跳躍表的頭結點和尾節點。level 用于記錄最大的層數,length 用于記錄我們的節點數量。

 跳躍表是有序集合的底層實現之一

主要有zskiplist 和zskiplistNode兩個結構組成

每個跳躍表節點的層高都是1至32之間的隨機數

在同一個跳躍表中,多個節點可以包含相同的分值,但每個節點的對象必須是唯一的

節點按照分值的大小從大到小排序,如果分值相同,則按成員對象大小排序

怎么使用跳表來實現O(logn)的增刪改查??

其實跳表的實現原理,我們可以結合二分法來看。

比如上圖,我們要查找55,如果通過遍歷,則必須得從頭遍歷到最后一個才能找到,所以在數組實現中,我們可以使用二分法來實現,但是在鏈表中,我們沒辦法直接通過下標來訪問元素,所以一般我們可以用二叉搜索樹,平衡樹來存儲元素,我們知道跳表就是來替代平衡樹的,那么跳表是如何快速查詢呢?看下圖:

從上圖我們可以看到,我們通過第4層,只需一步便可找到55,另外最耗時的訪問46需要6次查詢。即L4訪問55,L3訪問21、55,L2訪問37、55,L1訪問46。我們直覺上認為,這樣的結構會讓查詢有序鏈表的某個元素更快。這種實現方式跟二分很相似,其時間復雜度就是O(logn)。其插入,刪除都是O(logn)。

我們可以看到,redis正是通過定義這種結構來實現上邊的過程,其層數最高為32層,也就是他可以存儲2^32次方的數據,其查找過程與上圖很類似。

整數集合(Intset)

《Redis 設計與實現》 中這樣定義整數集合:“整數集合是集合建(sets)的底層實現之一,當一個集合中只包含整數,且這個集合中的元素數量不多時,redis就會使用整數集合intset作為集合的底層實現?!?/p>

我們可以這樣理解整數集合,他其實就是一個特殊的集合,里面存儲的數據只能夠是整數,并且數據量不能過大。

typedef struct intset{

//編碼方式

uint32_t enconding;

// 集合包含的元素數量

uint32_t length;

//保存元素的數組

int8_t contents[];

}

整數集合是集合建的底層實現之一.

整數集合的底層實現為數組,這個數組以有序,無重復的范式保存集合元素,在有需要時,程序會根據新添加的元素類型改變這個數組的類型.

壓縮列表

壓縮列表是列表鍵(list)和哈希鍵(hash)的底層實現之一。當一個列表鍵只有少量列表項,并且每個列表項要么就是小整數,要么就是長度比較短的字符串,那么Redis 就會使用壓縮列表來做列表鍵的底層實現。

1、zlbytes:用于記錄整個壓縮列表占用的內存字節數

2、zltail:記錄要列表尾節點距離壓縮列表的起始地址有多少字節

3、zllen:記錄了壓縮列表包含的節點數量。

4、entryX:要說列表包含的各個節點

5、zlend:用于標記壓縮列表的末端

壓縮列表是一種為了節約內存而開發的順序型數據結構

壓縮列表被用作列表鍵和哈希鍵的底層實現之一

壓縮列表可以包含多個節點,每個節點可以保存一個字節數組或者整數值

添加新節點到壓縮列表,可能會引發連鎖更新操作。

如果你喜歡我寫的技術文章以及面試總結,歡迎關注收看我的視頻,并且點贊、收藏、關注我哦。

我是luke,感謝你的關注!

也可以加入到我的圈子一起學習成長哦【架構師之路】點擊鏈接申請加入圈子

架構師之路 - 知乎?www.zhihu.com

總結

以上是生活随笔為你收集整理的bat 连续读取两行_Redis底层数据结构解析(BAT大厂必问)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 视频这里只有精品 | 成人午夜在线视频 | 国产精品久久久不卡 | 椎名由奈在线观看 | 日韩欧美在线观看一区 | 国产午夜伦鲁鲁 | 快色在线 | 三级av网 | 激情福利社 | 国产www性| 可以免费观看av | 深夜视频一区二区三区 | 久久夜色精品国产欧美乱极品 | 日日夜夜干 | 亚洲成人生活片 | 国内成人在线 | 人人舔人人| 国产精品日韩电影 | 成人片在线免费看 | 人人插人人 | 怡红院久久 | 日韩精品在线视频免费观看 | 天天综合一区 | 国产1区2区3区中文字幕 | 久久久久久久久久一区二区三区 | 99精品热视频 | 色噜噜在线播放 | 在线观看欧美亚洲 | 国产精品免费视频观看 | 黄av网站| 男人的网址 | 国产精品久久久久久99 | 国产理论片在线观看 | 亚洲图片在线 | 2017天天干 | 国产美女作爱全过程免费视频 | 欧美人与性动交a欧美精品 日韩免费高清视频 | 网站国产 | 草在线视频| 午夜男人的天堂 | 国内精品福利视频 | 红桃视频网站 | 99综合久久| 国产原创麻豆 | 久久二区三区 | 久久久少妇 | 亚洲精品一区三区三区在线观看 | 狠狠鲁影院 | 欧美综合一区二区 | 影音av在线| av在线网站观看 | 日韩在线视频一区二区三区 | 亚洲人免费 | 免费一区二区三区四区 | 夜夜噜噜噜| 在线资源站 | 一二三四av | 国产又粗又猛视频 | 亚洲av人人夜夜澡人人 | 日韩一区免费观看 | 男裸体无遮挡网站 | 日韩在线精品视频一区二区涩爱 | 国产小视频免费 | 人人91| 国产视频一区在线观看 | 国产成人无码精品久久二区三 | 久久一区国产 | 日韩毛片 | 欧美性久久 | 欧美亚洲二区 | 久久久久国产综合av天堂 | 国产在线一级 | 免费在线黄 | 男人插入女人下面视频 | 国产精品夜夜 | 欧美片网站免费 | xxxx视频在线观看 | 狼人av在线 | 在线电影一区 | 亚洲午夜视频在线观看 | 国产精品欧美激情在线 | 搡8o老女人老妇人老熟 | 狠狠干狠狠撸 | 狠狠爱成人 | 国产精品腿扒开做爽爽爽挤奶网站 | 自拍日韩亚洲一区在线 | 卡一卡二视频 | 亚洲一区二区自偷自拍 | 久久精品人妻av一区二区三区 | 最新三级网站 | 国产在线超碰 | 九九热在线观看视频 | 亚洲精品久久久乳夜夜欧美 | 亚洲欧美日本一区二区三区 | www.av黄色| 国内自拍真实伦在线观看 | 亚洲欧美色图在线 | 欧美在线亚洲 | 亚洲综合资源 |