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作为一种存储字符串的缓存结构,其具体实现是 ...
随机推荐
- Eclipse插件的各种安装方法
这篇文章我们总结下安装Eclipse的各种方法,首先一下的步骤都是在版本为“Kepler Service Release 1”的Eclipse下完成的.如果你的Eclipse版本不是"Kep ...
- jQuery测验题
1.在div元素中,包含了一个<span>元素,通过has选择器获取<div>元素中的<span>元素的语法是? 提示使用has() $("div:has ...
- Web平台安装及检测程序
软件名称:microsoft web platform installer 上图: 可以看做是一个App Store, 你再也不用东奔西跑去找什么开发软件,CMS等等了,直接打开这个,勾选上就安装吧, ...
- AndroidStudio运行项目出现Unsupported method: AndroidProject.getPluginGeneration()错误解决办法
一.错误描述 今天在使用AndroidStudio运行项目时出现了一个Unsupported method: AndroidProject.getPluginGeneration()错误,如下图所示: ...
- java算法 蓝桥杯(题+答案) 压缩变换
10.压缩变换 (程序设计) 小明最近在研究压缩算法.他知道,压缩的时候如果能够使得数值很小,就能通过熵编码得到较高的压缩比.然而,要使数值很小是一个挑战. 最近,小明需要压缩一些正整数的序列,这些 ...
- Vue基本入门
介绍 1.Vue.js是什么? Vue.js(读音:/vju:/,类似于view)是一套构建用户界面的渐进式框架,与其他重量级框架不同的是,Vue采用的是自底向上增量开发的设计. Vue的核心库只关注 ...
- go-common-pool设计原理分析
common-pool: 对于一些对象的频繁创建会带来很大的系统开销,并且需要对对象数量进行控制来降低资源消耗,比如数据库连接,线程等 common-pool采用了缓存思想来解决这个问题,预先把一些对 ...
- Hibernate中遇见的问题
1.在用Hibernate写增删改查时,有两种方式:a.HQL语句,b.原生态sql语句 如果我要执行的语句是:select * from Question HQL是这么写的: Session ses ...
- Web服务器磁盘满故障深入解析
问题:硬盘显示被写满,但是用du -sh /*查看时占用硬盘空间之和还远小于硬盘大小即找不到硬盘分区是怎么被写满的. 今天下午接到一学生紧急求助,说生产线服务器硬盘满了.该删的日志都删掉了.可空间还是 ...
- 关于微信小程序图片失真的解决方案
今天来说一说 关于微信小程序的图片失真问题的解决,微信小程序的image标签要设置其宽高,不然图片若宽高过大会撑开原始图片大小的区域:如下 但是宽高设置固定了会导致有些图片和规定显示图片大小的比例不一 ...