侧边栏壁纸
博主头像
落叶人生博主等级

走进秋风,寻找秋天的落叶

  • 累计撰写 130562 篇文章
  • 累计创建 28 个标签
  • 累计收到 9 条评论
标签搜索

目 录CONTENT

文章目录

Redis从入门到放弃系列(一) String

2023-12-17 星期日 / 0 评论 / 0 点赞 / 26 阅读 / 6911 字

Redis从入门到放弃系列(一) String 本文例子基于:5.0.4 字符串是Redis中最常见的数据结构,底层是采用SDS,是可以修改的字符串,类似ArrayList,采用预分配冗余空间的方

Redis从入门到放弃系列(一) String

本文例子基于:5.0.4 字符串是Redis中最常见的数据结构,底层是采用SDS,是可以修改的字符串,类似ArrayList,采用预分配冗余空间的方式来减少内存的频繁分配。

首先让我们来看一下该如何在redis里面使用字符串这种类型

// 将字符串 value 关联到 key// 如果key 已经有值了,直接覆盖 emmmm.跟我们使用HashMap是不是很像~// 如果已经存在一个带有过期时间的key,我们重新set会将原先的过期时间清除 (重点)>set key value [expiration EX seconds|PX milliseconds] [NX|XX]

可选参数:[expiration EX seconds|PX milliseconds] [NX|XX]

我们发现 set 方法存在两个可选参数 [expiration EX seconds|PX milliseconds] 跟 [NX|XX]

>EX seconds:将键的过期时间设置为 seconds 秒。 执行 SET key value EX seconds 的效果等同于执行 SETEX key seconds value >PX milliseconds:将键的过期时间设置为 milliseconds 毫秒。 执行 SET key value PX milliseconds 的效果等同于执行 PSETEX key milliseconds value>NX:只在键不存在时, 才对键进行设置操作。 执行 SET key value NX 的效果等同于执行 SETNX key value>XX:只在键已经存在时, 才对键进行设置操作

##代码示例:

//对不存在的健设置>set name "black-search"OK>get name "black-search"----------------------------------//对已经存在的值设置>set name "new-black-search"OK>get name"new-black-search"----------------------------------//使用 ex seconds >set name "black-search" ex 10OK>get name"black-search"//获取key的有效时间>ttl name (integer) 6//稍微等一会再执行下面的命令>get name (nil)----------------------------------// 使用nx>set name "black-search" nxOK>set name "black-search" nx(nil)----------------------------------// 使用xx>set name "new-black-search" xxOK//让我们设置一个不存在的key>set key value xx(nil)

至此,redisset的用法先告一段落.


debug object key

我们在使用redis进行普通的set处理的时候,我们会使用一下这样子的方式去设置:

>set name "black-search"OK// redis提供的调试 api>debug object nameValue at:0x179d060 refcount:1 encoding:embstr serializedlength:13 lru:14160370 lru_seconds_idle:17

从上面的输出我们重点关注一下serializedlength:13,我们输入的明明长度就只有12,为嘛会输出13呢~ 其实是redis里面帮我们做了处理,redis会在字符的结尾使用'/0'结尾

因为传统C字符串符合ASCII编码,这种编码的操作的特点就是:遇零则止 。即,当读一个字符串时,只要遇到’/0’结尾,就认为到达末尾,就忽略’/0’结尾以后的所有字符。因此,如果传统字符串保存图片,视频等二进制文件,操作文件时就被截断了 所以在这里读者是不是也像我一样去实践了?

127.0.0.1:6379> set name "ssssssss/0 asdasdasdasd"OK127.0.0.1:6379> get name"ssssssss0 asdasdasdasd"127.0.0.1:6379> set name "asdasdasd //0 asdasdasdasd "OK127.0.0.1:6379> get name"asdasdasd //0 asdasdasdasd "127.0.0.1:6379> set name "asdasdasd'/0' asdasdasdasdasd"OK127.0.0.1:6379> get name"asdasdasd'0' asdasdasdasdasd"127.0.0.1:6379> set name "asdasdasd '//0'asdasdasd"OK127.0.0.1:6379> get name"asdasdasd '//0'asdasdasd"

哈哈,其实这里redis已经做了过滤了呢~

sds sdscatrepr(sds s, const char *p, size_t len) {    s = sdscatlen(s,"/"",1);    while(len--) {        switch(*p) {        case '//':        case '"':            s = sdscatprintf(s,"//%c",*p);            break;        case '/n': s = sdscatlen(s,"//n",2); break;        case '/r': s = sdscatlen(s,"//r",2); break;        case '/t': s = sdscatlen(s,"//t",2); break;        case '/a': s = sdscatlen(s,"//a",2); break;        case '/b': s = sdscatlen(s,"//b",2); break;        default:            if (isprint(*p))                s = sdscatprintf(s,"%c",*p);            else                s = sdscatprintf(s,"//x%02x",(unsigned char)*p);            break;        }        p++;    }    return sdscatlen(s,"/"",1);}

回到开头

我们说字符串类似Java的ArrayList,那么它每次是怎样扩容呢?

//sds.h#define SDS_MAX_PREALLOC (1024*1024)//sds.csds sdsMakeRoomFor(sds s, size_t addlen) {    void *sh, *newsh;    size_t avail = sdsavail(s);    size_t len, newlen;    char type, oldtype = s[-1] & SDS_TYPE_MASK;    int hdrlen;    /* Return ASAP if there is enough space left. */    if (avail >= addlen) return s;    len = sdslen(s);    sh = (char*)s-sdsHdrSize(oldtype);    newlen = (len+addlen);    if (newlen < SDS_MAX_PREALLOC)        newlen *= 2;    else        newlen += SDS_MAX_PREALLOC;    type = sdsReqType(newlen);    /* Don't use type 5: the user is appending to the string and type 5 is     * not able to remember empty space, so sdsMakeRoomFor() must be called     * at every appending operation. */    if (type == SDS_TYPE_5) type = SDS_TYPE_8;    hdrlen = sdsHdrSize(type);    if (oldtype==type) {        newsh = s_realloc(sh, hdrlen+newlen+1);        if (newsh == NULL) return NULL;        s = (char*)newsh+hdrlen;    } else {        /* Since the header size changes, need to move the string forward,         * and can't use realloc */        newsh = s_malloc(hdrlen+newlen+1);        if (newsh == NULL) return NULL;        memcpy((char*)newsh+hdrlen, s, len+1);        s_free(sh);        s = (char*)newsh+hdrlen;        s[-1] = type;        sdssetlen(s, len);    }    sdssetalloc(s, newlen);    return s;}

从上面的代码可以看到,当字符串长度小于 1M 时,扩容都是加倍现有的空间,如果超过 1M,扩容时一次只会多扩 1M 的空间。(字符串最大长度为 512M)

应用场景

1.储存业务数据

>set user:1 '{"id":1,"name":"黑搜丶D","wechat":"black-search"}'

2.自增ID 当 value的值为整数类型时,redis可以把它当做是整数一样进行自增(incr)自减(decr)操作。由于 redis 所有的操作都是原子性的,所以不必担心多客户端连接时可能出现的事务问题。

广告 广告

评论区