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

歡迎訪問 生活随笔!

生活随笔

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

数据库

Redis存储(实现)原理

發布時間:2024/4/13 数据库 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Redis存储(实现)原理 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

數據模型

set hello word 為例,因為Redis 是KV 的數據庫,它是通過hashtable 實現的(我們把這個叫做外層的哈希)。所以每個鍵值對都會有一個dictEntry(源碼位置:dict.h),里面指向了key 和value 的指針。next 指向下一個dictEntry。

typedef struct dictEntry {void *key; /* key 關鍵字定義*/union {void *val; uint64_t u64; /* value 定義*/int64_t s64; double d;} v;struct dictEntry *next; /* 指向下一個鍵值對節點*/ } dictEntry;

key 是字符串,但是Redis 沒有直接使用C 的字符數組,而是存儲在自定義的SDS中。

value 既不是直接作為字符串存儲,也不是直接存儲在SDS 中,而是存儲在redisObject 中。實際上五種常用的數據類型的任何一種,都是通過redisObject 來存儲的。

redisObject

redisObject 定義在src/server.h 文件中。

typedef struct redisObject {unsigned type:4; /* 對象的類型,包括:OBJ_STRING、OBJ_LIST、OBJ_HASH、OBJ_SET、OBJ_ZSET */unsigned encoding:4; /* 具體的數據結構*/unsigned lru:LRU_BITS; /* 24 位,對象最后一次被命令程序訪問的時間,與內存回收有關*/int refcount; /* 引用計數。當refcount 為0 的時候,表示該對象已經不被任何對象引用,則可以進行垃圾回收了*/void *ptr; /* 指向對象實際的數據結構*/ } robj;

可以使用type 命令來查看對外的類型。

127.0.0.1:6379> type qs string 127.0.0.1:6379> set number 1 OK 127.0.0.1:6379> set qs "is a good teacher in gupao, have crossed mountains and sea " OK 127.0.0.1:6379> set jack bighead OK 127.0.0.1:6379> object encoding number "int" 127.0.0.1:6379> object encoding jack "embstr" 127.0.0.1:6379> object encoding qs "raw"

字符串類型的內部編碼有三種:

1、int,存儲8 個字節的長整型(long,2^63-1)。

2、embstr, 代表embstr 格式的SDS(Simple Dynamic String 簡單動態字符串),存儲小于44 個字節的字符串。

3、raw,存儲大于44 個字節的字符串(3.2 版本之前是39 字節)。為什么是39?

/* object.c */ #define OBJ_ENCODING_EMBSTR_SIZE_LIMIT 44

問題1、什么是SDS?

Redis 中字符串的實現。

在3.2 以后的版本中,SDS 又有多種結構(sds.h):sdshdr5、sdshdr8、sdshdr16、sdshdr32、sdshdr64,用于存儲不同的長度的字符串,分別代表2^5=32byte,2^8=256byte,2^16=65536byte=64KB,2^32byte=4GB。

/* sds.h */ struct __attribute__ ((__packed__)) sdshdr8 { uint8_t len; /* 當前字符數組的長度*/ uint8_t alloc; /*當前字符數組總共分配的內存大小*/ unsigned char flags; /* 當前字符數組的屬性、用來標識到底是sdshdr8 還是sdshdr16 等*/ char buf[]; /* 字符串真正的值*/ };

問題2、為什么Redis 要用SDS 實現字符串?

我們知道,C 語言本身沒有字符串類型(只能用字符數組char[]實現)。

1、使用字符數組必須先給目標變量分配足夠的空間,否則可能會溢出。

2、如果要獲取字符長度,必須遍歷字符數組,時間復雜度是O(n)。

3、C 字符串長度的變更會對字符數組做內存重分配。

4、通過從字符串開始到結尾碰到的第一個'\0'來標記字符串的結束,因此不能保存圖片、音頻、視頻、壓縮文件等二進制(bytes)保存的內容,二進制不安全。

SDS 的特點:

1、不用擔心內存溢出問題,如果需要會對SDS 進行擴容。

2、獲取字符串長度時間復雜度為O(1),因為定義了len 屬性。

3、通過“空間預分配”( sdsMakeRoomFor)和“惰性空間釋放”,防止多次重分配內存。

4、判斷是否結束的標志是len 屬性(它同樣以'\0'結尾是因為這樣就可以使用C語言中函數庫操作字符串的函數了),可以包含'\0'。

?

問題3、embstr 和raw 的區別?

embstr 的使用只分配一次內存空間(因為RedisObject 和SDS 是連續的),而raw需要分配兩次內存空間(分別為RedisObject 和SDS 分配空間)。

因此與raw 相比,embstr 的好處在于創建時少分配一次空間,刪除時少釋放一次空間,以及對象的所有數據連在一起,尋找方便。

而embstr 的壞處也很明顯,如果字符串的長度增加需要重新分配內存時,整個RedisObject 和SDS 都需要重新分配空間,因此Redis 中的embstr 實現為只讀。

問題4:int 和embstr 什么時候轉化為raw?

當int 數據不再是整數, 或大小超過了long 的范圍(2^63-1=9223372036854775807)時,自動轉化為embstr。

127.0.0.1:6379> set k1 1 OK 127.0.0.1:6379> append k1 a (integer) 2 127.0.0.1:6379> object encoding k1 "raw"

問題5:明明沒有超過閾值,為什么變成raw 了?

127.0.0.1:6379> set k2 a OK 127.0.0.1:6379> object encoding k2 "embstr" 127.0.0.1:6379> append k2 b (integer) 2 127.0.0.1:6379> object encoding k2 "raw"

對于embstr,由于其實現是只讀的,因此在對embstr 對象進行修改時,都會先轉化為raw 再進行修改。

因此,只要是修改embstr 對象,修改后的對象一定是raw 的,無論是否達到了44個字節。

?

問題6:當長度小于閾值時,會還原嗎?

關于Redis 內部編碼的轉換,都符合以下規律:編碼轉換在Redis 寫入數據時完成,且轉換過程不可逆,只能從小內存編碼向大內存編碼轉換(但是不包括重新set)。

?

問題7:為什么要對底層的數據結構進行一層包裝呢?

通過封裝,可以根據對象的類型動態地選擇存儲結構和可以使用的命令,實現節省空間和優化查詢速度。

?

總結

以上是生活随笔為你收集整理的Redis存储(实现)原理的全部內容,希望文章能夠幫你解決所遇到的問題。

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