Tuning Elasticsearch configuration for improved resource utilization
This article will explain what the application does better from the underlying Elasticsearch implementation, but I’m not going to dive into too much detail in a very obscure way, so I’ll try to use a simplified process.
Before we get started, let’s go over a few important Elasticsearch terminologies.
Index: This is like the table in RDBMS or the collection in MongoDB, not to be confused with index in RDBMS.
Shard (aka Primary Shard): The access point where the data is written to the index, and of course the data can be read.
Replica: Access point to read data, can not be used to write data.
These terminologies are closely related to today’s three tips.
So, what is the role of these components in Elasticsearch?
Let me illustrate with an example. Suppose we have a two-node Elasticsearch cluster with two indexes, A and B, and they are configured as follows.
A index
number_of_shards = 2
number_of_replicas = 1
B index
number_of_shards = 1
number_of_replicas = 2
These are the index settings, which clearly indicate how many shards and replica are required, and the following diagram shows how Elasticsearch looks internally.
The red block represents the shard, and the normal block is the replica; the user, or application, will access both nodes to manipulate the corresponding data.
Tip #1: The number of shards should align to the number of nodes
This tip is limited to those large indexes, because we know the more shards are used, the more evenly the data is distributed, and if the data in each shard is only a little bit and distributed in many nodes, it will cause more search overhead.
According to the official recommendation, the data volume of a shard should be less than 50GB.
Why such a tip? Let’s use a counter-example to observe. Suppose we have many nodes, but each index has only one shard.
A index
number_of_shards = 1
number_of_replicas = 3
Then the user’s read operations will be evenly distributed on each node, but the write operations will be concentrated on a single node.
In the case of a large volume of writes, such a setting can create a performance bottleneck on a single node and affect every operation on that node.
Since Elasticsearch 5.x started to support hot warm architecture, if a cluster has hot warm architecture enabled, then the number of shards should align to the number of hot nodes.
Tip #2: The document should be routed
Let’s continue with the top example, assuming a two-node cluster, and index A has two shards and one replica each.
When a user writes to a document, which node does he write to?
This is determined by Elasticsearch’s built-in routing rules, briefly, by the following formula.
shard = hash(_id) % number_of_shards
The _id is automatically dispatched by Elasticsearch, but can of course be specified by the user. As for the hash algorithm, it is murmur3, which is not a consistent hash, so a specific shard will be calculated.
So, the worst case would look like the following diagram.
When one user starts a bulk operation, writing a large volume of documents to the cluster, and at the same time, one user is searching, the two users’ operations interfere with each other.
If there is a way to keep the data of a single user in a single node, then the above case can be effectively avoided. At most, one user’s search performance will be affected when he writes a lot. This is called custom routing.
Elasticsearch is able to add a routing parameter to read and write operations, so that the formula mentioned earlier becomes
shard = hash(routing) % number_of_shards
Nevertheless, it is also the need to pay attention to whether there will be a hotspot problem, i.e., data is overly concentrated in certain nodes.
I have an article to explain the design of sharding keys. Although this article is about MongoDB, the concepts are the same.
Tip #3: Turn off replica and refresh when doing a large volume of writes
When doing a large volume of writes, stop replication first to reduce resource consumption, and then turn on replication when the large volume of writes is complete. This can significantly reduce write consumption and improve the performance of the cluster.
Disabling replica is easy to understand, but what about disabling refresh?
Before we explain, let’s understand the mechanism of refresh.
When a user writes data to the shard, it is first written to the memory buffer, and the data is invisible to the search operation at this time. Then, Elasticsearch writes the data in the memory buffer to the hard drive via refresh and converts it into Lucene format, and then it is really searchable.
There are two ways to refresh.
refresh_interval in Index setting, refresh will be triggered automatically when the time is up.
Explicitly call Refresh API.
We don’t actively invoke the Refresh API during heavy writes, so by turning it off, we mean turning refresh_interval longer or even off.
Of course, either turning off replica or turning off refresh is to reduce resource consumption so that all resources can be dedicated to processing large amounts of writes.
So, you may wonder, won’t turning off refresh cause data loss?
No, it won’t.
The reason is that Elasticsearch’s persistence model relies on more than just refresh. Let’s dig a little deeper.
From the above diagram, we can see even the process of writing from the memory buffer to the hard drive is not really on the hard drive yet, it still needs to go through flush before it is really persisted.
However, this is a pretty long path, which is totally unreliable for a database, so in fact, when writing data, it will write to two places at the same time: the memory buffer and the transaction log.
In the event of a disaster, the entire written dataset can still be recovered through the transaction log. But of course, there is a price to pay for this. There is no insurance mechanism, and if the transaction log is corrupted, the data will really be lost. Nevertheless, I believe the chance is definitely much lower, and the loss is only for the data during this bulk writing period.
Conclusion
This time, we introduce three ways to significantly improve the performance of Elasticsearch clusters.
I have to say that in the big data world, it is obviously not enough to use all kinds of data storage. Without a deep understanding of the underlying implementation of data storage, there is a high risk of wasting resources. This will not only result in lower cluster performance, but also in higher hardware costs as more hardware is used to solve these overheads.
In this article, we briefly explain a few terminologies behind Elasticsearch, and then use this knowledge to learn more about optimization techniques. I believe you may be able to learn more details through this process.
If you have any good tips, please feel free to share them with me.
Before explaining the process of importing HTAP into Xiaohongshu, let’s briefly introduce Xiaohongshu.
Xiaohongshu is known as the Chinese version of Instagram, but in addition to the usual social functions such as video and image sharing, it also includes an e-commerce platform. According to the official website of Xiaohongshu, as of 2019, active users have exceeded 100 million.
It is both a social media and e-commerce website, and the amount of data generated each day is in the billions. In order to support data-driven decision making, these massive amounts of data must go through multiple layers of cleansing, transformation and aggregation, and more importantly, the real-time nature of decision making also need to address.
In addition, Xiaohongshu’s application scenarios and user numbers continue to grow, so the scalability of the entire data infrastructure is also a major challenge.
In the end, Xiaohongshu adopted TiDB as their real-time streaming storage.
Why Xiaohongshu chose TiDB?
In fact, Xiaohongshu already adopted TiDB for some projects in 2017, but it was widely used until 2018.
The three main reasons are as follows.
Due to data-driven decision-making, there will be various OLAP needs of ad hoc queries, so the traditional OLTP database can not support the use.
Many OLAP databases can handle queries efficiently, but the write performance is not good, while TiDB write also has great performance.
In the scenario of horizontal scaling of data clusters, new instances must be available as fast as possible. TiDB keeps datasets consistent across all instances through a Raft majority mechanism.
Now, TiDB has been widely used in various domains of Xiaohongshu, including
Data warehouse application
Report Analysis
Real-time dashboard for sales promotion
Anti-fraud detection
etc.
Past Pain Points
In general, the entire data architecture is as follows.
There are three typical use cases in such an architecture with poor performance.
Firstly, ad hoc queries are performed on the production database. To avoid increasing the overhead on the production database, a complete copy of the production database must be made, and all ad hoc queries are run on the replica.
However, the MySQL for production uses sharding, which creates a high complexity in operation. In addition to the difficulty of operation, the implementation of aggregation, such as group by, join, etc., on the middleware of sharding also leads to the complexity of implementation, and on the other hand, the transactions on the sharding MySQL is also a big challenge.
Secondly, in order to query reports more efficiently, report analysis is generated through pre-aggregation and stored in a standalone MySQL to avoid the latency of Hadoop direct query. However, since it is pre-aggregated, it cannot effectively respond to requirement changes. Moreover, a standalone MySQL has less scalability due to the lack of sharding.
Finally, to perform anti-fraud detection in various application scenarios, it must rely on the data stored in the data lake, which is not real-time, but T + 1 ingestion. This makes it impossible for anti-fraud to work within time, and even if fraud is detected, the damage is already done.
Solution
The answer to these three cases is TiDB.
In the use case of ad hoc query, there are two pain points must be dealt with, one is the difficulty of operation, and the other is the complexity of implementation. Then, just put all the data into TiDB and maintain one TiDB. Because TiDB fully supports MySQL 5.7, so it can be synchronized by MySQL binlog.
Of course, it is not as simple as that. To put the sharding databases into TiDB, it needs to process the data properly, i.e. merge them into one big wide table. Therefore, it needs to deal with the issues of merging and auto-incrementing primary keys in the streaming. But eventually, all the production databases will be synchronized with TiDB, and even for the data volume of Xiaohongshu, the synchronization delay is within 1 second.
Then, the same practice can be applied to report analysis and anti-fraud.
Because of real-time streaming, anti-fraud no longer takes T + 1 time to respond, and real-time streaming can be handled directly through Flink SQL. On the other hand, report analysis is easier because all the data is available and can be operated on a single database.
New Problem Occurred
When TiDB was first introduced, the version of TiDB used in Xiaohongshu was 3.x.
TiDB 3.x is still a row-based storage engine, and although it can achieve good performance in OLAP, it is still not good enough for large data volume. Furthermore, all scenarios use the same TiDB cluster which cannot isolate ad hoc queries and production applications.
But these problems are solved after upgrading to TiDB 4.x version.
The reason is TiDB 4.x introduces the HTAP architecture, which enables the coexistence of row storage engine and column storage engine. The new column storage engine called TiFlash is independent from the original row storage engine TiKV and will not interfere with each other.
When the application writes data to TiKV, the data is quickly synchronized to TiFlash through the Raft learner mechanism, so that the row storage and column storage are consistent.
In addition, TiDB uses internal CBO to know whether the query should use the row or column engine as soon as it comes in and automatically routes the query, which greatly reduces the complexity of the application.
TiDB 4.x also has a new mechanism that helps improve the performance of the entire data architecture: pessimistic locking. In TiDB 3.x, there was only optimistic locking, which made it easily possible for inter-transaction conflicts under huge data volume of continuous writes.
Once a transaction conflict occurs, it can only be retried on the client side, which significantly increases the complexity of application development. But pessimistic locking can effectively solve this problem, and the error handling of the application becomes more simple.
Conclusion
Actually, Xiaohongshu has also referred to ClickHouse when making technical selections, and the performance of ClickHouse has some better than TiDB. However, ClickHouse is relatively difficult to operate and maintain.
In addition, ClickHouse is a column storage engine, in the data update scenario performance is not good. If rewriting “update” to “ append”, it needs a lot of modification cost on one hand and extra de-duplication logic on the other.
Therefore, TiDB is the final choice.
From my point of view, HTAP is the future and can cover various use cases by merging row storage engine and column storage engine. It is cost effective to use only one database to handle various needs with big data.
In the past, in order to support a variety of data products, we had to continuously make technical selections among many databases and carefully consider various use cases, but with HTAP, these complexities no longer exist.
Personally, I am looking forward to seeing the gradual simplification of data engineering.
A complete replacement for Medium with nearly zero overhead
Recently I reached 1,000 followers, and it’s been a long journey, much longer than I thought ever.
Since a year half ago, I’ve been trying to post one article every Monday (except holidays), mainly about my work and studies, with the intention of getting more consistent exposure to attract more software engineers to discuss with me.
In this process, I found out the algorithm of the article exposure should have been drastically modified, even though the number of followers exceeded 1,000 but the number of views could be counted, and this is not even the number of reads.
This is not quite the same as what I wanted to achieve in the beginning, so I had the idea of building my own blog.
To this blog, I have several requirements.
To be easy to manage, I do not want to manage this site also need too much coding or operation overhead.
It should be easy to publish articles, the biggest advantage of Medium is it is easy to write.
Be able to render charts and Mermaid. My articles rely heavily on these visualizations, but right now Medium is completely incapable of doing anything with charts and Mermaid, so I have to cut and paste them from another place.
Be able to leave comments, this is very important, I just want to have more discussion to write articles.
To support clipboard uploading images, this feature of Medium is really convenient.
Be able to track hit rates and SEO, but the technical details of this are more complicated and not my top priority.
In the end, I chose Hexo with Github Pages as my website.
Firstly, it is quite easy to achieve this through Hexo’s built-in functions, and the generated static website is directly rendered as a Github Pages without the operation.
Secondly, after integrating Github Action, basically, after writing a Markdown file, it can be automatically published by directly pushing it to Github, which is almost the same process as writing articles on Medium.
To do the third and fourth points need to choose a good theme, I chose NexT.
For the fifth point, I used some tricks, nevertheless, I was able to do it, by using VS Code with Paste Image plugin.
This article will enable every engineer familiar with Git and open source software to build a fully functional blog in less than an hour.
Hexo + Hexo Action
About the basic usage of Hexo and Hexo Action here will not explain in detail, their official documentation is written completely.
I provide a SOP of my own, which can quickly generate a blog on Github Pages.
Create two repositories, one for the Hexo source code and one for the generated static website. The name of the repository for the static website must be: <username>.github.io.
Hexo’s source code repository just needs to git push the entire package of code after hexo init blog. Hexo has a sufficient .gitignore.
If you need to use Hexo Action, you need to run npm install first to generate package-lock.json, which will be used in the deployment process.
Generate a pair of keys for the deployment, set the private key in Hexo’s source code repository, and set the public key in the repository where the static site is located (which is still empty). For detailed process, please refer to Hexo Action’s official document.
Put a Github Action workflow in the Hexo source code repository, basically the example of the official Hexo Action document can be applied directly after referring to the description.
At this point, the two repositories are now connected properly, although the static site is not yet deployed. So we need to modify the Hexo config file: _config.yml to complete the deploy section at the bottom. For example:
If you want to develop locally, remember to pull the submodule.
git submodule init git submodule update
Next, modify Hexo’s config file: _config.yml, change the default theme to next, and the theme is applied.
Then, it’s time to customize NexT.
Since Hexo 5.0.0, it supports the dedicated theme config, which is _config.[theme].yml. So we only need to copy the default NexT config file to modify it.
cp themes/next/_config.yml _config.next.yml
I only care about two functions, Mermaid and comments.
Mermaid’s settings are as follows. Just turn it on, of course, you can also modify the layout.
1 2 3 4 5 6
mermaid: enable: true # Available themes: default | dark | forest | neutral theme: light: default dark: dark
The comment function is more interesting, NexT supports many kinds of comment functions, including the popular Disqus and Gitalk.
But I am hosted in a public Github repository, so Disqus is directly out because Disqus requires a shortname setting, but if the shortname is obtained, it will directly cause the account breach. Many other comment functions also face the same security concerns.
Even if the Hexo source code is hosted in a private repository, as long as the static site repository used by Github Pages is public, it will still be compromised. By the way, you can pay Github to use a private repository to host Github Pages.
Therefore, the comment system needs to choose OAuth base Gitalk or Utterances, nevertheless, I don’t know why NexT needs to set Gitalk’s secret key, so Gitalk is also out.
To enable Utterances is simple as well, follow the official website process to install the OAuth App and modify the NexT config file.
1 2 3 4 5 6 7
utterances: enable: true repo: <installed repo> # Available values: pathname | url | title | og:title issue_term: title # Available values: github-light | github-dark | preferred-color-scheme | github-dark-orange | icy-dark | dark-blue | photon-dark | boxy-light theme: github-light
Thus, the third and fourth requirements are completed.
Uploading pictures via clipboard
To meet this requirement, we must first understand how the images are processed in Hexo.
There are two options for searching for images in Hexo:
Under source folder.
Under the asset folder, please refer to link for details.
We want to use the first way, as long as there are images under source, we can read them directly using Markdown syntax.

