上一篇文章从根本上理解了set/get的处理过程,相当于理解了 增、改、查的过程,现在就差一个删了。本篇我们来看一下删除过程。
对于客户端来说,删除操作无需区分何种数据类型,只管进行 del 操作即可。
零、删除命令 del 的定义
主要有两个: del/unlink, 差别是 unlink 速度会更快, 因为其使用了异步删除优化模式, 其定义如下:
// 标识只有一个 w, 说明就是一个普通的写操作,没啥好说的
{"del",delCommand,-2,"w",0,NULL,1,-1,1,0,0}
// 标识为 wF, 说明它是一个快速写的操作,其实就是有一个异步优化的过程,稍后详解
{"unlink",unlinkCommand,-2,"wF",0,NULL,1,-1,1,0,0}
一、delCommand
delCommand 的作用就是直接删除某个 key 的数据,释放内存即可。
// db.c, del 命令处理
void delCommand(client *c) {
// 同步删除
delGenericCommand(c,0);
}
/* This command implements DEL and LAZYDEL. */
void delGenericCommand(client *c, int lazy) {
int numdel = 0, j;
for (j = 1; j < c->argc; j++) {
// 自动过期数据清理
expireIfNeeded(c->db,c->argv[j]);
// 此处分同步删除和异步删除, 主要差别在于对于复杂数据类型的删除方面,如hash,list,set...
// 针对 string 的删除是完全一样的
int deleted = lazy ? dbAsyncDelete(c->db,c->argv[j]) :
dbSyncDelete(c->db,c->argv[j]);
// 写命令的传播问题
if (deleted) {
signalModifiedKey(c->db,c->argv[j]);
notifyKeyspaceEvent(NOTIFY_GENERIC,
"del",c->argv[j],c->db->id);
server.dirty++;
numdel++;
}
}
// 响应删除数据量, 粒度到 key 级别
addReplyLongLong(c,numdel);
}
框架代码一看即明,只是相比于我们普通的删除是多了不少事情。否则也不存在设计了。
二、unlinkCommand
如下,其实和del是一毛一样的,仅是变化了一个 lazy 标识而已。
// db.c, unlink 删除处理
void unlinkCommand(client *c) {
// 与 del 一致,只是 lazy 标识不一样
delGenericCommand(c,1);
}
三、删除数据过程详解
删除数据分同步和异步两种实现方式,道理都差不多,只是一个是后台删一个是前台删。我们分别来看看。
1. 同步删除 dbSyncDelete
同步删除很简单,只要把对应的key删除,val删除就行了,如果有内层引用,则进行递归删除即可。
// db.c, 同步删除数据
/* Delete a key, value, and associated expiration entry if any, from the DB */
int dbSyncDelete(redisDb *db, robj *key) {
/* Deleting an entry from the expires dict will not free the sds of
* the key, because it is shared with the main dictionary. */
// 首先从 expires 队列删除,然后再从 db->dict 中删除
if (dictSize(db->expires) > 0) dictDelete(db->expires,key->ptr);
if (dictDelete(db->dict,key->ptr) == DICT_OK) {
if (server.cluster_enabled) slotToKeyDel(key);
return 1;
} else {
return 0;
}
}
// dict.c, 如上, 仅仅是 dictDelete() 就可以了,所以真正的删除动作是在 dict 中实现的。
int dictDelete(dict *ht, const void *key) {
// nofree: 0, 即要求释放内存
return dictGenericDelete(ht,key,0);
}
// dict.c, nofree: 0:要释放相应的val内存, 1:不释放相应val内存只删除key
/* Search and remove an element */
static int dictGenericDelete(dict *d, const void *key, int nofree)
{
unsigned int h, idx;
dictEntry *he, *prevHe;
int table;
if (d->ht[0].size == 0) return DICT_ERR; /* d->ht[0].table is NULL */
if (dictIsRehashing(d)) _dictRehashStep(d);
h = dictHashKey(d, key);
// ht[0] 和 ht[1] 如有可能都进行扫描
for (table = 0; table <= 1; table++) {
idx = h & d->ht[table].sizemask;
he = d->ht[table].table[idx];
prevHe = NULL;
while(he) {
if (dictCompareKeys(d, key, he->key)) {
/* Unlink the element from the list */
if (prevH