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
- 下一篇: 前端面试题-数组去重和排序