浅解.Net分布式锁的实现
序言
我晚上有在公司多呆会儿的习惯,所以很多晚上我都是最后一个离开公司的。当然也有一些同事,跟我一样喜欢在公司多搞会儿。这篇文章就要从,去年年末一个多搞会的晚上说起,那是一个夜黑风高的晚上,公司应该没有几个人在啦,我司一技术男悠悠的走到我的背后,突然一句:“还没走啊?”!“我日,吓死我啦,你也没走啊”。此同事现在已被裁员,走啦,当晚他问我啦一个问题,至此时也没有机会告知,今天我就在这里就简单描述下他当时的问题,其实实现起来简单的不值一提,不过任何一个简单的问题往往都会有很多中解决方案,探索找到最佳的解决方案,然后把细节做好,那就是技术的精髓与乐趣所在。我这里只抛砖一下,希望能给我老同事一个思路。
回到问题
首先有如下二张表,字段有IsBuyed(0:未使用,1:已使用),ProductNo:产品编号,Count:使用次数。


就是针对这张表做需求扩展的。
1、每次请求过来,都随机拿到一个未使用过的产品编号
public int GetNo()
{
using (IDbConnection conn = GetConn())
{
return conn.ExecuteScalar<int>("select top 1 ProductNo from AStore where isBuyed=0 order by newid()");
}
}
2、每次请求过来,即为使用产品一次,使用未使用过的产品一次需产品的IsBuyed=1 , Count=Count+1 。
public bool UsingStore(int no)
{
using (IDbConnection conn = GetConn())
{
return conn.Execute("update AStore set isBuyed=1 where and productNo=" + no) > ;
}
}
public bool MinusStore(int no)
{
using (IDbConnection conn = GetConn())
{
return conn.Execute("update BStore set [count]=[count]+1 where and productNo=" + no) > ;
}
}
3、写一个接口,部署在集群环境中,模拟请求3秒内一万个请求,来消费表中只有10个的产品,最终结果为产品不能被多次使用,如果存在多次使用则产品的count将大于1,即为失败。同学如果你看到啦,问题我给你复原的跟你说的没多少出入吧?
.Net实现分布式锁
解决问题我就一步步来递进,慢慢深入,直至痛楚!!首先我把同事操作数据上面的2个方法先贴出来。
public bool UsingStore(int no)
{
using (IDbConnection conn = GetConn())
{
return conn.Execute("update AStore set isBuyed=1 where productNo=" + no) > ;
}
}
public bool MinusStore(int no)
{
using (IDbConnection conn = GetConn())
{
return conn.Execute("update BStore set [count]=[count]+1 where productNo=" + no) > ;
}
}
public int GetNo()
{
using (IDbConnection conn = GetConn())
{
return conn.ExecuteScalar<int>("select top 1 ProductNo from AStore where isBuyed=0 order by newid()");
}
}
初涉茅庐的同学可能会这样写接口。
public JsonResult Using1()
{
//获取未使用的产品编号
var no = data.GetNo();
if (no != )
{
//使用此产品
data.MinusStore(no);
data.UsingStore(no);
return Json(new { success = true, ip = Request.ServerVariables.Get("Local_Addr").ToString() + " : " + HttpContext.Request.Url.Port }, JsonRequestBehavior.AllowGet);
}
else
{
//无产品可使用
return Json(new { success = false, ip = Request.ServerVariables.Get("Local_Addr").ToString() + " : " + HttpContext.Request.Url.Port }, JsonRequestBehavior.AllowGet);
}
}
单机部署,1万个请求过来下啊。下面我们看看数据库的结果是什么?下面是3次实现结果。每执行一次,执行一下下面的脚本。
select * from [dbo].[AStore]
update AStore set isbuyed=0,count=0
表:astore
表:bstore
由结果可以看出,单机部署接口的情况下,还使一些产品被多次消费,这很显然不符合同学的要求。
那么我们进一步改进这个接口,使用单机锁,锁此方法,来实现此接口,如下。
public JsonResult Using()
{
string key = "%……¥%¥%77123吗,bnjhg%……%……&+orderno";
//锁此操作
lock (key)
{
//获取未使用的产品编号
var no = data.GetNo();
if (no != )
{
//使用此产品
data.MinusStore(no);
data.UsingStore(no);
return Json(new { success = true, ip = Request.ServerVariables.Get("Local_Addr").ToString() + " : " + HttpContext.Request.Url.Port }, JsonRequestBehavior.AllowGet);
}
else
{
//此产品已使用过
return Json(new { success = false, ip = Request.ServerVariables.Get("Local_Addr").ToString() + " : " + HttpContext.Request.Url.Port }, JsonRequestBehavior.AllowGet);
}
}
}
单机部署此接口,1000个请求来测试此接口

