Redis从入门到放弃系列(一) String
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 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
的用法先告一段落.
debug object key
我们在使用redis进行普通的set
处理的时候,我们会使用一下这样子的方式去设置:
>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'结尾
因为传统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已经做了过滤了呢~
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 所有的操作都是原子性的,所以不必担心多客户端连接时可能出现的事务问题。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
并发编程专题七-什么是线程安全
文档章节 一、什么是类的线程安全 1、操作的原子性 2、内存的可见性 二、让类的做到线程安全的方式 2.1、栈封闭 2.2、无状态 2.3、让类不可变 2.4、volatile 2.5、加锁和CAS 2.6、安全的发布 2.7、TheadLocal 三、线程安全引发的一些问题 3.1、死锁 3.1.1查找程序是否死锁的方式 3.1.2动态的死锁 3.2、活锁 3.3、线程饥饿 3.4、性能和思考 3.5、影响性能的因素 3.5.1、上下文切换 3.5.2、内存同步 3.5.3、阻塞 3.6、如何减少锁的竞争 3.6.1、减少锁的粒度 3.6.2、缩小锁的范围 3.6.3、避免多余的缩减锁的范围 3.6.4、锁分段 3.6.5、替换独占锁 一、什么是类的线程安全 既然今天的主题是线程安全,那什么是线程安全呢? 其实线程安全并没有一个明确的定义,Doug Lea大师(不认识的去百度,java不认识的去面壁)给下的定义为多线程下使用这个类,不过多线程如何使用和调度这个类,这个类总是表示出正确的行为,这个类就是线程安全的。 类的线程安全表现为: 操作的原子性 内存的可见性 不做正确的同步,在多...
- 下一篇
死磕 java并发包之LongAdder源码分析
问题 (1)java8中为什么要新增LongAdder? (2)LongAdder的实现方式? (3)LongAdder与AtomicLong的对比? 简介 LongAdder是java8中新增的原子类,在多线程环境中,它比AtomicLong性能要高出不少,特别是写多的场景。 它是怎么实现的呢?让我们一起来学习吧。 原理 LongAdder的原理是,在最初无竞争时,只更新base的值,当有多线程竞争时通过分段的思想,让不同的线程更新不同的段,最后把这些段相加就得到了完整的LongAdder存储的值。 源码分析 LongAdder继承自Striped64抽象类,Striped64中定义了Cell内部类和各重要属性。 主要内部类 // Striped64中的内部类,使用@sun.misc.Contended注解,说明里面的值消除伪共享 @sun.misc.Contended static final class Cell { // 存储元素的值,使用volatile修饰保证可见性 volatile long value; Cell(long x) { value = x; } // CA...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2全家桶,快速入门学习开发网站教程
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- MySQL8.0.19开启GTID主从同步CentOS8
- CentOS7,CentOS8安装Elasticsearch6.8.6
- 2048小游戏-低调大师作品
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- Windows10,CentOS7,CentOS8安装Nodejs环境
- CentOS7安装Docker,走上虚拟化容器引擎之路