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作为一种存储字符串的缓存结构,其具体实现是 ...
随机推荐
- 从覆盖bootstrap样式谈css选择器优先级
样式优先级 首先简单说几个定义样式的方式: 元素内嵌: <li><a href="" style="color:#ffffff;">SH ...
- kali linux qq 2013
按照网上的教程折腾了好几个小时,都没有搞定的qq for linux 在意外的尝试中成功了 文章有参考网友教程的部分:http://xiao106347.blog.163.com/blog/stati ...
- spring-AOP-ProxyFactoryBean代理的实例
1.一个代理模式的实例 通过 Proxy类进行代理 wait.java //定义一个接口 public interface wait { void say(); } //目标对象实现接口并重写方法 p ...
- MVC插件实现
本人第一篇随笔,在园子里逛了这么久,今天也记录一篇自己的劳动成果,也是给自己以后留个记录. 最近领导让我搞一下插件化,就是实现多个web工程通过配置文件进行组装.之前由于做过一个简单的算是有点经验,当 ...
- C#生成漂亮验证码完整代码类
using System;using System.Web;using System.Drawing;using System.Security.Cryptography; namespace Dot ...
- 关于Android开发的几点建议
绝不要在UI线程中做数据处理的工作,这会让你的app变慢,带来极差的用户体验. 要按照google发布的Design指导意见来设计app,比如一个holo主题app会给用户带来更好的用户体验. 不要复 ...
- [python爬虫]爬取学校教务处成绩
学校教务处网站 登陆窗口 表单数据 观察登陆窗口和提交的表单数据可知只要将账号.密码.验证码正确赋值提交即可模拟登陆. 账号和密码都有,问题的关键就在验证码上. 右键验证码图片审查观察源码如下图: 刚 ...
- Modbus通信协议的压力测试
最近物联网都比较的火,因此,特别为各位兄弟姐妹们,奉上一款Mobus协议的测试软件,可以用来做设备的压力测试,和通信测试. 起初软件开发缘由是我们最近在开发一款设备,需要将多个DS18B20并联起来, ...
- loadrunner提高篇-插入检查点与关联函数
插入检查点 靠LR自动生成的脚本是不够的,很难达到业务要求,因此需要对录制完的脚本进行完善,使其能达到业务模拟的要求 ,这样尽可能地使虚拟用户模拟时更接近用户的实际使用. 在进行压力测试时,经常会 ...
- Ant.SOA微服务框架开源
开源地址:https://github.com/yuzd/AntServiceStack 框架特色0.Service Management(服务治理) 1.CodeGen Contract Fir ...