偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

Redis基本類型及其數(shù)據(jù)結(jié)構(gòu)

開(kāi)發(fā) 前端 Redis
以前在使用Redis的時(shí)候,只是簡(jiǎn)單地使用它提供的基本數(shù)據(jù)類型和接口,并沒(méi)有深入研究它底層的數(shù)據(jù)結(jié)構(gòu)。最近打算重新學(xué)習(xí)梳理一下Redis方面的知識(shí),所以打算從介紹Redis的基本類型及其數(shù)據(jù)結(jié)構(gòu)入手。

以前在使用Redis的時(shí)候,只是簡(jiǎn)單地使用它提供的基本數(shù)據(jù)類型和接口,并沒(méi)有深入研究它底層的數(shù)據(jù)結(jié)構(gòu)。最近打算重新學(xué)習(xí)梳理一下Redis方面的知識(shí),所以打算從介紹Redis的基本類型及其數(shù)據(jù)結(jié)構(gòu)入手。

[[275524]]

redisObject

Redis的key是頂層模型,它的value是扁平化的。Redis中,所有的value都是一個(gè)object,它的結(jié)構(gòu)如下:

  1. typedef struct redisObject { 
  2.  unsigned [type] 4; 
  3.  unsigned [encoding] 4; 
  4.  unsigned [lru] REDIS_LRU_BITS; 
  5.  int refcount; 
  6.  void *ptr; 
  7. } robj; 

簡(jiǎn)單介紹一下這幾個(gè)字段:

  • type:數(shù)據(jù)類型,就是我們熟悉的string、hash、list等。
  • encoding:內(nèi)部編碼,其實(shí)就是本文要介紹的數(shù)據(jù)結(jié)構(gòu)。指的是當(dāng)前這個(gè)value底層是用的什么數(shù)據(jù)結(jié)構(gòu)。因?yàn)橥粋€(gè)數(shù)據(jù)類型底層也有多種數(shù)據(jù)結(jié)構(gòu)的實(shí)現(xiàn),所以這里需要指定數(shù)據(jù)結(jié)構(gòu)。
  • REDIS_LRU_BITS:當(dāng)前對(duì)象可以保留的時(shí)長(zhǎng)。這個(gè)我們?cè)诤竺嬷v鍵的過(guò)期策略的時(shí)候講。
  • refcount:對(duì)象引用計(jì)數(shù),用于GC。
  • ptr:指針,指向以encoding的方式實(shí)現(xiàn)這個(gè)對(duì)象的實(shí)際地址。
Redis基本類型及其數(shù)據(jù)結(jié)構(gòu)

string

在Redis內(nèi)部,string類型有兩種底層儲(chǔ)存結(jié)構(gòu)。Redis會(huì)根據(jù)存儲(chǔ)的數(shù)據(jù)及用戶的操作指令自動(dòng)選擇合適的結(jié)構(gòu):

  • int:存放整數(shù)類型;
  • SDS:存放浮點(diǎn)、字符串、字節(jié)類型;
  1. SDS: 簡(jiǎn)單動(dòng)態(tài)字符串 simple dynamic string 

SDS

SDS的內(nèi)部數(shù)據(jù)結(jié)構(gòu):

  1. typedef struct sdshdr { 
  2.  // buf中已經(jīng)占用的字符長(zhǎng)度 
  3.  unsigned int len; 
  4.  // buf中剩余可用的字符長(zhǎng)度 
  5.  unsigned int free
  6.  // 數(shù)據(jù)空間 
  7.  char buf[]; 

可見(jiàn),其底層是一個(gè)char數(shù)組。buf最大容量為512M,里面可以放字符串、浮點(diǎn)數(shù)和字節(jié)。所以你甚至可以放一張序列化后的圖片。它為什么沒(méi)有直接使用數(shù)組,而是包裝成了這樣的數(shù)據(jù)結(jié)構(gòu)呢?

因?yàn)閎uf會(huì)有動(dòng)態(tài)擴(kuò)容和縮容的需求。如果直接使用數(shù)組,那每次對(duì)字符串的修改都會(huì)導(dǎo)致重新分配內(nèi)存,效率很低。

buf的擴(kuò)容過(guò)程如下:

  • 如果修改后len長(zhǎng)度將小于1M,這時(shí)分配給free的大小和len一樣,例如修改過(guò)后為10字節(jié), 那么給free也是10字節(jié),buf實(shí)際長(zhǎng)度變成了10 + 10 + 1 = 21byte
  • 如果修改后len長(zhǎng)度將大于等于1M,這時(shí)分配給free的長(zhǎng)度為1M,例如修改過(guò)后為30M,那么給free是1M.buf實(shí)際長(zhǎng)度變成了30M + 1M + 1byte 
Redis基本類型及其數(shù)據(jù)結(jié)構(gòu)

惰性空間釋放指的是當(dāng)字符串縮短時(shí),并沒(méi)有真正的縮容,而是移動(dòng)free的指針。這樣將來(lái)字符串長(zhǎng)度增加時(shí),就不用重新分配內(nèi)存了。但這樣會(huì)造成內(nèi)存浪費(fèi),Redis提供了API來(lái)真正釋放內(nèi)存。

