Danger! The water is deep, let the uncle come-talk about the command query separation of power and responsibility mode (CQRS)

Many years ago, when I was young, I was just as skilled as a fish, and I even hoped that I could be a life-long front-line programmer.

But I have two small wishes to achieve: one is to make more money; the other is to have more initiative in the selection of the technology stack and architecture of the project.

Earn more money because I had just got married at the time and had my own family plan, so the desire to earn money was quite strong.

The reason for wanting to have more technical initiative is that the leaders appreciated me very much at that time. Some things were gradually delegated to me. I tasted the sweetness, so I also had some small ambitions of my own.

At that time, the leadership gave me a very important opportunity in my career.

At that time, the advertising alliance was in full swing, and the company wanted to participate in the game, so it decided to build an advertising platform from scratch.

And I happen to have some similar development experience, and I do things fairly reliable, so the leader wanted me to be the technical person in charge of this system.

If I can do a good job of the system, it is definitely an opportunity for me to prove myself, and it will be good for achieving my two small wishes in the future. It tempts me a lot.

It's just that God opens a door for you and always closes a window for you. This opportunity was not only taken by my leader. At that time, the boss of another department also took aim.

As a last resort, I went to a high-level meeting to discuss. The result of the discussion is to learn from the practices of other companies at the time, and internal competition.

The two departments build a set of platforms for each, and then put them online for a while. Whoever does a good job will get the opportunity of the company's full investment.

Well, the opportunity becomes an adventure. But at this point, I can't shrink back. Once I retreat, it will affect the appreciation of my leadership, and the future development of the company will be severely hindered, and I can only rush.

Essential information for a technical expert

In order to win this competition, I also communicated with the product owner of this system for a long time. Finally, two goals must be achieved:

1. This set of system functions must be as many as possible, especially for relevant business personnel.

The reason for this is because it is now an internal competition. As for internal competition, the business people who use our system are actually very powerful, and their satisfaction is likely to be the winner of the final evaluation.

At the same time, we also plan to prepare more data tracking and analysis functions with very good experience for advertisers who put on our system, so as to maximize the attractiveness of our products.

2. The stability and reliability requirements of this system are very high, and sometimes it is worthwhile to do some over-design and implementation for this.

Here we need to explain the meaning of stability and reliability in our scene at that time. Stability is to ensure that the performance is stable, which means that our system response time should do our best to ensure response in a very short time.

The reliability is that our system should do our best to ensure that there is no error, because errors are likely to cause the loss of users and cause our products to fail.

After setting the goals and providing the products with the requirements, I and the team entered the extremely difficult development work. At that time, I really gave my whole heart and soul.

In fact, I am a person who enjoys life better than works hard. Although I was very busy at work before, but I also had a comfortable time in my free time. Listen to songs, watch movies, sometimes find a restaurant with my wife to enjoy food, and sometimes play a hearty football.

However, since the beginning of the development of this advertising system, the leisurely days are gone forever.

I remember at that time I staggered away from work, and came back staggering to work. At that time, my biggest wish was to have a bed, and I would never be woken up when I lay down.

But even with such hard work, I still encountered unclear problems. These hard bones on the road of development have caused my development goals to be adjusted repeatedly.

One of the most troublesome is the performance problem of high concurrency.

At that time, my experience was still relatively shallow, and to be honest, the surrounding ecology of Java was not perfect. The cache and database can be used to host access. At the same time, due to copyright issues, I can only choose MySQL database.

In order to solve these performance problems, I also specially printed out the official MySQL manual and delved into it every day.

At the beginning, in order to resist the expected super-high concurrency, I used the read-write separation mode that was very popular at that time.

However, after actual testing, there are always various dissatisfactions. One of the most troublesome is the performance of various complex queries.

I said that in order to win internal competition, we want to rely on the two goals of high concurrency and multi-function as much as possible with this system. Therefore, for these two goals, this system actually has many more functions that are convenient for business personnel, and the goals of this function are:

Under high concurrency, it still remains stable and smooth.

Among them, the most typical business is the advertising ranking function that can be updated in real time.

