博客

2025年11月2日-英语学习

November 2, 2025
英语学习

A few different factors are likely conributing to such a tough job market for oung people right now.

年轻人面临如此严峻的就业市场,可能是由几个不同因素共同导致的。

That lack of turnover means that there are fewer opportunities for entry level workers to nab a role. “We’re seeing the labor market’s vension of the housing market’s ’lock-in’ effect, where employees are too nervous to make moves,” says Fisher. “This freeze is blocking normal opprotunities flow, so early career workers can’t break in, experienced workers can’t move up, and burned-out employees are staying put.”

...

Redis源码阅读之lpush&rpush

August 4, 2024
Redis
Redis

Redis版本:7.0.2


Redis的lpush和rpush命令用于在list的头部和尾部添加元素。源码如下:
/* LPUSH <key> <element> [<element> ...] */
void lpushCommand(client *c) {
    pushGenericCommand(c,LIST_HEAD,0);
}

/* RPUSH <key> <element> [<element> ...] */
void rpushCommand(client *c) {
    pushGenericCommand(c,LIST_TAIL,0);
}

可以看到,这两个命令都调用了pushGenericCommand函数,只是第二个参数不同,lpush对应的参数值是LIST_HEAD (0),头部插入; rpush对应的参数值是LIST_TAIL(1),尾部插入。pushGenericCommand函数的定义如下:

...

Redis源码阅读之zdel

June 25, 2024
Redis
Redis

Redis版本:7.0.2

zremCommand #

zrem命令对应的函数为zremCommand,其定义如下:

/***************t_zset.c****************/
void zremCommand(client *c) {
    robj *key = c->argv[1];
    robj *zobj;
    int deleted = 0, keyremoved = 0, j;

    if ((zobj = lookupKeyWriteOrReply(c,key,shared.czero)) == NULL ||
        checkType(c,zobj,OBJ_ZSET)) return;

    for (j = 2; j < c->argc; j++) {
        if (zsetDel(zobj,c->argv[j]->ptr)) deleted++;
        if (zsetLength(zobj) == 0) {
            dbDelete(c->db,key);
            keyremoved = 1;
            break;
        }
    }

    if (deleted) {
        notifyKeyspaceEvent(NOTIFY_ZSET,"zrem",key,c->db->id);
        if (keyremoved)
            notifyKeyspaceEvent(NOTIFY_GENERIC,"del",key,c->db->id);
        signalModifiedKey(c,c->db,key);
        server.dirty += deleted;
    }
    addReplyLongLong(c,deleted);
}

主要逻辑为:

...

Redis源码阅读之sadd

June 23, 2024
Redis
Redis

Redis版本:7.0.2

入口-saddCommand函数 #

sadd命令对应函数saddCommand,源码如下:

void saddCommand(client *c) {
    robj *set;
    int j, added = 0;

    set = lookupKeyWrite(c->db,c->argv[1]);
    if (checkType(c,set,OBJ_SET)) return;
    
    if (set == NULL) {
        set = setTypeCreate(c->argv[2]->ptr);
        dbAdd(c->db,c->argv[1],set);
    }

    for (j = 2; j < c->argc; j++) {
        if (setTypeAdd(set,c->argv[j]->ptr)) added++;
    }
    if (added) {
        signalModifiedKey(c,c->db,c->argv[1]);
        notifyKeyspaceEvent(NOTIFY_SET,"sadd",c->argv[1],c->db->id);
    }
    server.dirty += added;
    addReplyLongLong(c,added);
}

逻辑如下:

...

Redis源码阅读之zadd

June 23, 2024
Redis
Redis

Redis版本:7.0.2

有序集合的定义 #

有序集合(zet)有两种不同的编码类型:

  • 当元素数量较少且键的长度短时,使用listpack类型。
  • 否则,使用跳表编码。这种情况下,zset使用了跳表+哈希表。使用哈希表可以快速查找元素,判断元素是否存在。使用跳表可以对元素进行排序。

使用跳表编码时,源码定义如下:

...

Redis源码阅读之hdel命令

June 22, 2024
Redis
Redis

Redis版本:7.0.2

hdelCommand #

hdel命令对应的函数为hdelCommand,源码如下:

/*****************t_hash.c********************/
void hdelCommand(client *c) {
    robj *o;
    int j, deleted = 0, keyremoved = 0;

    if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL ||
        checkType(c,o,OBJ_HASH)) return;

    for (j = 2; j < c->argc; j++) {
        if (hashTypeDelete(o,c->argv[j]->ptr)) {
            deleted++;
            if (hashTypeLength(o) == 0) {
                dbDelete(c->db,c->argv[1]);
                keyremoved = 1;
                break;
            }
        }
    }
    if (deleted) {
        signalModifiedKey(c,c->db,c->argv[1]);
        notifyKeyspaceEvent(NOTIFY_HASH,"hdel",c->argv[1],c->db->id);
        if (keyremoved)
            notifyKeyspaceEvent(NOTIFY_GENERIC,"del",c->argv[1],
                                c->db->id);
        server.dirty += deleted;
    }
    addReplyLongLong(c,deleted);
}

其重要逻辑为:

...

Redis源码阅读之hget命令

June 22, 2024
Redis
Redis

Redis版本:7.0.2

hget命令对应的函数为hgetCommand,源码如下:

****************t_hash.c*******************

void hgetCommand(client *c) {
    robj *o;

    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.null[c->resp])) == NULL ||
        checkType(c,o,OBJ_HASH)) return;

    addHashFieldToReply(c, o, c->argv[2]->ptr);
}

