掃二維碼與項(xiàng)目經(jīng)理溝通
我們在微信上24小時期待你的聲音
解答本文疑問/技術(shù)咨詢/運(yùn)營咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流
我是 Redis,給開發(fā)者提供了 String(字符串)、Hashes(散列表)、Lists(列表)、Sets(無序集合)、Sorted Sets(可根據(jù)范圍查詢的排序集合)、Bitmap(位圖)、HyperLogLog、Geospatial (地理空間)和 Stream(流)等數(shù)據(jù)類型。

在吉安等地區(qū),都構(gòu)建了全面的區(qū)域性戰(zhàn)略布局,加強(qiáng)發(fā)展的系統(tǒng)性、市場前瞻性、產(chǎn)品創(chuàng)新能力,以專注、極致的服務(wù)理念,為客戶提供成都做網(wǎng)站、成都網(wǎng)站制作 網(wǎng)站設(shè)計制作按需網(wǎng)站開發(fā),公司網(wǎng)站建設(shè),企業(yè)網(wǎng)站建設(shè),品牌網(wǎng)站設(shè)計,營銷型網(wǎng)站,外貿(mào)營銷網(wǎng)站建設(shè),吉安網(wǎng)站建設(shè)費(fèi)用合理。
接下來我要重點(diǎn)介紹的是,String 數(shù)據(jù)類型的使用技巧和使用場景,以及String 數(shù)據(jù)類型底層數(shù)據(jù)結(jié)構(gòu)原理。
數(shù)據(jù)類型的使用技法和以及每種數(shù)據(jù)類型底層實(shí)現(xiàn)原理是你核心筑基必經(jīng)之路,好好修煉。
筑基穩(wěn)固,修煉心法,讓你的程序更快還能做到極致節(jié)省內(nèi)存。
字符串類型的使用最為廣泛,比如計數(shù)器、緩存、分布式鎖、用于存儲登錄后的用戶信息,key = token,value = Java 對象序列化成 JSON 后的字符串。
如下指令。
SET user:token:666 {"name": "碼哥",“gender”: “M”,“city”:"shenzhen"}接下來,我先帶你深入了解 String 類型,底層數(shù)據(jù)結(jié)構(gòu)和使用場景。
MySQL:“你都是用 C 語言開發(fā)出來的,C 語言本就有字符串,嚇唬誰呢?!?/p>
格局能不能打開一點(diǎn),我并沒有直接使用 C 語言的字符串,而是自己搞了一個 SDS 結(jié)構(gòu)體來表示字符串。SDS 的全稱是 Simple Dynamic String,中文叫做“簡單動態(tài)字符串”。
MySQL:“搞 SDS 的目的是啥?”
字符串使用最為廣泛,我要保證能支持豐富和高性能的字符串操作函數(shù),能保存二進(jìn)制數(shù)據(jù),同時還能節(jié)省內(nèi)存占用。
實(shí)現(xiàn)了你們領(lǐng)導(dǎo)平時經(jīng)常對你們提出的既要又要還要的目標(biāo)。
先看 C 語言字符串?dāng)?shù)組的結(jié)構(gòu)。比如通過 char *s = "MageByte"定義字符串變量。
注意,數(shù)組的最后一個字符串是 "\0",它表示字符串的結(jié)束。
因?yàn)?C 語言標(biāo)準(zhǔn)庫 string.h中的字符串有以下幾點(diǎn)不足,所以我才設(shè)計了 SDS。
\0這種特殊字符是因?yàn)?nbsp;\0 在 C 語言字符串中表示結(jié)尾。MySQL:“說說 SDS 結(jié)構(gòu)體吧,你是如何解決這些問題的。”
為了存儲字符串實(shí)際內(nèi)容,我需要有一個 char 類型數(shù)組來存儲,使用一個 int 類型的 len 字段用于記錄 char 數(shù)組使用了多少字節(jié)。
除此之外,還要有一個 int 類型 的 alloc 字段記錄分配的 char 數(shù)組總長度,alloc - len 就等于 char 類型的 buf 數(shù)組未使用的字節(jié)數(shù)(Redis 7.0 已經(jīng)去掉了表示未使用字節(jié)數(shù) free 字段)。
?SDS 也遵循 C 字符串以空字符“\0”結(jié)尾的慣例,保存空字符的大小不計算在 SDS 的 len 屬性中。
此外,添加空字符串“\0” 到字符串末尾等操作,都是由 SDS 函數(shù)自動完成的。
O(1) 時間復(fù)雜度獲取字符串長度
SDS 中 len 保存了字符串的長度,實(shí)現(xiàn)了O(1) 時間復(fù)雜度獲取字符串長度。
你注意到了沒,SDS 結(jié)構(gòu)有一個 flags 字段,表示的是 SDS 類型。實(shí)際上 SDS 一共設(shè)計了 5 種類型,分別是sdshdr5、sdshdr8、sdshdr16、sdshdr32 和 sdshdr64,區(qū)別在于數(shù)組的 len 長度和分配空間長度 alloc。
比如 sdshdr8。
struct __attribute__ ((__packed__)) sdshdr8 {
uint8_t len;
uint8_t alloc;
unsigned char flags;
char buf[];
};len、alloc 字段都是 uint8_t 這個類型,在 Java 中 int 就是 32 位,而 C 語言里面有不同長度的 int 值,uint8_t 就是占 8 位的無符號 int 值,能表示的最大值就是 2^8-1,那它的 buf 數(shù)組,最大長度就是 2^8 -1。
之所以這么設(shè)計,就是為了針對不同大小的字符串,使用不同的 SDS 類型保存,從而節(jié)省內(nèi)存占用。?
MySQL:“SDS 能存儲多大的字符串?”
alloc 表示當(dāng)前 sds 結(jié)構(gòu)允許容納的最大字符長度, 比如 uint32_t alloc? 的取值范圍是 0~2^32 = 4294967296。理論上 char 數(shù)組最大長度為 4294967296,一個 char 字符占用一個字節(jié),可以存儲 4 G,更不用說 sdshdr64 了。
這些都是理論值,實(shí)際上 Redis 內(nèi)部會限制最大的字符串長度是 512M。?
我還對 String 類型的數(shù)據(jù)采用了三種編碼格式來存儲,分別是 int、embstr、raw,你可使用 OBJECT encoding key 來查值對象所使用的編碼類型。
編碼選擇流程如圖 2-3 所示。
MySQL:“__attribute__ ((__packed__))是什么玩意?”
這是我使用了專門的編譯優(yōu)化手段來節(jié)省內(nèi)存空間。作用就是告訴編譯器,不要使用字節(jié)對齊的方式,而是采用緊湊的方式分配內(nèi)存。
默認(rèn)情況下,編譯器會按照 8 字節(jié)對齊的方式分配內(nèi)存,即使這個變量的大小不到 8 字節(jié)。
使用了 __attribute__ ((__packed__)) 定義結(jié)構(gòu)體,編譯器會按照實(shí)際占用來分配內(nèi)存空間。?
SDS 不僅可以存儲 String 類型數(shù)據(jù),還能存儲二進(jìn)制數(shù)據(jù)。SDS 并不是通過“\0” 來判斷字符串結(jié)束,用的是 len 標(biāo)志結(jié)束,所以可以直接將二進(jìn)制數(shù)據(jù)存儲。
在需要對 SDS 的空間進(jìn)行擴(kuò)容時,不僅僅分配所需的空間,還會分配額外的未使用空間。
通過預(yù)分配策略,減少了執(zhí)行字符串增長所需的內(nèi)存重新分配次數(shù),降低由于字符串增加操作的性能損耗。
當(dāng)對 SDS 進(jìn)行縮短操作時,程序并不會回收多余的內(nèi)存空間,如果后面需要 append 追加操作,則直接使用 buf 數(shù)組 alloc - len中未使用的空間。
通過惰性空間釋放策略,避免了減小字符串所需的內(nèi)存重新分配操作,為未來增長操作提供了優(yōu)化。?
我相信你會經(jīng)常遇到要生成唯一 ID 的場景,比如標(biāo)識每次請求、生成一個訂單編號、創(chuàng)建用戶需要創(chuàng)建一個用戶 ID。
分布式 ID 生成器需要滿足以下特性。
Redis 集群能保證高可用和高性能,為了節(jié)省內(nèi)存,ID 可以使用數(shù)字的形式,并且通過遞增的方式來創(chuàng)建新的 ID。
防止重啟數(shù)據(jù)丟失,你還需要把 Redis AOF 持久化開啟。
MySQL:“開啟 AOF 持久,為了性能設(shè)置成 everysec 策略還是有可能丟失一秒的數(shù)據(jù),所以你還可以使用一個異步機(jī)制將生成的最大 ID 持久化到一個 MySQL。”
好主意,在生成 ID 之后發(fā)送一條消息到 MQ 消息隊(duì)列中,把值持久化到 MySQL 中。
我提供了 INCR 指令,它能把 key 中存儲的數(shù)字加 1 并返回客戶端。如果 key 不存在,那么 key 的 value 先被初始化成 0,再執(zhí)行加 1 操作并返回給客戶端。
該指令的值限制在 64 位有符號數(shù)字之內(nèi)。
String 類型的實(shí)戰(zhàn)以及底層存儲原理就到這里了,接下來我會繼續(xù)介紹其他數(shù)據(jù)類型的底層存儲原理和實(shí)戰(zhàn)。

我們在微信上24小時期待你的聲音
解答本文疑問/技術(shù)咨詢/運(yùn)營咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流