The demand for this ad placement ranking is as follows:

  • First of all, our users must be able to see their own advertising rankings in the management background. The rankings are ranked according to indicators such as the amount of consumption and the number of clicks.
  • Secondly, in our back office, we also set up such a ranking for the business staff. The difference is that it is a global ranking, which is a total ranking of the advertisements placed by all our customers.
  • Then, this ranking must be able to change in real time according to changes in the amount of consumption and the number of clicks. Of course, this real-time can be made into quasi real-time, as long as the delay is not too high.

By itself, because there are many indicators used to make the rankings, it is necessary to write very complicated SQL to query in the database. Coupled with the need for real-time changes, you have to keep querying in the database.

In this case, no matter how I optimize, I can't get satisfactory results. If I cache this ranking, because this ranking requires various statistics and sorting, after querying it from the database, various model conversions are required. If the amount of concurrency rises, the query is converted again, and the performance really drops quickly.

At that time, I was under a lot of pressure. My mind was always thinking about performance issues, and the MySQL manual in my hand was almost rotten. Even when I went home to sleep, I closed my eyes and thought about how to solve these problems.

In the end, the time to go online is constantly approaching, but the project in hand is stuck in these performance problems and it is difficult to progress, but from time to time, competitors hear news that internal competitors have progressed smoothly to a certain extent.

I can't handle all of this anymore, and my inner voice persuading myself to give up is getting louder and louder.

I used to think that I was a very resilient person, but now it seems that I am actually just an ordinary wage earner.

I want to run away. I want to discuss with the product and let it go online. I don't want to worry about it. It's a matter of life or death. I bet that the other party also encounters this problem with me, or even worse than me.

It's just that when I was about to pull the product and finally decided to go online like this, my strong heart was unwilling to stop me. I think before I give up, I have to know what is going on with my competitors anyway, and what plans and ideas the other party has for me to refer to.

I searched all my company acquaintances, and kept inquiring about competitors. However, the result was not good, because the other party did better than me. They carried out closed development and were very vigilant.

In the end, I only got one keyword: CQRS . The other party uses CQRS to solve performance problems! ! !

When I was young, I didn't have a mobile phone, so I could always do a good job of reading wholeheartedly. The efficiency of reading was extremely high. But now that I have a mobile phone, and now I read books, I will always be distracted from time to time to look at the information in the mobile phone. Sometimes in order to read the book well, I have to leave the mobile phone far away to prevent distraction.

And CQRS is this kind of thinking. This model is not so much an architectural model as it is an idea.

CQRS believes that the operations in a system are divided into two categories: read and write. If a system is not specifically optimized for reading and writing separately, then the system will be like I read a book with a mobile phone, it will be dual-purpose, so that because of mutual influence, the performance of each can not reach the optimal.

Therefore, reading and writing should be separated and optimized separately.

In CQRS, the act of writing is called a command, and the act of reading is called a query . Because I want them to be separated, the Chinese translation of the CQRS mode is called the command query separation mode .

After I knew this line of thinking, I didn't care about it at first, because at first glance, this set of things is actually the same as the read-write separation of the database I used, which is to separate the read-write.

However, my technical intuition tells me that these are not that simple.

In the world of computers, a term will not appear for no reason, nor will it become popular for no reason. If it is really the same as the separation of read and write of the database, then it is just fine to call the separation of read and write of the database. There must be something different.

I was no longer satisfied with the Chinese search results, I went directly to Martin Flower's website to see the original version. Then, I found such an architecture diagram.

Combined with his original text, I immediately understood that it is the model, the model is different!

The original database read-write separation did separate the two behaviors of read-write, but it still has one important thing that it did not do, that is, the separation of responsibilities.

What is the separation of duties? That is, the reader and the reader should not engage in the same model. The problem of database read-write separation is here, and it uses the same model.

The problem here caused by using the same model is that this model has to consider not only that it is not too difficult to read data, but also that it is not too difficult to write data.

And this just violates the core idea of ​​CQRS: complete freedom of reading and writing .

If we use the CQRS idea, assuming that writing does not need to care about reading, and reading data does not need to care about writing, can both parties be able to completely free themselves?

For example, since writing data does not need to be read, I can use the Json format, use non-standard formats such as XML format, or even write a log directly. While reading the data, there is no need to consider the issue of writing. I can even make an index format that is easy to search.

In my opinion, CQRS is a panacea to solve the performance problem that stuck me.

