乐趣区

关于redis:Redis源码数据结构reidsServerredisDBrobjClientredisCommand

一、redisServer、redisDB、robj 关系

Redis 没有表的概念。然而能够用 key 作标识进行辨别,比方:user:1000 作为 key 值,示意在 user 表下 id 为 1000 的元素,相似于 user 表的 id=1000 的行。

Redis 逻辑关系: redisServer–>redisDB–>key-redisObject。所以这里次要介绍 redisServer、redisDB、redisObject 构造。

二、redisServer 构造体(在 server.h 中)

服务端构造体 redisServer 存储 Redis 服务器的所有信息,包含但不限于数据库、配置参数、命令表、监听端口与地址、客户端列表、若干统计信息、RDB 与 AOF 长久化相干信息、主从复制相干信息、集群相干信息等。

redis.h/redisServer 构造记录了和服务器相干的所有数据,这个构造体次要蕴含以下信息:

  • 服务器中的所有数据库是数组指针。
  • 已连贯客户端的信息 list 指针。
  • 服务器配置选项:redis.conf 中配置的存储。
  • 命令 hash 表:在执行命令时,依据字符来查找相应命令的实现函数。
  • 服务端的网络信息:套接字地址、端口,以及套接字描述符。
  • 日志(log)和慢查问日志(slowlog)的选项和相干信息。
  • 统计信息:比方键有多少次命令、不命中,服务器的运行工夫,等等。
  • 数据长久化(AOF 和 RDB)的配置和状态。
  • 实现订阅与公布(pub/sub)性能所需的数据结构。
  • Lua 脚本的运行环境及相干选项。
struct redisServer {
        /* General */

        // 配置文件门路
        char *configfile;           /* Absolute config file path, or NULL */
        //serverCron()调用频率
        int hz;                     /* serverCron() calls frequency in hertz */
        // 数据库对象数组指针
        redisDb *db;
        // 反对的命令列表
        dict *commands;             /* Command table */
        // 没有转化的命令
        dict *orig_commands;        /* Command table before command renaming. */
        // 事件
        aeEventLoop *el;
        // 每分钟减少一次
        unsigned lruclock:22;       /* Clock incrementing every minute, for LRU */
        unsigned lruclock_padding:10;
        int shutdown_asap;          /* SHUTDOWN needed ASAP */
        int activerehashing;        /* Incremental rehash in serverCron() */

        // 验证明码
        char *requirepass;          /* Pass for AUTH command, or NULL */
        char *pidfile;              /* PID file path */
        int arch_bits;              /* 32 or 64 depending on sizeof(long) */
        int cronloops;              /* Number of times the cron function run */
        char runid[REDIS_RUN_ID_SIZE+1];  /* ID always different at every exec. */
        int sentinel_mode;          /* True if this instance is a Sentinel. */


        /* Networking */
        int port;                   /* TCP listening port */
        int tcp_backlog;            /* TCP listen() backlog */
        char *bindaddr[REDIS_BINDADDR_MAX]; /* Addresses we should bind to */
        int bindaddr_count;         /* Number of addresses in server.bindaddr[] */
        char *unixsocket;           /* UNIX socket path */
        mode_t unixsocketperm;      /* UNIX socket permission */
        int ipfd[REDIS_BINDADDR_MAX]; /* TCP socket file descriptors */
        int ipfd_count;             /* Used slots in ipfd[] */
        int sofd;                   /* Unix socket file descriptor */
        int cfd[REDIS_BINDADDR_MAX];/* Cluster bus listening socket */
        int cfd_count;              /* Used slots in cfd[] */
        
        // 连贯的客户端
        list *clients;              /* List of active clients */
        list *clients_to_close;     /* Clients to close asynchronously */
        list *slaves, *monitors;    /* List of slaves and MONITORs */
        redisClient *current_client; /* Current client, only used on crash report */
        int clients_paused;         /* True if clients are currently paused */
        mstime_t clients_pause_end_time; /* Time when we undo clients_paused */
        char neterr[ANET_ERR_LEN];   /* Error buffer for anet.c */
        dict *migrate_cached_sockets;/* MIGRATE cached sockets */


