【Redis】-使用Lua脚本解决多线程下的超卖问题以及为什么?
一.多线程下引起的超卖问题呈现
1.1.我先初始化库存数量为1、订单数量为0

1.2.开启3个线程去执行业务
业务为:判断如果说库存数量大于0,则库存减1,订单数量加1
结果为:库存为-2,订单数量为3
原因:如下图所示,这是因为分别有6个指令(3个库存减1指令,3个订单数量加1指令)在redis服务端执行导致的。
namespace MengLin.Shopping.Redis.LuaScript
{
public class SecKillOriginal
{ static SecKillOriginal()
{
using (RedisClient client = new RedisClient("127.0.0.1", 6379))
{
//删除当前数据库中的所有Key, 默认删除的是db0
client.FlushDb();
//删除所有数据库中的key
client.FlushAll(); //初始化库存数量为1和订单数量为0
client.Set("inventoryNum", 1);
client.Set("orderNum", 0);
}
} public static void Show()
{
for (int i = 0; i < 3; i++)
{
Task.Run(() =>
{
using (RedisClient client = new RedisClient("127.0.0.1", 6379))
{
int inventoryNum = client.Get<int>("inventoryNum");
//如果库存数量大于0
if (inventoryNum > 0)
{
//给库存数量-1
var inventoryNum2 = client.Decr("inventoryNum");
Console.WriteLine($"给库存数量-1后的数量-inventoryNum: {inventoryNum2}");
//给订单数量+1
var orderNum = client.Incr("orderNum");
Console.WriteLine($"给订单数量+1后的数量-orderNum: {orderNum}");
}
else
{
Console.WriteLine($"抢购失败: 原因是因为没有库存");
}
}
});
}
}
}
}


二.使用Lua脚本解决多线程下超卖的问题以及为什么
2.1.修改后的代码如下
结果为:如下图所示,库存为0、订单数量为1,并没有出现超卖的问题且有2个线程抢不到。
namespace MengLin.Shopping.Redis.LuaScript
{
public class SecKillLua
{
/// <summary>
/// 使用Lua脚本解决多线程下变卖的问题
/// </summary>
static SecKillLua()
{
using (RedisClient client = new RedisClient("127.0.0.1", 6379))
{
//删除当前数据库中的所有Key, 默认删除的是db0
client.FlushDb();
//删除所有数据库中的key
client.FlushAll(); //初始化库存数量为1和订单数量为0
client.Set("inventoryNum", 1);
client.Set("orderNum", 0);
}
} public static void Show()
{
for (int i = 0; i < 3; i++)
{
Task.Run(() =>
{
using (RedisClient client = new RedisClient("127.0.0.1", 6379))
{
//如果库存数量大于0,则给库存数量-1,给订单数量+1
var lua = @"local count = redis.call('get',KEYS[1])
if(tonumber(count)>0)
then
--return count
redis.call('INCR',ARGV[1])
return redis.call('DECR',KEYS[1])
else
return -99
end";
Console.WriteLine(client.ExecLuaAsString(lua, keys: new[] { "inventoryNum" }, args: new[] { "orderNum" }));
}
});
}
}
}
}



三.为什么使用Lua脚本就能解决多线程下的超卖问题呢?
是因为Lua脚本把3个指令,分别是:判断库存数量是否大于0、库存减1、订单数量加1,这3个指令打包放在一起执行了且不能分割,相当于组装成了原子指令,所以避免了超卖问题。
在redis中我们尽量使用原子指令从而避免一些并发的问题。