In the above example, the absolute path to the image is actually <projectRoot>/source/images/abc.jpg.
But we also need a helper to read the clipboard, convert the content to image files and generate the corresponding Markdown syntax.
VS Code has a plugin, Paste Image, that makes this easy, and it can adjust the upload location and the resulting Markdown syntax accordingly. But Hexo currently places images under the source folder, which is different from the Markdown Preview plugin I currently use (Markdown Preview Enhanced).
The preview plugin is not very compatible, so I can’t get the project directory of VS Code to match the root directory of Hexo, so my VS Code project directory can only be opened under source folder as a compromise.
This makes the Paste Image settings need to be adjusted accordingly. Here are the settings I use, at <projectRoot>/source/.vsconde/settings.json.
The reason for this compromise is I need the Mermaid preview feature of Markdown Preview Enhanced. Until I find another way to do it, this compromise is a doable solution.
The good thing is that my writing process is not affected. When I need to publish an article, I just add a new Markdown to my VS Code project path, push it to Github after writing, and then it will be automatically deployed, so it doesn’t matter if the VS Code project path is source.
Conclusion
By combining these following components, it allows me to write articles in a more efficient way than Medium, and the previous pain points of having to cut and paste Mermaid and tables have disappeared.
Hexo
Github Action
Github Pages
NexT
In addition, there is more room for customization, which is the best solution at the moment.
Currently, my audiences are divided into English and Chinese circles, and Medium’s exposure to Chinese articles is lower than English, so I will gradually adjust the posting frequency and content in the future. After that, I will adjust my posting strategy based on the tracking data I collect.
My main profession is a data architect, so this is my experiment with Medium and the blog I own.