Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

What are the tips for improving the performance of Redis

2025-04-02 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Database >

Share

Shulou(Shulou.com)05/31 Report--

This article mainly introduces the tips to improve the performance of Redis, which have a certain reference value, interested friends can refer to, I hope you can learn a lot after reading this article, the following let the editor take you to understand it.

01 use pipeline

Redis is a TCP server based on the request-response model. It means a single request RTT (round trip time), depending on the current network condition. This can cause a single Redis request to be very fast, such as through a local loop network card. It can be very slow, such as in an environment with poor network conditions.

On the other hand, every request-response of Redis involves read and write system calls. It may even trigger multiple epoll_wait system calls (Linux platform). This causes Redis to keep switching between user mode and kernel mode.

Static int connSocketRead (connection * conn, void * buf, size_t buf_len) {/ / read system call int ret = read (conn- > fd, buf, buf_len);} static int connSocketWrite (connection * conn, const void * data, size_t data_len) {/ / write system call int ret = write (conn- > fd, data, data_len) } int aeProcessEvents (aeEventLoop * eventLoop, int flags) {/ / event triggered, numevents = aeApiPoll (eventLoop, tvp) for epoll_wait system call under Linux;}

So, how to save round-trip time and system calls? Batch processing is a good way.

To this end, Redis provides "pipeline". The principle of pipeline is simple: multiple commands are packaged and sent as "one command". After the Redis is received, it is parsed into multiple commands to execute. Finally, multiple results are packaged and returned.

Pipeline can effectively improve the performance of Redis.

However, there are a few things you need to pay attention to when using pipeline

Pipeline cannot guarantee atomicity. During one pipeline command execution, other client-initiated commands may be executed. Keep in mind that pipeline is just batch processing commands. To ensure atomicity, use MULTI or Lua scripts.

Do not have too many pipeline commands at a time. When using pipeline, Redis temporarily stores the response result of the pipeline command in memory Reply buffer and waits for the execution of all commands to return. If there are too many pipeline commands, it may result in more memory consumption. You can split a single pipeline into multiple pipeline.

02 enable IO multithreading

Before the "Redis 6" version, Redis was "single-threaded" to read, parse, and execute commands. Starting with Redis 6, IO multithreading was introduced.

The IO thread is responsible for reading commands, parsing commands, and returning results. When enabled, the IO performance can be effectively improved.

I drew a diagram for your reference.

As shown in the figure above, the main thread and the IO thread jointly participate in the reading, parsing, and result response of the command.

The "main thread" can execute the command.

The IO thread is turned off by default. You can change the following configuration of redis.conf to enable it.

Io-threads 4io-threads-do-reads yes

"io-threads" is the number of IO threads (including the main thread). I suggest you set different values for stress testing according to the machine and take the optimal value.

03 avoid big key

Redis executes commands in a single thread, which means that the Redis operation "big key" is at risk of blocking.

Big key usually means that the value stored by Redis is too large. These include:

A single value is too large. Such as 200m String.

Too many collection elements. Such as List, Hash, Set, ZSet, there are hundreds or tens of millions of data.

For example, suppose we have a 200m String key named "foo".

Execute the following command

127.0.0.1 purl 6379 > GET foo

When the result is returned, Redis allocates 200m of memory and performs a copy of the memcpy.

Void _ addReplyProtoToList (client * c, const char * s, size_t len) {. If (len) {/ * Create a new node, make sure it is allocated to at * least PROTO_REPLY_CHUNK_BYTES * / size_t size = len

< PROTO_REPLY_CHUNK_BYTES? PROTO_REPLY_CHUNK_BYTES: len; // 分配内存(例子中为 200m) tail = zmalloc(size + sizeof(clientReplyBlock)); /* take over the allocation's internal fragmentation */ tail->

Size = zmalloc_usable_size (tail)-sizeof (clientReplyBlock); tail- > used = len; / / memory copy memcpy (tail- > buf, s, len); listAddNodeTail (c-> reply, tail); c-> reply_bytes + = tail- > size; closeClientOnOutputBufferLimitReached (c, 1);}}

