Redis 性能优化的 13 条军规!史上最全

Redis 是基于单线程模型实现的,也就是 Redis 是使用一个线程来处理所有的客户端请求的,尽管 Redis 使用了非阻塞式 IO,并且对各种命令都做了优化(大部分命令操作时间复杂度都是 O(1)),但由于 Redis 是单线程执行的特点,因此它对性能的要求更加苛刻,本文我们将通过一些优化手段,让 Redis 更加高效的运行。

本文我们将使用以下手段,来提升 Redis 的运行速度:

  1. 缩短键值对的存储长度;
  2. 使用 lazy free(延迟删除)特性;
  3. 设置键值的过期时间;
  4. 禁用长耗时的查询命令;
  5. 使用 slowlog 优化耗时命令;
  6. 使用 Pipeline 批量操作数据;
  7. 避免大量数据同时失效;
  8. 客户端使用优化;
  9. 限制 Redis 内存大小;
  10. 使用物理机而非虚拟机安装 Redis 服务;
  11. 检查数据持久化策略;
  12. 禁用 THP 特性;
  13. 使用分布式架构来增加读写速度。

1.缩短键值对的存储长度

键值对的长度是和性能成反比的,比如我们来做一组写入数据的性能测试,执行结果如下:

img

从以上数据可以看出,在 key 不变的情况下,value 值越大操作效率越慢,因为 Redis 对于同一种数据类型会使用不同的内部编码进行存储,比如字符串的内部编码就有三种:int(整数编码)、raw(优化内存分配的字符串编码)、embstr(动态字符串编码),这是因为 Redis 的作者是想通过不同编码实现效率和空间的平衡,然而数据量越大使用的内部编码就越复杂,而越是复杂的内部编码存储的性能就越低。

这还只是写入时的速度,当键值对内容较大时,还会带来另外几个问题:

  • 内容越大需要的持久化时间就越长,需要挂起的时间越长,Redis 的性能就会越低;
  • 内容越大在网络上传输的内容就越多,需要的时间就越长,整体的运行速度就越低;
  • 内容越大占用的内存就越多,就会更频繁的触发内存淘汰机制,从而给 Redis 带来了更多的运行负担。

因此在保证完整语义的同时,我们要尽量的缩短键值对的存储长度,必要时要对数据进行序列化和压缩再存储,以 Java 为例,序列化我们可以使用 protostuff 或 kryo,压缩我们可以使用 snappy。

2.使用 lazy free 特性

lazy free 特性是 Redis 4.0 新增的一个非常使用的功能,它可以理解为惰性删除或延迟删除。意思是在删除的时候提供异步延时释放键值的功能,把键值释放操作放在 BIO(Background I/O) 单独的子线程处理中,以减少删除删除对 Redis 主线程的阻塞,可以有效地避免删除 big key 时带来的性能和可用性问题。

lazy free 对应了 4 种场景,默认都是关闭的:

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 机制删除。

建议开启其中的 lazyfree-lazy-eviction、lazyfree-lazy-expire、lazyfree-lazy-server-del 等配置,这样就可以有效的提高主线程的执行效率。

3.设置键值的过期时间

我们应该根据实际的业务情况,对键值设置合理的过期时间,这样 Redis 会帮你自动清除过期的键值对,以节约对内存的占用,以避免键值过多的堆积,频繁的触发内存淘汰策略。

4.禁用长耗时的查询命令

Redis 绝大多数读写命令的时间复杂度都在 O(1) 到 O(N) 之间,在官方文档对每命令都有时间复杂度说明,地址:redis.io/commands,如下…

image.png

其中 O(1) 表示可以安全使用的,而 O(N) 就应该当心了,N 表示不确定,数据越大查询的速度可能会越慢。因为 Redis 只用一个线程来做数据查询,如果这些指令耗时很长,就会阻塞 Redis,造成大量延时。

要避免 O(N) 命令对 Redis 造成的影响,可以从以下几个方面入手改造:

  • 决定禁止使用 keys 命令;
  • 避免一次查询所有的成员,要使用 scan 命令进行分批的,游标式的遍历;
  • 通过机制严格控制 Hash、Set、Sorted Set 等结构的数据大小;
  • 将排序、并集、交集等操作放在客户端执行,以减少 Redis 服务器运行压力;
  • 删除 (del) 一个大数据的时候,可能会需要很长时间,所以建议用异步删除的方式 unlink,它会启动一个新的线程来删除目标数据,而不阻塞 Redis 的主线程。

