目录
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原子性聊到库存控制
说点什么
您将是第一位评论人!