網站面對高并發的情況下,除了增加硬件, 優化程序提高以響應速度外,還可以通過并行改串行的思路來解決。這種思想常見的實踐方式就是數據庫鎖和消息隊列的方式。這種方式的缺點是需要排隊,響應速度慢,優點是節省成本。
演示一下現象
創建一個在售產品表
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("成功購買"); } } } }這樣每一個請求都是依次執行,不會出現并發問題了。
優點:解決了并發的問題。
缺點:效率太慢,用戶體驗性太差,不適合大數據量場景。
第二階段,拉消息隊列,通過生產者,消費者的模式
新聞熱點
疑難解答
圖片精選