The output buf of Redis is 16k.

/ / server.h#define PROTO_REPLY_CHUNK_BYTES (16024) / * 16k output buffer * / typedef struct client {. Char buf [proto _ REPLY_CHUNK_BYTES];} client

This means that Redis cannot return response data at a time and needs to register "writable events" to trigger multiple write system calls.

Here are two time-consuming points:

Allocate large memory (it is also possible to free memory, such as the DEL command)

Trigger multiple writable events (frequent execution of system calls, such as write, epoll_wait)

So, how do you find big key?

If there are simple commands in slow log, such as GET, SET, DEL, there is a good chance that big key will appear.

127.0.1 SLOWLOG GET 6379 > GET 3) (integer) 201323 / / Unit subtle 4) 1) "GET" 2) "foo"

Second, you can find big key through the Redis analysis tool.

$redis-cli-- bigkeys-I 0.1... [00.00%] Biggest string found so far'"foo" 'with 209715200 bytes- summary-Sampled 1 keys in the keyspaceful Total key length in bytes is 3 (avg len 3.00) Biggest string found' "foo" 'has 209715200 bytes1 strings with 209715200 bytes (100.0000% of keys, avg size 209715200.00) 0 lists with 0 items (00.0000% of keys, avg size 0.00) 0 hashs with 0 fields (00.0000% of keys) Avg size 0.00) 0 streams with 0 entries (00.005% of keys, avg size 0.00) 0 sets with 0 members (00.005% of keys, avg size 0.00) 0zsets with 0 members (00.0000% of keys, avg size 0.00)

For big key, here are some suggestions:

1. Try to avoid big key in the business. When big key appears, you have to judge whether the design is reasonable, or whether there is bug.

two。 Split the big key into several small key.

3. Use alternate commands.

If the Redis version is greater than 4. 0, you can use the UNLINK command instead of DEL. If the Redis version is greater than 6. 0, the lazy-free mechanism can be enabled. The memory operation is freed and executed by the background thread.

LRANGE, HGETALL, etc., are replaced by LSCAN and HSCAN.

But I still recommend avoiding big key in your business.

04 avoid executing commands with high time complexity

We know that Redis executes commands "in a single thread". Executing commands with high time complexity is likely to block other requests.

Complex commands are related to the number of elements. There are usually two scenarios.

Too many elements, consuming IO resources. For example, HGETALL, LRANGE, the time complexity is O (N).

The calculation is too complex and consumes CPU resources. For example, ZUNIONSTORE, the time complexity is O (N) + O (M log (M))

The Redis official manual, which marks the time complexity of command execution. It is recommended that you check the manual and pay attention to the time complexity before using unfamiliar commands.

In real business, you should try to avoid commands with high time complexity. If you have to, there are two suggestions.

Ensure that the number of elements for the operation is as small as possible.

Separation of reading and writing. Complex commands are usually read requests and can be executed on the "slave" node.

05 use laziness to delete Lazy free

When key expires or when the DEL delete command is used, Redis frees up the memory allocated by the object in addition to removing the object from the global hash table. When big key is encountered, freeing memory will cause the main thread to block.

To this end, Redis 4.0 introduces the UNLINK command, which frees the object memory operation into the bio background thread to execute. So as to effectively reduce the blocking of the main thread.

Redis 6. 0 goes a step further by introducing Lazy-free-related configurations. When the configuration is enabled, the "release object" operation is "executed asynchronously" within the key expiration and DEL commands.

Void delCommand (client * c) {delGenericCommand (CPE server.lazyfreeways lazyhands usernames);} void delGenericCommand (client * c, int lazy) {int numdel = 0, j; for (j = 1; j)

< c->

Argc; jacks +) {expireIfNeeded (c-> db,c- > argv [j]); / / enable lazy free to delete int deleted = lazy asynchronously? DbAsyncDelete (c-> db,c- > argv [j]): dbSyncDelete (c-> db,c- > argv [j]);...}}

It is recommended that you upgrade to at least Redis 6 and turn on Lazy-free.

06 separation of read and write

