How to Increase Throughput and Increase Throughput
Performance improvement is just a few elements, as I mentioned in my previous blog post. This article is just a little simpler.
Because the hardware configuration is fixed, let's simply talk about how to use some simple small tricks to greatly improve the performance of a project that uses C # for development.
1. Bypass annoying but unavoidable DB operations.
DB operations are inevitable, but are part of the project. What optimizations can we make?
First, database operations are divided into real-time and non-real-time ones based on the business. For non-real-time operations, we can separate them to a thread for separate processing. The Code is as follows:
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 = nul L) {_ 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 (_ syncQueueObje Ct) {_ 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
The Code shows that a separate thread is created for processing during the instantiation.
You can use the following code to perform operations such as logs:
DbActionQueue. Instance. Push () =>{ LogBLL. Instance. Add (new Log {action_time = DateTime. Now ;});});View Code
It is not hard to ask here. If the data volume is too large and a single queue cannot meet the requirements, how can this problem be solved. Optimization of queue monitoring is not discussed in this article. some third-party queues can be introduced in a more popular way. In addition, in the project, in fact, we are not performing Insert, Update, Delete, or other operations, but Select operations. That's about some of the Select cache processing, it is not discussed in this article, because there are too many middleware about Cache, And the length is too large.
In some cases, we may still feel that the processing of a single thread is too slow. If we want to open several more threads to process DB requests, we can configure them based on actual business conditions and machines, initialize any thread for processing, the above code needs to be slightly modified, the singleton can be freely instantiated, the Code is as follows:
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
The code for processing between multiple threads is as follows:
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) {_ ins Tance = 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
In addition, for the reason why Task is not used for processing, although the processing of the Task on the thread is still very good, please decide on your own here. For instant DB operations, you can use Redis, MongoDb, or self-compiled cache as the middleware, and then talk about the specific inbound operations, and put them in the previously compiled queue processing.