MVC用戶訪問多線程,通常的lock是沒法形成單例的。html
存儲過程既是一種解決方案,先來看看存儲過程優缺點:sql
A、 存儲過程容許標準組件式編程數據庫
存儲過程建立後能夠在程序中被屢次調用執行,而沒必要從新編寫該存儲過程的SQL語句。並且數據庫專業人員能夠隨時對存儲過程進行修改,但對應用程序源代碼卻毫無影響,從而極大的提升了程序的可移植性。編程
B、 存儲過程可以實現較快的執行速度安全
若是某一操做包含大量的T-SQL語句代碼,分別被屢次執行,那麼存儲過程要比批處理的執行速度快得多。由於存儲過程是預編譯的,在首次運行一個存儲過程時,查詢優化器對其進行分析、優化,並給出最終被存在系統表中的存儲計劃。而批處理的T-SQL語句每次運行都須要預編譯和優化,因此速度就要慢一些。網絡
C、 存儲過程減輕網絡流量多線程
對於同一個針對數據庫對象的操做,若是這一操做所涉及到的T-SQL語句被組織成一存儲過程,那麼當在客戶機上調用該存儲過程時,網絡中傳遞的只是該調用語句,不然將會是多條SQL語句。從而減輕了網絡流量,下降了網絡負載。併發
D、 存儲過程可被做爲一種安全機制來充分利用分佈式
系統管理員能夠對執行的某一個存儲過程進行權限限制,從而可以實現對某些數據訪問的限制,避免非受權用戶對數據的訪問,保證數據的安全。測試
引用地址:http://www.cnblogs.com/hoojo/archive/2011/07/19/2110862.html
再來講說存儲過程的事務原理
1、sql事務
1.什麼是事務:事務是一個不可分割的工做邏輯單元,在數據庫系統上執行併發操做時事務是作爲最小的控制單元來使用的。他包含的全部數據庫操做命令做爲一個總體一塊兒向系提交或撤消,這一組數據庫操做命令要麼都執行,要麼都不執行。
2.事務的語句
開始事物:BEGIN TRANSACTION
提交事物:COMMIT TRANSACTION
回滾事務:ROLLBACK TRANSACTION
3.事務的4個特性
①原子性(Atomicity):事務中的全部元素做爲一個總體提交或回滾,是不可折分的,事務是一個完整的操做。
②一致性(Consistemcy):事物完成時,數據必須是一致的,也就是說,和事物開始以前,數據存儲中的數據處於一致狀態。保證數據的無損。
③隔離性(Isolation):對數據進行修改的多個事務是彼此隔離的。這代表事務必須是獨立的,不該該以任何方式來影響其餘事務。
④持久性(Durability):事務完成以後,它對於系統的影響是永久的,該修改即便出現系統故障也將一直保留,真實的修改了數據庫
4.事務的分類.
按事務的啓動與執行方式,能夠將事務分爲3類:
①顯示事務 :也稱之爲用戶定義或用戶指定的事務,便可以顯式地定義啓動和結束的事務。分佈式事務屬於顯示事務
②自動提交事務:默認事務管理模式。若是一個語句成功地完成,則提交該語句;若是遇到錯誤,則回滾該語句。
③隱性事務:當鏈接以此模式進行操做時,sql將在提交或回滾當前事務後自動啓動新事務。無須描述事務的開始,只需提交或回滾每一個事務。它生成連續的事務鏈。
已事務中的全局變量爲基準數,判斷@@trancount;
說了這麼多,來看看代碼
ALTER PROCEDURE [dbo].[GetLottery]AS SET NOCOUNT ON; declare @trancount int --commit,rollback只控制本存儲過程 set @trancount = @@trancount; if (@trancount=0) begin tran GetLottery else save tran GetLottery *************************************************** if @@Error <> 0 begin Rollback tran GetLottery return @id end else begin commit tran GetLottery return @id end
全局@@trancount計數:
執行或者回歸都會使計數歸零。
最後自行作了測試引用縮寫的存儲過程發現大併發下並不能徹底控制搶佔資源問題。
當存儲過程執行都在千分之一秒相同的狀況下。會形成資源搶佔衝突並引發「超發,多發問題」
原理也其實至關簡單,數據庫支持的時間格式也是千分之一秒。
一下貼出線程測試代碼。
public class Threadlist { /// <summary> /// 狀態 /// </summary> public static StaticType Static { get; private set; } /// <summary> /// 線程池 /// </summary> public static List<Thread> Threads { get; set; } /// <summary> /// 守護線程 /// </summary> public static Thread KeeperThread { get; set; } /// <summary> /// 結果 /// </summary> public static List<String> Result { get; set; } public static Int32 CustomerId { get; set; } static Threadlist() { Static = StaticType.StandBy; Threads = new List<Thread>(); Result = new List<string>(); } public static void Start() { switch (Static) { case StaticType.Done://已經完成,至關於從新開始 case StaticType.StandBy://就緒狀態 Result = new List<string>(); Threads = new List<Thread>(); Static = StaticType.Running; for (int i = 0; i < 300; i++) { CustomerId = i + 200; Threads.Add(new Thread(delegate() { SqlTest(CustomerId, 14); })); Threads.Add(new Thread(delegate() { SqlTest(CustomerId, 15); })); Threads[i].Start(); } KeeperThread = new Thread(new ThreadStart(Keeper)); KeeperThread.Start(); break; default: break; } } private static void Keeper() { while (Static == StaticType.Running) { Thread.Sleep(500); if (!Threads.Any(t => t.IsAlive))//做業所有完成 Static = StaticType.Done; } } public enum StaticType { StandBy, Running, Done } public static void SqlTest(Int32 CustomerId, Int32 CommodityId) { var db = new WebDatabase(); var Code = Guid.NewGuid().ToString();var lottery = db.EventLotteryHistories.FirstOrDefault(x => x.BonusId == null); var Type = 0; var PayCount = 1; var sqlstatue = 0; var orderId = 0; while (sqlstatue==0) { sqlstatue = 1; try { var lotteryId = new SqlParameter { ParameterName = "LotteryHistoryId", Value = lottery.Id, Direction = ParameterDirection.Input }; var result = new SqlParameter { ParameterName = "id", Value = 0, Direction = ParameterDirection.Output }; db.Database.ExecuteSqlCommand(System.Data.Entity.TransactionalBehavior.DoNotEnsureTransaction, "exec @id = GetLottery @LotteryHistoryId", lotteryId, result); //////////////////////////增長錯誤捕獲代碼/////////////////////////////// var statue = result.Value; Result.Add(CustomerId.ToString() + "... " + statue.ToString()); } catch (Exception) { sqlstatue = 0; } } } } }
以前在使用ef調用存儲過程當中遇到了卡了一天的一個坑。
ef直接調用存儲過程當中會在外殼添加一個事務循環,以此來判斷事務是否正確運行。
可是若是在調用事務中本來就有事務致使衝突,所以調用時請選擇事務狀態:「不執行事務模式」---「System.Data.Entity.TransactionalBehavior.DoNotEnsureTransaction」