Redis优化
一:Key:
1.1.Key的规范:
测试如下:
1.2.拒绝BigKey:
我们可以用:
MEMORY USAGE name
命令来看它的大小。
注意,这里的第二种之所以不使用Keys *,因为在实际生产时,会阻塞线程,而scan就不会。使用举例:
import com.heima.jedis.util.JedisConnectionFactory;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.ScanResult;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class JedisTest {
private Jedis jedis;
@BeforeEach
void setUp() {
jedis = JedisConnectionFactory.getJedis();
jedis.auth("123321");
jedis.select(0);
}
final static int STR_MAX_LEN = 10 * 1024;
final static int HASH_MAX_LEN = 500;
@Test
void testScan() {
int maxLen = 0;
long len = 0;
String cursor = "0";
do {
ScanResult<String> result = jedis.scan(cursor);
cursor = result.getCursor();
List<String> list = result.getResult();
if (list == null || list.isEmpty()) {
break;
}
for (String key : list) {
String type = jedis.type(key);
switch (type) {
case "string":
len = jedis.strlen(key);
maxLen = STR_MAX_LEN;
break;
case "hash":
len = jedis.hlen(key);
maxLen = HASH_MAX_LEN;
break;
case "list":
len = jedis.llen(key);
maxLen = HASH_MAX_LEN;
break;
case "set":
len = jedis.scard(key);
maxLen = HASH_MAX_LEN;
break;
case "zset":
len = jedis.zcard(key);
maxLen = HASH_MAX_LEN;
break;
default:
break;
}
if (len >= maxLen) {
System.out.printf("Found big key : %s, type: %s, length or size: %d %n", key, type, len);
}
}
} while (!cursor.equals("0"));
}
@AfterEach
void tearDown() {
if (jedis != null) {
jedis.close();
}
}
}
建议放在从节点进行。
1.3.合适的结构:
用Hash存储:
用string:
用多级Hash:
二:批处理优化:
2.1.MSET与Pipeline:
导入大量数据,一次导入与多次导入谁时间更优秀呢:
先来看看多次导入:
通过测试,导入时间主要是花在了网络传输上面了。因此,这明显不如第二种:
下面我们来看看基于mset的批处理:
但是,有很多的命令具有局限性,其他的数据结构未必有批处理,就算是集合,它的sadd也只是针对同一个key而已。所以我们使用Pipeline:
当然,在spring-redis中,也有批处理,举例如下:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.api.sync.RedisCommands;
@Service
public class RedisPipelinedService {
@Autowired
private RedisTemplate<String, String> redisTemplate;
public void executePipelinedCommands() {
redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
RedisCommands<String, String> commands = connection.sync();
commands.set("key1", "value1");
commands.set("key2", "value2");
return null;
});
}
}
小细节:mset快于Pipeline。
2.2.集群下的批处理:
串行:
并行:
我们还可以使用Spring的方法来实现并行:
三:服务端优化:
3.1.持久化配置:
关于第五点:为什么要有这样一个配置:
这是AOF的刷盘机制,我们可以看到,当刷盘时间大于2s,会导致主节点阻塞,而如果我们在进行刷盘时,磁盘也在进行大量的IO,比如AOF的重写,或者RDB的fork,就会很容易导致主线程等待时间过长。当然,如果设置为yes,可能会丢失一段数据。
3.2.慢查询:
当我们执行了一个keys *后,比如我们用慢查询日志来查看一下:
当然,在redis客户端,可以直接看到慢查询日志:
这是一些其他的命令;
3.3.命令与安全配置:
举例:
设置为“”表示不允许使用,而第一种表示要用它来替代改命令,其他人就不可能破解。
3.4.内存配置:
定期重启可以清除数据内存,而进程问题影响也不是很大,所以我们主要是缓冲区问题。
四:集群与主从: