前言:
该篇内容为我对redis的学习记录,欢迎指正批评。
一.redis 数据库流程结构
二.结构解析
server.h - client结构体
typedefstructclient {
uint64_t id; /* 客户端唯一增量ID. */
int fd; /* 客户端socket fd. */
redisDb *db; /* 指向当前选定数据库的指针。select命令可以改变 */
robj *name; /* 由客户端SETNAME设置. */
sds querybuf; /* 用于累积客户端查询的缓冲区. */
sds pending_querybuf; /* 如果这是 master,则此缓冲区表示但没有应用复制流从master那里得到的。. */
size_t querybuf_peak; /* querybuf大小的接近(100ms或更大)峰值。*/
int argc; /* 当前命令的参数个数 */
robj **argv; /* 当前命令参数. */
structredisCommand *cmd, *lastcmd; /* 上次执行的命令. */
int reqtype; /* 请求协议类型: PROTO_REQ_* */
int multibulklen; /* 要读取的多个大容量参数的数目。*/
long bulklen; /* 多批量请求中批量参数的长度. */
list *reply; /* 要发送到客户端的答复对象列表. */
unsignedlonglong reply_bytes; /* 应答列表中对象的总字节数. */
size_t sentlen; /* 当前已发送的字节数正在发送的缓冲区或对象. */
time_t ctime; /* 客户端创建时间*/
time_t lastinteraction; /* 上次交互的时间,用于超时 */
time_t obuf_soft_limit_reached_time;
int flags; /* 客户端标志: CLIENT_* 宏. */
int authenticated; /* 当requirepass为非空时.授权 */
int replstate; /* 如果这是从机,则为复制状态. */
int repl_put_online_on_ack; /* 在ACK上安装从写处理程序. */
int repldbfd; /* 复制数据库文件描述符。*/
off_t repldboff; /* 复制数据库文件偏移. */
off_t repldbsize; /* 复制数据库文件大小. */
sds replpreamble; /* 复制数据库前导. */
longlong read_reploff; /* 如果这是主服务器,则读取复制偏移量. */
longlong reploff; /* 如果这是主服务器,则应用复制偏移量. */
longlong repl_ack_off; /* 复制确认偏移量(如果这是从机). */
longlong repl_ack_time;/* 复制确认时间,如果这是从机. */
longlong psync_initial_offset; /* FULLRESYNC应答偏移量其他从机复制此从输出缓冲区应该用. */
char replid[CONFIG_RUN_ID_SIZE+1]; /* 主复制ID(如果是主复制)。*/
int slave_listening_port; /* 配置为:SLAVECONF侦听端口 */
char slave_ip[NET_IP_STR_LEN]; /* 可选地由REPLCONF ip地址提供 */
int slave_capa; /* 从机能力: SLAVE_CAPA_* 按位 OR. */
multiState mstate; /* MULTI/EXEC 状态 */
int btype; /* 如果客户端被阻止,则阻止操作的类型 */
blockingState bpop; /* 阻塞状态 */
longlong woff; /* 上次写入全局复制偏移量. */
list *watched_keys; /* 监视多个/EXEC CAS的keys */
dict *pubsub_channels; /* 频道 订阅与发布使用 (SUBSCRIBE) */
list *pubsub_patterns; /* 模式 订阅与发布使用 (SUBSCRIBE) */
sds peerid; /*缓存对等ID. */
/* 响应缓冲区 */
int bufpos;
char buf[PROTO_REPLY_CHUNK_BYTES];
} client;
server.h - redisDb数据结构
/* Redis数据库表示。已识别多个数据库从0(默认数据库)
到配置的最大值的整数数据库。数据库号是结构中的“id”字段。
*/
typedefstructredisDb {
dict *dict; /* 数据库的键值空间、键值都放在这里面 */
dict *expires; /* 设置了超时的键超时 */
dict *blocking_keys; /* 客户端等待数据的keys(BLPOP)*/
dict *ready_keys; /* 收到一个 PUSH 堵塞 keys*/
dict *watched_keys; /* MULTI/EXEC CAS的监视 keys */
int id; /* 数据库ID */
longlong avg_ttl; /* 只是为了统计平均的TTL*/
} redisDb;
dict.h - dict字典结构
typedefstructdict {
dictType *type;
void *privdata;
dictht ht[2];
long rehashidx; /* rehashi 如果 rehashidx == -1 */
unsignedlong iterators; /* 当前运行的迭代器数 */
} dict;
/*每个字典都使用两个哈希表ht[2],从而实现渐进rehash*/
typedefstructdictht {
dictEntry **table; //哈希表数组, 每个元素都是一条链表
unsignedlong size; //哈希表大小
unsignedlong sizemask; //哈希表大小掩码,用于计算索引值
unsignedlong used; //该哈希表已有节点的数量
} dictht;
// dictEntry 哈希表节点
typedefstructdictEntry {
void *key; //键
union {
void *val;
uint64_t u64;
int64_t s64;
double d;
} v; //字典的值
structdictEntry *next; //指向下一个哈希表节点,形成链表
} dictEntry;
server.h - redisObject结构体
typedefstructredisObject {
unsigned type:4; //对象类型
unsigned encoding:4; //对象编码
unsigned lru:LRU_BITS; /* LRU time (relative to global lru_clock) or
* LFU data (least significant 8 bits frequency
* and most significant 16 bits decreas time). */
int refcount; //表示对象的引用计数
void *ptr; //指向实际值的指针
} robj;
robj结构其实就是redis对象,它是一种能够保存 字符串/列表/集的类型。
对象名称 - server.h 结构体内常量
对象常量 | 对应值 | 对象名称 |
OBJ_STRING | 0 | 字符串对象 |
OBJ_LIST | 1 | 列表对象 |
OBJ_SET | 2 | 集合对象 |
OBJ_ZSET | 3 | 有序集合对象 |
OBJ_HASH | 4 | 哈希对象 |
对象编码 - server.h 结构体内常量
对象编码常量 | 对应值 | 编码名称 |
OBJ_ENCODING_RAW | 0 | 原始表示 |
OBJ_ENCODING_INT | 1 | long类型的整数 |
OBJ_ENCODING_HT | 2 | hash表,其实对应字典 |
OBJ_ENCODING_ZIPMAP | 3 | 压缩字典 |
OBJ_ENCODING_LINKEDLIST | 4 | 双端链表,redis 4.0.0没有使用,这个是老的list |
OBJ_ENCODING_ZIPLIST | 5 | 压缩表 |
OBJ_ENCODING_INTSET | 6 | 整数集合intset |
OBJ_ENCODING_SKIPLIST | 7 | 使用跳跃表和字典 |
OBJ_ENCODING_EMBSTR | 8 | 使用embstr编码的动态字符串对象 |
OBJ_ENCODING_QUICKLIST | 9 | 编码为quicklist的双向链表 |
三.源码解析
查看字典中是否存在key
dictEntry *dictFind(dict *d, constvoid *key)
{
dictEntry *he;
unsignedint h, idx, table;
if (d->ht[0].used + d->ht[1].used == 0) returnNULL; /* 判断字典是否被使用,没有返回NULL */
if (dictIsRehashing(d)) _dictRehashStep(d); //rehashidx != -1 时rehash
h = dictHashKey(d, key); //获得hash值
for (table = 0; table <= 1; table++) {
idx = h & d->ht[table].sizemask;
he = d->ht[table].table[idx];
while(he) {
if (key==he->key || dictCompareKeys(d, key, he->key)) //对比键值
return he; //返回键值的
he = he->next; //没有匹配到就查找下一个键值
}
if (!dictIsRehashing(d)) returnNULL;
}
returnNULL;
}
lldb调试图,dictEntry结构键名称。
dictEntry结构中robj对象具体值。