结果如下:
表:astore
表:bstore
哇塞,貌似同事的问题解决啦,哈哈,同事不急,这只是单机部署下的结果,如果把这个接口集群部署的话是什么结果呢?
使用nginx做集群部署,搞5个站点做测试,对得起吗,同事?
upstream servers{
server 192.168.10.150:9000 weight=1;
server 172.18.11.79:1112 weight=1;
server 192.168.10.150:1114 weight=1;
server 192.168.10.150:1115 weight=1;
server 192.168.10.150:1116 weight=1;
}
server{
keepalive_requests 1200;
listen 8080;
server_name abc.nginx3.com;
location ~*^.+$ {
proxy_pass http://servers;
}
}
再来看此接口运行的结果。结果如下:
表:astore
表:bstore
由图可以看出,站点部署的集群对的住你,结果可令我们不满意啊,显然一个产品还是存在多次消费的情况,这种锁对集群部署无用,并且还要排队,性能也跟不上来。我们来进一步改写这个接口。如下:
public JsonResult Using3()
{
//锁此操作
string key = "%……¥%¥%77123吗,bnjhg%……%……&+orderno";
lock (key)
{
//获取未使用的产品编号
var no = data.GetNo();
//单号做为key插入memcached,值为true。
var getResult = AMemcached.cache.Add("Miaodan_ProNo:" + no, true);
if (getResult)
{
//使用此产品
data.MinusStore(no);
data.UsingStore(no);
return Json(new { success = true, ip = Request.ServerVariables.Get("Local_Addr").ToString() + " : " + HttpContext.Request.Url.Port }, JsonRequestBehavior.AllowGet);
}
else
{
//此产品已使用过
return Json(new { success = false, ip = Request.ServerVariables.Get("Local_Addr").ToString() + " : " + HttpContext.Request.Url.Port }, JsonRequestBehavior.AllowGet);
}
}
}
在集群下跑此接口看结果,结果如下。
表:astore
表:bstore
功能实现,同事可以安息啦。不过这里还有很多优化,和分布式锁带来的弊端,比如一单被分布式锁,锁住业务即便后续算法没有使用该产品,怎么优雅的释放锁,怎么解决遇到已经使用过的产品后再此分配新资源等等,当然也有其他一些实现方案,比如基于redis,zookeeper实现的分布式锁,我这里就不说明啦。同事,你好自珍重,祝多生孩子,多挣钱啊。
总结
接下来是大家最喜欢的总结内容啦,内容有二,如下:
1、希望能关注我其他的文章。
2、博客里面有没有很清楚的说明白,或者你有更好的方式,那么欢迎加入左上方的2个交流群,我们一起学习探讨。
浅解.Net分布式锁的实现的更多相关文章
- 浅谈redis分布式锁用法
使用redis的setnx命令进行实现 @Component @Slf4j public class RedisLock { @Autowired private StringRedisTemplat ...
- .net 分布式锁
原文 : 浅解.Net分布式锁的实现 序言 我晚上有在公司多呆会儿的习惯,所以很多晚上我都是最后一个离开公司的.当然也有一些同事,跟我一样喜欢在公司多搞会儿.这篇文章就要从,去年年末一个多搞会的晚 ...
- Redis分布式锁实现方式(附有正解及错误示例)
一.前言 本文内容主要来自博客:https://wudashan.com/2017/10/23/Redis-Distributed-Lock-Implement/,本文用于归纳总结及笔记用途,如有需要 ...
- Redis全方位详解--数据类型使用场景和redis分布式锁的正确姿势
一.Redis数据类型 1.string string是Redis的最基本数据类型,一个key对应一个value,每个value最大可存储512M.string一半用来存图片或者序列化的数据. 2.h ...
- 面试题详解:如何用Redis实现分布式锁?
说一道常见面试题: 使用Redis分布式锁的详细方案是什么? 一个很简单的答案就是去使用 Redission 客户端.Redission 中的锁方案就是 Redis 分布式锁的比较完美的详细方案. 那 ...
- ZooKeeper分布式锁浅谈(一)
一.概述 清明节的时候写了一篇分布式锁概述,里面介绍了分布式锁实现的几种方式,其实那时候我一直沉迷于使用redis的悲观锁和乐观锁来实现分布式锁,直到一个血案的引发才让我重新认识了redis分布式锁的 ...
- Java分布式锁实现详解
在进行大型网站技术架构设计以及业务实现的过程中,多少都会遇到需要使用分布式锁的情况.那么问题也就接踵而至,哪种分布式锁更适合我们的项目? 下面就这个问题,我做了一些分析: 分布式锁现状: 目前几乎很多 ...
- 什么是分布式锁?Redis实现分布式锁详解
在很多场景中,我们为了保证数据的最终一致性,需要很多的技术方案来支持,比如分布式事务.分布式锁等.那具体什么是分布式锁,分布式锁应用在哪些业务场景.如何来实现分布式锁呢?今天继续由陈睿|mikeche ...
- Redis高并发分布式锁详解
为什么需要分布式锁 1.为了解决Java共享内存模型带来的线程安全问题,我们可以通过加锁来保证资源访问的单一,如JVM内置锁synchronized,类级别的锁ReentrantLock. 2.但是随 ...
随机推荐
- cordova环境搭建
首先,需要了解一下cordova 和 phonegap 的关系.phoneGap是原先的名字,Cordova是phoneGap被捐给apache之后 用的项目名,phoneGap的名字也被保留了. 1 ...
- Oracle数据库常用技术
一.视图(重点) 什么是视图? ·视图由一个或多个表(或视图)中提取数据而成 ·视图是一种虚拟表 ·视图一经创建,可以当作表来使用. 使用视图的好处? · 简化复杂数据查询 · 提高运行效率 · 屏蔽 ...
- 【蓝牙数据采集模块】-02-Sensor Controller 编写CC2650STK外设驱动
上次说到把所有的传感器模块在Sensor Controller中实现读写,并封装驱动. 先来看一下CC2650STK的原理图 用红色方框标记的DIO_0~DIO_7, DIO_23~DIO_30是可以 ...
- Jenkins SSH timeout
问题如下: Started by user carzone Building -test SSH: Connecting from host [jenkins232] SSH: Connecting ...
- freemarker配置,使用
最近在项目中用到freemarker,总是报一些莫名其妙的错误. 调查得知是由于在配置文件中属性[tag_syntax]的设置问题,我们的环境下该属性(auto_detect)默认设置了自动检测,也就 ...
- Mysql中日期时间型解析
- LVS 负载均衡解决方案 (windows IIS)
LVS 负载均衡解决方案 因为我们的产品运行的主流平台是WINDOWS+IIS+SQLSERVER(2000以上版本),而LVS+KEEPALIVED是LINUX下的四层负载均衡软件.其有如下特点: ...
- 测试web网站兼容性的方法
随着操作系统,浏览器越来越多样性,导致软件兼容性测试在目前软件测试领域占有很重要的地位,无论是B/S架构还是C/S架构的软件都需要进行兼容性测试,为了充分保证产品的平台无关性,使用户充分感受到软件的友 ...
- MyBatis 模糊查询
<!--${}是不进行预编译的,会直接进行sql语句的拼接:{}中的内容必须通过Map或者查询对象中获得--><select id="selectPersonByName& ...
- JavaScript的核心
对象 ECMAScript做为一个高度抽象的面向对象语言,是通过对象来交互的.即使ECMAScript里边也有基本类型,但是,当需要的时候,它们也会被转换成对象. 一个对象就是一个属性集合,并拥有一个 ...