mvc 高並發解決方案之一---存儲過程


MVC用戶訪問多線程,一般的lock是無法造成單例的。

存儲過程既是一種解決方案,先來看看存儲過程優缺點:

        A、 存儲過程允許標准組件式編程

        存儲過程創建后可以在程序中被多次調用執行,而不必重新編寫該存儲過程的SQL語句。而且數據庫專業人員可以隨時對存儲過程進行修改,但對應用程序源代碼卻毫無影響,從而極大的提高了程序的可移植性。

        B、 存儲過程能夠實現較快的執行速度

        如果某一操作包含大量的T-SQL語句代碼,分別被多次執行,那么存儲過程要比批處理的執行速度快得多。因為存儲過程是預編譯的,在首次運行一個存儲過程時,查詢優化器對其進行分析、優化,並給出最終被存在系統表中的存儲計划。而批處理的T-SQL語句每次運行都需要預編譯和優化,所以速度就要慢一些。

        C、 存儲過程減輕網絡流量

        對於同一個針對數據庫對象的操作,如果這一操作所涉及到的T-SQL語句被組織成一存儲過程,那么當在客戶機上調用該存儲過程時,網絡中傳遞的只是該調用語句,否則將會是多條SQL語句。從而減輕了網絡流量,降低了網絡負載。

        D、 存儲過程可被作為一種安全機制來充分利用

        系統管理員可以對執行的某一個存儲過程進行權限限制,從而能夠實現對某些數據訪問的限制,避免非授權用戶對數據的訪問,保證數據的安全。

引用地址:http://www.cnblogs.com/hoojo/archive/2011/07/19/2110862.html

 

再來說說存儲過程的事務原理

一、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”

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM