redis 字符串

概述

redis 没有使用 c 语言风格的字符串表示(以 "\0" 作为结尾), 而是使用自定义的 sds 结构

字符串结构

定义位置 (src/sds.h)

// 类型别名, 用于指向 sdshdr 的 buf 属性
typedef char *sds; // 字符串对象的结构
struct sdshdr {
// 字符串长度
int len;
// 字符串剩余可用空间的长度 (不包括 "\0")
int free;
/*
* 数据空间
* sds 指向的地址
* 遵循以 "\0" 作为结尾, 这样对于 sds 变量可以使用 c 标准库的函数
* 比如: printf("%s", sdshdr->buf)
*/
char buf[];
};
  • len 字段

    • 字符串长度 (不包括 "\0")

    • 通过 len 长度来判断字符串的结尾, 二进制安全

    • 常数复杂度获取字符串长度

    • 若不设置 len 长度, 会存在缓冲区溢出问题, 如: strcat 函数, 此函数假定已经为字符串连接留下了空间, 一旦假定不成立, 就会缓冲区溢出

      sds 结构完全杜绝了缓冲区溢出的问题, 在进行字符串操作的时候, 会判断空间是否满足要求
      举例:
      // 字符串连接
      sds sdscat(sds s, const char *t) {
      return sdscatlen(s, t, strlen(t));
      }
      // 将长度为 len 的字符串 t 追加到 s 后面
      sds sdscatlen(sds s, const void *t, size_t len) {
      struct sdshdr *sh;
      // 原有字符串长度
      size_t curlen = sdslen(s);
      /*
      * 扩展 sds 空间
      * 根据 s 的 free 字段是否满足 len 长度, 决定是否扩展
      */
      s = sdsMakeRoomFor(s,len);
      // 扩展失败, 直接返回
      if (s == NULL) return NULL;
      // 复制 t 中的内容到字符串后部
      sh = (void*) (s-(sizeof(struct sdshdr)));
      memcpy(s+curlen, t, len);
      // 更新属性
      sh->len = curlen+len;
      sh->free = sh->free-len;
      // 添加新结尾符号
      s[curlen+len] = '\0';
      // 返回新 sds
      return s;
      }
      • sdsMakeRoomFor(s, len): 对 s 进行操作时候判断剩余空间是否满足, 决定是否扩展
      • sh = (void *) (s - (sizeof(struct sdshdr))): 对于字符串的操作, 根据 sds 地址计算出 sdshdr 的地址, 然后进行相应的属性赋值等操作
  • free 字段

    • 减少内存预分配带来的系统调用所造成的性能损耗

      c 语言对 string 结构并未进行任何的封装, 单纯的以 "\0" 作为结束符, 所以对字符串进行操作时, 比如增长操作时, 需要重新分配内存, 否则会造成缓冲区溢出; 缩减操作时, 需要重新分配内存, 否则会造成内存泄漏
    • 空间预分配 (字符串增长操作)

      /*
      * 为 sds 分配空间
      */
      sds sdsMakeRoomFor(sds s, size_t addlen) {
      struct sdshdr *sh, *newsh;
      // 获取 s 目前的空余空间长度
      size_t free = sdsavail(s);
      size_t len, newlen;
      // s 目前的空余空间已经足够,无须再进行扩展,直接返回
      if (free >= addlen) return s;
      // 获取 s 目前已占用空间的长度
      len = sdslen(s);
      sh = (void*) (s-(sizeof(struct sdshdr)));
      // s 最少需要的长度
      newlen = (len+addlen);
      // 根据新长度,为 s 分配新空间所需的大小
      if (newlen < SDS_MAX_PREALLOC)
      // 如果新长度小于 SDS_MAX_PREALLOC
      // 那么为它分配两倍于所需长度的空间
      newlen *= 2;
      else
      // 否则,分配长度为目前长度加上 SDS_MAX_PREALLOC
      newlen += SDS_MAX_PREALLOC; newsh = zrealloc(sh, sizeof(struct sdshdr)+newlen+1);
      // 内存不足,分配失败,返回
      if (newsh == NULL) return NULL;
      // 更新 sds 的空余长度
      newsh->free = newlen - len;
      // 返回 sds
      return newsh->buf;
      }
      • 分配内存的策略: 若新的字符串长度小于 SDS_MAX_PREALLOC (1024 * 1024), 则分配2倍的空间, 若大于, 则 += SDS_MAX_PREALLOC
    • 惰性空间释放 (字符串缩短操作)

      字符串在进行缩短操作时, 不会立即通过内存分配回收空闲空间, 而是将其更新到 free 字段, 以待将来增长操作时使用