5.使用 slowlog 优化耗时命令

我们可以使用 slowlog 功能找出最耗时的 Redis 命令进行相关的优化,以提升 Redis 的运行速度,慢查询有两个重要的配置项:

  • slowlog-log-slower-than :用于设置慢查询的评定时间,也就是说超过此配置项的命令,将会被当成慢操作记录在慢查询日志中,它执行单位是微秒 (1 秒等于 1000000 微秒);
  • slowlog-max-len :用来配置慢查询日志的最大记录数。

我们可以根据实际的业务情况进行相应的配置,其中慢日志是按照插入的顺序倒序存入慢查询日志中,我们可以使用 slowlog get n 来获取相关的慢查询日志,再找到这些慢查询对应的业务进行相关的优化。

6.使用 Pipeline 批量操作数据

Pipeline (管道技术) 是客户端提供的一种批处理技术,用于一次处理多个 Redis 命令,从而提高整个交互的性能。

我们使用 Java 代码来测试一下 Pipeline 和普通操作的性能对比,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毫秒

从以上的结果可以看出,管道的执行时间是 297 毫秒,而普通命令执行时间是 17276 毫秒,管道技术要比普通的执行大约快了 58 倍。

7.避免大量数据同时失效

Redis 过期键值删除使用的是贪心策略,它每秒会进行 10 次过期扫描,此配置可在 redis.conf 进行配置,默认值是 hz 10,Redis 会随机抽取 20 个值,删除这 20 个键中过期的键,如果过期 key 的比例超过 25% ,重复执行此流程,如下图所示:

img

如果在大型系统中有大量缓存在同一时间同时过期,那么会导致 Redis 循环多次持续扫描删除过期字典,直到过期字典中过期键值被删除的比较稀疏为止,而在整个执行过程会导致 Redis 的读写出现明显的卡顿,卡顿的另一种原因是内存管理器需要频繁回收内存页,因此也会消耗一定的 CPU。

为了避免这种卡顿现象的产生,我们需要预防大量的缓存在同一时刻一起过期,就简单的解决方案就是在过期时间的基础上添加一个指定范围的随机数。

8.客户端使用优化

在客户端的使用上我们除了要尽量使用 Pipeline 的技术外,还需要注意要尽量使用 Redis 连接池,而不是频繁创建销毁 Redis 连接,这样就可以减少网络传输次数和减少了非必要调用指令。

9.限制 Redis 内存大小

在 64 位操作系统中 Redis 的内存大小是没有限制的,也就是配置项 maxmemory 是被注释掉的,这样就会导致在物理内存不足时,使用 swap 空间既交换空间,而当操心系统将 Redis 所用的内存分页移至 swap 空间时,将会阻塞 Redis 进程,导致 Redis 出现延迟,从而影响 Redis 的整体性能。因此我们需要限制 Redis 的内存大小为一个固定的值,当 Redis 的运行到达此值时会触发内存淘汰策略,内存淘汰策略在 Redis 4.0 之后有 8 种

  1. noeviction:不淘汰任何数据,当内存不足时,新增操作会报错,Redis 默认内存淘汰策略;
  2. allkeys-lru:淘汰整个键值中最久未使用的键值;
  3. allkeys-random:随机淘汰任意键值;
  4. volatile-lru:淘汰所有设置了过期时间的键值中最久未使用的键值;
  5. volatile-random:随机淘汰设置了过期时间的任意键值;
  6. volatile-ttl:优先淘汰更早过期的键值。

在 Redis 4.0 版本中又新增了 2 种淘汰策略:

  1. volatile-lfu:淘汰所有设置了过期时间的键值中,最少使用的键值;
  2. allkeys-lfu:淘汰整个键值中最少使用的键值。

其中 allkeys-xxx 表示从所有的键值中淘汰数据,而 volatile-xxx 表示从设置了过期键的键值中淘汰数据。

我们可以根据实际的业务情况进行设置,默认的淘汰策略不淘汰任何数据,在新增时会报错。

10.使用物理机而非虚拟机

