这是我参与更文挑战的第1天,活动详情查看: 更文挑战
概述:
我们都知道redis底层是用C语言实现的,而C的字符串是以‘\0’来判断结束的,是一个对特殊字符存储不安全的。那redis是怎么做到存储二进制安全的呢?
重头戏-redis中的字符串
redis中还是有用到C中字符串的地方,比如一些不需要修改的字面量一些reids要输出的日志信息等。而需要修改的字符串redis抽象成了我们的SDS对象:简单动态字符串(simple dynamic string,SDS)。这部分的源码对应在这两个文件中
sds.h
和 sds.c
SDS的定义
每个 sds.h/sdshdr
结构表示一个 SDS 值,结构如下:
1 | c复制代码struct sdshdr { |
我们的buf中存放的就是我们真正的按照\0结束的字符串内容(在3.2版本之后此处改成了一个指针会根据不同长度指向不同的结构),len是我们的字符串内容的长度。还可以看到有一个free的子段,这个子段的作用先卖个关子下边说😄
SDS中free的作用
在c语言中使用不当经常会有内存溢出的问题,导致我们的字符串被覆盖,而redis是怎么避免这种问题的出现的呢?
首先我们在redis中插入一个key:
set demo hello;
这个时候的SDS对象是:
1 | c复制代码struct sdshdr { |
set demo ‘hello world’;
这个时候的SDS对象是:
1 | c复制代码struct sdshdr { |
我们在set hello world之后 redis会根据我们的free子段去判断需不需要向操作系统申请内存,如果free够用则不申请。这个时候就涉及到redis的内存分配策略了,为了避免多次向操作系统申请内存的开销,redis在每次申请都会按照当前len的两倍去申请(当len于1M时候就只会多申请1M),也就可以解释我们的free为什么是11了,这就是redis的空间预分配。
我们可以看下redis-5源码涉及到内存分配的策略,扩容是这个方法XDM可以去看看 sds sdsMakeRoomFor(sds s, size_t addlen)
还有一个惰性空间释放的概念会涉及到我们的free子段,当我们将redis的字符串内容减少的时候,redis并不会立马释放内存而是仅仅修改free子段,以便下次使用。
总结
- 因为SDS有存储字符串长度len,所以我们获取redis字符串长度是一个O(1)的操作。
- len的存在避免了内存溢出的问题
- 通过内存分配策略,减少了字符串修改时候的内存分配次数
本文转载自: 掘金