网站面对高并发的情况下,除了增加硬件, 优化程序提高以响应速度外,还可以通过并行改串行的思路来解决。这种思想常见的实践方式就是数据库锁和消息队列的方式。这种方式的缺点是需要排队,响应速度慢,优点是节省成本。
演示一下现象
创建一个在售产品表
CREATE TABLE [dbo].[product]( [id] [int] NOT NULL,--唯一主键 [name] [nvarchar](50) NULL,--产品名称 [status] [int] NULL ,--0未售出 1 售出 默认为0 [username] [nvarchar](50) NULL--下单用户 )
添加一条记录
insert into product(id,name,status,username) values(1,'小米手机',0,null)
创建一个抢票程序
public ContentResult PlaceOrder(string userName) { using (RuanMou2020Entities db = new RuanMou2020Entities()) { var product = db.product.Where<product>(p => p.status== 0).FirstOrDefault(); if (product.status == 1) { return Content("失败,产品已经被卖光"); } else { //模拟数据库慢造成并发问题 Thread.Sleep(5000); product.status = 1; product.username= userName; db.SaveChanges(); return Content("成功购买"); } } }
如果我们在5秒内一次访问以下两个地址,那么返回的结果都是成功购买且数据表中的username是lisi。
/controller/PlaceOrder?username=zhangsan
/controller/PlaceOrder?username=lisi
这就是并发带来的问题。
第一阶段,利用线程锁简单粗暴
Web程序是多线程的,那我们把他在容易出现并发的地方加一把锁就可以了,如下图处理方式。
private static object _lock = new object(); public ContentResult PlaceOrder(string userName) { using (RuanMou2020Entities db = new RuanMou2020Entities()) { lock (_lock) { var product = db.product.Where<product>(p => p.status == 0).FirstOrDefault(); if (product.status == 1) { return Content("失败,产品已经被卖光"); } else { //模拟数据库慢造成并发问题 Thread.Sleep(5000); product.status = 1; product.username = userName; db.SaveChanges(); return Content("成功购买"); } } } }
这样每一个请求都是依次执行,不会出现并发问题了。
优点:解决了并发的问题。
缺点:效率太慢,用户体验性太差,不适合大数据量场景。
第二阶段,拉消息队列,通过生产者,消费者的模式
新闻热点
疑难解答
图片精选