在虚拟机中运行 Redis 服务器,因为和物理机共享一个物理网口,并且一台物理机可能有多个虚拟机在运行,因此在内存占用上和网络延迟方面都会有很糟糕的表现,我们可以通过 ./redis-cli --intrinsic-latency 100 命令查看延迟时间,如果对 Redis 的性能有较高要求的话,应尽可能在物理机上直接部署 Redis 服务器。

11.检查数据持久化策略

Redis 的持久化策略是将内存数据复制到硬盘上,这样才可以进行容灾恢复或者数据迁移,但维护此持久化的功能,需要很大的性能开销。

在 Redis 4.0 之后,Redis 有 3 种持久化的方式:

  • RDB(Redis DataBase,快照方式)将某一个时刻的内存数据,以二进制的方式写入磁盘;
  • AOF(Append Only File,文件追加方式),记录所有的操作命令,并以文本的形式追加到文件中;
  • 混合持久化方式,Redis 4.0 之后新增的方式,混合持久化是结合了 RDB 和 AOF 的优点,在写入的时候,先把当前的数据以 RDB 的形式写入文件的开头,再将后续的操作命令以 AOF 的格式存入文件,这样既能保证 Redis 重启时的速度,又能减低数据丢失的风险。

RDB 和 AOF 持久化各有利弊,RDB 可能会导致一定时间内的数据丢失,而 AOF 由于文件较大则会影响 Redis 的启动速度,为了能同时拥有 RDB 和 AOF 的优点,Redis 4.0 之后新增了混合持久化的方式,因此我们在必须要进行持久化操作时,应该选择混合持久化的方式。

查询是否开启混合持久化可以使用 config get aof-use-rdb-preamble 命令,执行结果如下图所示:

image.png

其中 yes 表示已经开启混合持久化,no 表示关闭,Redis 5.0 默认值为 yes。 如果是其他版本的 Redis 首先需要检查一下,是否已经开启了混合持久化,如果关闭的情况下,可以通过以下两种方式开启:

  • 通过命令行开启
  • 通过修改 Redis 配置文件开启

① 通过命令行开启

使用命令 config set aof-use-rdb-preamble yes 执行结果如下图所示:

image.png

命令行设置配置的缺点是重启 Redis 服务之后,设置的配置就会失效。

② 通过修改 Redis 配置文件开启

在 Redis 的根路径下找到 redis.conf 文件,把配置文件中的 aof-use-rdb-preamble no 改为 aof-use-rdb-preamble yes 如下图所示:

image.png

配置完成之后,需要重启 Redis 服务器,配置才能生效,但修改配置文件的方式,在每次重启 Redis 服务之后,配置信息不会丢失。

需要注意的是,在非必须进行持久化的业务中,可以关闭持久化,这样可以有效的提升 Redis 的运行速度,不会出现间歇性卡顿的困扰。

12.禁用 THP 特性

Linux kernel 在 2.6.38 内核增加了 Transparent Huge Pages (THP) 特性 ,支持大内存页 2MB 分配,默认开启。

当开启了 THP 时,fork 的速度会变慢,fork 之后每个内存页从原来 4KB 变为 2MB,会大幅增加重写期间父进程内存消耗。同时每次写命令引起的复制内存页单位放大了 512 倍,会拖慢写操作的执行时间,导致大量写操作慢查询。例如简单的 incr 命令也会出现在慢查询中,因此 Redis 建议将此特性进行禁用,禁用方法如下:

echo never > /sys/kernel/mm/transparent_hugepage/enabled

为了使机器重启后 THP 配置依然生效,可以在 /etc/rc.local 中追加 echo never > /sys/kernel/mm/transparent_hugepage/enabled

13.使用分布式架构来增加读写速度

Redis 分布式架构有三个重要的手段:

  • 主从同步
  • 哨兵模式
  • Redis Cluster 集群

使用主从同步功能我们可以把写入放到主库上执行,把读功能转移到从服务上,因此就可以在单位时间内处理更多的请求,从而提升的 Redis 整体的运行速度。

而哨兵模式是对于主从功能的升级,但当主节点奔溃之后,无需人工干预就能自动恢复 Redis 的正常使用。

Redis Cluster 是 Redis 3.0 正式推出的,Redis 集群是通过将数据库分散存储到多个节点上来平衡各个节点的负载压力。