Redis realizes the "master-slave" operation mode through copy, which is the cornerstone of failover and is used to improve the reliability of system operation. It also supports read-write separation to improve read performance.

You can deploy one master node and multiple slave nodes. Distribute the read command to the slave node, so as to reduce the pressure of the master node and improve the performance.

07 bind CPU

Redis 6.0supports binding CPU, which can effectively reduce thread context switching.

CPU affinity (CPU Affinity) is a scheduling property that "binds" a process or thread to a CPU or group of CPU. Also known as CPU binding.

Setting CPU affinity can avoid CPU context switching to some extent and improve the hit rate of CPU L1 and L2 Cache.

Under the early "SMP" architecture, each CPU shared resources through the BUS bus. CPU binding doesn't make much sense.

Under the current mainstream "NUMA" architecture, each CPU has its own local memory. Access to local memory is faster. Accessing other CPU memory results in significant latency. At this time, CPU binding is of great significance to improve the running speed of the system.

The real-world NUMA architecture is more complex than the image above, usually grouping CPU, and allocating a set of memory to several CPU, called "node".

You can view NUMA hardware information through the "numactl-H" command.

Numactl-Havailable: 2 nodes (0-1) node 0 cpus: 0 24 68 10 12 14 16 18 20 22 26 28 30 32 34 36 38node 0 size: 32143 MBnode 0 free: 26681 MBnode 1 cpus: 13 5 7 9 11 13 17 21 21 23 25 27 29 31 33 35 37 39node 1 size: 32309 MBnode 1 free: 24958 MBnode distances:node 0 10: 10 21 1: 21 10

In the figure above, you can see that the machine has 40 CPU, grouped into 2 node.

Node distances is a two-dimensional matrix that represents the "access distance" between node, with 10 as the base value. As you can see from the above command, node itself accesses a distance of 10. Cross-node access, such as node 0 to node 1, is 21. It shows that the "cross-node access speed" of the machine is 2.1 times slower than that of "node itself".

In fact, as early as 2015, it was suggested that Redis needed to support setting CPU affinity, and Redis did not support IO multithreading at that time, and the proposal was shelved.

Redis 6.0introduces IO multithreading. At the same time, it also supports setting CPU affinity.

I drew a picture of the thread family of Redis 6.0for your reference.

The above picture can be divided into three modules.

Main thread and IO thread: responsible for command reading, parsing, and result return. Command execution is done by the main thread.

Bio thread: responsible for performing time-consuming asynchronous tasks, such as close fd.

Background process: the fork child process to execute time-consuming commands.

Redis supports configuring the CPU affinity of the above modules separately. You can find the following configuration at redis.conf (this configuration needs to be turned on manually).

# IO thread (including main thread) bound to CPU 0, 2, 4, 6server_cpulist 0-7 bio thread bound to CPU 1, 3bio_cpulist 1 management aof rewrite background process to CPU 8, 9, 10, 11aof_rewrite_cpulist 8-1 "bgsave background process bound to CPU 1, 10, 11bgsave_cpulist 1 10-11

I tested the IO thread and the main thread on the above machine as follows:

First, turn on the IO thread configuration.

Io-threads 4 # main thread + 3 IO threads io-threads-do-reads yes # IO thread enables reading and parsing commands

Test the following three scenarios:

CPU binding configuration is not enabled.

Bind to a different node.

"server_cpulist 0pm 1pm 2pm 3"

Bind to the same node.

"server_cpulist 0pic 2pr 4pr 6"

Benchmark the get command through redis-benchmark, executing 3 times for each scenario.

$redis-benchmark-n 5000000-c 50-t get-- threads 4

The results are as follows:

1. Do not enable CPU binding configuration

Throughput summary: 248818.11 requests per secondthroughput summary: 248694.36 requests per secondthroughput summary: 249004.00 requests per second

two。 Bind different node

Throughput summary: 248880.03 requests per secondthroughput summary: 248447.20 requests per secondthroughput summary: 248818.11 requests per second

3. Bind the same node

Throughput summary: 284414.09 requests per secondthroughput summary: 284333.25 requests per secondthroughput summary: 265252.00 requests per second

