Welcome everyone

从Redis原子性聊到库存控制

redis 汪明鑫 478浏览 0评论

Redis原子性

一般我们会通过加锁来控制并发请求,但是加锁肯定是有消耗的,而且加锁本身也会引入其他一系列的问题,如分布式锁失效等。

那么原子操作便是另一个控制并发请求的手段,即无锁操作。

Redis有2种原子操作方法:

1.单命令操作

2.lua脚本

Redis是使用单线程来串行处理客户端的请求操作命令的,所以当Redis执行某个命令操作时,其他命令是无法执行的,这相当于命令操作是互斥执行的。

Redis incr 实现

incr可以理解成把读取、加值、赋值这三个操作合成一个命令了

在t_string.c中找到incr/decr对应的代码

void incrCommand(client *c) {
    incrDecrCommand(c,1);
}

void decrCommand(client *c) {
    incrDecrCommand(c,-1);
}

void incrbyCommand(client *c) {
    long long incr;

    if (getLongLongFromObjectOrReply(c, c->argv[2], &incr, NULL) != C_OK) return;
    incrDecrCommand(c,incr);
}

void decrbyCommand(client *c) {
    long long incr;

    if (getLongLongFromObjectOrReply(c, c->argv[2], &incr, NULL) != C_OK) return;
    /* Overflow check: negating LLONG_MIN will cause an overflow */
    if (incr == LLONG_MIN) {
        addReplyError(c, "decrement would overflow");
        return;
    }
    incrDecrCommand(c,-incr);
}

void incrDecrCommand(client *c, long long incr) {
    long long value, oldvalue;
    robj *o, *new;

    o = lookupKeyWrite(c->db,c->argv[1]);  // 拿到key对应的redisObject
    if (checkType(c,o,OBJ_STRING)) return;

    // 从redisObject拿到值赋值给变量 value
    if (getLongLongFromObjectOrReply(c,o,&value,NULL) != C_OK) return;

    oldvalue = value;
    // incr不能越界
    if ((incr < 0 && oldvalue < 0 && incr < (LLONG_MIN-oldvalue)) ||
        (incr > 0 && oldvalue > 0 && incr > (LLONG_MAX-oldvalue))) {
        addReplyError(c,"increment or decrement would overflow");
        return;
    }

    // 执行相加
    value += incr;

    if (o && o->refcount == 1 && o->encoding == OBJ_ENCODING_INT &&
        (value < 0 || value >= OBJ_SHARED_INTEGERS) &&
        value >= LONG_MIN && value <= LONG_MAX)
    {
        new = o;
        o->ptr = (void*)((long)value);
    } else {
        // 新创建一个字符串对象
        new = createStringObjectFromLongLongForValue(value);
        if (o) {
            dbReplaceValue(c->db,c->argv[1],new);  // 重写
        } else {
            dbAdd(c->db,c->argv[1],new);  // 新增 k-v
        }
    }

    // 键值改动后发送信号
    signalModifiedKey(c,c->db,c->argv[1]);
    // 发送"incrby"事件通知
    notifyKeyspaceEvent(NOTIFY_STRING,"incrby",c->argv[1],c->db->id);
    server.dirty++;
    // 返回给client信息
    addReplyLongLong(c, value);
}

使用incr来做库存控制

比如我们库存是100

用incr怎么就保证不会被超发呢?

上来就incr, 刚才说了incr能保证原子性,不会产生并发问题

incr会返回结果result

如果result 97 <= 库存值100,就说明没超,再做后续的逻辑

如果此时并发请求出现大量的incr, result = 101, result = 102的都通过不了校验

当然库存最精准当然是通过db来控制

分时间片库存

其实就是在上文中的库存消耗的redis key中拼上时间属性

举个例子:

"timeSliceStocks" : [
            {
                "beginDateTime": "20230208 00:00:00.000",
                "endDateTime": "20230208 06:00:00.000",
                "round" : 1,
                "stock": 100
            },
            {
                "beginDateTime": "20230208 06:00:00.000",
                "endDateTime": "20230208 17:00:00.000",
                "round" : 2,
                "stock": 300
            },
            {
                "beginDateTime": "20230208 17:00:00.000",
                "endDateTime": "20230209 00:00:00.000",
                "round" : 3,
                "stock": 800
            }
        ]

比如时间是晚上8点,我映射的就是round = 3, 对应库存值有800个

那么就记录库存消耗

redis key 就是 stock_3, value就是当前库存消耗量

转载请注明:汪明鑫的个人博客 » 从Redis原子性聊到库存控制

喜欢 (0)

说点什么

您将是第一位评论人!

提醒
avatar
wpDiscuz