Redis Cluster 采用虚拟哈希槽分区,所有的键根据哈希函数映射到 0 ~ 16383 整数槽内,计算公式:slot = CRC16(key) & 16383,每一个节点负责维护一部分槽以及槽所映射的键值数据。这样 Redis 就可以把读写压力从一台服务器,分散给多台服务器了,因此性能会有很大的提升。

在这三个功能中,我们只需要使用一个就行了,毫无疑问 Redis Cluster 应该是首选的实现方案,它可以把读写压力自动的分担给更多的服务器,并且拥有自动容灾的能力。

关注下面二维码,订阅更多精彩内容。

Java中文社群公众号二维码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/546310.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux Debian11 Podman容器常用命令

Podman 是一个开源的容器运行时项目&#xff0c;可在大多数 Linux 平台上使用。Podman 提供与 Docker 非常相似的功能。它不需要在你的系统上运行任何守护进程&#xff0c;并且它也可以在没有 root 权限的情况下运行。 Podman 可以管理和运行任何符合 OCI&#xff08;Open Con…

惊呆了,竟然可以用这种方式秒建Redis集群?

前面我们讲了《Redis 性能优化的 13 条军规&#xff01;》&#xff0c;其中最重要的一条就是使用 Redis 的集群功能&#xff0c;那么本文我们就来看看&#xff0c;如何用 1s 钟的时间来创建一个 Redis 集群。 Redis Cluster 是 Redis 3.0 版本推出的 Redis 集群方案&#xff0…

华为交换机S5735S-L24T4S-QA2无法telnet远程访问

以前都是按照https://datutu.blog.csdn.net/article/details/106810113方法配置不同网段通过静态路由实现互通,华为S5700交换机开启telnet远程指定IP登陆配置(强烈推荐),现在新买的华为数通智选交换机S5735S-L24T4S-QA2 也是按照这步骤配置,令人不解的是,竟然无法telnet访…

99%的程序员都在用Lombok,原理竟然这么简单?我也手撸了一个!|建议收藏!!!...

世界上只有一种英雄主义&#xff0c;就是看清生活的真相之后依然热爱生活。对于 Lombok 我相信大部分人都不陌生&#xff0c;但对于它的实现原理以及缺点却鲜为人知&#xff0c;而本文将会从 Lombok 的原理出发&#xff0c;手撸一个简易版的 Lombok&#xff0c;让你理解这个热门…

PyQt5中多线程模块QThread解决界面卡顿无响应问题,线程池ThreadPoolExecutor解决多任务耗时操作问题

使用多线程模块QThread可以解决PyQt5界面程序执行比较耗时操作时,程序卡顿出现的无响应以及界面输出无法实时显示的问题。 下面例子演示: 单击“运行”按钮,启动主线程,在子线程中采用线程池ThreadPoolExecuto解决ping多个IP耗时问题。在主界面把已ping过的IP添加到显示框…

干货!操作系统基础知识汇总!转给要面试的同学吧

作者&#xff1a;Guide哥来源&#xff1a;公众号 JavaGuide很多读者抱怨计算操作系统的知识点比较繁杂&#xff0c;自己也没有多少耐心去看&#xff0c;但是面试的时候又经常会遇到。所以&#xff0c;我带着我整理好的操作系统的常见问题来啦&#xff01;这篇文章总结了一些我觉…

eclipse的tomcat运行mave web项目

2019独角兽企业重金招聘Python工程师标准>>> 在eclipse中配置maven 安装成功后&#xff0c;就会多出Maven选项&#xff0c;在Installations中添加下载的Maven 在User Settings中配置本机的默认仓库 使用Eclipse的maven构建一个web项目 1、选择建立Maven Project 选…

Python在shell终端中显示进度条

Python在shell终端中显示进度条简单代码&#xff1a; from time import sleep from tqdm import tqdmmax 100 for i in tqdm(range(max)):sleep(0.1)效果如下&#xff1a;

没用过这些IDEA插件?怪不得写代码头疼

小伙伴们&#xff0c;大家好。今天准备和大家一起分享一下实际工作中常用的几款能提升幸福感和工作效率的IDEA插件吧&#xff0c;也欢迎小伙伴们在评论区安利出你们用过的觉得非常不错的插件&#xff0c;大家一起交流进步。1、Background Image Plus默认IDEA的背景一般都比较单…

颠覆与重构——戴尔助力徐工集团等行业客户实现业务转型