static void addHashFieldToReply(client *c, robj *o, sds field) {
    if (o == NULL) {
        addReplyNull(c);
        return;
    }

    unsigned char *vstr = NULL;
    unsigned int vlen = UINT_MAX;
    long long vll = LLONG_MAX;

    if (hashTypeGetValue(o, field, &vstr, &vlen, &vll) == C_OK) {
        if (vstr) {
            addReplyBulkCBuffer(c, vstr, vlen);
        } else {
            addReplyBulkLongLong(c, vll);
        }
    } else {
        addReplyNull(c);
    }
}

/* Higher level function of hashTypeGet*() that returns the hash value
 * associated with the specified field. If the field is found C_OK
 * is returned, otherwise C_ERR. The returned object is returned by
 * reference in either *vstr and *vlen if it's returned in string form,
 * or stored in *vll if it's returned as a number.
 * hashTypGet*()的高级函数,返回与指定字段关联的哈希值。如果找到字段,返回C_OK,否则返回C_ERR。
 * 如果返回的是字符串形式,则以*vstr和*vlen的引用的方式返回对象。如果返回的是数字,则存储在*vll中。
 *
 * If *vll is populated *vstr is set to NULL, so the caller
 * can always check the function return by checking the return value
 * for C_OK and checking if vll (or vstr) is NULL. 
 * 如果*vll被填充,*vstr被设置为NULL,调用者就可以通过检查检查返回值是否为C_OK以及vll(或vstr)是否为NULL来检查函数返回。
 * */
int hashTypeGetValue(robj *o, sds field, unsigned char **vstr, unsigned int *vlen, long long *vll) {
    if (o->encoding == OBJ_ENCODING_LISTPACK) {
        *vstr = NULL;
        if (hashTypeGetFromListpack(o, field, vstr, vlen, vll) == 0)
            return C_OK;
    } else if (o->encoding == OBJ_ENCODING_HT) {
        sds value;
        if ((value = hashTypeGetFromHashTable(o, field)) != NULL) {
            *vstr = (unsigned char*) value;
            *vlen = sdslen(value);
            return C_OK;
        }
    } else {
        serverPanic("Unknown hash encoding");
    }
    return C_ERR;
}

/* Get the value from a hash table encoded hash, identified by field.
 * Returns NULL when the field cannot be found, otherwise the SDS value
 * is returned. 
 * 从一个使用hash表存储的hash中获取值,由field标识。当找不到field时返回NULL,否则返回SDS值。
 * */
sds hashTypeGetFromHashTable(robj *o, sds field) {
    dictEntry *de;

    serverAssert(o->encoding == OBJ_ENCODING_HT);

    de = dictFind(o->ptr, field);
    if (de == NULL) return NULL;
    return dictGetVal(de);
}

**********************dict.c************************
dictEntry *dictFind(dict *d, const void *key)
{
    dictEntry *he;
    uint64_t h, idx, table;

    if (dictSize(d) == 0)
        return NULL; /* dict is empty */
    if (dictIsRehashing(d))
        _dictRehashStep(d);
    h = dictHashKey(d, key);
    for (table = 0; table <= 1; table++)
    {
        idx = h & DICTHT_SIZE_MASK(d->ht_size_exp[table]);
        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))
            return NULL;
    }
    return NULL;
}

可以看到,查找的主要逻辑为:

...

Redis源码阅读之hset命令

June 22, 2024
Redis
Redis

Redis版本:7.0.2

hset命令对应函数hsetCommand,位于t_hash.c文件中,源码如下:

void hsetCommand(client *c) {
    int i, created = 0;
    robj *o;

    if ((c->argc % 2) == 1) {
        addReplyErrorArity(c);
        return;
    }

    if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
    hashTypeTryConversion(o,c->argv,2,c->argc-1);

    for (i = 2; i < c->argc; i += 2)
        created += !hashTypeSet(o,c->argv[i]->ptr,c->argv[i+1]->ptr,HASH_SET_COPY);

    /* HMSET (deprecated) and HSET return value is different. */
    char *cmdname = c->argv[0]->ptr;
    if (cmdname[1] == 's' || cmdname[1] == 'S') {
        /* HSET */
        addReplyLongLong(c, created);
    } else {
        /* HMSET */
        addReply(c, shared.ok);
    }
    signalModifiedKey(c,c->db,c->argv[1]);
    notifyKeyspaceEvent(NOTIFY_HASH,"hset",c->argv[1],c->db->id);
    server.dirty += (c->argc - 2)/2;
}

逻辑梳理:

...

Redis源码阅读之渐进式rehash

June 22, 2024
Redis
Redis

Redis版本:7.0.2

在哈希表扩容或缩容时,如果一次迁移完所有数据,可能会导致性能阻塞问题。为了避免出现这种情况,Redis采用了渐进式 rehash的方式,将rehash操作分为多次进行,以确保系统的稳定性。在rehash过程中,redis会逐步将旧哈希表中的数据迁移到新 哈希表中,直到旧哈希表中的所有数据都迁移完毕。

...

Redis源码阅读之del命令

June 20, 2024
Redis
Redis

redis版本:7.0.2

delCommand位于db.c文件,函数定义如下:

/* 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], 0);
        int deleted = lazy ? dbAsyncDelete(c->db, c->argv[j]) : dbSyncDelete(c->db, c->argv[j]);
        if (deleted)
        {
            signalModifiedKey(c, c->db, c->argv[j]);
            notifyKeyspaceEvent(NOTIFY_GENERIC,
                                "del", c->argv[j], c->db->id);
            server.dirty++;
            numdel++;
        }
    }
    addReplyLongLong(c, numdel);
}

void delCommand(client *c)
{
    delGenericCommand(c, server.lazyfree_lazy_user_del);
}

逻辑梳理:

...