        /* RDB / AOF loading information */
        int loading;                /* We are loading data from disk if true */
        off_t loading_total_bytes;
        off_t loading_loaded_bytes;
        time_t loading_start_time;
        off_t loading_process_events_interval_bytes;
        /* Fast pointers to often looked up command */
        struct redisCommand *delCommand, *multiCommand, *lpushCommand, *lpopCommand,
        *rpopCommand;


        /* Fields used only for stats */
        time_t stat_starttime;          /* Server start time */
        long long stat_numcommands;     /* Number of processed commands */
        long long stat_numconnections;  /* Number of connections received */
        long long stat_expiredkeys;     /* Number of expired keys */
        long long stat_evictedkeys;     /* Number of evicted keys (maxmemory) */
        long long stat_keyspace_hits;   /* Number of successful lookups of keys */
        long long stat_keyspace_misses; /* Number of failed lookups of keys */
        size_t stat_peak_memory;        /* Max used memory record */
        long long stat_fork_time;       /* Time needed to perform latest fork() */
        long long stat_rejected_conn;   /* Clients rejected because of maxclients */
        long long stat_sync_full;       /* Number of full resyncs with slaves. */
        long long stat_sync_partial_ok; /* Number of accepted PSYNC requests. */
        long long stat_sync_partial_err;/* Number of unaccepted PSYNC requests. */

        // 保留慢日志命令
        list *slowlog;                  /* SLOWLOG list of commands */
        long long slowlog_entry_id;     /* SLOWLOG current entry ID */
        long long slowlog_log_slower_than; /* SLOWLOG time limit (to get logged) */
        unsigned long slowlog_max_len;     /* SLOWLOG max number of items logged */
        /* The following two are used to track instantaneous "load" in terms
        * of operations per second. */
        long long ops_sec_last_sample_time; /* Timestamp of last sample (in ms) */
        long long ops_sec_last_sample_ops;  /* numcommands in last sample */
        long long ops_sec_samples[REDIS_OPS_SEC_SAMPLES];
        int ops_sec_idx;


        /* Configuration */
        int verbosity;                  /* Loglevel in redis.conf */
        int maxidletime;                /* Client timeout in seconds */
        int tcpkeepalive;               /* Set SO_KEEPALIVE if non-zero. */
        int active_expire_enabled;      /* Can be disabled for testing purposes. */
        size_t client_max_querybuf_len; /* Limit for client query buffer length */
        int dbnum;                      /* Total number of configured DBs */
        int daemonize;                  /* True if running as a daemon */
        clientBufferLimitsConfig client_obuf_limits[REDIS_CLIENT_LIMIT_NUM_CLASSES];


        /* AOF persistence */
        int aof_state;                  /* REDIS_AOF_(ON|OFF|WAIT_REWRITE) */
        int aof_fsync;                  /* Kind of fsync() policy */
        char *aof_filename;             /* Name of the AOF file */
        int aof_no_fsync_on_rewrite;    /* Don't fsync if a rewrite is in prog. */
        int aof_rewrite_perc;           /* Rewrite AOF if % growth is > M and... */
        off_t aof_rewrite_min_size;     /* the AOF file is at least N bytes. */
        off_t aof_rewrite_base_size;    /* AOF size on latest startup or rewrite. */
        off_t aof_current_size;         /* AOF current size. */
        int aof_rewrite_scheduled;      /* Rewrite once BGSAVE terminates. */
        pid_t aof_child_pid;            /* PID if rewriting process */
        list *aof_rewrite_buf_blocks;   /* Hold changes during an AOF rewrite. */
        sds aof_buf;      /* AOF buffer, written before entering the event loop */
        int aof_fd;       /* File descriptor of currently selected AOF file */
        int aof_selected_db; /* Currently selected DB in AOF */
        time_t aof_flush_postponed_start; /* UNIX time of postponed AOF flush */
        time_t aof_last_fsync;            /* UNIX time of last fsync() */
        time_t aof_rewrite_time_last;   /* Time used by last AOF rewrite run. */
        time_t aof_rewrite_time_start;  /* Current AOF rewrite start time. */
        int aof_lastbgrewrite_status;   /* REDIS_OK or REDIS_ERR */
        unsigned long aof_delayed_fsync;  /* delayed AOF fsync() counter */
        int aof_rewrite_incremental_fsync;/* fsync incrementally while rewriting? */
        int aof_last_write_status;      /* REDIS_OK or REDIS_ERR */
        int aof_last_write_errno;       /* Valid if aof_last_write_status is ERR */


