本文例子基于:5.0.4 字符串是Redis中最多见的数据结构,底层是采用SDS,是能够修改的字符串,相似ArrayList,采用预分配冗余空间的方式来减小内存的频繁分配。java
首先让咱们来看一下该如何在redis里面使用字符串这种类型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]api
咱们发现 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:只在键已经存在时, 才对键进行设置操做
##代码示例:app
//对不存在的健设置 >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 10 OK >get name "black-search" //获取key的有效时间 >ttl name (integer) 6 //稍微等一会再执行下面的命令 >get name (nil) ---------------------------------- // 使用nx >set name "black-search" nx OK >set name "black-search" nx (nil) ---------------------------------- // 使用xx >set name "new-black-search" xx OK //让咱们设置一个不存在的key >set key value xx (nil)
至此,redisset
的用法先告一段落.编码
咱们在使用redis进行普通的set
处理的时候,咱们会使用一下这样子的方式去设置:spa
>set name "black-search" OK // redis提供的调试 api >debug object name Value at:0x179d060 refcount:1 encoding:embstr serializedlength:13 lru:14160370 lru_seconds_idle:17
从上面的输出咱们重点关注一下serializedlength:13
,咱们输入的明明长度就只有12,为嘛会输出13呢~ 实际上是redis里面帮咱们作了处理,redis会在字符的结尾使用'\0'结尾debug
由于传统C字符串符合ASCII编码,这种编码的操做的特色就是:遇零则止 。即,当读一个字符串时,只要遇到’\0’结尾,就认为到达末尾,就忽略’\0’结尾之后的全部字符。所以,若是传统字符串保存图片,视频等二进制文件,操做文件时就被截断了 因此在这里读者是否是也像我同样去实践了?调试
127.0.0.1:6379> set name "ssssssss\0 asdasdasdasd" OK 127.0.0.1:6379> get name "ssssssss0 asdasdasdasd" 127.0.0.1:6379> set name "asdasdasd \\0 asdasdasdasd " OK 127.0.0.1:6379> get name "asdasdasd \\0 asdasdasdasd " 127.0.0.1:6379> set name "asdasdasd'\0' asdasdasdasdasd" OK 127.0.0.1:6379> get name "asdasdasd'0' asdasdasdasdasd" 127.0.0.1:6379> set name "asdasdasd '\\0'asdasdasd" OK 127.0.0.1:6379> get name "asdasdasd '\\0'asdasdasd"
哈哈,其实这里redis已经作了过滤了呢~code
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.c sds 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 全部的操做都是原子性的,因此没必要担忧多客户端链接时可能出现的事务问题。