如何簡易的提高吞吐量


    性能提升還是那幾個要素,就像我在之前的博文里面提到的一樣,這一篇只是更加簡單一點而已。

    因為硬件配置是固定的,那我們只是簡單說一說在使用C#進行開發的項目中,如何使用一些簡單的小招數讓性能有一個比較大幅度的顯著提升。

    一、繞開那些煩人卻又不可避免的DB操作。

    DB操作是不可避免卻又是項目重中之中的所在,那我們可以做哪些優化呢?

    首先,根據業務將DB操作分為需要即時和非即時的兩塊,關於非即時的我們可以將其單獨的交給某一個線程來單獨慢慢處理。代碼如下:

public class DbActionQueue : IDisposable
    {
        public Queue<Action> _transQueue;
        private Thread _thread;
        private bool _isDispose = false;

        private static readonly object _syncObject = new object();
        private readonly object _syncQueueObject = new object();
        private static DbActionQueue _instance;
        public static DbActionQueue Instance
        {
            get
            {
                if (_instance == null)
                {
                    lock (_syncObject)
                    {
                        if (_instance == null)
                        {
                            _instance = new DbActionQueue();
                        }
                    }
                }
                return _instance;
            }
        }
        private DbActionQueue()
        {
            if (_transQueue == null)
            {
                _transQueue = new Queue<Action>();
            }
            if (_thread == null)
            {
                _thread = new Thread(Thread_Work)
                {
                    IsBackground = true
                };
            }
            _thread.Start();
        }

        public void Push(Action action)
        {
            if (_transQueue == null) throw new ArgumentNullException("dbActionQueue is not init");

            lock (_syncQueueObject)
            {
                _transQueue.Enqueue(action);
            }
        }

        public void Thread_Work()
        {
            while (!_isDispose)
            {
                Action[] items = null;
                if (_transQueue != null && _transQueue.Count > 0)
                {
                    lock (_syncQueueObject)
                    {
                        items = new Action[_transQueue.Count];
                        _transQueue.CopyTo(items, 0);
                        _transQueue.Clear();
                    }
                }

                if (items != null && items.Length > 0)
                {
                    foreach (var item in items)
                    {
                        try
                        {
                            item.Invoke();
                        }
                        catch (Exception ex)
                        {
                            LogHelper.Write(string.Format("DbActionQueue error. | Exception.StackTrace:{0}", ex.StackTrace), ex);
                        }
                    }
                }
                Thread.Sleep(1);
            }
        }

        public void Dispose()
        {
            _isDispose = true;
            _thread.Join();
        }
    }
View Code

    通過代碼可以看到,我們在實例化的時候,單獨創建了一個線程,用來做處理。

    那對於一些像日志之類的操作,則可以通過以下代碼進行操作:

DbActionQueue.Instance.Push(() =>
{
    LogBLL.Instance.Add(new Log
                        {
                            action_time = DateTime.Now;
                        });
});            
View Code

    到這里不難免要問了,如果數據量過大,單個隊列已經無法滿足的時候,怎么做處理。關於隊列的監控,優化不在該文討論,通俗一點的做法可以引入一些第三方的隊列。另外在項目中,其實我們更多的時候,實際上並不是在Insert,Update,Delete等操作,而是Select的操作,那關於Select的一些緩存處理,也不在該文的討論范疇,因為關於Cache的各種中間件實在太多,而且篇幅太大。

    可能在某些時候,我們還是覺得單個線程處理太慢,希望多開幾個線程來處理對於DB的請求,則我們可以根據實際業務情況和機器配置,初始化任意個線程來處理,則以上代碼需要稍稍改裝一下,將單例的換成可自由實例化的,代碼如下:

 public class DbQueue
    {
        public Queue<Action> _transQueue;
        private Thread _thread;
        private bool _isDispose = false;

        private readonly object _syncQueueObject = new object();

        public DbQueue()
        {
            if (_transQueue == null)
            {
                _transQueue = new Queue<Action>();
            }
            if (_thread == null)
            {
                _thread = new Thread(Thread_Work)
                {
                    IsBackground = true
                };
            }
            _thread.Start();
        }

        public void Thread_Work()
        {
            while (!_isDispose)
            {
                Action[] items = null;
                if (_transQueue != null && _transQueue.Count > 0)
                {
                    lock (_syncQueueObject)
                    {
                        items = new Action[_transQueue.Count];
                        _transQueue.CopyTo(items, 0);
                        _transQueue.Clear();
                    }
                }

                if (items != null && items.Length > 0)
                {
                    foreach (var item in items)
                    {
                        try
                        {
                            item.Invoke();
                        }
                        catch (Exception ex)
                        {
                            LogHelper.Write(string.Format("DbActionQueue error. | Exception.StackTrace:{0}", ex.StackTrace), ex);
                        }
                    }
                }
                Thread.Sleep(1);
            }
        }

        public void Push(Action action)
        {
            if (_transQueue == null) throw new ArgumentNullException("dbActionQueue is not init");

            lock (_syncQueueObject)
            {
                _transQueue.Enqueue(action);
            }
        }

        public void Dispose()
        {
            _isDispose = true;
            _thread.Join();
        }
    }
View Code

 那多個線程之間的處理,代碼則如下:

 public class DbQueueManage
    { 
        private int _threadNumber = 2;
        private DbQueue[] _dbQueues;
        private Random random = new Random();
        private DbQueueManage()
        {
            
        }

        static DbQueueManage()
        {

        }

        private static readonly object _syncObject = new object();
        private static DbQueueManage _instance;
        public static DbQueueManage Instance
        {
            get
            {
                if (_instance == null)
                {
                    lock (_syncObject)
                    {
                        if (_instance == null)
                        {
                            _instance = new DbQueueManage();
                        }
                    }
                }
                return _instance;
            }
        }

        public void Init(Action action, int threadNum = 2)
        {
            if (_dbQueues == null)
            {
                this._threadNumber = threadNum;
                _dbQueues = new DbQueue[threadNum];
                for (var i = 0; i < threadNum; i++)
                {
                    _dbQueues[i] = new DbQueue();
                }
            }
        }

        public void Push(Action action)
        {
            var index = GetRandomThreadIndex();
            if (_dbQueues != null && _dbQueues.Length > index)
            {
                _dbQueues[index].Push(action);
            }
        }

        public int GetRandomThreadIndex()
        {
            return random.Next(0, this._threadNumber);
        }

    }
View Code

另外關於為什么不選用Task來做處理,雖然Task關於線程的處理還是很優秀的,這里請各位同僚自行定奪。關於即時性的DB操作,則可以以Redis,MongoDb,或者自行編寫的緩存來作為中間件,然后再講具體入庫的操作,放入之前編寫好的隊列處理里面。

    


免責聲明!

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



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