        /* RDB persistence */
        long long dirty;                /* Changes to DB from the last save */
        long long dirty_before_bgsave;  /* Used to restore dirty on failed BGSAVE */
        pid_t rdb_child_pid;            /* PID of RDB saving child */
        struct saveparam *saveparams;   /* Save points array for RDB */
        int saveparamslen;              /* Number of saving points */
        char *rdb_filename;             /* Name of RDB file */
        int rdb_compression;            /* Use compression in RDB? */
        int rdb_checksum;               /* Use RDB checksum? */
        time_t lastsave;                /* Unix time of last successful save */
        time_t lastbgsave_try;          /* Unix time of last attempted bgsave */
        time_t rdb_save_time_last;      /* Time used by last RDB save run. */
        time_t rdb_save_time_start;     /* Current RDB save start time. */
        int lastbgsave_status;          /* REDIS_OK or REDIS_ERR */
        int stop_writes_on_bgsave_err;  /* Don't allow writes if can't BGSAVE */
        /* Propagation of commands in AOF / replication */
        redisOpArray also_propagate;    /* Additional command to propagate. */


        /* Logging */
        char *logfile;                  /* Path of log file */
        int syslog_enabled;             /* Is syslog enabled? */
        char *syslog_ident;             /* Syslog ident */
        int syslog_facility;            /* Syslog facility */


        /* Replication (master) */
        int slaveseldb;                 /* Last SELECTed DB in replication output */
        long long master_repl_offset;   /* Global replication offset */
        int repl_ping_slave_period;     /* Master pings the slave every N seconds */
        char *repl_backlog;             /* Replication backlog for partial syncs */
        long long repl_backlog_size;    /* Backlog circular buffer size */
        long long repl_backlog_histlen; /* Backlog actual data length */
        long long repl_backlog_idx;     /* Backlog circular buffer current offset */
        long long repl_backlog_off;     /* Replication offset of first byte in the
        backlog buffer. */
        time_t repl_backlog_time_limit; /* Time without slaves after the backlog
        gets released. */
        time_t repl_no_slaves_since;    /* We have no slaves since that time.
        Only valid if server.slaves len is 0. */
        int repl_min_slaves_to_write;   /* Min number of slaves to write. */
        int repl_min_slaves_max_lag;    /* Max lag of <count> slaves to write. */
        int repl_good_slaves_count;     /* Number of slaves with lag <= max_lag. */


        /* Replication (slave) */
        char *masterauth;               /* AUTH with this password with master */
        char *masterhost;               /* Hostname of master */
        int masterport;                 /* Port of master */
        int repl_timeout;               /* Timeout after N seconds of master idle */
        redisClient *master;     /* Client that is master for this slave */
        redisClient *cached_master; /* Cached master to be reused for PSYNC. */
        int repl_syncio_timeout; /* Timeout for synchronous I/O calls */
        int repl_state;          /* Replication status if the instance is a slave */
        off_t repl_transfer_size; /* Size of RDB to read from master during sync. */
        off_t repl_transfer_read; /* Amount of RDB read from master during sync. */
        off_t repl_transfer_last_fsync_off; /* Offset when we fsync-ed last time. */
        int repl_transfer_s;     /* Slave -> Master SYNC socket */
        int repl_transfer_fd;    /* Slave -> Master SYNC temp file descriptor */
        char *repl_transfer_tmpfile; /* Slave-> master SYNC temp file name */
        time_t repl_transfer_lastio; /* Unix time of the latest read, for timeout */
        int repl_serve_stale_data; /* Serve stale data when link is down? */
        int repl_slave_ro;          /* Slave is read only? */
        time_t repl_down_since; /* Unix time at which link with master went down */
        int repl_disable_tcp_nodelay;   /* Disable TCP_NODELAY after SYNC? */
        int slave_priority;             /* Reported in INFO and used by Sentinel. */
        char repl_master_runid[REDIS_RUN_ID_SIZE+1];  /* Master run id for PSYNC. */
        long long repl_master_initial_offset;         /* Master PSYNC offset. */


