Redis-動態(tài)字符串

1.動態(tài)字符串

redis中的字符串,是對c語言中的字符串(以空字符串結尾的字符數(shù)組)進行了一層包裝,自己定義了一個結構塊,名為動態(tài)字符串(simple dynamic string ,SDS)的抽象類型,并且將SDS作為redis默認字符串類型(可以表示字符串盟戏、整數(shù)、位圖)

參考:redis的設計與實現(xiàn)

1.1.動態(tài)字符串結構

struct SDS{
    //記錄buf數(shù)組中已經(jīng)使用的字節(jié)數(shù)量
    //等于sds所保存的字符串長度
    int8 len;  // 1byte
    // 記錄buf中未使用的字節(jié)數(shù)量
    int8 free; // 1byte
    //類型標記  int(long 類型整數(shù)), embstr 嵌入式字符串(編碼后長度小于 44 字節(jié)的字符串) , raw(sds 字符串)
    int8 flags;  // 1byte
    //字符數(shù)組,用于保存字符串
    byte[] buf[];  // 內(nèi)聯(lián)數(shù)組,長度為 len
}

1.2.SDS字符串優(yōu)點

為什么redis性能高,為什么redis要封裝字符串.其原因主要在以下幾點:

  1. 在常數(shù)的獲取復雜度上:

    SDS可以直接返回字符串長度,C語言的需要遍歷

  2. 緩沖區(qū)溢出問題:

    在擴容或者修改時,redis每次會檢查free的值,從而直接指導buf是否夠用.不夠會進行擴容處理.而C語言中的字符串則可能出現(xiàn)由于內(nèi)存已經(jīng)分配,修改其中的字符串導致內(nèi)存溢出問題.

  3. 內(nèi)存分配問題

    每次C語言處理字符串時,需要重新分配內(nèi)存,但是redis由于是自定義的結構,相當于可以預支一定的內(nèi)存.所以可以減少分配次數(shù).在SDS中,如果len小于1MB,則free和len相同,否則redis會每次預支1MB的free

  4. 惰性釋放

    redis的惰性釋放由于優(yōu)化redis中字符串的縮短操作.當字符串縮短時,redis不會立馬就釋放空間,而是使用free記錄可利用的空間,便于以后使用.

1.3.字符串常用關命令

命令 描述
set key value 存放一個key-value鍵值對
get key 根據(jù)key獲取對應的值
strlen key 獲取字符串長度
getrange key index1 index2 獲取字符串指定索引范圍字符
getset key newValue 獲取key的值,并為其設置新的值
mset key1 value1 key2 value2 批量設置key value值
mget key1 key2 批量獲取key的值
setnx key value 不存在key 就插入 key value ,返回值 1 成功 0 失敗
setrange key index value 找到指定的key,使用value值,從index索引處開始替換
incr key 遞增,只對值為數(shù)字生效
incrby key 值 指定自增的值
decr key 遞減
decrby key 值 指定遞減的值
incrbyfloat key 值 指定遞增的小數(shù),不推薦可能精度丟失
append key 值 為key的值追加內(nèi)容
127.0.0.1:6379> set num 123456789
OK
127.0.0.1:6379> get num
"123456789"
127.0.0.1:6379> strlen num
(integer) 9
127.0.0.1:6379> getrange num 0 1
"12"
127.0.0.1:6379> getset num 1234567890
"123456789"
127.0.0.1:6379> get num
"1234567890"
127.0.0.1:6379> mset num1 1 num2 2
OK
127.0.0.1:6379> get num1
"1"
127.0.0.1:6379> mget num1 num2
1) "1"
2) "2"
127.0.0.1:6379> setnx num2 2
(integer) 0
127.0.0.1:6379> setnx num3 3
(integer) 1
127.0.0.1:6379> setrange num 1 000000000
(integer) 10
127.0.0.1:6379> get num
"1000000000"
127.0.0.1:6379> incr num1
(integer) 2
127.0.0.1:6379> incrby num2 10
(integer) 12
127.0.0.1:6379> decr num1
(integer) 1
127.0.0.1:6379> get num1
"1"
127.0.0.1:6379> decrby num2 10
(integer) 2
127.0.0.1:6379> incrbyfloat num1 0.222
"1.222"
127.0.0.1:6379> append num 1
(integer) 11
127.0.0.1:6379> get num
"10000000001"

1.4.字符串類型問題

使用命令 object encoding key可以獲取redis中存儲的數(shù)據(jù)的類型.每個數(shù)據(jù)在redis中都是一個對象.

這個命令的返回值有:

  1. int long整形
  2. embstr 嵌入式字符串(redis 5.x 新增的)
  3. raw redis中的動態(tài)字符串

在使用該命令時,會發(fā)現(xiàn)一個有意思的問題.

