zoukankan      html  css  js  c++  java
  • (一)redis源码阅读

    1. srand()和rand()函数以及随机数种子

    srand(time(NULL)^getpid());
    
    struct timeval tv;
    gettimeofday(&tv,NULL);
    dictSetHashFunctionSeed(tv.tv_sec^tv.tv_usec^getpid());
    

    2. 守护进程及pid文件

    在main()函数中,通过配置文件判断是否以守护进程模式运行。

    if (server.daemonize) daemonize();     // 守护进程
    if (server.daemonize) createPidFile();  // 记录pid的文件
    
    // 将程序以守护进程方式运行
    void daemonize(void) {
        int fd;
    
        if (fork() != 0) exit(0); /* parent exits */
        setsid(); /* create a new session */
    
        /* Every output goes to /dev/null. If Redis is daemonized but
         * the 'logfile' is set to 'stdout' in the configuration file
         * it will not log at all. */
        if ((fd = open("/dev/null", O_RDWR, 0)) != -1) {
            dup2(fd, STDIN_FILENO);
            dup2(fd, STDOUT_FILENO);
            dup2(fd, STDERR_FILENO);
            if (fd > STDERR_FILENO) close(fd);
        }
    }
    
    // 保存进程ID到文件
    void createPidFile(void) {
        /* Try to write the pid file in a best-effort way. */
        FILE *fp = fopen(server.pidfile,"w");
        if (fp) {
            fprintf(fp,"%d
    ",(int)getpid());
            fclose(fp);
        }
    }
    

    3. 初始化服务器时的信号处理

    // 初始化服务器函数
    void initServer(void) {
    
        // 忽视信号: SIGHUP, SIGPIPE 
        signal(SIGHUP, SIG_IGN);
        signal(SIGPIPE, SIG_IGN);
    
        // 设置SIGTERM, SIGINT 信号处理函数
        setupSignalHandlers();
    
    // 设置信号处理函数,当SIGTERN, SIGINT时,退出程序
    void setupSignalHandlers(void) {
        struct sigaction act;
    
        /* When the SA_SIGINFO flag is set in sa_flags then sa_sigaction is used.
         * Otherwise, sa_handler is used. */
        sigemptyset(&act.sa_mask);
        act.sa_flags = 0;
        act.sa_handler = sigShutdownHandler;
        sigaction(SIGTERM, &act, NULL);
        sigaction(SIGINT, &act, NULL);
    
    #ifdef HAVE_BACKTRACE
        sigemptyset(&act.sa_mask);
        act.sa_flags = SA_NODEFER | SA_RESETHAND | SA_SIGINFO;
        act.sa_sigaction = sigsegvHandler;
        sigaction(SIGSEGV, &act, NULL);
        sigaction(SIGBUS, &act, NULL);
        sigaction(SIGFPE, &act, NULL);
        sigaction(SIGILL, &act, NULL);
    #endif
        return;
    }
    
    // 信号处理函数,ctrl+c 会产生 SIGINT信号,连续两次,会退出程序。
    static void sigShutdownHandler(int sig) {
        char *msg;
    
        switch (sig) {
        case SIGINT:
            msg = "Received SIGINT scheduling shutdown...";
            break;
        case SIGTERM:
            msg = "Received SIGTERM scheduling shutdown...";
            break;
        default:
            msg = "Received shutdown signal, scheduling shutdown...";
        };
    
        /* SIGINT is often delivered via Ctrl+C in an interactive session.
         * If we receive the signal the second time, we interpret this as
         * the user really wanting to quit ASAP without waiting to persist
         * on disk. */
        if (server.shutdown_asap && sig == SIGINT) {
            redisLogFromHandler(REDIS_WARNING, "You insist... exiting now.");
            rdbRemoveTempFile(getpid());
            exit(1); /* Exit with an error since this was not a clean shutdown. */
        } else if (server.loading) {
            exit(0);
        }
    
        redisLogFromHandler(REDIS_WARNING, msg);
    
    	// 在接收到SIGTERM/SIGINT信号后,在信号处理函数中将server.shutdown_asap置为1;
    	// 当下次运行serverCron函数时,会检查该值,然后安全退出程序。
        server.shutdown_asap = 1;
    }
    

    4. 在程序coredump时记录堆栈信息StackTrace

    /* Logs the stack trace using the backtrace() call. This function is designed
     * to be called from signal handlers safely. */
    void logStackTrace(ucontext_t *uc) {
        void *trace[100];
        int trace_size = 0, fd;
        int log_to_stdout = server.logfile[0] == '';
    
        /* Open the log file in append mode. */
        fd = log_to_stdout ?
            STDOUT_FILENO :
            open(server.logfile, O_APPEND|O_CREAT|O_WRONLY, 0644);
        if (fd == -1) return;
    
        /* Generate the stack trace */
        trace_size = backtrace(trace, 100);
    
        /* overwrite sigaction with caller's address */
        if (getMcontextEip(uc) != NULL)
            trace[1] = getMcontextEip(uc);
    
        /* Write symbols to log file */
        backtrace_symbols_fd(trace, trace_size, fd);
    
        /* Cleanup */
        if (!log_to_stdout) close(fd);
    }
    

    5. 线程主循环

    // 主循环
    void aeMain(aeEventLoop *eventLoop) {
        eventLoop->stop = 0;
        while (!eventLoop->stop) {
            aeProcessEvents(eventLoop, AE_ALL_EVENTS);
        }
    }
    

    6. 时间戳

    /* Return the UNIX time in microseconds */
    long long ustime(void) {
        struct timeval tv;
        long long ust;
    
        gettimeofday(&tv, NULL);
        ust = ((long long)tv.tv_sec)*1000000;
        ust += tv.tv_usec;
        return ust;
    }
    
    /* Return the UNIX time in milliseconds */
    long long mstime(void) {
        return ustime()/1000;
    }
    

    7. 全局时间缓存

    /* We take a cached value of the unix time in the global state because with
     * virtual memory and aging there is to store the current time in objects at
     * every object access, and accuracy is not needed. To access a global var is
     * a lot faster than calling time(NULL) */
     // 创建一个全局的时间缓存,是因为,访问全局变量比调用系统调用快。
    void updateCachedTime(void) {
        server.unixtime = time(NULL);
        server.mstime = mstime();
    }
    
    
    /*
    time_t unixtime;        /* Unix time sampled every cron cycle. */
    long long mstime;       /* Like 'unixtime' but with milliseconds resolution. */
    */  
    

    8. run_with_period 宏

    /* Using the following macro you can run code inside serverCron() with the
     * specified period, specified in milliseconds.
     * The actual resolution depends on server.hz. */
    #define run_with_period(_ms_) if ((_ms_ <= 1000/server.hz) || !(server.cronloops%((_ms_)/(1000/server.hz))))
    
    
    run_with_period(100) {
            trackInstantaneousMetric(REDIS_METRIC_COMMAND,server.stat_numcommands);
            trackInstantaneousMetric(REDIS_METRIC_NET_INPUT,
                    server.stat_net_input_bytes);
            trackInstantaneousMetric(REDIS_METRIC_NET_OUTPUT,
                    server.stat_net_output_bytes);
        }
    

    9. /proc/getpid()/stat

    #if defined(HAVE_PROC_STAT)
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    
    // /proc/%d/stat 等文件的作用:
    size_t zmalloc_get_rss(void) {
        int page = sysconf(_SC_PAGESIZE);
        size_t rss;
        char buf[4096];
        char filename[256];
        int fd, count;
        char *p, *x;
    
        snprintf(filename,256,"/proc/%d/stat",getpid());
        if ((fd = open(filename,O_RDONLY)) == -1) return 0;
        if (read(fd,buf,4096) <= 0) {
            close(fd);
            return 0;
        }
        close(fd);
    
        // 分离出第24个字段
        p = buf;
        count = 23; /* RSS is the 24th field in /proc/<pid>/stat */
        while(p && count--) {
            p = strchr(p,' ');
            if (p) p++;
        }
        if (!p) return 0;
        x = strchr(p,' ');
        if (!x) return 0;
        *x = '';
    
        rss = strtoll(p,NULL,10);
        rss *= page;
        return rss;
    }
    #endif
    

    10. Redis Command Table

    如果是C++中,则可使用std::map来实现命令表

    /** std::string   name;  // 命令名
      *  std::function<void(client*)>  func;  // 处理函数
    */
    std::map<std::string, std::function<void(client *)>> commandMap;
    
    /* Populates the Redis Command Table starting from the hard coded list
     * we have on top of redis.c file. */
    void populateCommandTable(void) {
        int j;
        int numcommands = sizeof(redisCommandTable)/sizeof(struct redisCommand);
    
        for (j = 0; j < numcommands; j++) {
            struct redisCommand *c = redisCommandTable+j;
            char *f = c->sflags;
            int retval1, retval2;
    
            while(*f != '') {
                switch(*f) {
                case 'w': c->flags |= REDIS_CMD_WRITE; break;
                case 'r': c->flags |= REDIS_CMD_READONLY; break;
                case 'm': c->flags |= REDIS_CMD_DENYOOM; break;
                case 'a': c->flags |= REDIS_CMD_ADMIN; break;
                case 'p': c->flags |= REDIS_CMD_PUBSUB; break;
                case 's': c->flags |= REDIS_CMD_NOSCRIPT; break;
                case 'R': c->flags |= REDIS_CMD_RANDOM; break;
                case 'S': c->flags |= REDIS_CMD_SORT_FOR_SCRIPT; break;
                case 'l': c->flags |= REDIS_CMD_LOADING; break;
                case 't': c->flags |= REDIS_CMD_STALE; break;
                case 'M': c->flags |= REDIS_CMD_SKIP_MONITOR; break;
                case 'k': c->flags |= REDIS_CMD_ASKING; break;
                case 'F': c->flags |= REDIS_CMD_FAST; break;
                default: redisPanic("Unsupported command flag"); break;
                }
                f++;
            }
    
            retval1 = dictAdd(server.commands, sdsnew(c->name), c);
            /* Populate an additional dictionary that will be unaffected
             * by rename-command statements in redis.conf. */
            retval2 = dictAdd(server.orig_commands, sdsnew(c->name), c);
            redisAssert(retval1 == DICT_OK && retval2 == DICT_OK);
        }
    }
    

    在redis.h文件中定义有Redis Command Table:

    // redisCommand 结构,记录一个redis命令的实现信息。
    struct redisCommand {
        char *name;              // 命令名
        redisCommandProc *proc;  // 命令处理函数
        int arity;               // 参数数量
        char *sflags; /* Flags as string representation, one char per flag. */
        int flags;    /* The actual flags, obtained from the 'sflags' field. */
        /* Use a function to determine keys arguments in a command line.
         * Used for Redis Cluster redirect. */
        redisGetKeysProc *getkeys_proc;
        /* What keys should be loaded in background when calling this command? */
        int firstkey; /* The first argument that's a key (0 = no keys) */
        int lastkey;  /* The last argument that's a key */
        int keystep;  /* The step between first and last key */
        long long microseconds, calls;
    };
    
    /* Our command table.
     *
     * Every entry is composed of the following fields:
     *
     * name: a string representing the command name.
     * function: pointer to the C function implementing the command.
     * arity: number of arguments, it is possible to use -N to say >= N
     * sflags: command flags as string. See below for a table of flags.
     * flags: flags as bitmask. Computed by Redis using the 'sflags' field.
     * get_keys_proc: an optional function to get key arguments from a command.
     *                This is only used when the following three fields are not
     *                enough to specify what arguments are keys.
     * first_key_index: first argument that is a key
     * last_key_index: last argument that is a key
     * key_step: step to get all the keys from first to last argument. For instance
     *           in MSET the step is two since arguments are key,val,key,val,...
     * microseconds: microseconds of total execution time for this command.
     * calls: total number of calls of this command.
     *
     * The flags, microseconds and calls fields are computed by Redis and should
     * always be set to zero.
     *
     * Command flags are expressed using strings where every character represents
     * a flag. Later the populateCommandTable() function will take care of
     * populating the real 'flags' field using this characters.
     *
     * This is the meaning of the flags:
     *
     * w: write command (may modify the key space).
     * r: read command  (will never modify the key space).
     * m: may increase memory usage once called. Don't allow if out of memory.
     * a: admin command, like SAVE or SHUTDOWN.
     * p: Pub/Sub related command.
     * f: force replication of this command, regardless of server.dirty.
     * s: command not allowed in scripts.
     * R: random command. Command is not deterministic, that is, the same command
     *    with the same arguments, with the same key space, may have different
     *    results. For instance SPOP and RANDOMKEY are two random commands.
     * S: Sort command output array if called from script, so that the output
     *    is deterministic.
     * l: Allow command while loading the database.
     * t: Allow command while a slave has stale data but is not allowed to
     *    server this data. Normally no command is accepted in this condition
     *    but just a few.
     * M: Do not automatically propagate the command on MONITOR.
     * k: Perform an implicit ASKING for this command, so the command will be
     *    accepted in cluster mode if the slot is marked as 'importing'.
     * F: Fast command: O(1) or O(log(N)) command that should never delay
     *    its execution as long as the kernel scheduler is giving us time.
     *    Note that commands that may trigger a DEL as a side effect (like SET)
     *    are not fast commands.
     */
    struct redisCommand redisCommandTable[] = {
        {"get",getCommand,2,"rF",0,NULL,1,1,1,0,0},
        {"set",setCommand,-3,"wm",0,NULL,1,1,1,0,0},
        {"setnx",setnxCommand,3,"wmF",0,NULL,1,1,1,0,0},
        {"setex",setexCommand,4,"wm",0,NULL,1,1,1,0,0},
        {"psetex",psetexCommand,4,"wm",0,NULL,1,1,1,0,0},
        {"append",appendCommand,3,"wm",0,NULL,1,1,1,0,0},
        {"strlen",strlenCommand,2,"rF",0,NULL,1,1,1,0,0},
        {"del",delCommand,-2,"w",0,NULL,1,-1,1,0,0},
        {"exists",existsCommand,-2,"rF",0,NULL,1,-1,1,0,0},
        {"setbit",setbitCommand,4,"wm",0,NULL,1,1,1,0,0},
        {"getbit",getbitCommand,3,"rF",0,NULL,1,1,1,0,0},
        {"setrange",setrangeCommand,4,"wm",0,NULL,1,1,1,0,0},
        {"getrange",getrangeCommand,4,"r",0,NULL,1,1,1,0,0},
        {"substr",getrangeCommand,4,"r",0,NULL,1,1,1,0,0},
        {"incr",incrCommand,2,"wmF",0,NULL,1,1,1,0,0},
        {"decr",decrCommand,2,"wmF",0,NULL,1,1,1,0,0},
        {"mget",mgetCommand,-2,"r",0,NULL,1,-1,1,0,0},
        {"rpush",rpushCommand,-3,"wmF",0,NULL,1,1,1,0,0},
        {"lpush",lpushCommand,-3,"wmF",0,NULL,1,1,1,0,0},
        {"rpushx",rpushxCommand,3,"wmF",0,NULL,1,1,1,0,0},
        {"lpushx",lpushxCommand,3,"wmF",0,NULL,1,1,1,0,0},
        {"linsert",linsertCommand,5,"wm",0,NULL,1,1,1,0,0},
        {"rpop",rpopCommand,2,"wF",0,NULL,1,1,1,0,0},
        {"lpop",lpopCommand,2,"wF",0,NULL,1,1,1,0,0},
        {"brpop",brpopCommand,-3,"ws",0,NULL,1,1,1,0,0},
        {"brpoplpush",brpoplpushCommand,4,"wms",0,NULL,1,2,1,0,0},
        {"blpop",blpopCommand,-3,"ws",0,NULL,1,-2,1,0,0},
        {"llen",llenCommand,2,"rF",0,NULL,1,1,1,0,0},
        {"lindex",lindexCommand,3,"r",0,NULL,1,1,1,0,0},
        {"lset",lsetCommand,4,"wm",0,NULL,1,1,1,0,0},
        {"lrange",lrangeCommand,4,"r",0,NULL,1,1,1,0,0},
        {"ltrim",ltrimCommand,4,"w",0,NULL,1,1,1,0,0},
        {"lrem",lremCommand,4,"w",0,NULL,1,1,1,0,0},
        {"rpoplpush",rpoplpushCommand,3,"wm",0,NULL,1,2,1,0,0},
        {"sadd",saddCommand,-3,"wmF",0,NULL,1,1,1,0,0},
        {"srem",sremCommand,-3,"wF",0,NULL,1,1,1,0,0},
        {"smove",smoveCommand,4,"wF",0,NULL,1,2,1,0,0},
        {"sismember",sismemberCommand,3,"rF",0,NULL,1,1,1,0,0},
        {"scard",scardCommand,2,"rF",0,NULL,1,1,1,0,0},
        {"spop",spopCommand,2,"wRsF",0,NULL,1,1,1,0,0},
        {"srandmember",srandmemberCommand,-2,"rR",0,NULL,1,1,1,0,0},
        {"sinter",sinterCommand,-2,"rS",0,NULL,1,-1,1,0,0},
        {"sinterstore",sinterstoreCommand,-3,"wm",0,NULL,1,-1,1,0,0},
        {"sunion",sunionCommand,-2,"rS",0,NULL,1,-1,1,0,0},
        {"sunionstore",sunionstoreCommand,-3,"wm",0,NULL,1,-1,1,0,0},
        {"sdiff",sdiffCommand,-2,"rS",0,NULL,1,-1,1,0,0},
        {"sdiffstore",sdiffstoreCommand,-3,"wm",0,NULL,1,-1,1,0,0},
        {"smembers",sinterCommand,2,"rS",0,NULL,1,1,1,0,0},
        {"sscan",sscanCommand,-3,"rR",0,NULL,1,1,1,0,0},
        {"zadd",zaddCommand,-4,"wmF",0,NULL,1,1,1,0,0},
        {"zincrby",zincrbyCommand,4,"wmF",0,NULL,1,1,1,0,0},
        {"zrem",zremCommand,-3,"wF",0,NULL,1,1,1,0,0},
        {"zremrangebyscore",zremrangebyscoreCommand,4,"w",0,NULL,1,1,1,0,0},
        {"zremrangebyrank",zremrangebyrankCommand,4,"w",0,NULL,1,1,1,0,0},
        {"zremrangebylex",zremrangebylexCommand,4,"w",0,NULL,1,1,1,0,0},
        {"zunionstore",zunionstoreCommand,-4,"wm",0,zunionInterGetKeys,0,0,0,0,0},
        {"zinterstore",zinterstoreCommand,-4,"wm",0,zunionInterGetKeys,0,0,0,0,0},
        {"zrange",zrangeCommand,-4,"r",0,NULL,1,1,1,0,0},
        {"zrangebyscore",zrangebyscoreCommand,-4,"r",0,NULL,1,1,1,0,0},
        {"zrevrangebyscore",zrevrangebyscoreCommand,-4,"r",0,NULL,1,1,1,0,0},
        {"zrangebylex",zrangebylexCommand,-4,"r",0,NULL,1,1,1,0,0},
        {"zrevrangebylex",zrevrangebylexCommand,-4,"r",0,NULL,1,1,1,0,0},
        {"zcount",zcountCommand,4,"rF",0,NULL,1,1,1,0,0},
        {"zlexcount",zlexcountCommand,4,"rF",0,NULL,1,1,1,0,0},
        {"zrevrange",zrevrangeCommand,-4,"r",0,NULL,1,1,1,0,0},
        {"zcard",zcardCommand,2,"rF",0,NULL,1,1,1,0,0},
        {"zscore",zscoreCommand,3,"rF",0,NULL,1,1,1,0,0},
        {"zrank",zrankCommand,3,"rF",0,NULL,1,1,1,0,0},
        {"zrevrank",zrevrankCommand,3,"rF",0,NULL,1,1,1,0,0},
        {"zscan",zscanCommand,-3,"rR",0,NULL,1,1,1,0,0},
        {"hset",hsetCommand,4,"wmF",0,NULL,1,1,1,0,0},
        {"hsetnx",hsetnxCommand,4,"wmF",0,NULL,1,1,1,0,0},
        {"hget",hgetCommand,3,"rF",0,NULL,1,1,1,0,0},
        {"hmset",hmsetCommand,-4,"wm",0,NULL,1,1,1,0,0},
        {"hmget",hmgetCommand,-3,"r",0,NULL,1,1,1,0,0},
        {"hincrby",hincrbyCommand,4,"wmF",0,NULL,1,1,1,0,0},
        {"hincrbyfloat",hincrbyfloatCommand,4,"wmF",0,NULL,1,1,1,0,0},
        {"hdel",hdelCommand,-3,"wF",0,NULL,1,1,1,0,0},
        {"hlen",hlenCommand,2,"rF",0,NULL,1,1,1,0,0},
        {"hkeys",hkeysCommand,2,"rS",0,NULL,1,1,1,0,0},
        {"hvals",hvalsCommand,2,"rS",0,NULL,1,1,1,0,0},
        {"hgetall",hgetallCommand,2,"r",0,NULL,1,1,1,0,0},
        {"hexists",hexistsCommand,3,"rF",0,NULL,1,1,1,0,0},
        {"hscan",hscanCommand,-3,"rR",0,NULL,1,1,1,0,0},
        {"incrby",incrbyCommand,3,"wmF",0,NULL,1,1,1,0,0},
        {"decrby",decrbyCommand,3,"wmF",0,NULL,1,1,1,0,0},
        {"incrbyfloat",incrbyfloatCommand,3,"wmF",0,NULL,1,1,1,0,0},
        {"getset",getsetCommand,3,"wm",0,NULL,1,1,1,0,0},
        {"mset",msetCommand,-3,"wm",0,NULL,1,-1,2,0,0},
        {"msetnx",msetnxCommand,-3,"wm",0,NULL,1,-1,2,0,0},
        {"randomkey",randomkeyCommand,1,"rR",0,NULL,0,0,0,0,0},
        {"select",selectCommand,2,"rlF",0,NULL,0,0,0,0,0},
        {"move",moveCommand,3,"wF",0,NULL,1,1,1,0,0},
        {"rename",renameCommand,3,"w",0,NULL,1,2,1,0,0},
        {"renamenx",renamenxCommand,3,"wF",0,NULL,1,2,1,0,0},
        {"expire",expireCommand,3,"wF",0,NULL,1,1,1,0,0},
        {"expireat",expireatCommand,3,"wF",0,NULL,1,1,1,0,0},
        {"pexpire",pexpireCommand,3,"wF",0,NULL,1,1,1,0,0},
        {"pexpireat",pexpireatCommand,3,"wF",0,NULL,1,1,1,0,0},
        {"keys",keysCommand,2,"rS",0,NULL,0,0,0,0,0},
        {"scan",scanCommand,-2,"rR",0,NULL,0,0,0,0,0},
        {"dbsize",dbsizeCommand,1,"rF",0,NULL,0,0,0,0,0},
        {"auth",authCommand,2,"rsltF",0,NULL,0,0,0,0,0},
        {"ping",pingCommand,-1,"rtF",0,NULL,0,0,0,0,0},
        {"echo",echoCommand,2,"rF",0,NULL,0,0,0,0,0},
        {"save",saveCommand,1,"ars",0,NULL,0,0,0,0,0},
        {"bgsave",bgsaveCommand,1,"ar",0,NULL,0,0,0,0,0},
        {"bgrewriteaof",bgrewriteaofCommand,1,"ar",0,NULL,0,0,0,0,0},
        {"shutdown",shutdownCommand,-1,"arlt",0,NULL,0,0,0,0,0},
        {"lastsave",lastsaveCommand,1,"rRF",0,NULL,0,0,0,0,0},
        {"type",typeCommand,2,"rF",0,NULL,1,1,1,0,0},
        {"multi",multiCommand,1,"rsF",0,NULL,0,0,0,0,0},
        {"exec",execCommand,1,"sM",0,NULL,0,0,0,0,0},
        {"discard",discardCommand,1,"rsF",0,NULL,0,0,0,0,0},
        {"sync",syncCommand,1,"ars",0,NULL,0,0,0,0,0},
        {"psync",syncCommand,3,"ars",0,NULL,0,0,0,0,0},
        {"replconf",replconfCommand,-1,"arslt",0,NULL,0,0,0,0,0},
        {"flushdb",flushdbCommand,1,"w",0,NULL,0,0,0,0,0},
        {"flushall",flushallCommand,1,"w",0,NULL,0,0,0,0,0},
        {"sort",sortCommand,-2,"wm",0,sortGetKeys,1,1,1,0,0},
        {"info",infoCommand,-1,"rlt",0,NULL,0,0,0,0,0},
        {"monitor",monitorCommand,1,"ars",0,NULL,0,0,0,0,0},
        {"ttl",ttlCommand,2,"rF",0,NULL,1,1,1,0,0},
        {"pttl",pttlCommand,2,"rF",0,NULL,1,1,1,0,0},
        {"persist",persistCommand,2,"wF",0,NULL,1,1,1,0,0},
        {"slaveof",slaveofCommand,3,"ast",0,NULL,0,0,0,0,0},
        {"role",roleCommand,1,"lst",0,NULL,0,0,0,0,0},
        {"debug",debugCommand,-2,"as",0,NULL,0,0,0,0,0},
        {"config",configCommand,-2,"art",0,NULL,0,0,0,0,0},
        {"subscribe",subscribeCommand,-2,"rpslt",0,NULL,0,0,0,0,0},
        {"unsubscribe",unsubscribeCommand,-1,"rpslt",0,NULL,0,0,0,0,0},
        {"psubscribe",psubscribeCommand,-2,"rpslt",0,NULL,0,0,0,0,0},
        {"punsubscribe",punsubscribeCommand,-1,"rpslt",0,NULL,0,0,0,0,0},
        {"publish",publishCommand,3,"pltrF",0,NULL,0,0,0,0,0},
        {"pubsub",pubsubCommand,-2,"pltrR",0,NULL,0,0,0,0,0},
        {"watch",watchCommand,-2,"rsF",0,NULL,1,-1,1,0,0},
        {"unwatch",unwatchCommand,1,"rsF",0,NULL,0,0,0,0,0},
        {"cluster",clusterCommand,-2,"ar",0,NULL,0,0,0,0,0},
        {"restore",restoreCommand,-4,"wm",0,NULL,1,1,1,0,0},
        {"restore-asking",restoreCommand,-4,"wmk",0,NULL,1,1,1,0,0},
        {"migrate",migrateCommand,-6,"w",0,migrateGetKeys,0,0,0,0,0},
        {"asking",askingCommand,1,"r",0,NULL,0,0,0,0,0},
        {"readonly",readonlyCommand,1,"rF",0,NULL,0,0,0,0,0},
        {"readwrite",readwriteCommand,1,"rF",0,NULL,0,0,0,0,0},
        {"dump",dumpCommand,2,"r",0,NULL,1,1,1,0,0},
        {"object",objectCommand,3,"r",0,NULL,2,2,2,0,0},
        {"client",clientCommand,-2,"rs",0,NULL,0,0,0,0,0},
        {"eval",evalCommand,-3,"s",0,evalGetKeys,0,0,0,0,0},
        {"evalsha",evalShaCommand,-3,"s",0,evalGetKeys,0,0,0,0,0},
        {"slowlog",slowlogCommand,-2,"r",0,NULL,0,0,0,0,0},
        {"script",scriptCommand,-2,"rs",0,NULL,0,0,0,0,0},
        {"time",timeCommand,1,"rRF",0,NULL,0,0,0,0,0},
        {"bitop",bitopCommand,-4,"wm",0,NULL,2,-1,1,0,0},
        {"bitcount",bitcountCommand,-2,"r",0,NULL,1,1,1,0,0},
        {"bitpos",bitposCommand,-3,"r",0,NULL,1,1,1,0,0},
        {"wait",waitCommand,3,"rs",0,NULL,0,0,0,0,0},
        {"command",commandCommand,0,"rlt",0,NULL,0,0,0,0,0},
        {"pfselftest",pfselftestCommand,1,"r",0,NULL,0,0,0,0,0},
        {"pfadd",pfaddCommand,-2,"wmF",0,NULL,1,1,1,0,0},
        {"pfcount",pfcountCommand,-2,"r",0,NULL,1,-1,1,0,0},
        {"pfmerge",pfmergeCommand,-2,"wm",0,NULL,1,-1,1,0,0},
        {"pfdebug",pfdebugCommand,-3,"w",0,NULL,0,0,0,0,0},
        {"latency",latencyCommand,-2,"arslt",0,NULL,0,0,0,0,0}
    };
    
  • 相关阅读:
    Python Generators vs Iterators
    python staticmethod classmethod
    静态类型、动态类型、强类型以及弱类型语言
    Python串行运算、并行运算、多线程、多进程对比实验
    python字典根据value排序
    解读Python内存管理机制
    两个list 求交集效率对比
    Python error: Unable to find vcvarsall.bat
    max-length兼容ie
    return false 与return true 困惑
  • 原文地址:https://www.cnblogs.com/walkinginthesun/p/9777221.html
Copyright © 2011-2022 走看看