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.内存配置:
定期重启可以清除数据内存,而进程问题影响也不是很大,所以我们主要是缓冲区问题。
四:集群与主从: