redis 字符串
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 字符串的更多相关文章
- redis字符串
字符串类型是redis的基本类型 字符串数据类型的相关命令用于管理 redis 字符串值,基本语法如下:COMMAND KEY_NAME SET 和GET用于设置和读取key的值 1.SET key ...
- Redis 字符串(String)
Redis 字符串数据类型的相关命令用于管理 redis 字符串值,基本语法如下: 语法 redis 127.0.0.1:6379> COMMAND KEY_NAME 实例 redis 12 ...
- 2016022608 - redis字符串命令集合
redis字符串命令: Redis字符串命令用于在Redis管理字符串值.使用Redis字符串命令的语法如下所示: redis 127.0.0.1:6379> COMMAND KEY_NAME ...
- redis 字符串的管理的一点理解
redis字符串可以实现通过地址偏移找到所在结构体的首地址,struct sdshdr *sh = (void *)(s - (sizeof(struct sdshdr))) 也就是通过buf地址可以 ...
- Python操作redis字符串(String)详解 (三)
# -*- coding: utf-8 -*- import redis #这个redis不能用,请根据自己的需要修改 r =redis.Redis(host=") 1.SET 命令用于设置 ...
- Redis 字符串(String)
Redis 字符串(String) Redis 字符串数据类型的相关命令用于管理 redis 字符串值,基本语法如下: 语法 redis 127.0.0.1:6379> COMMAND KEY_ ...
- redis字符串基本操作
redis之字符串类型: 字符串类型是redis中最基本的数据类型,同时它也是memcached中仅有的数据类型.redis字符串类型的键能存储任何形式的字符串,包括二进制数据,例如,存储json化的 ...
- Redis字符串(STRING)中BIT相关命令
上篇文章我们对STRING数据类型中一些基本的命令进行了介绍,但是没有涉及到BIT相关的命令,本文我们就来看看几个和BIT相关的命令. 本文是Redis系列的第四篇文章,了解前面的文章有助于更好的理解 ...
- Redis字符串键的底层原理
before C语言基础 Redis基础 导入 redis的命令如下: set x "hello"; get x; hello Redis作为一种存储字符串的缓存结构,其具体实现是 ...
随机推荐
- 让SQL再快一点儿
文章转载自「开发者圆桌」一个关于开发者入门.进阶.踩坑的微信公众号 SQL即结构化查询语言(Structured Query Language),是一种特殊目的的编程语言,是一种数据库查询和程序设计语 ...
- .NET 发布网站步骤
本文章分为三个部分: web网站发布.IIS6 安装方法.ASP.NET v4.0 安装方法 一.web网站发布 1.打开 Visual Studio 2013 编译环境 2.在其解决方案上右击弹出重 ...
- TuSDK 简易使用方法 持有图片对象方式
TuSDK 为涂图照相应用的SDK,打包后文件大小约为5M,缺点为包比较大,且图片清晰度较差一些,优点为直接可以引用滤镜贴纸,方便易用. 使用方法如下: 1.AppDelegate.m 中加 ...
- wemall app中基于Java获取和保存图片的代码
wemall-mobile是基于WeMall的android app商城,只需要在原商城目录下上传接口文件即可完成服务端的配置,客户端可定制修改.分享其中关于 保存正在下载的图片URL集合和图片三种获 ...
- .NET的HTTP辅助类:RestSharp
示例: var client = new RestClient("http://example.com");// client.Authenticator = new HttpBa ...
- 深度剖析Spark分布式执行原理
让代码分布式运行是所有分布式计算框架需要解决的最基本的问题. Spark是大数据领域中相当火热的计算框架,在大数据分析领域有一统江湖的趋势,网上对于Spark源码分析的文章有很多,但是介绍Spark如 ...
- H5 拖放
HTML 5 拖放 HTML5 音频 HTML5 画布 拖放(Drag 和 drop)是 HTML5 标准的组成部分. 拖放 拖放是一种常见的特性,即抓取对象以后拖到另一个位置. 在 HTML5 中, ...
- 20144306《网络对抗》MAL_逆向与Bof基础
实践目标 本次实践的对象是一个名为pwn1的linux可执行文件. 该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串. 该程序同时包含另一个代码片段,getShe ...
- pcntl_fork 导致 MySQL server has gone away 解决方案
pcntl_fork 前连数据库,就会报 MySQL server has gone away 错误.原因是子进程会继承主进程的数据库连接,当mysql返回数据时,这些子进程都可以通过这个连接读到数据 ...
- Linux中的sed命令
sed - stream editor for filtering and transforming text 流编辑器的过滤和转换文本 sed [-nerf] [动作] 参数: -i 修改源文件 危 ...