Redis-动态字符串

1.动态字符串

redis中的字符串,是对c语言中的字符串(以空字符串结尾的字符数组)进行了一层包装,自己定义了一个结构块,名为动态字符串(simple dynamic string ,SDS)的抽象类型,并且将SDS作为redis默认字符串类型(可以表示字符串、整数、位图)

参考:redis的设计与实现

1.1.动态字符串结构

struct SDS{
    //记录buf数组中已经使用的字节数量
    //等于sds所保存的字符串长度
    int8 len;  // 1byte
    // 记录buf中未使用的字节数量
    int8 free; // 1byte
    //类型标记  int(long 类型整数), embstr 嵌入式字符串(编码后长度小于 44 字节的字符串) , raw(sds 字符串)
    int8 flags;  // 1byte
    //字符数组,用于保存字符串
    byte[] buf[];  // 内联数组,长度为 len
}

1.2.SDS字符串优点

为什么redis性能高,为什么redis要封装字符串.其原因主要在以下几点:

  1. 在常数的获取复杂度上:

    SDS可以直接返回字符串长度,C语言的需要遍历

  2. 缓冲区溢出问题:

    在扩容或者修改时,redis每次会检查free的值,从而直接指导buf是否够用.不够会进行扩容处理.而C语言中的字符串则可能出现由于内存已经分配,修改其中的字符串导致内存溢出问题.

  3. 内存分配问题

    每次C语言处理字符串时,需要重新分配内存,但是redis由于是自定义的结构,相当于可以预支一定的内存.所以可以减少分配次数.在SDS中,如果len小于1MB,则free和len相同,否则redis会每次预支1MB的free

  4. 惰性释放

    redis的惰性释放由于优化redis中字符串的缩短操作.当字符串缩短时,redis不会立马就释放空间,而是使用free记录可利用的空间,便于以后使用.

1.3.字符串常用关命令

命令 描述
set key value 存放一个key-value键值对
get key 根据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 递增,只对值为数字生效
incrby key 值 指定自增的值
decr key 递减
decrby key 值 指定递减的值
incrbyfloat key 值 指定递增的小数,不推荐可能精度丢失
append key 值 为key的值追加内容
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中存储的数据的类型.每个数据在redis中都是一个对象.

这个命令的返回值有:

  1. int long整形
  2. embstr 嵌入式字符串(redis 5.x 新增的)
  3. raw redis中的动态字符串

在使用该命令时,会发现一个有意思的问题.

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中,每个数据都会当做一个对象处理,而每个对象都会有个头信息.每个对象的头信息一般是==16==个字节

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

其中:

  1. refcount引用计数,当引用计数为0时,对象就会被销毁,内存会回收.4字节
  2. ptr指针指向对象内容的具体存储位置.8字节

SDS结构体的大小

struct SDS {
 int8 capacity; // 1byte
 int8 len; // 1byte
 int8 flags; // 1byte
 byte[] content; // 内联数组,长度为 capacity
}

SDS的大小是: 1+1+1+?,所以一个SDS的大小最小是3个字节.所以存在redis中一个字符串数据大小,最小16+3个字节,19个字节.

而内存分配器等分内存的大小的单位是2的幂次:2/4/8/16/32/64.为了能容纳一个完成的字符串,那么最少分配32个字节空间.如果字符串稍微大一点就是64个字节空间.如果总体超出了 64 字节,Redis 认为它是一个大字符串,不再使用 emdstr 形式存储,而该用 raw 形式。

为什么redis会在超过64个字节时当做raw处理呢.或者说为什么字符串长度为44时,就变为了raw呢?

首先,raw是指redis动态字符串,是radis对c语言原生字符串的一种包装.而原生c语言的字符串,最后一个始终使用\0的字符串结尾,是为了方便使用glibc的字符串函数处理,及便于打印输出.而64-19(所有头占用的)=45个字符串.字符串又是以\0结尾,所以embstr 最大能容纳的字符串长度就是 44.

image-20210202204954583.png

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容