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

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

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

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

1// 将字符串 value 关联到 key 2// 如果key 已经有值了,直接覆盖 emmmm.跟我们使用HashMap是不是很像~ 3// 如果已经存在一个带有过期时间的key,我们重新set会将原先的过期时间清除 (重点) 4>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]

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

##代码示例:

1//对不存在的健设置 2>set name "black-search" 3OK 4>get name 5"black-search" 6---------------------------------- 7//对已经存在的值设置 8>set name "new-black-search" 9OK 10>get name 11"new-black-search" 12---------------------------------- 13//使用 ex seconds 14>set name "black-search" ex 10 15OK 16>get name 17"black-search" 18//获取key的有效时间 19>ttl name 20(integer) 6 21//稍微等一会再执行下面的命令 22>get name 23(nil) 24---------------------------------- 25// 使用nx 26>set name "black-search" nx 27OK 28>set name "black-search" nx 29(nil) 30---------------------------------- 31// 使用xx 32>set name "new-black-search" xx 33OK 34//让我们设置一个不存在的key 35>set key value xx 36(nil)

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


debug object key

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

1>set name "black-search" 2OK 3// redis提供的调试 api 4>debug object name 5Value 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’结尾以后的所有字符。因此,如果传统字符串保存图片,视频等二进制文件,操作文件时就被截断了 所以在这里读者是不是也像我一样去实践了?

1127.0.0.1:6379> set name "ssssssss\0 asdasdasdasd" 2OK 3127.0.0.1:6379> get name 4"ssssssss0 asdasdasdasd" 5127.0.0.1:6379> set name "asdasdasd \\0 asdasdasdasd " 6OK 7127.0.0.1:6379> get name 8"asdasdasd \\0 asdasdasdasd " 9127.0.0.1:6379> set name "asdasdasd'\0' asdasdasdasdasd" 10OK 11127.0.0.1:6379> get name 12"asdasdasd'0' asdasdasdasdasd" 13127.0.0.1:6379> set name "asdasdasd '\\0'asdasdasd" 14OK 15127.0.0.1:6379> get name 16"asdasdasd '\\0'asdasdasd"

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

1sds sdscatrepr(sds s, const char *p, size_t len) { 2 s = sdscatlen(s,"\"",1); 3 while(len--) { 4 switch(*p) { 5 case '\\': 6 case '"': 7 s = sdscatprintf(s,"\\%c",*p); 8 break; 9 case '\n': s = sdscatlen(s,"\\n",2); break; 10 case '\r': s = sdscatlen(s,"\\r",2); break; 11 case '\t': s = sdscatlen(s,"\\t",2); break; 12 case '\a': s = sdscatlen(s,"\\a",2); break; 13 case '\b': s = sdscatlen(s,"\\b",2); break; 14 default: 15 if (isprint(*p)) 16 s = sdscatprintf(s,"%c",*p); 17 else 18 s = sdscatprintf(s,"\\x%02x",(unsigned char)*p); 19 break; 20 } 21 p++; 22 } 23 return sdscatlen(s,"\"",1); 24}

回到开头

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

1//sds.h 2#define SDS_MAX_PREALLOC (1024*1024) 3//sds.c 4sds sdsMakeRoomFor(sds s, size_t addlen) { 5 void *sh, *newsh; 6 size_t avail = sdsavail(s); 7 size_t len, newlen; 8 char type, oldtype = s[-1] & SDS_TYPE_MASK; 9 int hdrlen; 10 11 /* Return ASAP if there is enough space left. */ 12 if (avail >= addlen) return s; 13 14 len = sdslen(s); 15 sh = (char*)s-sdsHdrSize(oldtype); 16 newlen = (len+addlen); 17 if (newlen < SDS_MAX_PREALLOC) 18 newlen *= 2; 19 else 20 newlen += SDS_MAX_PREALLOC; 21 22 type = sdsReqType(newlen); 23 24 /* Don't use type 5: the user is appending to the string and type 5 is 25 * not able to remember empty space, so sdsMakeRoomFor() must be called 26 * at every appending operation. */ 27 if (type == SDS_TYPE_5) type = SDS_TYPE_8; 28 29 hdrlen = sdsHdrSize(type); 30 if (oldtype==type) { 31 newsh = s_realloc(sh, hdrlen+newlen+1); 32 if (newsh == NULL) return NULL; 33 s = (char*)newsh+hdrlen; 34 } else { 35 /* Since the header size changes, need to move the string forward, 36 * and can't use realloc */ 37 newsh = s_malloc(hdrlen+newlen+1); 38 if (newsh == NULL) return NULL; 39 memcpy((char*)newsh+hdrlen, s, len+1); 40 s_free(sh); 41 s = (char*)newsh+hdrlen; 42 s[-1] = type; 43 sdssetlen(s, len); 44 } 45 sdssetalloc(s, newlen); 46 return s; 47}

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

应用场景

1.储存业务数据

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

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

黑搜丶D

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

Redis从入门到放弃系列(二) Hash

Redis从入门到放弃系列(二)Hash本文例子基于:5.0.4Hash是Redis中一种比较常见的数据结构,其实现为hashtable/ziplist,默认创建时为ziplist,当到达一定量级时,redis会将ziplist转化为hashtableRedis从入门到放弃系列(一)String

Redis从入门到放弃系列(四) Set

Redis从入门到放弃系列(四)Set本文例子基于:5.0.4Set是Redis中一种比较常见的数据结构,当存储的member为十进制64位有符号整数范围内的整数的字符串的时候其实现为intset,其他为hashtableRedis从入门到放弃系列(三)List(https://www.osch

Redis从入门到放弃系列(十) Cluster

Redis从入门到放弃系列(十)Cluster本文例子基于:5.0.4RedisCluster集群高可用方案,去中心化,最基本三主多从,主从切换类似Sentinel,关于Sentinel内容可以查看编者另外一篇【Redis从入门到放弃系列(九)Sentinel(https://www.o