Redis系列(四):数据结构String类型中基本操作命令和源码解析
1.介绍
string类型本质上是char[]数组的封装
中文网:http://www.redis.cn/commands.html#string
2.常用命令
set 命令
set命令的时间复杂度是O(1)
将键key
设定为指定的“字符串”值。
如果 key 已经保存了一个值,那么这个操作会直接覆盖原来的值,并且忽略原始类型。
当set
命令执行成功之后,之前设置的过期时间都将失效
SET key value [EX seconds] [PX milliseconds] [NX|XX]
EX
seconds – 设置键key的过期时间,单位时秒
PX
milliseconds – 设置键key的过期时间,单位时毫秒
NX
– 只有键key不存在的时候才会设置key的值
XX
– 只有键key存在的时候才会设置key的值
127.0.0.1:> set myKey "Hello"
OK
127.0.0.1:> get myKey
"Hello"
127.0.0.1:> set userId ""
OK
127.0.0.1:> get userId
""
127.0.0.1:> object encoding userId
"int"
127.0.0.1:> object encoding myKey
"embstr"
127.0.0.1:> set myKey World NX
(nil)
127.0.0.1:> set myKey World XX
OK
127.0.0.1:>
NX :应用场景分布式锁:通过myKey的赋值来判断是否获取到了一个分布式锁 如果OK说明获取到了锁 如果nil说明没有获取到了锁
如果存放到string中的value是int,那么在内部还是int ,可以从encoding
redisObject中有一个type属性和encoding属性
incr/incrby/decr/decrby命令
incr/decr 自增或者自减1
incrby/decrby:自增或者自减指定数
127.0.0.1:> INCR userId
(integer)
127.0.0.1:> INCR userId
(integer)
127.0.0.1:> get userId
""
127.0.0.1:> INCRBY userId
(integer)
127.0.0.1:> DECR userId
(integer)
127.0.0.1:> DECRBY userId
(integer)
append命令
127.0.0.1:> APPEND myKey "!"
(integer)
127.0.0.1:> get myKey
"World!"
127.0.0.1:>
3.源码解析
redisCommand存放着所有的命令
{"set",setCommand,-,
"write use-memory @string",
,NULL,,,,,,},
setCommand源码
setCommand传入一个client结构体
最后调用setGenericCommand函数来处理set
/* SET key value [NX] [XX] [KEEPTTL] [EX <seconds>] [PX <milliseconds>] */
void setCommand(client *c) {
int j;
robj *expire = NULL;
int unit = UNIT_SECONDS;
int flags = OBJ_SET_NO_FLAGS; for (j = ; j < c->argc; j++) {
char *a = c->argv[j]->ptr;
robj *next = (j == c->argc-) ? NULL : c->argv[j+]; if ((a[] == 'n' || a[] == 'N') &&
(a[] == 'x' || a[] == 'X') && a[] == '\0' &&
!(flags & OBJ_SET_XX))
{
flags |= OBJ_SET_NX;
} else if ((a[] == 'x' || a[] == 'X') &&
(a[] == 'x' || a[] == 'X') && a[] == '\0' &&
!(flags & OBJ_SET_NX))
{
flags |= OBJ_SET_XX;
} else if (!strcasecmp(c->argv[j]->ptr,"KEEPTTL") &&
!(flags & OBJ_SET_EX) && !(flags & OBJ_SET_PX))
{
flags |= OBJ_SET_KEEPTTL;
} else if ((a[] == 'e' || a[] == 'E') &&
(a[] == 'x' || a[] == 'X') && a[] == '\0' &&
!(flags & OBJ_SET_KEEPTTL) &&
!(flags & OBJ_SET_PX) && next)
{
flags |= OBJ_SET_EX;
unit = UNIT_SECONDS;
expire = next;
j++;
} else if ((a[] == 'p' || a[] == 'P') &&
(a[] == 'x' || a[] == 'X') && a[] == '\0' &&
!(flags & OBJ_SET_KEEPTTL) &&
!(flags & OBJ_SET_EX) && next)
{
flags |= OBJ_SET_PX;
unit = UNIT_MILLISECONDS;
expire = next;
j++;
} else {
addReply(c,shared.syntaxerr);
return;
}
} c->argv[] = tryObjectEncoding(c->argv[]);
setGenericCommand(c,flags,c->argv[],c->argv[],expire,unit,NULL,NULL);
}
setGenericCommand源码
可以看出调用了genericSetKey函数
void setGenericCommand(client *c, int flags, robj *key, robj *val, robj *expire, int unit, robj *ok_reply, robj *abort_reply) {
long long milliseconds = ; /* initialized to avoid any harmness warning */ if (expire) {
if (getLongLongFromObjectOrReply(c, expire, &milliseconds, NULL) != C_OK)
return;
if (milliseconds <= ) {
addReplyErrorFormat(c,"invalid expire time in %s",c->cmd->name);
return;
}
if (unit == UNIT_SECONDS) milliseconds *= ;
} if ((flags & OBJ_SET_NX && lookupKeyWrite(c->db,key) != NULL) ||
(flags & OBJ_SET_XX && lookupKeyWrite(c->db,key) == NULL))
{
addReply(c, abort_reply ? abort_reply : shared.null[c->resp]);
return;
}
genericSetKey(c,c->db,key,val,flags & OBJ_SET_KEEPTTL,);
server.dirty++;
if (expire) setExpire(c,c->db,key,mstime()+milliseconds);
notifyKeyspaceEvent(NOTIFY_STRING,"set",key,c->db->id);
if (expire) notifyKeyspaceEvent(NOTIFY_GENERIC,
"expire",key,c->db->id);
addReply(c, ok_reply ? ok_reply : shared.ok);
}
genericSetKey源码
void genericSetKey(client *c, redisDb *db, robj *key, robj *val, int keepttl, int signal) {
if (lookupKeyWrite(db,key) == NULL) {
dbAdd(db,key,val);
} else {
dbOverwrite(db,key,val);
}
incrRefCount(val);
if (!keepttl) removeExpire(db,key);
if (signal) signalModifiedKey(c,db,key);
}
dbOverwrite源码
可以看出最后存入dict中
void dbOverwrite(redisDb *db, robj *key, robj *val) {
dictEntry *de = dictFind(db->dict,key->ptr); serverAssertWithInfo(NULL,key,de != NULL);
dictEntry auxentry = *de;
robj *old = dictGetVal(de);
if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {
val->lru = old->lru;
}
dictSetVal(db->dict, de, val); if (server.lazyfree_lazy_server_del) {
freeObjAsync(old);
dictSetVal(db->dict, &auxentry, NULL);
} dictFreeVal(db->dict, &auxentry);
}
Redis系列(四):数据结构String类型中基本操作命令和源码解析的更多相关文章
- Redis系列(五):数据结构List双向链表中基本操作操作命令和源码解析
1.介绍 List是通过ListNode实现的双向链表. 1.双端:获取某个结点的前驱和后继结点都是O(1) 2.无环:表头的prev指针和表尾的next指针都指向NULL,对链表的访问都是以NULL ...
- Redis系列(十二):数据结构SortedSet跳跃表中基本操作命令和源码解析
1.SkipList Redis的sortedSet数据结构是有序不重复的(索引为唯一的,数据(score)却可以重复), 跳表是redis的一个核心组件,也同时被广泛地运用到了各种缓存地实现当中,它 ...
- Redis系列-存储篇string主要操作命令
Redis系列-存储篇string主要操作命令 通过上两篇的介绍,我们的redis服务器基本跑起来.db都具有最基本的CRUD功能,我们沿着这个脉络,开始学习redis丰富的数据结构之旅,当然先从最简 ...
- [转]C# 互操作性入门系列(四):在C# 中调用COM组件
传送门 C#互操作系列文章: C# 互操作性入门系列(一):C#中互操作性介绍 C# 互操作性入门系列(二):使用平台调用调用Win32 函数 C# 互操作性入门系列(三):平台调用中的数据封送处理 ...
- ***Redis hash是一个string类型的field和value的映射表.它的添加、删除操作都是O(1)(平均)。hash特别适合用于存储对象
http://redis.readthedocs.org/en/latest/hash/hset.html HSET HSET key field value (存一个对象的时候key存) 将哈希 ...
- Redis hash 是一个 string 类型的 field 和 value 的映射表.它的添加、删除操作都是 O(1)(平均)。
2.3 hashes 类型及操作 Redis hash 是一个 string 类型的 field 和 value 的映射表.它的添加.删除操作都是 O(1)(平均).hash 特别适合用于存储对象.相 ...
- String类型中 "=="和"equals"比较的差别
String类型中 "=="和"equals"比较的差别 先说明一下String类型的变量的创建方式 在创建新的String类型的变量时,首先会在缓冲区查找是否 ...
- Scala 深入浅出实战经典 第48讲:Scala类型约束代码实战及其在Spark中的应用源码解析
王家林亲授<DT大数据梦工厂>大数据实战视频 Scala 深入浅出实战经典(1-64讲)完整视频.PPT.代码下载:百度云盘:http://pan.baidu.com/s/1c0noOt6 ...
- Redis(五):hash/hset/hget 命令源码解析
Redis作为nosql数据库,kv string型数据的支持是最基础的,但是如果仅有kv的操作,也不至于有redis的成功.(memcache就是个例子) Redis除了string, 还有hash ...
随机推荐
- nodejs链接mysql 中的问题
首先你得对mysql ,有个大概的认识. 比如说:如何安装,使用基本的语法,测试安装是否能成功,以及成功之后简单的对于数据库的,操作(增删改查)... 下面是业务场景:在爬虫过程中,租后需要将信息输出 ...
- Linux centos 7 目录结构
一.目录结构与用途: /boot:系统引导文件.内核 /bin:用户的基本命令 /dev:设备文件 /etc:配置文件 /home:用户目录 /root:root用户目录 /sbin:管理类的基本命令 ...
- ngnix随笔一
nginx安装及启动 1.用yum源安装,首先配置yum源 在“/etc/yum.repo.d/”下创建yum源文件 例如:cd /etc/yum.repo.d/nginx.repo [nginx-s ...
- [PHP学习教程 - 文件]001.高速读写大数据“二进制”文件,不必申请大内存(Byte Block)
引言:读写大“二进制”文件,不必申请很大内存(fopen.fread.fwrite.fclose)!做到开源节流,提高速度! 每天告诉自己一次,『我真的很不错』.... 加速读写大文件,在实际工作过程 ...
- [Objective-C] 005_Category(类别)
Category的实际作用就是为已有的类来添加方法.为现有的类添加的方法可以先不用实现,在需要的时候再实现也是可以的.在我们的实际代码中如何来实现Category的呢?我们上篇的Person 类为例. ...
- string 去重复
//AABB>>AB //AAA>>A //ABBAA>ABA public static string SpiltString(stri ...
- day 7 while循环
#隐式布尔值: 0 none 空# 一.流程控制# 短路运算:偷懒原则,偷懒到哪个位置,就把当前位置的值返回# 为0# (10>3 and 10 and 0 and 10 )or( 10> ...
- 折腾自己的js闭包(二)
前面我大致探讨了js里的闭包的相关概念,那么,到底在什么时候用它最好呢?存在即真理,只不过以前没发现它而已,先来看看下面的这几个用途吧 一.我首先想到的就是从函数外面访问它的内部变量,从而达到自己的一 ...
- Java实现 蓝桥杯 算法提高 小X的购物计划
试题 算法提高 小X的购物计划 问题描述 小X打算去超市shopping.小X没什么钱,只有N元.超市里有M种物品,每种物品都需要money,在小X心中有一个重要度.有的物品有无限件,有的物品只有几件 ...
- Java实现 LeetCode 235 二叉搜索树的最近公共祖先
235. 二叉搜索树的最近公共祖先 给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先. 百度百科中最近公共祖先的定义为:"对于有根树 T 的两个结点 p.q,最近公共祖先表示为一个 ...