A common question during an interview is:

  • Have you done any performance optimizations?
  • What has been optimized?
  • How do you optimize it?
  • What is the effect of optimization?

Rapid-fire ask down, for the old driver who has done optimization, certainly can resist. For no real optimization of the small white, certainly can not carry this series of questioning, and finally can only fail to end the interview.

So what is performance optimization optimizing for? Let’s take stock of some common optimization methods.

SQL optimization

You need to optimize your interface when the response time exceeds 200ms, but 200ms is not absolute, depending on the application scenario. Take App for example, 5 interfaces can be called in a page (aside: aggregation can also be done), so the total time is 1s, the experience is ok for users, of course, the faster the response is the better.

The interface takes 200ms, and most of the operations are performed on the database. An interface has N database operations. So optimizing SQL has the highest speed priority, and a lot of slow SQL can drag down the entire system.

SQL optimization is not the focus of this article, most of the slow SQL or with your usual development habits have a relationship. Most of the time when writing SQL will not consider performance, as long as it is written, join readily, also do not comb the query field, do not add index, at the beginning of the online no problem, until the concurrent amount of data up when cool.

About the database under the terms you can use the reference of this article: mp.weixin.qq.com/s/mFsK7YSKc…

When the amount of data is large, we must do read and write separation and sub-library sub-table, which is the only way to optimize. Related articles can also refer to my previous writing some: mp.weixin.qq.com/mp/homepage…

Reduce repeated calls

Another deadly problem with poor performance is repeated calls, where the same logic repeats queries to the database in different methods, repeats calls to RPC services, and so on.

For example:

skuDao.querySkus(productId).stream().map(sku -> {
   skuDao.getById(sku.getId());
})
Copy the code

Clearly the data has been queried, and then to query again according to the ID, the more the number, the more time wasted. Here are just examples, I believe in in the real project there are a lot of repeated queries, before I had written an article, how to solve the problem of the repeated queries, interested can look at this article: mp.weixin.qq.com/s/1k4OtNYIo…

According to the need to query

Many business logic is not complex functions, but the response is slow. Often, the code is written without thinking and calls existing methods, resulting in slow overall response. To sum up, performance problems are mostly written in code.

Here’s a scene that I’m sure you’ve all seen. Parameter is a commodity ID, the function is to put goods on the shelf, need to judge the state, meet the conditions to put on the shelf. In this scenario, all you need to do is get the status of the item and sometimes you’ll see code like this:

GoodsDetail goods = goodsService.detail(id);
if (goods.getStatus() == GoodsStatusEnum.XXXX) {
Copy the code

}

There’s a lot of logic in the detail, there’s a lot more to it than just basic product information, and that’s why it’s slow.

Parallel call

For an interface, if multiple internal RPC services or multiple external interfaces are designed, we can use parallel invocation to improve performance when there is no correlation between the interfaces.

CompletableFuture is perfect for parallel calls. This article does not elaborate on the use of CompletableFuture.

In addition to CompletableFuture, parallelStream can be used to implement parallel calls for the processing of collection classes.

In microservices, there is a layer dedicated to aggregation API. The aggregation layer is very suitable for parallel invocation. A function or page presentation involves multiple interfaces, and the aggregation layer aggregates interfaces and tailoring data on the back end to respond to the front end.

The cache

Caching is also the most commonly used optimization, the most obvious improvement, and the cost is small. As for caching, don’t abuse it either. Not all scenarios can improve performance with heap caching.

First of all, business scenarios with low real-time requirements can give priority to using cache, and the issue of updating is not considered too much. Natural expiration is ok.

In business scenarios with high real-time requirements, a complete cache update mechanism must be provided when using cache, otherwise it is easy to cause inconsistency between business data and cache data.

The recommended approach is to subscribe to binlog to update the cache uniformly, do not update or invalidate the cache in the code, simple scenarios are fine, there are only a few entries, not a problem. Some data is used in multiple scenarios and there are too many entries to update,

Asynchronous processing

Some logic that doesn’t need to be fed back to the user in real time can be processed in the background in an asynchronous way.

Asynchronous processing way of the most common task is to add to the thread pool for processing, thread pool need to consider the capacity and the monitoring of some indicators, related articles can check my this article: mp.weixin.qq.com/s/JM9idgFPZ…

In addition to monitoring some metrics, another concern with thread pool usage is the persistence of tasks. It’s fine if your data is already stored and then read out and run through the thread pool. If it is thrown directly into the thread pool for execution without persistence, it can be lost, such as in a service restart scenario.

Persistence, whether it’s a thread pool or an EventBus, can be encountered, so I recommend using message queues for asynchronous scenarios.

Message queues can store task information without losing it. Individual consumer message queue logic processing, if want to increase the speed of consumption, also can use in the queue consumer thread pool for multithreaded consumption, multithreading consumption also want to avoid the condition of the missing information, can check my this article: mp.weixin.qq.com/s/Bbh1GDpmk…

JVM parameter tuning

Tweaking JVM parameters is not something we normally need to do. Occasionally some code is not written well, resulting in memory overflow, at this time I will do some adjustments and optimize the code.

The main reason for this is to reduce GC pauses. If your program is always GC, always pauses, your interface will be slow.

As long as there is no frequent Full GC, tuning JVM parameters can be done last, with SQL optimizations taking precedence.

With the machine

Adding machines is the ultimate trick, when the amount of concurrency up, how you optimize a single machine and a single database against concurrency is also limited, this time can only be extended horizontally.

If it is in the early stage of start-up and developing rapidly, adding machines is the most direct way of optimization. Although the cost increases, the development resources are also the cost, which can realize more business needs. Wait until the medium term stabilizes to consider the architecture, performance aspects of the overall optimization and refactoring.

As with games, the best equipped players are the best, and the same is true for back-end applications, superior machines, superior database configurations, superior caches, etc.

About the author: Yin Jihuan, a simple technology lover, the author of Spring Cloud Micro-service – Full stack Technology and Case Analysis, Spring Cloud Micro-service Introduction combat and Progress, the founder of the public account Monkey World.

I have organized a very complete learning material. If you are interested, you can search “Monkey World” on wechat and reply to the keyword “learning material” to obtain the Spring Cloud, Spring Cloud Alibaba, Sharing-JDBC sub-library sub-table and task scheduling framework XXL-job that I have organized. MongoDB, crawler and other related information.