127.0.0.1:6379> set num '1234567890 1234567890 1234567890 1234567890'
OK
127.0.0.1:6379> strlen num
(integer) 43
127.0.0.1:6379> object encoding num
"embstr"
127.0.0.1:6379>set num '1234567890 1234567890 1234567890 1234567890 1'
OK
127.0.0.1:6379> object encoding num
"raw"

注意:當字符串長度為不小于44時,該類型為raw類型

在redis中,每個數(shù)據(jù)都會當做一個對象處理,而每個對象都會有個頭信息.每個對象的頭信息一般是==16==個字節(jié)

struct RedisObject {
 int4 type; // 4bits     類型
 int4 encoding; // 4bits  
 int24 lru; // 24bits   3字節(jié)  LRU 信息
 int32 refcount; // 4bytes   4字節(jié)
 void *ptr; // 8bytes,64-bit system   8字節(jié)
};

其中:

  1. refcount引用計數(shù),當引用計數(shù)為0時,對象就會被銷毀,內(nèi)存會回收.4字節(jié)
  2. ptr指針指向?qū)ο髢?nèi)容的具體存儲位置.8字節(jié)

SDS結構體的大小

struct SDS {
 int8 capacity; // 1byte
 int8 len; // 1byte
 int8 flags; // 1byte
 byte[] content; // 內(nèi)聯(lián)數(shù)組,長度為 capacity
}

SDS的大小是: 1+1+1+?,所以一個SDS的大小最小是3個字節(jié).所以存在redis中一個字符串數(shù)據(jù)大小,最小16+3個字節(jié),19個字節(jié).

而內(nèi)存分配器等分內(nèi)存的大小的單位是2的冪次:2/4/8/16/32/64.為了能容納一個完成的字符串,那么最少分配32個字節(jié)空間.如果字符串稍微大一點就是64個字節(jié)空間.如果總體超出了 64 字節(jié)花鹅,Redis 認為它是一個大字符串,不再使用 emdstr 形式存儲,而該用 raw 形式点骑。

為什么redis會在超過64個字節(jié)時當做raw處理呢.或者說為什么字符串長度為44時,就變?yōu)榱藃aw呢?

首先,raw是指redis動態(tài)字符串,是radis對c語言原生字符串的一種包裝.而原生c語言的字符串,最后一個始終使用\0的字符串結尾,是為了方便使用glibc的字符串函數(shù)處理,及便于打印輸出.而64-19(所有頭占用的)=45個字符串.字符串又是以\0結尾,所以embstr 最大能容納的字符串長度就是 44.

image-20210202204954583.png

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市谍夭,隨后出現(xiàn)的幾起案子黑滴,更是在濱河造成了極大的恐慌,老刑警劉巖紧索,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件跷跪,死亡現(xiàn)場離奇詭異,居然都是意外死亡齐板,警方通過查閱死者的電腦和手機吵瞻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來甘磨,“玉大人橡羞,你說我怎么就攤上這事〖糜撸” “怎么了卿泽?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長滋觉。 經(jīng)常有香客問我签夭,道長,這世上最難降的妖魔是什么椎侠? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任第租,我火速辦了婚禮,結果婚禮上我纪,老公的妹妹穿的比我還像新娘慎宾。我一直安慰自己,他們只是感情好浅悉,可當我...
    茶點故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布趟据。 她就那樣靜靜地躺著,像睡著了一般术健。 火紅的嫁衣襯著肌膚如雪汹碱。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天荞估,我揣著相機與錄音咳促,去河邊找鬼色难。 笑死,一個胖子當著我的面吹牛等缀,可吹牛的內(nèi)容都是我干的枷莉。 我是一名探鬼主播,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼尺迂,長吁一口氣:“原來是場噩夢啊……” “哼笤妙!你這毒婦竟也來了?” 一聲冷哼從身側響起噪裕,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤蹲盘,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后膳音,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體召衔,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年祭陷,在試婚紗的時候發(fā)現(xiàn)自己被綠了苍凛。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡兵志,死狀恐怖醇蝴,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情想罕,我是刑警寧澤悠栓,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站按价,受9級特大地震影響惭适,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜楼镐,卻給世界環(huán)境...
    茶點故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一癞志、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧鸠蚪,春花似錦今阳、人聲如沸师溅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽墓臭。三九已至蘸鲸,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間窿锉,已是汗流浹背酌摇。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工膝舅, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人窑多。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓仍稀,卻偏偏與公主長得像,于是被迫代替她去往敵國和親埂息。 傳聞我的和親對象是個殘疾皇子技潘,可洞房花燭夜當晚...
    茶點故事閱讀 44,619評論 2 354

推薦閱讀更多精彩內(nèi)容