sds api (src/sds.c)

函数 作用 备注
sdsnewlen 创建一个 sds, 使用指定的字符串和长度 sds sdsnewlen(const void *init, size_t initlen)
sdsempty 创建一个空 sds sds sdsempty(void)
sdsnew 创建一个 sds, 使用指定的字符串 sds sdsnew(const char *init)
sdsdup 复制给定的 sds sds sdsdup(const sds s)
sdsfree 释放给定的 sds void sdsfree(sds s)
sdsupdatelen 更新 sds 的长度字段 (free, len) void sdsupdatelen(sds s)
sdsclear 重置 sds 字符串对象为空 void sdsclear(sds s)
sdsMakeRoomFor sds 字符串增长 addlen长度 (判断空余空间) sds sdsMakeRoomFor(sds s, size_t addlen)
sdsRemoveFreeSpace 移除 sds 空闲空间 (free字段) sds sdsRemoveFreeSpace(sds s)
sdsAllocSize 返回给定 sds 对象存储所分配的空间 size_t sdsAllocSize(sds s)
sdsIncrLen sds 增长 incr 长度 (更新 len, free) void sdsIncrLen(sds s, int incr)
sdsgrowzero 将 sds 增长到 len 长度, 超出部分用 0 填充 sds sdsgrowzero(sds s, size_t len)
sdscatlen 将字符串 t 的 len 长度追加到 s 后面 sds sdscatlen(sds s, const void *t, size_t len)
sdscat 将字符串 t 追加到 s 后面 sds sdscat(sds s, const char *t)
sdscatsds 将 sds 结构的 t 追加到 s 后面 sds sdscatsds(sds s, const sds t)
sdscpylen 将 t 的 len 长度的字符串复制给 s, 作为新 s sds sdscpylen(sds s, const char *t, size_t len)
sdscpy 将字符串 t 复制给 s, 覆盖 s 原有字符串 sds sdscpy(sds s, const char *t)
sdsll2str 将 long long 类型转换为 string 类型 int sdsll2str(char *s, long long value)
sdsull2str 将 unsign long long 类型转换为 string 类型 int sdsull2str(char *s, unsigned long long v)
sdsfromlonglong 用 long long 类型创建一个 sds 类型字符串 sds sdsfromlonglong(long long value)
sdscatvprintf 将格式化后的字符串追加到 s 后 sds sdscatvprintf(sds s, const char *fmt, va_list ap)
sdscatprintf 将格式化后的字符串追加到 s 后 sds sdscatprintf(sds s, const char *fmt, ...)
sdscatfmt 将格式化后的字符串追加到 s 后 (redis自定义的格式标识符) sds sdscatfmt(sds s, char const *fmt, ...)
sdstrim 将 s 两端去掉指定字符集合中的字符 sds sdstrim(sds s, const char *cset)
sdsrange 截取 s 中的一段, 并赋值给 s void sdsrange(sds s, int start, int end)
sdstolower 将 s 转换为小写字母 void sdstolower(sds s)
sdstoupper 将 s 转换为大写字母 void sdstoupper(sds s)
sdscmp 比较 s1, s2 的大小 int sdscmp(const sds s1, const sds s2)
sdssplitlen 以指定分隔符 sep 将 s 分割成多个 token, 返回 sds 数组 sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count)
sdsfreesplitres 释放 sds 数组的 count 个 sds 内存空间 void sdsfreesplitres(sds *tokens, int count)
sdscatrepr 将 p 字符串的 len 长度中的转义字符跳脱转义显示出来, 追加到 s 后面 sds sdscatrepr(sds s, const char *p, size_t len)
hex_digit_to_int 将16进制字符转换为10进制的数值 int hex_digit_to_int(char c)
sdssplitargs 将给定字符串转义字符进行转义返回, 并按照 space 作为分隔符进行分隔, 分隔后的参数列表保存在 argc sds *sdssplitargs(const char *line, int *argc)
sdsmapchars 将 from 中的字符与 to 中的 setlen 字符一一对应, 对 s 进行替换 sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen)
sdsjoin 将 sds 数组中的 argc 个 sds 按照 sep 分隔符连接 sds sdsjoin(char **argv, int argc, char *sep)