list

list底層有兩種數(shù)據(jù)結(jié)構(gòu):鏈表linkedlist和壓縮列表ziplist。當(dāng)list元素個(gè)數(shù)少且元素內(nèi)容長(zhǎng)度不大時(shí),使用ziplist實(shí)現(xiàn),否則使用linkedlist。

鏈表

Redis使用的鏈表是雙向鏈表。為了方便操作,使用了一個(gè)list結(jié)構(gòu)來(lái)持有這個(gè)鏈表。如圖所示:

Redis基本類型及其數(shù)據(jù)結(jié)構(gòu)
  1. typedef struct list{ 
  2.  //表頭節(jié)點(diǎn) 
  3.  listNode *head; 
  4.  //表尾節(jié)點(diǎn) 
  5.  listNode *tail; 
  6.  //鏈表所包含的節(jié)點(diǎn)數(shù)量 
  7.  unsigned long len; 
  8.  //節(jié)點(diǎn)值復(fù)制函數(shù) 
  9.  void *(*dup)(void *ptr); 
  10.  //節(jié)點(diǎn)值釋放函數(shù) 
  11.  void *(*free)(void *ptr); 
  12.  //節(jié)點(diǎn)值對(duì)比函數(shù) 
  13.  int (*match)(void *ptr,void *key); 
  14. }list; 

data存的其實(shí)也是一個(gè)指針。鏈表里面的元素是上面介紹的string。因?yàn)槭请p向鏈表,所以可以很方便地把它當(dāng)成一個(gè)?;蛘哧?duì)列來(lái)使用。

壓縮列表

與上面的鏈表相對(duì)應(yīng),壓縮列表有點(diǎn)兒類似數(shù)組,通過(guò)一片連續(xù)的內(nèi)存空間,來(lái)存儲(chǔ)數(shù)據(jù)。不過(guò),它跟數(shù)組不同的一點(diǎn)是,它允許存儲(chǔ)的數(shù)據(jù)大小不同。每個(gè)節(jié)點(diǎn)上增加一個(gè)length屬性來(lái)記錄這個(gè)節(jié)點(diǎn)的長(zhǎng)度,這樣比較方便地得到下一個(gè)節(jié)點(diǎn)的位置。

Redis基本類型及其數(shù)據(jù)結(jié)構(gòu)

上圖的各字段含義為:

  • zlbytes:列表的總長(zhǎng)度
  • zltail:指向最末元素
  • zllen:元素的個(gè)數(shù)
  • entry:元素的內(nèi)容,里面記錄了前一個(gè)Entry的長(zhǎng)度,用于方便雙向遍歷
  • zlend:恒為0xFF,作為ziplist的定界符

壓縮列表不只是list的底層實(shí)現(xiàn),也是hash的底層實(shí)現(xiàn)之一。當(dāng)hash的元素個(gè)數(shù)少且內(nèi)容長(zhǎng)度不大時(shí),使用壓縮列表來(lái)實(shí)現(xiàn)。

hash

hash底層有兩種實(shí)現(xiàn):壓縮列表和字典(dict)。壓縮列表剛剛上面已經(jīng)介紹過(guò)了,下面主要介紹一下字典的數(shù)據(jù)結(jié)構(gòu)。

字典

字典其實(shí)就類似于Java語(yǔ)言中的Map,Python語(yǔ)言中的dict。與Java中的HashMap類似,Redis底層也是使用的散列表作為字典的實(shí)現(xiàn),解決hash沖突使用的是鏈表法。Redis同樣使用了一個(gè)數(shù)據(jù)結(jié)構(gòu)來(lái)持有這個(gè)散列表:

Redis基本類型及其數(shù)據(jù)結(jié)構(gòu)

在鍵增加或減少時(shí),會(huì)擴(kuò)容或縮容,并且進(jìn)行rehash,根據(jù)hash值重新計(jì)算索引值。那如果這個(gè)字典太大了怎么辦呢?

為了解決一次性擴(kuò)容耗時(shí)過(guò)多的情況,可以將擴(kuò)容操作穿插在插入操作的過(guò)程中,分批完成。當(dāng)負(fù)載因子觸達(dá)閾值之后,只申請(qǐng)新空間,但并不將老的數(shù)據(jù)搬移到新散列表中。當(dāng)有新數(shù)據(jù)要插入時(shí),將新數(shù)據(jù)插入新散列表中,并且從老的散列表中拿出一個(gè)數(shù)據(jù)放入到新散列表。每次插入一個(gè)數(shù)據(jù)到散列表,都重復(fù)上面的過(guò)程。經(jīng)過(guò)多次插入操作之后,老的散列表中的數(shù)據(jù)就一點(diǎn)一點(diǎn)全部搬移到新散列表中了。這樣沒(méi)有了集中的一次一次性數(shù)據(jù)搬移,插入操作就都變得很快了。這個(gè)過(guò)程也被稱為漸進(jìn)式rehash。

set

