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) > 0;
}
}
public bool MinusStore(int no)
{
using (IDbConnection conn = GetConn())
{
return conn.Execute("update BStore set [count]=[count]+1 where and productNo=" + no) > 0;
}
}

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) > 0;
}
}
public bool MinusStore(int no)
{
using (IDbConnection conn = GetConn())
{
return conn.Execute("update BStore set [count]=[count]+1 where productNo=" + no) > 0;
}
}
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 != 0)
{
//使用此产品
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 != 0)
{
//使用此产品
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实现的分布式锁,我这里就不说明啦。同事,你好自珍重,祝多生孩子,多挣钱啊。

Net锁的更多相关文章

  1. 使用redis构建可靠分布式锁

    关于分布式锁的概念,具体实现方式,直接参阅下面两个帖子,这里就不多介绍了. 分布式锁的多种实现方式 分布式锁总结 对于分布式锁的几种实现方式的优劣,这里再列举下 1. 数据库实现方式 优点:易理解 缺 ...

  2. 多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类)

    前言:刚学习了一段机器学习,最近需要重构一个java项目,又赶过来看java.大多是线程代码,没办法,那时候总觉得多线程是个很难的部分很少用到,所以一直没下决定去啃,那些年留下的坑,总是得自己跳进去填 ...

  3. java中的锁

    java中有哪些锁 这个问题在我看了一遍<java并发编程>后尽然无法回答,说明自己对于锁的概念了解的不够.于是再次翻看了一下书里的内容,突然有点打开脑门的感觉.看来确实是要学习的最好方式 ...

  4. 分布式锁1 Java常用技术方案

    前言:       由于在平时的工作中,线上服务器是分布式多台部署的,经常会面临解决分布式场景下数据一致性的问题,那么就要利用分布式锁来解决这些问题.所以自己结合实际工作中的一些经验和网上看到的一些资 ...

  5. 如何在高并发环境下设计出无锁的数据库操作(Java版本)

    一个在线2k的游戏,每秒钟并发都吓死人.传统的hibernate直接插库基本上是不可行的.我就一步步推导出一个无锁的数据库操作. 1. 并发中如何无锁. 一个很简单的思路,把并发转化成为单线程.Jav ...

  6. 如何定位Oracle数据库被锁阻塞会话的根源

    首先再次明确下,数据库因为要同时保证数据的并发性和一致性,所以操作有锁等待是正常的. 只有那些长时间没有提交或回滚的事物,阻塞了其他业务正常操作,才是需要去定位处理的. 1.单实例环境 2.RAC环境 ...

  7. java 线程 Lock 锁使用Condition实现线程的等待(await)与通知(signal)

    一.Condition 类 在前面我们学习与synchronized锁配合的线程等待(Object.wait)与线程通知(Object.notify),那么对于JDK1.5 的 java.util.c ...

  8. Android 死锁和重入锁

    死锁的定义: 1.一般的死锁 一般的死锁是指多个线程的执行必须同时拥有多个资源,由于不同的线程需要的资源被不同的线程占用,最终导致僵持的状态,这就是一般死锁的定义. package com.cxt.t ...

  9. Xcode 锁终端

    锁终端 输入: <1>cd /Applications/Xcode.app 回车 结果显示: Xcode.app 输入: <2>sudo chown -hR root:whee ...

  10. mysql 行级锁的使用以及死锁的预防

    一.前言 mysql的InnoDB,支持事务和行级锁,可以使用行锁来处理用户提现等业务.使用mysql锁的时候有时候会出现死锁,要做好死锁的预防. 二.MySQL行级锁 行级锁又分共享锁和排他锁. 共 ...

随机推荐

  1. 关于mybatis中,批量增删改查以及參数传递的问题

    1.參数传递的问题 大多数情况下,我们都是利用map作为參数,而且大部分情况下都是仅仅有一个參数. 可是,我们也能够利用@param注解,来传入多个參数,此时,mybatis会自己主动将參数封装成ma ...

  2. python3中numpy函数的argsort()

    摘自:https://www.cnblogs.com/yushuo1990/p/5880041.html argsort函数argsort函数返回的是数组值从小到大的索引值 Examples----- ...

  3. 6.2、Android硬件访问服务编写系统代码

    1.实现接口文件给App使用,接口文件是应用程序查询获得服务时获得 使用AIDL(Android接口定义语言)来实现ILedService.java接口 定义ILedService.aidl inte ...

  4. [CSS3] Create a fixed-fluid-fixed layout using CSS calc()

    CSS calc() allows you to mix and match units to get real-time calculations. It's useful when you nee ...

  5. 结合Wireshark捕获分组深入理解TCP/IP协议之IP协议

    摘要:     本文简单介绍了网络层理论知识,详细讲解了IP数据报各个字段,并从Wireshark俘获分组中选取IP数据报进行分析,也阐述了分组和分片的区别.   一.IPv4数据报     网络层是 ...

  6. hibernate---java.lang.UnsupportedOperationException: The user must supply a JDBC connection

        在配置hibernate时.运行代码时一直抛错: Exception in thread "main" java.lang.UnsupportedOperationExce ...

  7. [React] Setup 'beforeunload' listener

    In this lesson we'll show how to take a beforeUnload call and convert it to a declarative React Comp ...

  8. 中小研发团队架构实践之分布式协调器.Net版ZooKeeper

    原文:中小研发团队架构实践之分布式协调器.Net版ZooKeeper 一.ZooKeeper是什么  Apache ZooKeeper是由Apache Hadoop的子项目发展而来,于2010年11月 ...

  9. [Grid Layout] Use the repeat function to efficiently write grid-template values

    We can use the repeat() function if we have repeating specifications for columns and rows. With the  ...

  10. 适合前端开发的 Chrome 扩展有哪些?(十款)

    适合前端开发的 Chrome 扩展有哪些?(十款) 一.总结 好的插件或者框架对程序员的意义重大. 二.适合前端开发的 Chrome 扩展有哪些?(十款) 掘金是一个高质量的技术社区,从 ECMASc ...