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. grpc-gateway:grpc对外提供http服务的解决方案

    我所在公司的项目是采用基于Restful的微服务架构,随着微服务之间的沟通越来越频繁,就希望可以做成用rpc来做内部的通讯,对外依然用Restful.于是就想到了google的grpc. 使用grpc ...

  2. Ajax基础与登入

    Ajax 是 Asynchronous JavaScript and XML的缩写. Ajax的优点: 优点:减轻服务器的负担,按需取数据,最大程度的减少冗余请求 局部刷新页面,减少用户心理和实际的等 ...

  3. 交互神器 Facebook Origami

    最近用到了一个非常强大的工具,这是一款由 facebook 出品的原型设计软件,老规矩我们先来看一下效果.大家也可以先进去官网看看效果Origami 官网 scroll.gif swipe.gif O ...

  4. Convert Sorted Array to Binary Search Tree & Convert Sorted List to Binary Search Tree

    Given an array where elements are sorted in ascending order, convert it to a height balanced BST. Su ...

  5. 购买的wemall 6.0商城系统源码分享

    使用方法 1.解压目录 2.cd wemall6 && npm i 3.配置config下的config.json 4.npm start 摒弃以往的开发框架thinkphp,使用no ...

  6. 深入理解java虚拟机之——JVM垃圾回收策略总结

    如何判断一个对象是否存活 引用计数算法:给对象中添加一个引用计数器,每当有引用它时,计数器值就加1:当引用失效时,计数器值就减1:任何时刻计数器为0的对象就是不可能再被使用.  Java虚拟机里面没有 ...

  7. Thrift序列化与反序列化的实现机制分析

    Thrift是如何实现序死化与反序列化的,在IDL文件中,更改IDL文件中的变量序号或者[使用默认序号的情况下,新增变量时,将新增的变量不放在IDL文件的结尾,均会导致Thrift文件的反序列后无法做 ...

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

    1: 2: 3:resultMap的用法

  9. NIO(一、概述)

    离上次发布文章已过去好几个月,说好的积累和分享都烟消云散.似乎忙碌从未终止过,加班成为常态,甚至阅读都需要更琐碎的时间. 目录 NIO(一.概述) NIO(二.Buffer) 概述 JavaNIO(n ...

  10. fastlane安装流程和fastlane match同步证书和PP文件方法

    分步指南 安装fastlane:  ① Make sure you have the latest version of the Xcode command line tools installed: ...