Loading

REDIS中字符串是如何实现的?

首先在说redis的字符串之前,希望你昨天应该补充过redis字符串的一些操作api了。其实redis的字符串本质上就是利用C语言的字符串来实现的,但是又不能单纯的C语言字符串,而是改造过的加强版本的字符串,官方名称叫做SDS,中文名字叫做简单动态字符串。这个东西数据结构就是一个C语言的结构体,结构如下:

struct sds {
  // 字符串中已经存储的字符长度
  int len;
  // string中剩余空间
  int free;
  // 字符数组,用来存储字符的真正容器
  char string[];
}

首先说下在C语言中是没有字符串这种数据结构的,只有一个字符数据结构,比如下面这样:

#include <stdio.h>
int main ( int argc, char * argv[] ) {
  // 用单引号扩充起来的,叫做字符
  char chr = 'A';
  // 用双引号扩起来的,叫做字符串,原理就是一坨字符组成的字符数组
  char string[] = "helloworld";
}

字符串有一个很重要的特征,那就是C语言为会为字符串结尾自动添加一个\0(空字符),注意不是0,是\0!
现在来看redis的SDS字符串有何高明的地方,大概有如下几点:

1.假如SDS的string成员如下:

// 这里我用 | 来假设的存储空格,两个 | 中间就是一个存储单位 
string = | h | e | l | l | o | \0 |  |  |  |

这个时候,SDS的len就是5,注意最后的\0不计入有效长度,此时free成员变量就是3。所以,当我们使用redis的操作命令获取字符串长度的时候,就是直接获取SDS的len成员变量,这就是传说中的O( 1 )时间复杂度。

2.更加快捷地字符串拼接和剪裁。

假如还是原来的hello,如果这会儿我们需要在hello后面添加world,这会儿就会利用到free属性了,redis会先判断free是否足够容纳world,如果足够了,就拼接上去;如果不够,那么redis就需要申请分配内存了(类似于malloc方法)。但是redis申请内存很奇怪,比如要拼接world,但是redis并不会申请五个内存空间,而是大于五个内存空间。为什么redis要这么做?这是为了防止再次连续地拼接字符串造成频繁地申请内存,因为申请内存是一件极为浪费性能的事情,预先分配足够大的内存可以避免这种现象。除此之外,还有字符串剪裁。比如现在字符串已经成”helloworld”了,现在要做去掉ld两个字母,剪裁是不需要判断free的,只需要直接减就行了,但是减了后redis并不会马上就释放这块儿内存空间,而是依然保留预防又有一些拼接操作。

3.防止内存溢出。

当然了,这个就是针对字符串拼接的时候了。所谓内存溢出就是内存空间不够,但是塞了一个很大的内容进去,导致溢出。由于我们有了free属性,所以就能避免内存溢出现象。
4.看完了SDS的结构,那么你应该也能感觉到了吧,SDS实际上用C语言的一些函数也是可以直接操作的。

转:https://t.ti-node.com/

posted @ 2018-09-05 13:22  王召波  阅读(653)  评论(0编辑  收藏  举报