        /* Replication script cache. */
        dict *repl_scriptcache_dict;        /* SHA1 all slaves are aware of. */
        list *repl_scriptcache_fifo;        /* First in, first out LRU eviction. */
        int repl_scriptcache_size;          /* Max number of elements. */
        /* Synchronous replication. */
        list *clients_waiting_acks;         /* Clients waiting in WAIT command. */
        int get_ack_from_slaves;            /* If true we send REPLCONF GETACK. */


        /* Limits */
        unsigned int maxclients;        /* Max number of simultaneous clients */
        unsigned long long maxmemory;   /* Max number of memory bytes to use */
        int maxmemory_policy;           /* Policy for key eviction */
        int maxmemory_samples;          /* Pricision of random sampling */


        /* Blocked clients */
        unsigned int bpop_blocked_clients; /* Number of clients blocked by lists */
        list *unblocked_clients; /* list of clients to unblock before next loop */
        list *ready_keys;        /* List of readyList structures for BLPOP & co */
        /* Sort parameters - qsort_r() is only available under BSD so we
        * have to take this state global, in order to pass it to sortCompare() */
        int sort_desc;
        int sort_alpha;
        int sort_bypattern;
        int sort_store;


        /* Zip structure config, see redis.conf for more information  */
        size_t hash_max_ziplist_entries;
        size_t hash_max_ziplist_value;
        size_t list_max_ziplist_entries;
        size_t list_max_ziplist_value;
        size_t set_max_intset_entries;
        size_t zset_max_ziplist_entries;
        size_t zset_max_ziplist_value;
        time_t unixtime;        /* Unix time sampled every cron cycle. */
        long long mstime;       /* Like 'unixtime' but with milliseconds resolution. */


        /* Pubsub */
        dict *pubsub_channels;  /* Map channels to list of subscribed clients */
        list *pubsub_patterns;  /* A list of pubsub_patterns */
        int notify_keyspace_events; /* Events to propagate via Pub/Sub. This is an
        xor of REDIS_NOTIFY... flags. */


        /* Cluster */
        int cluster_enabled;      /* Is cluster enabled? */
        mstime_t cluster_node_timeout; /* Cluster node timeout. */
        char *cluster_configfile; /* Cluster auto-generated config file name. */
        struct clusterState *cluster;  /* State of the cluster */
        int cluster_migration_barrier; /* Cluster replicas migration barrier. */


        /* Scripting */
        lua_State *lua; /* The Lua interpreter. We use just one for all clients */
        redisClient *lua_client;   /* The "fake client" to query Redis from Lua */
        redisClient *lua_caller;   /* The client running EVAL right now, or NULL */
        dict *lua_scripts;         /* A dictionary of SHA1 -> Lua scripts */
        mstime_t lua_time_limit;  /* Script timeout in milliseconds */
        mstime_t lua_time_start;  /* Start time of script, milliseconds time */
        int lua_write_dirty;  /* True if a write command was called during the
        execution of the current script. */
        int lua_random_dirty; /* True if a random command was called during the
        execution of the current script. */
        int lua_timedout;     /* True if we reached the time limit for script
        execution. */
        int lua_kill;         /* Kill the script if true. */


        /* Assert & bug reporting */
        char *assert_failed;
        char *assert_file;
        int assert_line;
        int bug_report_start; /* True if bug report header was already logged. */
        int watchdog_period;  /* Software watchdog period in ms. 0 = off */
};

三、redisDB 构造体(在 server.h 中)