无论在IT领域&#xff0c;还是传统行业&#xff0c;颠覆与重构都是不可回避的话题。利用ICT领域的技术创新与互联网思维&#xff0c;传统企业可以更好地实现业务转型与创新。阿里集团与上汽共研互联网汽车&#xff0c;东软致力于推动远程医疗新模式&#xff0c;Mock颠覆传统学习…

Linux多命令顺序执行与管道符

1.多命令顺序执行 实例测试&#xff1a; 2.管道符 实例测试&#xff1a; 3.grep命令 实例测试&#xff1a; 获取更多技术干货&#xff0c;请访问大土土随笔 如果本文对您有所帮助&#xff0c;请关注微信公众号“捷创源科技”。

99%的程序员都在用Lombok,原理竟然这么简单?我也手撸了一个!|建议收藏

世界上只有一种英雄主义&#xff0c;就是看清生活的真相之后依然热爱生活。 对于 Lombok 我相信大部分人都不陌生&#xff0c;但对于它的实现原理以及缺点却鲜为人知&#xff0c;而本文将会从 Lombok 的原理出发&#xff0c;手撸一个简易版的 Lombok&#xff0c;让你理解这个热…

震惊!这样终止线程,竟然会导致服务宕机?

在开始之前&#xff0c;我们先来看以下代码会有什么问题&#xff1f; public class ThreadStopExample {public static void main(String[] args) throws InterruptedException {Thread t1 new Thread(() -> {try {System.out.println("子线程开始执行");// 模拟…

华为交换机系统软件升级和安全漏洞修复教程

华为交换机官网 可以查询到华为交换机官方电话:400-822-9999,通过下面2个命令查询出需要升级的交换机软件版本和补丁版本号发给华为,获取新的升级系统软件和补丁以及升级教程。下面是我升级华为交换机总结的教程,作为参考 <HUAWEI> display version<HUAWEI&g…

想读Spring源码?先从这篇「 极简教程」开始

这是我的第 47 篇原创文章。为什么要阅读源码&#xff1f;这是一个有趣的问题&#xff0c;类似的问题还有&#xff0c;为什么要看书&#xff1f;为什么要爬山&#xff1f;这也是一个哲学问题&#xff0c;我想每个人都有不同的答案&#xff0c;下面我是对阅读源码好处的一些思考…

Linux Shell接收键盘输入

1.read命令格式 read [选项] [变量名] 选项&#xff1a; -p “提示信息”&#xff1a;在等待read输入时&#xff0c;输出提示信息 -t “秒数”&#xff1a; read命令会一致等待用户输入&#xff0c;使用此选项可以指定等待时间 -n “字符数”&#xff1a; read命令只接受指…

想快速拥有个人网站?来试试这个...

一、简介Hugo 是Go语言实现的一款静态网站生成器。它简单、易用、高效、易扩展、快速部署。相比较其他静态网站生成器&#xff0c;它的优点有这几点&#xff1a;项目构建特别快主题目录与站点目录结构一样配置文件为*.toml 格式&#xff0c;语法常简单易懂&#xff0c;没有缩进…

Java对象都是在堆上分配空间吗?答案竟然是...

作者 l Hollis来源 l Hollis&#xff08;ID&#xff1a;hollischuang&#xff09;Java作为一种面向对象的&#xff0c;跨平台语言&#xff0c;其对象、内存等一直是比较难的知识点&#xff0c;所以&#xff0c;即使是一个Java的初学者&#xff0c;也一定或多或少的对JVM有一些了…

服务器运行容器工具大盘点!

服务器到底是什么&#xff1f;服务器的硬件好理解&#xff0c;其实就是一台性能、稳定性、扩展性等等比我们普通个人PC强的一台机器而已&#xff0c;它也需要搭载操作系统&#xff0c;比如有专门的Windows Server或者各种Linux发行版系统。只不过咱这里很多小伙伴可能还是处于学…

想读Spring源码?先从这篇「 极简教程」开始吧...

为什么要阅读源码&#xff1f;这是一个有趣的问题&#xff0c;类似的问题还有&#xff0c;为什么要看书&#xff1f;为什么要爬山&#xff1f; 这也是一个哲学问题&#xff0c;我想每个人都有不同的答案&#xff0c;下面我是对阅读源码好处的一些思考。 &#xff08;PS&#x…