redis 字符串的更多相关文章

  1. redis字符串

    字符串类型是redis的基本类型 字符串数据类型的相关命令用于管理 redis 字符串值,基本语法如下:COMMAND KEY_NAME SET 和GET用于设置和读取key的值 1.SET key ...

  2. Redis 字符串(String)

      Redis 字符串数据类型的相关命令用于管理 redis 字符串值,基本语法如下: 语法 redis 127.0.0.1:6379> COMMAND KEY_NAME 实例 redis 12 ...

  3. 2016022608 - redis字符串命令集合

    redis字符串命令: Redis字符串命令用于在Redis管理字符串值.使用Redis字符串命令的语法如下所示: redis 127.0.0.1:6379> COMMAND KEY_NAME ...

  4. redis 字符串的管理的一点理解

    redis字符串可以实现通过地址偏移找到所在结构体的首地址,struct sdshdr *sh = (void *)(s - (sizeof(struct sdshdr))) 也就是通过buf地址可以 ...

  5. Python操作redis字符串(String)详解 (三)

    # -*- coding: utf-8 -*- import redis #这个redis不能用,请根据自己的需要修改 r =redis.Redis(host=") 1.SET 命令用于设置 ...

  6. Redis 字符串(String)

    Redis 字符串(String) Redis 字符串数据类型的相关命令用于管理 redis 字符串值,基本语法如下: 语法 redis 127.0.0.1:6379> COMMAND KEY_ ...

  7. redis字符串基本操作

    redis之字符串类型: 字符串类型是redis中最基本的数据类型,同时它也是memcached中仅有的数据类型.redis字符串类型的键能存储任何形式的字符串,包括二进制数据,例如,存储json化的 ...

  8. Redis字符串(STRING)中BIT相关命令

    上篇文章我们对STRING数据类型中一些基本的命令进行了介绍,但是没有涉及到BIT相关的命令,本文我们就来看看几个和BIT相关的命令. 本文是Redis系列的第四篇文章,了解前面的文章有助于更好的理解 ...

  9. Redis字符串键的底层原理

    before C语言基础 Redis基础 导入 redis的命令如下: set x "hello"; get x; hello Redis作为一种存储字符串的缓存结构,其具体实现是 ...

随机推荐

  1. Java程序员入门:Java程序员面试失败的5大原因

    1 说得太少 尤其是那些开放式的问题,如"请介绍下你自己"或"请讲一下你曾经解决过的复杂问题".面试官会通过你对这些技术和非技术问题的回答来评估你的激情.他们也 ...

  2. h5标签基础 table表格标签

    一.表格的定义:用于有规范的显示数据. 二.基本组成: 行<tr>/列<td>/表头<caption>/表标题<th> eg: <table> ...

  3. mac上搭建appium环境过程以及遇到的问题

    Mac环境安装appium 一.Java环境 下载java sdk http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downl ...

  4. C/C++学习路线图

    文章转载自「开发者圆桌」一个关于开发者入门.进阶.踩坑的微信公众号 这里整理的C/C++学习路线图包含初中高三个部分,你可以通过百度云盘下载观看对应的视频 链接: http://pan.baidu.c ...

  5. AndroidStudio运行项目出现Unsupported method: AndroidProject.getPluginGeneration()错误解决办法

    一.错误描述 今天在使用AndroidStudio运行项目时出现了一个Unsupported method: AndroidProject.getPluginGeneration()错误,如下图所示: ...

  6. 一个可以将 json 字符串 直接绑定到 view 上的Android库

    android-data-binding 这是一个可以将 json 字符串 直接绑定到 view 上的库, 不用先将 json 转换为 model 类. 传送门(https://github.com/ ...

  7. 3DES 加密 解密

    p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px Menlo; color: #c91b13 } p.p2 { margin: 0.0px 0. ...

  8. HTML+CSS-采用DIV+CSS布局的利弊

    单纯的TABLE表格,在浏览器运行上面来说,其效率不如DIV+CSS方式快速,当然,这样也就会影响用的体验. 而且,针对于相同的HTML页面来说TABLE布局,不利于搜索引擎的爬虫爬行下载页面.从而造 ...

  9. docker X509 证书错误的终极解决办法

    最近在做Docker相关的东西,发现只要一pull镜像,就出现如下的ERROR x509: certificate signed by unknown authority. 调查后发现,是公司IT把h ...

  10. 安装rabbitmq以及集群配置

    前言: (一些有用没用的唠叨,反正看了也不少肉,跳过也没啥) 情况是这样的:虚拟机.CentOS 6.5.免编译包安装rabbitmq集群,可不用连外网. 我原计划是安装在虚拟机上wyt1/wyt2/ ...