typedef struct redisDb {int id;                     /* id 是本数据库的序号,为 0 -15(默认 Redis 有 16 个数据库) */
    dict *dict;                 /* 存储数据库所有的 key-value */
    dict *expires;              /* 键的过期工夫,字典的键为键,字典的值为过期 UNIX 工夫戳 */
    dict *blocking_keys;        /* blpop 存储阻塞 key 和客户端对象 */
    dict *ready_keys;           /* 阻塞后 push 响应阻塞客户端 存储阻塞后 push 的 key 和客户端对象 */
    dict *watched_keys;         /* 存储 watch 监控的的 key 和客户端对象 */
    long long avg_ttl;          /* 存储的数据库对象的均匀 ttl(time to live),用于统计 */
    list *defrag_later;         /* List of key names to attempt to defrag one by one, gradually. */
} redisDb;
  • long long 类型

在 32 位的零碎中:shortint 占两个字节,long 占四个字节,long long 占八个字节;

在 64 位的零碎中:short占两个字节,intlong 占四个字节,long long 占八个字节。

C short/int/long/long long 等数据类型大小

C 的规范并没有规定每种类型占多少位,只是说“sizeof(long)>=sizeof(int)>=sizeof(short)”,所以具体的字节数都是依据编译器来确定的

数据类型 字节大小 数值范畴
char 1 字节 -128 到 127 或 0 到 255
unsigned char 1 字节 0 到 255
short int (短整型) 2 字节 -32 768 〜+32 767(2^15 – 1)
unsigned short int (无符号短整型) 2 字节 0 〜+65 535(2^16 – 1)
int (整型) 2 或 4 字节 4 字节:-2 147 483 648 〜+2 147 483 647(2^31 – 1)
unsigned int (无符号整型) 2 或 4 字节 4 字节:0 〜4 294 967 295 (2^32 – 1)
long int (长整型) 4 字节 -2 147 483 648 〜+2 147 483 647(2^31 – 1)
unsigned long int (无符号长整型) 4 字节 0 〜4 294 967 295(2^32 – 1)
long long int (超长整型) 8 字节 -9 223 372 036 854 775 808~9 223 372 036 854 775 807 (2^63 – 1)
unsigned long long int (无符号超长整型) 8 字节 048 446 744 073 709 551 615 (2^64 – 1)

unsigned、signed、short、long 润饰 int,int 能够省略

四、robj 构造体(server.h)

typedef struct redisObject {
    // 类型 0-string 1-list 2-set 3-zset 4-hash,在宏中定义。unsigned type:4; // :4 是位域(位段),示意只占用 4bit,2^4,http://c.biancheng.net/view/2037.html
    // 对象编码。某些类型的对象(如字符串和哈希)能够通过多种形式在外部示意。ENCODING 表明示意形式。unsigned encoding:4;
      // LRU_BITS = 24,共 24 位,高 16 位存储一个分钟数级别的工夫戳,低 8 位存储拜访计数(lfu : 最近拜访次数)
    unsigned lru:LRU_BITS; 
    // 援用次数
    int refcount;
    // 指针指向具体数据,void * 类型,从而能够执行那六大数据结构
    void *ptr;
} robj;

4.1 type(server.h)

别离对应着 5 种根底数据类型(非数据结构类型)

/* The actual Redis Object */
#define OBJ_STRING 0    /* String object. */
#define OBJ_LIST 1      /* List object. */
#define OBJ_SET 2       /* Set object. */
#define OBJ_ZSET 3      /* Sorted set object. */
#define OBJ_HASH 4      /* Hash object. */

4.2 encoding(server.h)

对象编码(数据结构类型)。某些类型的对象(如字符串和哈希)能够通过多种形式在外部示意。ENCODING 表明示意形式。

