redis usage specification and performance optimization

Table of contents

key design

  • Take into account readability and manageability
    Prefix the business name (or database name) (to prevent key conflicts), separated by colons, such as business name: table name: id
  • Conciseness
    On the premise of ensuring the semantics, control the length of the key. When there are many keys, the memory usage cannot be ignored. For example: user:{uid}:friends:messages:{mid}simplified asu:{uid}:fr:m:{mid}
  • Do not contain special characters (mandatory)

value design

reject bigkey (mandatory)

In Redis, a string can be up to 512MB, and a secondary data structure (such as hash, list, set, zset) can store about 4 billion (2^32-1) elements, but in practice, if the following two situations, It will be considered a bigkey.

  • String type: its big is reflected in the fact that a single value is very large, and it is generally considered that a bigkey exceeds 10KB.
  • Non-string types: hash, list, set, ordered set, their big is reflected in the number of elements.

Generally speaking, the string type is controlled within 10KB, and the number of hash, list, set, and zset elements should not exceed 5000. Counterexample: A list with 2 million elements.

For non-string bigkeys, do not use del to delete, use hscan, sscan, zscan to delete gradually, and at the same time, pay attention to prevent automatic deletion of bigkey expiration time (for example, a 2 million zset setting expires in 1 hour, which will trigger the del operation, causing block)

Bigkey hazards:

  1. Cause redis to block
  2. Network congestion
    bigkey means that each acquisition will generate a large network traffic. Assuming a bigkey is 1MB, and the client visits per second is 1000, then 1000MB traffic is generated per second. For ordinary gigabit network cards (according to the word 128MB/s) server is simply a disaster, and generally the server will be deployed in a single-machine multi-instance mode, which means that a bigkey may also affect other instances, and the consequences are unimaginable.
  3. There is a bigkey for expired deletion
    , which is safe and sound (only executes simple commands, such as hget, lpop, zscore, etc.), but it sets an expiration time. When it expires, it will be deleted. If you do not use Redis 4.0's expired asynchronous deletion ( lazyfree-lazy-expire yes), there is a possibility of blocking Redis.
    Bigkey generation:

generation of bigKey

Generally speaking, the generation of bigkey is due to improper program design or unclear prediction of the data size. Let’s look at a few examples:

  1. Social category: fan list, if some celebrities or big v do not carefully design, it must be bigkey.
  2. Statistics: For example, storing a certain function or user collection of a website by day, unless few people use it, it must be bigkey.
  3. Cache class: Serialize the data from the database load into Redis. This method is very common, but there are two points to pay attention to. First, is it necessary to cache all fields; second, is there any correlation For the data, some students store the relevant data under a key for the convenience of the picture, and generate a bigkey.

How to optimize bigkey

  1. Split
    the big list: list1, list2, ... listN is to divide the large list into several small lists to store
    big hash: you can talk about segmented data storage, such as a large key, assuming that 1 million user data are stored, it can be split into 200 keys, each key stores 5000 user data

  2. If bigkey is unavoidable, think about whether to take out all elements every time (for example, sometimes only hmget is needed instead of hgetall), and the same is true for deletion, try to use an elegant way to handle it.

Choose the appropriate data type

Control the life cycle of the key, redis is not a trash can

It is recommended to use expire to set the expiration time (if conditions permit, the expiration time can be dispersed to prevent centralized expiration).

use of commands

Focus on number of elements

For example, hgetall, lrange, smembers, zrange, sinter, etc. are not impossible to use, but the value of N needs to be specified. If there is a need for traversal, hscan, sscan, and zscan can be used instead.

Disable dangerous commands

It is forbidden to use keys, flushall, flushdb, etc. online, and ban commands through the rename mechanism of redis, or use scan to process progressively.

Batch operations improve efficiency

  • Native commands: eg mget, mset.
  • Non-native commands: You can use pipeline to improve efficiency.

It is not recommended to use self-contained transactions

Redis transaction function is weak, it is not recommended to use too much, you can use lua instead

Client use

Avoid using one redis service for multiple businesses (recommended)

Multiple irrelevant businesses should use different redis services, and public data should be used as a service

Use a client with a connection pool