set里面沒(méi)有重復(fù)的集合。set的實(shí)現(xiàn)比較簡(jiǎn)單。如果是整數(shù)類型,就直接使用整數(shù)集合intset。使用二分查找來(lái)輔助,速度還是挺快的。不過(guò)在插入的時(shí)候,由于要移動(dòng)元素,時(shí)間復(fù)雜度是O(N)。

如果不是整數(shù)類型,就使用上面在hash那一節(jié)介紹的字典。key為set的值,value為空。

zset

zset是可排序的set。與hash的實(shí)現(xiàn)方式類似,如果元素個(gè)數(shù)不多且不大,就使用壓縮列表ziplist來(lái)存儲(chǔ)。不過(guò)由于zset包含了score的排序信息,所以在ziplist內(nèi)部,是按照score排序遞增來(lái)存儲(chǔ)的。意味著每次插入數(shù)據(jù)都要移動(dòng)之后的數(shù)據(jù)。

跳表

跳表(skiplist)是另一種實(shí)現(xiàn)dict的數(shù)據(jù)結(jié)構(gòu)。跳表是對(duì)鏈表的一個(gè)增強(qiáng)。我們?cè)谑褂面湵淼臅r(shí)候,即使元素的有序排列的,但如果要查找一個(gè)元素,也需要從頭一個(gè)個(gè)查找下去,時(shí)間復(fù)雜度是O(N)。而跳表顧名思義,就是跳躍了一些元素,可以抽象多層。

如下圖所示,比如我們要查找8,先在最上層L2查找,發(fā)現(xiàn)在1和9之間;然后去L1層查找,發(fā)現(xiàn)在5和9之間;然后去L0查找,發(fā)現(xiàn)在7和9之間,然后找到8。

當(dāng)元素比較多時(shí),使用跳表可以顯著減少查找的次數(shù)。

Redis基本類型及其數(shù)據(jù)結(jié)構(gòu)

同list類似,Redis內(nèi)部也不是直接使用的跳表,而是使用了一個(gè)自定義的數(shù)據(jù)結(jié)構(gòu)來(lái)持有跳表。下圖左邊藍(lán)色部分是skiplist,右邊是4個(gè)zskiplistNode。zskiplistNode內(nèi)部有很多層L1、L2等,指針指向這一層的下一個(gè)結(jié)點(diǎn)。BW是回退指針(backward),用于查找的時(shí)候回退。然后下面是score和對(duì)象本身object。

Redis基本類型及其數(shù)據(jù)結(jié)構(gòu)

總結(jié)

Redis對(duì)外暴露的是對(duì)象(數(shù)據(jù)類型),而每個(gè)對(duì)象都是用一個(gè)redisObject持有,通過(guò)不同的編碼,映射到不同的數(shù)據(jù)結(jié)構(gòu)。從最開(kāi)始的那個(gè)圖可以知道,有時(shí)候不同對(duì)象可能會(huì)底層使用同一種數(shù)據(jù)結(jié)構(gòu),比如壓縮列表和字典等。

在了解數(shù)據(jù)結(jié)構(gòu)后,我們就能夠更清楚應(yīng)該選用什么樣的對(duì)象,出現(xiàn)問(wèn)題時(shí)應(yīng)該如何優(yōu)化了。

責(zé)任編輯:華軒 來(lái)源: 今日頭條
相關(guān)推薦

2020-10-21 12:45:12

Redis數(shù)據(jù)結(jié)構(gòu)

2020-03-02 13:45:18

Redis數(shù)據(jù)結(jié)構(gòu)Java

2023-11-12 21:49:10

Redis數(shù)據(jù)庫(kù)

2022-05-23 08:19:19

Redis數(shù)據(jù)結(jié)構(gòu)內(nèi)存

2010-03-05 10:04:38

Python運(yùn)算符

2009-07-21 09:31:00

Scala基本類型文本

2019-09-18 08:31:47

數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì)

2020-06-29 07:44:36

Redis

2024-01-26 06:42:05

Redis數(shù)據(jù)結(jié)構(gòu)

2019-06-12 22:51:57

Redis軟件開(kāi)發(fā)

2020-12-31 05:31:01

數(shù)據(jù)結(jié)構(gòu)算法

2010-04-23 15:07:07

Oracle數(shù)字

2019-04-17 15:35:37

Redis數(shù)據(jù)庫(kù)數(shù)據(jù)結(jié)構(gòu)

2019-10-29 08:59:16

Redis底層數(shù)據(jù)

2009-04-12 09:08:32

Symbian諾基亞移動(dòng)OS

2020-10-20 09:27:48

Python開(kāi)發(fā)數(shù)據(jù)類型

2023-10-31 08:51:25

數(shù)據(jù)結(jié)構(gòu)存儲(chǔ)數(shù)據(jù)

2011-03-31 15:41:51

Cacti數(shù)據(jù)表結(jié)構(gòu)

2012-04-28 14:21:47

Java數(shù)據(jù)結(jié)構(gòu)線性結(jié)構(gòu)

2021-05-16 19:23:11

引用類型包裝
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)