#define OBJ_ENCODING_RAW 0          // 编码为字符串 c 语言类型
#define OBJ_ENCODING_INT 1          // 编码为整数
#define OBJ_ENCODING_HT 2           // 编码为哈希表
#define OBJ_ENCODING_ZIPMAP 3       // 编码为 zipmap
#define OBJ_ENCODING_LINKEDLIST 4 /* No longer used: old list encoding. */
#define OBJ_ENCODING_ZIPLIST 5      // 编码为压缩列表
#define OBJ_ENCODING_INTSET 6       // 编码为整数汇合
#define OBJ_ENCODING_SKIPLIST 7  // 编码为跳跃表
#define OBJ_ENCODING_EMBSTR 8       // 编码为 SDS 字符串
#define OBJ_ENCODING_QUICKLIST 9 /* 疾速列表 压缩列表 + 链表 */
#define OBJ_ENCODING_STREAM 10      /* Encoded as a radix tree of listpacks */

4.3 lru(Least frequently used)

lru 记录的是对象最初一次被命令程序拜访的工夫,(4.0 以上版本占 24 位,2.6 版本占 22 位)。高 16 位存储一个分钟数级别的工夫戳,低 8 位存储拜访计数(lfu : 最近拜访次数)。

  • lru—-> 高 16 位: 最初被拜访的工夫,工夫戳秒级十进制是 19 位,所以分钟级别的是 16 位。
  • lfu—–> 低 8 位: 最近拜访次数

4.4 refcount

refcount 记录的是该对象被援用的次数,类型为整型。refcount 的作用,次要在于对象的援用计数和内存回收。

当对象的 refcount>1 时,称为共享对象,Redis 为了节俭内存,当有一些对象反复呈现时(例如常常返回的 ”OK” 字符串),新的程序不会创立新的对象,而是依然应用原来的对象。

4.5 ptr

ptr 指针指向具体的数据,比方:set hello world,ptr 指向蕴含字符串 world 的 SDS。

4.6 encoding 与 type 组合关系

当然是为了谋求速度,不同数据类型应用不同的数据结构速度才得以晋升。每种数据类型都有一种或者多种数据结构来撑持,底层数据结构有 6 种。

type 与 encoding 有固定的搭配,encoding 示意的是对应的数据结构。

type encoding-simple encoding 形容
string int REDIS_ENCODING_INT(整数) value 为纯数字时
embstr REDIS_ENCODING_EMBSTR(embstr 编码的简略动静字符串(SDS)) value 为小字符串,长度 小于 44(OBJ_ENCODING_EMBSTR_SIZE_LIMIT)个字节
raw REDIS_ENCODING_RAW(简略动静字符串) value 为大字符串,长度 大于 44(OBJ_ENCODING_EMBSTR_SIZE_LIMIT)个字节
list quicklist REDIS_ENCODING_QUICKLIST(疾速列表)
set intset REDIS_ENCODING_INTSET(整数汇合) 当 Redis 汇合类型的所有元素均是整数并且都处在 64 位有符号整数范畴内。
dict REDIS_ENCODING_HT(字典) 当 Redis 汇合类型的元素是非整数或蕴含处在 64 位有符号整数范畴外元素。
zset ziplist REDIS_ENCODING_ZIPLIST(压缩列表) 当元素的个数比拟少,且元素都是小整数或短字符串时。
skiplist REDIS_ENCODING_SKIPLIST(跳表) 当元素的个数比拟多或元素不是小整数或短字符串时。
hash ziplist REDIS_ENCODING_ZIPLIST(压缩列表) 当散列表元素的个数比拟少,且元素都是小整数或短字符串时。
dict REDIS_ENCODING_HT(字典) 当散列表元素的个数比拟多或元素不是小整数或短字符串时。
stream ziplist OBJ_ENCODING_STREAM(流) 相似音讯队列

五、client 构造体(server.h)

Redis 是典型的客户端 / 服务器 (C/S) 构造,客户端通过 socket 与服务端建设网络连接并发送命令申请,服务端解决命令申请并回复。Redis应用构造体 client 存储客户端连贯的所有信息。包含但不限于客户端的名称、客户端连贯的套接字描述符、客户端以后抉择的数据库 ID、客户端的输出缓冲区与输入缓冲区等。构造体 client 字段较多,此处只介绍命令解决主流程所需的关键字段。

