大厂Redis 性能优化的 13 条军规!收好了
Redis 是基于单线程模型实现的,也就是 Redis 是使用一个线程来处理所有的客户端请求的,尽管 Redis 使用了非阻塞式 IO,并且对各种命令都做了优化(大部分命令操作时间复杂度都是 O(1)),但由于 Redis 是单线程执行的特点,因此它对性能的要求更加苛刻,本文我们将通过一些优化手段,让 Redis 更加高效的运行。
-
缩短键值对的存储长度; -
使用 lazy free(延迟删除)特性; -
设置键值的过期时间; -
禁用长耗时的查询命令; -
使用 slowlog 优化耗时命令; -
使用 Pipeline 批量操作数据; -
避免大量数据同时失效; -
客户端使用优化; -
限制 Redis 内存大小; -
使用物理机而非虚拟机安装 Redis 服务; -
检查数据持久化策略; -
禁用 THP 特性; -
使用分布式架构来增加读写速度。
1.缩短键值对的存储长度
-
内容越大需要的持久化时间就越长,需要挂起的时间越长,Redis 的性能就会越低; -
内容越大在网络上传输的内容就越多,需要的时间就越长,整体的运行速度就越低; -
内容越大占用的内存就越多,就会更频繁的触发内存淘汰机制,从而给 Redis 带来了更多的运行负担。
2.使用 lazy free 特性
lazyfree-lazy-eviction no
lazyfree-lazy-expire no
lazyfree-lazy-server-del no
slave-lazy-flush no
-
lazyfree-lazy-eviction:表示当 Redis 运行内存超过 maxmeory 时,是否开启 lazy free 机制删除; -
lazyfree-lazy-expire:表示设置了过期时间的键值,当过期之后是否开启 lazy free 机制删除; -
lazyfree-lazy-server-del:有些指令在处理已存在的键时,会带有一个隐式的 del 键的操作,比如 rename 命令,当目标键已存在,Redis 会先删除目标键,如果这些目标键是一个 big key,就会造成阻塞删除的问题,此配置表示在这种场景中是否开启 lazy free 机制删除; -
slave-lazy-flush:针对 slave(从节点) 进行全量数据同步,slave 在加载 master 的 RDB 文件前,会运行 flushall 来清理自己的数据,它表示此时是否开启 lazy free 机制删除。
3.设置键值的过期时间
4.禁用长耗时的查询命令
-
决定禁止使用 keys 命令; -
避免一次查询所有的成员,要使用 scan 命令进行分批的,游标式的遍历; -
通过机制严格控制 Hash、Set、Sorted Set 等结构的数据大小; -
将排序、并集、交集等操作放在客户端执行,以减少 Redis 服务器运行压力; -
删除 (del) 一个大数据的时候,可能会需要很长时间,所以建议用异步删除的方式 unlink,它会启动一个新的线程来删除目标数据,而不阻塞 Redis 的主线程。
5.使用 slowlog 优化耗时命令
-
slowlog-log-slower-than
:用于设置慢查询的评定时间,也就是说超过此配置项的命令,将会被当成慢操作记录在慢查询日志中,它执行单位是微秒 (1 秒等于 1000000 微秒); -
slowlog-max-len
:用来配置慢查询日志的最大记录数。
slowlog get n
来获取相关的慢查询日志,再找到这些慢查询对应的业务进行相关的优化。
6.使用 Pipeline 批量操作数据
public class PipelineExample {
public static void main(String[] args) {
Jedis jedis = new Jedis( "127.0.0.1", 6379);
// 记录执行开始时间
long beginTime = System.currentTimeMillis();
// 获取 Pipeline 对象
Pipeline pipe = jedis.pipelined();
// 设置多个 Redis 命令
for ( int i = 0; i < 100; i++) {
pipe.set( "key" + i, "val" + i);
pipe.del( "key"+i);
}
// 执行命令
pipe.sync();
// 记录执行结束时间
long endTime = System.currentTimeMillis();
System.out.println( "执行耗时:" + (endTime - beginTime) + "毫秒");
}
}
执行耗时:297毫秒
public class PipelineExample {
public static void main(String[] args) {
Jedis jedis = new Jedis( "127.0.0.1", 6379);
// 记录执行开始时间
long beginTime = System.currentTimeMillis();
for ( int i = 0; i < 100; i++) {
jedis.set( "key" + i, "val" + i);
jedis.del( "key"+i);
}
// 记录执行结束时间
long endTime = System.currentTimeMillis();
System.out.println( "执行耗时:" + (endTime - beginTime) + "毫秒");
}
}
执行耗时:17276毫秒
7.避免大量数据同时失效
hz 10
,Redis 会随机抽取 20 个值,删除这 20 个键中过期的键,如果过期 key 的比例超过 25% ,重复执行此流程,如下图所示:
8.客户端使用优化
9.限制 Redis 内存大小
maxmemory <bytes>
是被注释掉的,这样就会导致在物理内存不足时,使用 swap 空间既交换空间,而当操心系统将 Redis 所用的内存分页移至 swap 空间时,将会阻塞 Redis 进程,导致 Redis 出现延迟,从而影响 Redis 的整体性能。因此我们需要限制 Redis 的内存大小为一个固定的值,当 Redis 的运行到达此值时会触发内存淘汰策略,
内存淘汰策略在 Redis 4.0 之后有 8 种:
-
noeviction:不淘汰任何数据,当内存不足时,新增操作会报错,Redis 默认内存淘汰策略; -
allkeys-lru:淘汰整个键值中最久未使用的键值; -
allkeys-random:随机淘汰任意键值; -
volatile-lru:淘汰所有设置了过期时间的键值中最久未使用的键值; -
volatile-random:随机淘汰设置了过期时间的任意键值; -
volatile-ttl:优先淘汰更早过期的键值。
-
volatile-lfu:淘汰所有设置了过期时间的键值中,最少使用的键值; -
allkeys-lfu:淘汰整个键值中最少使用的键值。
10.使用物理机而非虚拟机
./redis-cli --intrinsic-latency 100
命令查看延迟时间,如果对 Redis 的性能有较高要求的话,应尽可能在物理机上直接部署 Redis 服务器。
11.检查数据持久化策略
-
RDB(Redis DataBase,快照方式)将某一个时刻的内存数据,以二进制的方式写入磁盘; -
AOF(Append Only File,文件追加方式),记录所有的操作命令,并以文本的形式追加到文件中; -
混合持久化方式,Redis 4.0 之后新增的方式,混合持久化是结合了 RDB 和 AOF 的优点,在写入的时候,先把当前的数据以 RDB 的形式写入文件的开头,再将后续的操作命令以 AOF 的格式存入文件,这样既能保证 Redis 重启时的速度,又能减低数据丢失的风险。
config get aof-use-rdb-preamble
命令,执行结果如下图所示:
-
通过命令行开启 -
通过修改 Redis 配置文件开启
① 通过命令行开启
config set aof-use-rdb-preamble yes
执行结果如下图所示:
② 通过修改 Redis 配置文件开启
aof-use-rdb-preamble no
改为
aof-use-rdb-preamble yes
如下图所示:
12.禁用 THP 特性
echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/enabled
。
13.使用分布式架构来增加读写速度
-
主从同步 -
哨兵模式 -
Redis Cluster 集群
之前,给大家发过四份Java面试宝典,这次新增了更全面的资料,相信在跳槽前准备准备,基本没大问题。
《java基础:设计模式等》(初中级)
《JVM:整理BAT最新题库》《并发编程》(中高级)
《分布式微服务架构》《架构|软技能》(资深)
《一线互联网公司面试指南》(资深)
分别适用于初中级,中高级,资深级工程师的面试复习。
内容包含java基础、JVM、并发编程、分布式微服务、架构|软技能、算法等等。
学习视频包含 深入运行时数据区、垃圾回收、详解类装载过程及类加载机制、手写Spring-IOC容器、redis入门到高性能缓存组件等等