It can effectively control the number of connections and improve efficiency at the same time. The standard usage is as follows:

JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxTotal(5);
jedisPoolConfig.setMaxIdle(2);
jedisPoolConfig.setTestOnBorrow(true);

JedisPool jedisPool = new JedisPool(jedisPoolConfig, "192.168.0.60", 6379, 3000, null);

Jedis jedis = null;
try {
    
    
    jedis = jedisPool.getResource();
    //具体的命令
    jedis.executeCommand()
} catch (Exception e) {
    
    
    logger.error("op key {} error: " + e.getMessage(), key, e);
} finally {
    
    
    //注意这里不是关闭连接,在 JedisPool 模式下,Jedis 会被归还给资源池。
    if (jedis != null) 
        jedis.close();
}

Connection pool parameter meaning:

serial number parameter name meaning Defaults Recommendations
1 maxTotal The maximum number of connections in the resource pool 8 See below for setup recommendations
2 maxIdle The resource pool allows the maximum number of idle connections 8 See below for setup recommendations
3 minIdle The resource pool ensures the minimum number of idle connections 0 See below for setup recommendations
4 blockWhenExhausted Whether the caller should wait when the resource pool is exhausted. Only when it is true, the following maxWaitMillis will take effect true It is recommended to use the default
5 maxWaitMillis When the resource pool connection is exhausted, the caller's maximum waiting time (in milliseconds) -1: means never timeout It is not recommended to use the default value
6 testOnBorrow Whether to perform connection validity detection (ping) when borrowing a connection from the resource pool, and invalid connections will be removed false It is recommended to set it to false when the business volume is heavy (one more ping overhead).
7 testOnReturn Whether to check the validity of the connection (ping) when returning the connection to the resource pool, and the invalid connection will be removed false It is recommended to set it to false when the business volume is heavy (one more ping overhead).
8 jmxEnabled Whether to enable jmx monitoring, which can be used for monitoring true It is recommended to enable it, but the application itself must also be enabled

optimization suggestion

maxTotal: the maximum number of connections, earlier called maxActive

How to set this value is relatively difficult to answer, there is no fixed calculation method, and there are many factors to consider:

  • The business wants the concurrency of redis
  • The time the client executes the command
  • Redis resources, the number of applications * maxTotal < the maximum number of redis connections maxclients
  • Resource overhead, for example, although you want to control idle connections (connections that the connection pool can use immediately at the moment), you don't want unnecessary overhead caused by frequent release of connection pools to create connections.

example:

Assumption: the average time of a command (borrow|return resource + Jedis execution command (including network)) is about 1ms, the QPS of a connection is about 1000, and the QPS expected by the business is 50000

Then the theoretically required resource pool size is 50000 / 1000 = 50. But in fact, this is a theoretical value, and some resources should be reserved more than the theoretical value. Generally speaking, maxTotal can be larger than the theoretical value.
But this value is not as big as possible. On the one hand, too many connections occupy client and server resources. On the other hand, for a server with high QPS such as Redis, blocking a large command will not help even if a large resource pool is set.

maxIdle and minIdel

maxIdle is actually the maximum number of connections required by the business, and maxTotal is to give the balance, so maxIdele should not be set too small, otherwise new connections will continue to occur and the connection overhead will be released.

The best performance of the connection pool is maxTotal = maxIdle, which avoids the performance interference caused by the expansion of the connection pool. However, when the concurrency is not large, or the maxTotal setting is too high, it will lead to unnecessary waste of connection resources. It is generally recommended that maxIdle follow the With the above calculation method setting, maxTotal can be doubled.

minIdle (minimum number of idle connections), not so much the minimum number of idle connections, as it is "at least the number of idle connections that need to be maintained". In the process of using the connection, if the number of connections exceeds minIdle, then continue to establish a connection. If maxIdle is set, the excess connection will be slowly removed from the connection pool and released after executing the business

Connection pool preheating

If there are many requests immediately after the system is started, you can warm up the connection pool, such as quickly creating some redis connections, executing simple commands, such as ping, and quickly increasing the number of connections in the connection pool to the number of minIdel

Sample code:

List<Jedis> minIdleJedisList = new ArrayList<Jedis>(jedisPoolConfig.getMinIdle());

