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字节码操纵框架ASM小试

    本文主要内容: ASM是什么 JVM指令 Java字节码文件 ASM编程模型 ASM示例 参考资料汇总 JVM详细指令 ASM是什么 ASM是一个Java字节码操纵框架,它能被用来动态生成类或者增强既 ...

  2. 基于Struts自定义MVC-1

    自定义MVC        数据库:Oracle表:User(id,uname,upwd)自定义Struts框架一.定义Action接口 1 import javax.servlet.http.*; ...

  3. 巧用*_his表记录操作历史

    文章转载自「开发者圆桌」一个关于开发者入门.进阶.踩坑的微信公众号 许多OLTP应用的开发者都知道,一些重要的操作要记录操作历史,把操作前的数据备份到历史表,然后再执行相应的修改操作.这样可以获取某个 ...

  4. 【NodeJs】记录一个阿里云redis的坑

    背景 一个风和日丽的下午,一个上线的大好日子,以为一切准备好,上个线也就三五分钟的事.但是... 图样图森破. 背景是这样的,项目需要,在阿里云开通redis,在项目中配好redis的连接字符串后,以 ...

  5. wx模块小实例

    功能介绍: 查询数据库表数据,提取数据并显示 main.py(执行文件) #coding:gbk __author__ = 'Hito' import querySmscode import wx c ...

  6. mybatis基础学习3---特殊sql语句(备忘)

    1: 2: 3:resultMap的用法

  7. iOS网络编程笔记——社交网络编程

    社交网络编程主要使用iOS提供的social框架,目前social框架主要分为两个类: (1)SLComposeViewController提供撰写社交信息(如微博信息)的视图控制器,由iOS系统提供 ...

  8. linux vi hjkl由来

    很远原因来自历史 I was reading about vim the other day and found out why it used hjkl keys as arrow keys. Wh ...

  9. 分享几个不错的Android开源音视频播放器

    整理了一下Github上几个开源的音视频播放器项目,有兴趣的同学可以clone代码去研究学习.   UniversalMusicPlayer https://github.com/googlesamp ...

  10. 在Windows的DOS中运行java编程中的问题

    1.苦恼着我的就是找不到或无法加载主类!