redis源码解读二
生活随笔
收集整理的这篇文章主要介绍了
redis源码解读二
小编觉得挺不错的,现在分享给大家,帮大家做个参考.
上一篇解读了一下SDS,本来觉得完了,但之后想想感觉少点什么,现在我们从使用的角度去梳理一下,大家想想对于字符串, 我们经常使用的有哪几个方法呢?这些方法又是怎么实现的?
在研究上面的几个方法之前我们先来了解一下redisObject对象,源码在redis.h中,以下是源码:
typedef struct redisObject {unsigned type:4; //对象类型,string、hash、list、set、zsetunsigned encoding:4; //内部编码类型,SDS等unsigned lru:REDIS_LRU_BITS; //记录对象最后一次被访问的时间,计时时钟int refcount; //记录当前对象被引用的次数,引用计数器void *ptr; //如果是整数,直接存储数据,否则是指向数据的指针 } robj;set msg "hello world"; 上面这个指令会创建两个RedisObject对象,一个表示键,一个表示值, 键的type都为string,encoding为int/embstr/raw的一种 那么这三个类型分别被设置在什么情况下?我们具体聊一聊,当字符串对象保存的是整数值,并且这个值可以用long来表示,那么字符串对象会将该值保存在RedisObject的ptr属性里(将void*转换成long),encoding就为int;如果字符串对象保存的是一个字符串值,并且长度大于39字节,那么字符串对象使用一个SDS来保存这个值,encoding为raw如果字符串对象保存的是一个字符串值,并且长度小于39字节,那么字符串对象使用一个SDS来保存这个值,encoding为embstr raw与embstr有什么分别?raw和embstr都使用RedisObject和sdshdr来表示字符串对象embstr是专门保存短字符串的一种优化编码方式,调用一次内存分配函数来分配一块连续的空间,依次包含RedisObject和sdshdrraw会调用两次内存分配函数来分别创建RedisObject和sdshdr结构 注:int、embstr是可以被转为raw的struct redisCommand redisCommandTable[] = {};//共有157个命令 接下来我们先看看几个常用的字符串命令的源码实现,定义在redis.h,具体实现在t_string.c中 set:void setCommand(redisClient *c); get:void getCommand(redisClient *c); mget:void mgetCommand(redisClient *c); mset:void msetCommand(redisClient *c); incr:void incrCommand(redisClient *c); 参数都是redisClient,定义在redis.h,如下 typedef struct redisClient {uint64_t id; /* Client incremental unique ID. */int fd;redisDb *db;int dictid;robj *name; /* As set by CLIENT SETNAME */sds querybuf;size_t querybuf_peak; /* Recent (100ms or more) peak of querybuf size */int argc;robj **argv;struct redisCommand *cmd, *lastcmd;int reqtype;int multibulklen; /* number of multi bulk arguments left to read */long bulklen; /* length of bulk argument in multi bulk request */list *reply;unsigned long reply_bytes; /* Tot bytes of objects in reply list */int sentlen; /* Amount of bytes already sent in the currentbuffer or object being sent. */time_t ctime; /* Client creation time */time_t lastinteraction; /* time of the last interaction, used for timeout */time_t obuf_soft_limit_reached_time;int flags; /* REDIS_SLAVE | REDIS_MONITOR | REDIS_MULTI ... */int authenticated; /* when requirepass is non-NULL */int replstate; /* replication state if this is a slave */int repl_put_online_on_ack; /* Install slave write handler on ACK. */int repldbfd; /* replication DB file descriptor */off_t repldboff; /* replication DB file offset */off_t repldbsize; /* replication DB file size */sds replpreamble; /* replication DB preamble. */long long reploff; /* replication offset if this is our master */long long repl_ack_off; /* replication ack offset, if this is a slave */long long repl_ack_time;/* replication ack time, if this is a slave */long long psync_initial_offset; /* FULLRESYNC reply offset other slavescopying this slave output buffershould use. */char replrunid[REDIS_RUN_ID_SIZE+1]; /* master run id if this is a master */int slave_listening_port; /* As configured with: SLAVECONF listening-port */int slave_capa; /* Slave capabilities: SLAVE_CAPA_* bitwise OR. */multiState mstate; /* MULTI/EXEC state */blockingState bpop; /* blocking state */list *watched_keys; /* Keys WATCHED for MULTI/EXEC CAS */dict *pubsub_channels; /* channels a client is interested in (SUBSCRIBE) */list *pubsub_patterns; /* patterns a client is interested in (SUBSCRIBE) */sds peerid; /* Cached peer ID. *//* Response buffer */int bufpos;char buf[REDIS_REPLY_CHUNK_BYTES]; } redisClient;void setGenericCommand(redisClient *c, int flags, robj *key, robj *val, robj *expire, int unit, robj *ok_reply, robj *abort_reply) {long long milliseconds = 0; /* initialized to avoid any harmness warning */if (expire) {if (getLongLongFromObjectOrReply(c, expire, &milliseconds, NULL) != REDIS_OK)return;if (milliseconds <= 0) {addReplyErrorFormat(c,"invalid expire time in %s",c->cmd->name);return;}if (unit == UNIT_SECONDS) milliseconds *= 1000;}if ((flags & REDIS_SET_NX && lookupKeyWrite(c->db,key) != NULL) ||(flags & REDIS_SET_XX && lookupKeyWrite(c->db,key) == NULL)){addReply(c, abort_reply ? abort_reply : shared.nullbulk);return;}setKey(c->db,key,val);server.dirty++;if (expire) setExpire(c->db,key,mstime()+milliseconds);notifyKeyspaceEvent(REDIS_NOTIFY_STRING,"set",key,c->db->id);if (expire) notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"expire",key,c->db->id);addReply(c, ok_reply ? ok_reply : shared.ok); }/* SET key value [NX] [XX] [EX <seconds>] [PX <milliseconds>] */ void setCommand(redisClient *c) {int j;robj *expire = NULL;int unit = UNIT_SECONDS;int flags = REDIS_SET_NO_FLAGS;for (j = 3; j < c->argc; j++) {char *a = c->argv[j]->ptr;robj *next = (j == c->argc-1) ? NULL : c->argv[j+1];if ((a[0] == 'n' || a[0] == 'N') &&(a[1] == 'x' || a[1] == 'X') && a[2] == '\0') {flags |= REDIS_SET_NX;} else if ((a[0] == 'x' || a[0] == 'X') &&(a[1] == 'x' || a[1] == 'X') && a[2] == '\0') {flags |= REDIS_SET_XX;} else if ((a[0] == 'e' || a[0] == 'E') &&(a[1] == 'x' || a[1] == 'X') && a[2] == '\0' && next) {unit = UNIT_SECONDS;expire = next;j++;} else if ((a[0] == 'p' || a[0] == 'P') &&(a[1] == 'x' || a[1] == 'X') && a[2] == '\0' && next) {unit = UNIT_MILLISECONDS;expire = next;j++;} else {addReply(c,shared.syntaxerr);return;}}c->argv[2] = tryObjectEncoding(c->argv[2]);setGenericCommand(c,flags,c->argv[1],c->argv[2],expire,unit,NULL,NULL); } int getGenericCommand(redisClient *c) {robj *o;if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL)return REDIS_OK;if (o->type != REDIS_STRING) {addReply(c,shared.wrongtypeerr);return REDIS_ERR;} else {addReplyBulk(c,o);return REDIS_OK;} }void getCommand(redisClient *c) {getGenericCommand(c); }void mgetCommand(redisClient *c) {int j;addReplyMultiBulkLen(c,c->argc-1);for (j = 1; j < c->argc; j++) {robj *o = lookupKeyRead(c->db,c->argv[j]);if (o == NULL) {addReply(c,shared.nullbulk);} else {if (o->type != REDIS_STRING) {addReply(c,shared.nullbulk);} else {addReplyBulk(c,o);}}} }void msetGenericCommand(redisClient *c, int nx) {int j, busykeys = 0;if ((c->argc % 2) == 0) {addReplyError(c,"wrong number of arguments for MSET");return;}/* Handle the NX flag. The MSETNX semantic is to return zero and don't* set nothing at all if at least one already key exists. */if (nx) {for (j = 1; j < c->argc; j += 2) {if (lookupKeyWrite(c->db,c->argv[j]) != NULL) {busykeys++;}}if (busykeys) {addReply(c, shared.czero);return;}}for (j = 1; j < c->argc; j += 2) {c->argv[j+1] = tryObjectEncoding(c->argv[j+1]);setKey(c->db,c->argv[j],c->argv[j+1]);notifyKeyspaceEvent(REDIS_NOTIFY_STRING,"set",c->argv[j],c->db->id);}server.dirty += (c->argc-1)/2;addReply(c, nx ? shared.cone : shared.ok); }void msetCommand(redisClient *c) {msetGenericCommand(c,0); }void incrCommand(redisClient *c) {incrDecrCommand(c,1); }void incrDecrCommand(redisClient *c, long long incr) {long long value, oldvalue;robj *o, *new;o = lookupKeyWrite(c->db,c->argv[1]);if (o != NULL && checkType(c,o,REDIS_STRING)) return;if (getLongLongFromObjectOrReply(c,o,&value,NULL) != REDIS_OK) return;oldvalue = value;if ((incr < 0 && oldvalue < 0 && incr < (LLONG_MIN-oldvalue)) ||(incr > 0 && oldvalue > 0 && incr > (LLONG_MAX-oldvalue))) {addReplyError(c,"increment or decrement would overflow");return;}value += incr;new = createStringObjectFromLongLong(value);if (o)dbOverwrite(c->db,c->argv[1],new);elsedbAdd(c->db,c->argv[1],new);signalModifiedKey(c->db,c->argv[1]);notifyKeyspaceEvent(REDIS_NOTIFY_STRING,"incrby",c->argv[1],c->db->id);server.dirty++;addReply(c,shared.colon);addReply(c,new);addReply(c,shared.crlf); }以上一堆代码很是枯燥吧,我是一个java开发,所以平时用的jedis去连接redis,那么我们的jedis又是如何连上redis, 把我们的命令传过去的呢?这部分涉及到网络连接的源码分析,所以统一放到最后了!这样对redis中关于字符串的源码解读就完了,大家觉得还有什么遗漏或者不对的,欢迎评论。复制代码转载于:https://juejin.im/post/5c808765f265da2de165cae8
总结
以上是生活随笔为你收集整理的redis源码解读二的全部内容,希望文章能够帮你解决所遇到的问题。
- 上一篇: 自定义DataSet
- 下一篇: 社交电商为何这么火