for (int i = 0; i < jedisPoolConfig.getMinIdle(); i++) {
    
    
    Jedis jedis = null;
    try {
    
    
        jedis = pool.getResource();
        minIdleJedisList.add(jedis);
        jedis.ping();
    } catch (Exception e) {
    
    
        logger.error(e.getMessage(), e);
    } finally {
    
    
        //注意,这里不能马上 close 将连接还回连接池,否则最后连接池里只会建立 1 个连接。
        //jedis.close();
    }
}
//统一将预热的连接还回连接池
for (int i = 0; i < jedisPoolConfig.getMinIdle(); i++) {
    
    
    Jedis jedis = null;
    try {
    
    
        jedis = minIdleJedisList.get(i);
        //将连接归还回连接池,注意这里是规范连接,而不是关闭
        jedis.close();
    } catch (Exception e) {
    
    
        logger.error(e.getMessage(), e);
    } finally {
    
    
    }
}

System kernel parameter optimization

vm.swapniess

Swap is more important to the operating system. When the physical memory is insufficient, you can also swap part of the memory to the hard disk, which is urgent. For applications that require high concurrency and high throughput, disk IO will usually become the bottleneck of the system. Linux In , swap is not used until all physical memory is used up. The system parameter swapniess determines the tendency of the operating system to use swap. The value range of swapniess is 0-100. The larger the value, the higher the probability of the operating system using swapniess. Lower values ​​indicate that the operating system prefers to use physical memory.

If the kernel version is < 3.5, then the value of swapniess is 0, the system would rather swap than oom killer (kill the process)
If the kernel version >= 3.5, then the value of swapniess is 1, the system would rather swap than oom killer (kill drop process)

The OOM killer mechanism means that when the Linux operating system finds that the available memory is insufficient, it will forcibly kill some user processes (non-kernel processes) to ensure that the system has enough available memory for allocation.

Generally, it is necessary to ensure that redis will not be killed

Check the system version first, then set the value

cat /proc/version
echo 1 > /proc/sys/vm/swapniess
echo vm.swapniess=1 >> /etc/sysctl.conf

vm.overcommit_memory

The default value is 0

  • 0: Indicates that the kernel will check whether there is enough physical memory for the process to use (instead of checking whether it is exhausted). If there is enough memory, the memory application is allowed, otherwise the memory application fails and an error is returned to the application process
  • 1: Indicates that the kernel allows all physical memory to be allocated regardless of the current memory state

If it is 0, operations such as fork may fail, and insufficient memory space may be applied

Redis recommends setting this value to 1 in order to allow fork to run even with low memory

cat /proc/sys/vm/overcommit_memory
echo "vm.overcommit_memory=1" >> /etc/sysctl.conf
sysctl vm.overcommit_memory=1

Reasonably set the number of file handles

The operating system process tries to open a file (or handle), but the number of handles opened by the process has reached the upper limit, and if it continues to open, an error will be reported: "Too many open files"

ulimit -a  #查看系统文件句柄数,看 open files 那项
ulimit -n 65535  #设置系统文件句柄数

slow log query

Redis slow log command description:

config get slow* #查询有关慢日志的配置信息
config set slowlog-log-slower-than 20000  #设置慢日志使时间阈值,单位微秒,此处为 20 毫秒,即超过 20 毫秒的操作都会记录下来,生产环境建议设置 1000,也就是 1ms,这样理论上 redis 并发至少达到 1000,如果要求单机并发达到 1 万以上,这个值可以设置为 100
config set slowlog-max-len 1024  #设置慢日志记录保存数量,如果保存数量已满,会删除最早的记录,最新的记录追加进来。记录慢查询日志时 Redis 会对长命令做截断操作,并不会占用大量内存,建议设置稍大些,防止丢失日志
config rewrite #将服务器当前所使用的配置保存到 redis.conf
slowlog len #获取慢查询日志列表的当前长度
slowlog get 5 #获取最新的 5 条慢查询日志。慢查询日志由四个属性组成:标识 ID,发生时间戳,命令耗时,执行命令和参数
slowlog reset #重置慢查询日志

Guess you like

Origin blog.csdn.net/a141210104/article/details/129869521