併發處理方案 我的總結

併發的危害數據庫

多個用戶(線程)在爭強同一資源。若是發生 程序和數據的一致性、可靠性、惟一性均可能受到破壞。若是沒有資源競爭或者沒有重複的增量併發就不叫併發而叫並行了。解決併發的各類方案和思路我的感受萬變不離其中,都是將並行的事件轉換爲串行執行,下面我根據我的對併發的出現場景和解決方案作必定的分享歡迎拍磚這樣你們就能夠互相學習。安全

多線程的併發多線程

 多線程通常用於同時處理多個並行的任務,當並行任務出現資源爭搶,咱們第一個想到的解決方案就是鎖 lock,使用了鎖以後就會讓並行的任務等待而且串行執行。要注意的就是主線程或者控制線程不要有過多的堵塞,這樣會給客戶帶來很差的體驗度。在網上看到一篇不錯的博客提供了一個方法來處理了這種類型的問題,不過他只使用了一個額外的worker線程處理任務。不論外部使用多少個線程訪問他都經過隊列和線程通知 巧妙的繞開 代碼以下併發

 

public class Logger 
    {
        /// <summary>
        /// 存放寫日誌任務的隊列
        /// </summary> 
        private Queue<Action> _queue;
        /// <summary>
        /// 用於寫日誌的線程
        /// </summary>
        private Thread _loggingThread;
        /// <summary>
        /// 用於通知是否有新日誌要寫的「信號器」
        /// </summary> 
        private ManualResetEvent _hasNew;
        /// <summary>
        /// 單例模式,保持一個Logger對象實例
        /// </summary>
        private static readonly Logger _logger = new Logger();
  
        private static Logger GetInstance()
        {
            /* 不安全代碼
            lock (locker) {
                if (_logger == null) {
                    _logger = new Logger();
                }
            }*/
            return _logger;
        }

        private void Process()
        {
            while (true)
            {
                // 等待接收信號,阻塞線程。
                _hasNew.WaitOne();

                // 接收到信號後,重置「信號器」,信號關閉。
                _hasNew.Reset();

                // 因爲隊列中的任務可能在極速地增長,這裏等待是爲了一次能處理更多的任務,減小對隊列的頻繁「進出」操做。
                Thread.Sleep(100);

                // 開始執行隊列中的任務。
                // 因爲執行過程當中還可能會有新的任務,因此不能直接對原來的 _queue 進行操做,
                // 先將_queue中的任務複製一份後將其清空,而後對這份拷貝進行操做。

                Queue<Action> queueCopy;
                lock (_queue)
                {
                    queueCopy = new Queue<Action>(_queue);
                    _queue.Clear();
                }

                foreach (var action in queueCopy)
                {
                    action();
                }
            }
        }
        /// <summary>
        ///  構造函數,初始化。
        /// </summary> 
        private Logger()
        {
            _queue = new Queue<Action>();
            _hasNew = new ManualResetEvent(false);
            _loggingThread = new Thread(Process);
            _loggingThread.IsBackground = true;
            _loggingThread.Start();
        }
        private void WriteLog(string content)
        {
            lock (_queue)
            { // todo: 這裏存在線程安全問題,可能會發生阻塞。
                // 將任務加到隊列
                _queue.Enqueue(() => File.AppendAllText("log.txt", content+" runtime:"+DateTime.Now.ToString()));
            }

            // 打開「信號」
            _hasNew.Set();
        }
        // 公開一個Write方法供外部調用
        public static void Write(string content)
        {
            // WriteLog 方法只是向隊列中添加任務,執行時間極短,因此使用Task.Run。
            Task.Run(() => GetInstance().WriteLog(content));
        }

DEOM分佈式

當外部線程過多WriteLog方法中的鎖就可能會出現堵塞。目前沒有測試出堵塞與機器性能也有必定關係函數

鎖DEMO性能

非分佈式系統併發解決方案學習

  insert併發測試

  方案一 設置惟一列非主鍵,每次添加先查詢最後一列數據的 惟一列值 作遞增量由業務程序控制,原理經過惟一約束引起異常方案比較穩定可行。spa

  方案二 業務程序級鎖控制,使用條件改業務系統不能爲分佈式部署而且,只有一個insert方法添加數據。    

  方案三 數據庫事務獨佔鎖(排它鎖) 

我的目前想到較爲可靠的方案,但願看到的朋友能幫我補充。

  update delete 併發

  方案一 依然是數據庫上的處理方案,添加一個timestamp類型字段,若是沒有該類型的字段可使用觸發器或者本身業務程序實現一個版本號字段原理很簡單每次 CRUD操做都遞增該字段。當update一行數據將timestamp 做爲必要條件帶入 

Update xx set xxx =xxx where key='1' and timestamp='0x000000000000272C' 

  方案二 依然是業務程序的鎖    

lock  語法糖鎖  Monitor 
ReaderWriterLock  讀寫鎖 性能低下  不受歡迎的鎖不少問題 之後專門解釋
ReaderWriterLockSlim  類支持三種鎖定模式:Read,Write,UpgradeableRead

 

  方案三 數據庫事務獨佔鎖(排它鎖) 

獨佔鎖 能夠到毫秒級 不過不能設置1毫秒至少爲2毫秒  

begin tran
  SELECT getdate() 
WAITFOR DELAY   '00:00:01' 
SELECT getdate()
commit tran


begin tran
  SELECT getdate() 
WAITFOR DELAY   '00:00:00.04' 
SELECT getdate()
commit tran

 

分佈式系統併發解決方案

  
  分佈式系統處理併發,最好將併發業務單獨剝離,交由一臺服務機處理,其餘機器調用這一臺服務機的接口。這種狀況這一臺服務機壓力也異常大,那麼儘可能減小併發業務粒度。咱們目前通常開發系統仍是使用關係型數據庫,那麼數據庫分佈式的並很少,最多就是作讀寫分離,非分佈式系統的方案一依然通用。可能產生併發的事務處理最好也在同一庫處理,就算你分庫產生併發的業務數據也應該是在同一庫中。像什麼 DRDS這種高端的目前尚未用過更沒有解決方案!

相關文章
相關標籤/搜索