redis的list为空时为什么键被删除

当Redis的List队列为空,通过BRPOP操作后,该键会被删除。这源于Redis源码中,List为空时会触发删除操作,根据配置可能是同步或异步删除。这种行为在读多写少的场景下可能导致资源浪费。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题

一个协程brpop在一个redis list队列上,当list为空时,使用key查询redis db,发现此队列的key不存在;只有当list不空时才能keys到。以至于以为list push不成功。

原因

命令字:

struct redisCommand redisCommandTable[] = {
   {"brpop",brpopCommand,-3,"ws",0,NULL,1,-2,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},

}

void blpopCommand(client *c) {
    blockingPopGenericCommand(c,LIST_HEAD);
}

void brpopCommand(client *c) {
    blockingPopGenericCommand(c,LIST_TAIL);
}

功能实现:

void blockingPopGenericCommand(client *c, int where) {
    robj *o;
    mstime_t timeout;
    int j;

    if (getTimeoutFromObjectOrReply(c,c->argv[c->argc-1],&timeout,UNIT_SECONDS)
        != C_OK) return;

    for (j = 1; j < c->argc-1; j++) {
        o = lookupKeyWrite(c->db,c->argv[j]);
        if (o != NULL) {
            if (o->type != OBJ_LIST) {
                addReply(c,shared.wrongtypeerr);
                return;
            } else {
                if (listTypeLength(o) != 0) {
                    /* Non empty list, this is like a non normal [LR]POP. */
                    char *event = (where == LIST_HEAD) ? "lpop" : "rpop";
                    robj *value = listTypePop(o,where);
                    serverAssert(value != NULL);

                    addReplyMultiBulkLen(c,2);
                    addReplyBulk(c,c->argv[j]);
                    addReplyBulk(c,value);
                    decrRefCount(value);
                    notifyKeyspaceEvent(NOTIFY_LIST,event,
                                        c->argv[j],c->db->id);
                    if (listTypeLength(o) == 0) { //这里将key删掉了
                        dbDelete(c->db,c->argv[j]);
                        notifyKeyspaceEvent(NOTIFY_GENERIC,"del",
                                            c->argv[j],c->db->id);
                    }
                    signalModifiedKey(c->db,c->argv[j]);
                    server.dirty++;

                    /* Replicate it as an [LR]POP instead of B[LR]POP. */
                    rewriteClientCommandVector(c,2,
                        (where == LIST_HEAD) ? shared.lpop : shared.rpop,
                        c->argv[j]);
                    return;
                }
            }
        }
    }

    /* If we are inside a MULTI/EXEC and the list is empty the only thing
     * we can do is treating it as a timeout (even with timeout 0). */
    if (c->flags & CLIENT_MULTI) {
        addReply(c,shared.nullmultibulk);
        return;
    }

    /* If the list is empty or the key does not exists we must block */
    blockForKeys(c,BLOCKED_LIST,c->argv + 1,c->argc - 2,timeout,NULL,NULL);
}
unsigned long listTypeLength(const robj *subject) {
    if (subject->encoding == OBJ_ENCODING_QUICKLIST) {
        return quicklistCount(subject->ptr);
    } else {
        serverPanic("Unknown list encoding");
    }
}

unsigned long quicklistCount(const quicklist *ql) { return ql->count; }

看以上源码其中当list为空时执行了删除

if (listTypeLength(o) == 0) { //这里将key删掉了
    dbDelete(c->db,c->argv[j]);
    notifyKeyspaceEvent(NOTIFY_GENERIC,"del",c->argv[j],c->db->id);
}

每次当list为空时,都执行一次删除调用,删除有两个选择,看实现

/* This is a wrapper whose behavior depends on the Redis lazy free
 * configuration. Deletes the key synchronously or asynchronously. */
int dbDelete(redisDb *db, robj *key) {
    return server.lazyfree_lazy_server_del ? dbAsyncDelete(db,key) : dbSyncDelete(db,key);
}

分为同步删除和异步删除,具体决定于用户配置是否开启 lazyfree_lazy_server_del。
虽然异步删除,但是不是不删除,也不是懒惰删除,而是拉起一个进程执行非阻塞删除,这样做只是为了优化单线程redis的删除延迟问题。

结论

如此看来,将list作为缓冲队列,对于读大于写请求的情况下,必然会导致写一次删除一次list key的资源浪费。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值