typedef struct client {
   uint64_t id;     // 客户端惟一 ID,通过全局变量 server.next_client_id 实现。int fd;                 // socket 的文件描述符。redisDb *db;     // select 命令抉择的数据库对象
   robj *name;         // 客户端名称,能够应用命令 CLIENT SETNAME 设置。time_t lastinteraction // 客户端上次与服务器交互的工夫,以此实现客户端的超时解决。sds querybuf;  // 输出缓冲区,recv 函数接管的客户端命令申请会临时缓存在此缓冲区。int argc;
   robj **argv;
   struct redisCommand *cmd;
   list *reply;
   unsigned long long reply_bytes;
   size_t sentlen;
   char buf[PROTO_REPLY_CHUNK_BYTES];
   int bufpos;
} client;
  • id 为客户端惟一 ID,通过全局变量 server.next_client_id 实现。
  • fd 为客户端 socket 的文件描述符。
  • db 为客户端应用 select 命令抉择的数据库对象。
  • name:客户端名称,能够应用命令 CLIENT SETNAME 设置。
  • lastinteraction:客户端上次与服务器交互的工夫,以此实现客户端的超时解决。
  • querybuf:输出缓冲区,recv 函数接管的客户端命令申请会临时缓存在此缓冲区。
  • argc:输出缓冲区的命令申请是依照 Redis 协定格局编码字符串,须要解析出命令申请的所有参数,参数个数存储在 argc 字段,参数内容被解析为 robj 对象,存储在 argv 数组。
  • cmd:待执行的客户端命令;解析命令申请后,会依据命令名称查找该命令对应的命令对象,存储在客户端 cmd 字段,能够看到其类型为 struct redisCommand。
  • reply:输入链表,存储待返回给客户端的命令回复数据。链表节点存储的值类型为 clientReplyBlock,定义如下:
typedef struct clientReplyBlock {
 size_t size, used;
 char buf[];} clientReplyBlock;

能够看到链表节点实质上就是一个缓冲区(buffffer),其中 size 示意缓冲区空间总大小,used 示意缓冲区已应用空间大小。

  • reply_bytes:示意输入链表中所有节点的存储空间总和;
  • sentlen:示意已返回给客户端的字节数;
  • buf:输入缓冲区,存储待返回给客户端的命令回复数据,
  • bufpos 示意输入缓冲区中数据的最大字节地位,显然 sentlen~bufpos 区间的数据都是须要返回给客户端的。能够看到 reply 和 buf 都用于缓存待返回给客户端的命令回复数据,为什么同时须要 reply 和 buf 的存在呢?其实二者只是用于返回不同的数据类型而已,将在前面解说。

六、redisCommand 构造体(server.h)

redis 反对的所有命令初始都存储在全局变量 redisCommandTable 中,类型为 redisCommand。

struct redisCommand redisCommandTable[] = {{"module",moduleCommand,-2,"as",0,NULL,0,0,0,0,0},
    {"get",getCommand,2,"rF",0,NULL,1,1,1,0,0},
    {"set",setCommand,-3,"wm",0,NULL,1,1,1,0,0},
      // ……疏忽局部代码……
};

redisCommand 构造也非常简单:

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;
};
  • name:命令名称。
  • proc:命令处理函数。
  • arity:命令参数数目,用于校验命令申请格局是否正确;当 arity 小于 0 时,表示命令参数数目大于等于 arity;当 arity 大于 0 时,表示命令参数数目必须为 arity;留神命令申请中,命令的名称自身也是一个参数,如 get 命令的参数数目为 2,命令申请格局为 get key。
  • sflags:命令标记,例如标识命令时读命令还是写命令,详情参见表 9 -2;留神到 sflags 的类型为字符串,此处只是为了良好的可读性。

  • flags:命令的二进制标记,服务器启动时解析 sflags 字段生成。
  • calls:从服务器启动至今命令执行的次数,用于统计。
  • microseconds:从服务器启动至今命令总的执行工夫,
  • microseconds/calls 即可计算出该命令的均匀解决工夫,用于统计。
退出移动版