According to the test results, binding to the same node,qps increases by about 15%.

With binding CPU, you need to pay attention to the following points:

Under Linux, you can use "numactl-- hardware" to view the hardware layout and make sure that NUMA is supported and enabled.

Threads should be distributed in "different CPU, same node" as much as possible, and setting CPU affinity is effective. Otherwise, it will result in frequent context switching and remote memory access.

You should be familiar with the CPU architecture and do a good job of testing. Otherwise, it may be counterproductive, resulting in a decline in Redis performance.

08 rational allocation of persistence strategy

Redis supports two persistence strategies, RDB and AOF.

RDB generates data snapshots in binary format through the fork child process.

AOF is an incremental log, text format, and usually large. The log is rewritten through AOF rewrite to save space.

In addition to manually executing the "BGREWRITEAOF" command, the following four points also trigger AOF rewriting

Execute the "config set appendonly yes" command

The AOF file size ratio exceeds the threshold, "auto-aof-rewrite-percentage"

The absolute size of the AOF file exceeds the threshold, "auto-aof-rewrite-min-size"

Master-slave copy completes RDB loading

Both RDB and AOF trigger execution in the main thread. Although the implementation is specific, it will be handed over to the backstage child process through fork. However, the fork operation will copy the process data structure, page table, and so on, which will affect the performance when the instance memory is large.

AOF supports the following three strategies.

Appendfsync no: it is up to the operating system to decide when to execute fsync. For Linux, the fsync is usually executed every 30 seconds, brushing the data from the buffer to disk. If the Redis qps is too high or big key is written, it may cause the buffer to be full, thus triggering the fsync frequently.

Appendfsync everysec: fsync is executed once per second.

Appendfsync always: fsync will be called once for each "write", which has a great impact on performance.

Both AOF and RDB put high pressure on disk IO. Where AOF rewrite traverses all the data in the Redis hash table and writes to disk. It will have a certain impact on performance.

Online business Redis is usually highly available. If it is not sensitive to cached data loss. Consider shutting down RDB and AOF to improve performance.

If it cannot be closed, here are some suggestions:

RDB chooses business trough, usually in the early hours of the morning. Keep the memory of a single instance no more than 32 GB. Too much memory can cause fork to take more time.

AOF choose appendfsync no or appendfsync everysec.

The AOF auto-aof-rewrite-min-size configuration is larger, such as 2G. Avoid triggering rewrite frequently.

AOF can be opened only in the slave node to reduce the pressure on the master node.

According to local tests, if AOF is not enabled, write performance can be improved by about 20%.

09 use long connection

Redis is a request-response server based on the TCP protocol. The use of short connections results in frequent creation of connections.

Short connections have the following slow operations:

When creating a connection, TCP executes policies such as three-way handshake, slow start, and so on.

Redis triggers a new / disconnected event to perform time-consuming operations such as assigning / destroying clients.

If you are using Redis Cluster, when you create a new connection, the client will pull the slots information to initialize. It is slower to establish a connection.

Therefore, compared to the fast-performing Redis, creating a connection is a very slow operation.

It is recommended that you use connection pooling and set the connection pool size reasonably.

However, when using long connections, you need to pay attention to the "automatic reconnection" strategy. Avoid network anomalies that lead to connection failure and affect normal business.

10 turn off SWAP

SWAP is a memory swapping technology. Copy the memory by page to the predetermined disk space.

Memory is fast and expensive. The disk is low-speed and cheap.

In general, the more SWAP you use, the lower the system performance.

Redis is an in-memory database, and using SWAP can lead to rapid performance degradation.

It is recommended that you leave enough memory and turn off SWAP.

I have drawn a mind map to facilitate your memory.

As you can see, performance optimization is not easy and requires us to know a lot of low-level knowledge and test it fully. Redis will have different performance under different machines, different systems and different configurations.

Thank you for reading this article carefully. I hope the article "what are the tips for improving the performance of Redis" shared by the editor will be helpful to you. At the same time, I also hope that you will support us and pay attention to the industry information channel. More related knowledge is waiting for you to learn!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report