【Redis】-使用Lua脚本解决多线程下的超卖问题以及为什么?的更多相关文章
- 使用Lua脚本通过原子减防止超卖
需求 双十二要搞一个一分钱门票抢购的活动. 分析 性能分析,抢购时会发生高并发,如果仅仅依靠Mysql数据库,有可能因为大量的请求频繁访问数据库造成服务器雪崩,所以考虑通过Redis减库存,最终的数据 ...
- 快速入门Redis调用Lua脚本及使用场景介绍
Redis 是一种非常流行的内存数据库,常用于数据缓存与高频数据存储.大多数开发人员可能听说过redis可以运行 Lua 脚本,但是可能不知道redis在什么情况下需要使用到Lua脚本. 一.阅读本文 ...
- Redis结合Lua脚本实现高并发原子性操作
从 2.6版本 起, Redis 开始支持 Lua 脚本 让开发者自己扩展 Redis … 案例-实现访问频率限制: 实现访问者 $ip 在一定的时间 $time 内只能访问 $limit 次. 非脚 ...
- 要想用活Redis,Lua脚本是绕不过去的坎
前言 Redis 当中提供了许多重要的高级特性,比如发布与订阅,Lua 脚本等.Redis 当中也提供了自增的原子命令,但是假如我们需要同时执行好几个命令的同时又想让这些命令保持原子性,该怎么办呢?这 ...
- redis中lua脚本的简单使用
一.背景 在使用redis的过程中,发现有些时候需要原子性去操作redis命令,而redis的lua脚本正好可以实现这一功能.比如: 扣减库存操作.限流操作等等. redis的pipelining虽然 ...
- 基于Lua脚本解决实时数据处理流程中的关键问题
摘要 在处理实时数据的过程中需要缓存的参与,由于在更新实时数据时并发处理的特点,因此在更新实时数据时经常产生新老数据相互覆盖的情况,针对这个情况调查了Redis事务和Lua脚本后,发现Redis事务并 ...
- 【spring boot】【redis】spring boot基于redis的LUA脚本 实现分布式锁
spring boot基于redis的LUA脚本 实现分布式锁[都是基于redis单点下] 一.spring boot 1.5.X 基于redis 的 lua脚本实现分布式锁 1.pom.xml &l ...
- .Net Core使用分布式缓存Redis:Lua脚本
一.前言 运行环境window,redis版本3.2.1.此处暂不对Lua进行详细讲解,只从Redis的方面讲解. 二.Redis的Lua脚本 在Redis的2.6版本推出了脚本功能,允许开发者使用L ...
- PHP中使用redis执行lua脚本示例
摸索了一下在PHP中如何使用redis执行lua脚本,写了一个脚本如下,供以后参考 <?php $redis = new Redis(); #实例化redis类 $redis->conne ...
- redis之lua脚本
背景介绍 redis数据库提供了一些管理功能比如 流水线:打包发送多条命令,并在一个回复里面接收所有被执行命令的结果.事务:一次执行多条命令,被执行的命令要么就全部都被执行,要么就一个也不执行.并且事 ...
随机推荐
- 深入理解css 笔记(7)
前面讲了几种控制网页布局的方式,flex,gird 和 float.这下我们初略讲下 position.这个我日常中用到的已经挺多了.定位和其他控制文档流的行为不同.它将元素彻底从文档流中移走,它 ...
- 拖拽改变div宽、高(转)
$(function () { //绑定需要拖拽改变大小的元素对象 bindResize(document.getElementById('test')); }); function bindResi ...
- git合入代码过程中问题记录
问题一. 对远端仓库没有操作权限 ERROR: Repository not found. fatal: Could not read from remote repository. 定位思路 1.检 ...
- JSON常见用法
https://blog.csdn.net/weixin_43631296/article/details/105253434
- (转载)Python 浅析线程(threading模块)和进程(process)
线程是操作系统能够进行运算调度的最小单位.它被包含在进程之中,是进程中的实际运作单位.一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务 进程与线程 什么 ...
- Python学习笔记--序列+集合+字典
序列 切片:从一个序列中,取出一个子序列 注意: 案例: 实现: 集合 无序性.唯一性 添加新元素: .add 移除元素: .remove 随机取出某个元素: 清空集合: .clear 取两个集合的差 ...
- OVS内核流表查询过程
概括 现在的OVS使用microflow+megaflow缓存查询流表,ovs整体流程是从ovs_vport_receive(datapath/vport.c)开始,然后进入ovs_dp_proces ...
- vue 前端项目创建
一.创建项目 将vue-admin-template-master 模板放入创建的 VS code 的工作空间.重命名为自己的项目. 模块获取方法:关注"Java程序员进阶",回复 ...
- 使用golang+antlr4构建一个自己的语言解析器(完结篇)
Goland 中Antlr4插件 在goland中安装Antlr4插件,用于识别输入的字符在在语法文件中生成的语法树的样子,大概就是如下的摸样 下载步骤: 1.点击文件中的设置选项 2.在插件目录下输 ...
- uniapp H5图片编辑器(安卓/iOS适用)
箭头绘制参考了:https://blog.csdn.net/qq_45939676/article/details/127425426 这位大佬的文章 gitee地址: https://gitee.c ...