Taking the problem of ad ranking as an example, the trouble of ad ranking is troublesome. Every time you load the ranking, you need to have a very complicated query to read the data in the database.

If you can completely put charts read and updated charts click on those dependent consumption indicators separately, then I worried leaderboard performance problems can be solved.

After trying hard, I followed the original idea of ​​CQRS to make a design idea like this:

Here, data statistics are clicks, consumption and other data needed for ad ranking. These data will be placed in a separate database, this database is only used for writing, regardless of reading.

Then, the function of displaying ad ranking will directly read the model of ad ranking from the cache and display it directly, without any special conversion. There is also no problem of complex queries.

However, our requirement is to let the ad ranking be automatically updated based on data such as clicks and consumption in quasi-real time. What should we do if the data writing and data reading models are separated?

Many years ago, when I first bought something online, I had a question in my mind: I placed an order, how did the merchant who sold my thing know? Do you have to keep staring?

This problem was only known when I personally developed the e-commerce system. When we placed an order, we needed to send a notification to the corresponding merchant, telling the merchant which customer purchased which product.

Therefore, there is a solution for automatic update of advertising rankings, which is the same as e-commerce orders to notify merchants. When there is data to be written, we only need to make a copy of the written data and notify the model that reads the data.

Ok, now the whole logic is complete.

However, I was not eager to apply the CQRS model to actual projects immediately. Because, I discovered that I didn't even know what the shortcomings of the CQRS model were.

You know, there is no perfect solution in the world, all of which have both advantages and disadvantages. As for CQRS, I actually felt that it solved my problem perfectly, which shows that there is still a problem with my understanding of this model.

At that time, it was getting closer and closer to the agreed-upon time, and there was almost a week left. I really want to close my eyes and implement the plan.

However, no, I always like to think things through and understand things very clearly before doing things.

I decided to take the risk of spending two days to implement two function points, and then personally experience the gains and losses of introducing CQRS.

Two days later, I finally discovered the problem: after introducing the CQRS model, the biggest problem was the introduction of excessive complexity .

Due to the need to separate reading and writing, the workload of our development has been virtually doubled. With the introduction of CQRS, this becomes more complicated.

Because we found that for different functions, only by using different read or write models can the advantages of CQRS be fully utilized.

For example, ad ranking may use cache middleware to access existing rankings. To search for various suitable advertisements based on keywords, you may have to consider open source search engine middleware. Each introduction will increase development costs, server costs, and more complexity.

In the end, our advertising system went live on time.

It's just that the CQRS mode is not widely adopted. I just used CQRS for the most important function points. For the rest of the performance issues, I decided to temporarily put it down.

The reason for this is because I think most of the problems are actually caused by our over-design. Even if I failed because of this, I recognized it.

I don't want to bury huge hidden dangers for the system I built by myself, and I don't want to bring unnecessary workload to the team. I don't want to get into this.

After going online, I was so nervous, especially in the first two months of going online.

I don’t know if my compromise will cause huge problems, and I don’t know if what I did is really right.

The competition between the two systems took place two months after they went live.

The result was obtained so quickly, precisely because my opponents widely used the CQRS model.

From the beginning of his design, he thought of a blockbuster. Seven or eight kinds of middleware were introduced into his system. Splitting a large number of functions into two parts, reading and writing, has caused a huge disaster. Excessive complexity makes the entire system difficult to control.

One of the most headaches is that due to the introduction of CQRS, they must communicate to read and write two sets of components through message transmission.

However, when the reading component received the message, it found that the writing failed. After seeing the corresponding data, after a period of time, the user finds that the data does not match the previous one.

For example, when the number of clicks is 1000, it turns out to be 999 after two hours.

This type of problem appears every day, and because the system is too complicated, the time for investigating, locating, and solving problems has been greatly lengthened. In the end, the customers quit one after another, and the company had to transfer the customers to my platform.

The competition is over, I won, but I really can't get up happy. Because today he failed because of the wrong introduction of new technology, then tomorrow I will not fail because of misuse of new technology and new ideas? Isn't he today's me tomorrow?

May programmers all over the world think deeply and be familiar with everything, and be cautious in their words and deeds!

Essential information for a technical expert

Originality is not easy. If you think the article is well written, let's have a three-link. It can also be regarded as my support for my codeword.