草庐IT

Redis变慢?深入浅出Redis性能诊断系列文章(四)

数据库架构师 2023-03-28 原文
(本文首发于“数据库架构师”公号,订阅“数据库架构师”公号,一起学习数据库技术,助力职业发展)
 
本篇为Redis性能问题诊断系列的第四篇,也是最后一篇,主要从应用程序、系统、服务器硬件及网络系统等层面上进行讲解,重点分享了哪些配置需要重点关注和调整优化,才能最大程度的发挥Redis的处理能力;
 
一、服务器预留足够内存,监控SWAP使用
Swap是操作系统层面行为,指当服务器内存不足时,会将原本在内存中的一部分数据拿出放入磁盘,如果再次访问这部分数据就会响应很慢,因为磁盘的访问速度是远远不如内存的。
Redis作为内存数据库,有个常识一定要记住:所有的数据默认都是在内存中,不存在一部分在内存一部分在磁盘中的情况,除非被迫发生了SWAP。
说明:Redis在2.6版本之前有个VM【虚拟内存】特性,可以支持数据存放在内存和磁盘中,不过带来的性能波动影响太大,就被废弃了。但现在网上还有不少人在传抄相关VM特性的文章,太有误导性!
官方VM废弃说明:https://redis.io/docs/reference/internals/internals-vm/
 
可以通过以下方式来查看 Redis 进程是否使用到了 Swap:
1.获取redis对应的进程id
shell> redis-cli info | grep process_id
2.查看 Swap 使用情况
shell>cat /proc/$pid/smaps | egrep '^(Swap|Size)'
输出结果如下:
Size: 1492 kB
Swap: 0 kB
Size: 32 kB
Swap: 0 kB
Size: 2196 kB
Swap: 0 kB
Size: 2048 kB
Swap: 0 kB
Size: 4 kB
Swap: 0 kB
Size: 1576 kB
上图中size代表Redis进程占用的一块内存空间大小,并对应一个Swap。
Swap后的数字表示该内存空间有多少已经被换到磁盘上了,如果两者相等,则代表这块内存空间的数据全都被换到了上了。
针对使用swap的解决方案可以参考如下:
a.提高Redis所在服务器的内存并预留可用内存,建议剩余可用内存至少保留20%以上;
b.Redis单独部署或降低单机部署实例数量,不和其他应用程序混合部署,避免多服务争用内存导致Redis数据被swap到磁盘上。
平时对Redis所在服务器的剩余可用内存及Swap 使用情况进行监控,在内存不足或使用到 Swap 时报警出来,及时干预处理。
 
二、.使用万兆网卡,避免网络带宽打满
Redis 的高性能,除了数据都在内存之外,就在于网络 IO 了,如果网络存在瓶颈,那么也会严重影响 Redis 的性能。
网络带宽过载的情况下,比如带宽被打满,那么服务器在 TCP 层和网络层就会出现数据包发送延迟、丢包等情况。
如果确实出现这种情况,我们需要及时核对原因,主要有以下几个:
a.某个Redis服务访问量过大,可能QPS高叠加操作的Key过大,导致网络满载;
b.所在服务器网卡上限过小,如千兆网卡或者虚拟机限速200MB等;
c.服务器网卡/网线/驱动等问题,导致万兆的网卡降频为千兆或者被限流。
 
查看网卡速度:ethtool eth0
 
针对网络过载可以采用以下方案来解决:
a.降低单机部署Redis实例个数,打散重度使用网络带宽的Redis服务到多台服务器;
b.对Redis服务使用的网络带宽进行监控,可以关注性能指标:instantaneous_input_kbps、instantaneous_output_kbps
c.使用万兆网卡的服务器,并添加对带宽上限【警惕网卡从万兆降为千兆】、网络带宽使用、丢包情况的监控;
d.遵守Redis使用规范,比如控制写入Redis中的VALUE大小、限制使用smembers或hgetall等操作的集合成员个数等。
 
三、根据场景选择是否使用SSD磁盘
大家要根据自己的实际场景判断,比如使用单节点且用于缓存服务的情形,就不需要使用SSD磁盘。
但是如果希望使用Redis的持久化能力来保证数据安全,那么磁盘IO能力就不得不重视了。
这里对于Redis的持久化不做详细介绍,具体可以参考上篇文章。
Redis中对IO比较敏感的操作主要有下面几类:
a.AOF持久化,相关磁盘操作有: AOF命令落盘、AOF文件重写;
b.RDB持久化,相关磁盘操作有:主从复制主节点RDB生成快照、从节点加载RDB文件、备份触发RDB快照、配置触发自动RDB快照
上面列出的都会严重依赖磁盘IO能力,特别是单机部署多Redis实例的情况,如果磁盘IO能力一般,那么就会严重影响Redis的性能。
 
四、系统参数配置
1.内存分配策略参数vm.overcommit_memory
Redis启动给出Warning提示:
WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add ‘vm.overcommit_memory = 1’ to /etc/sysctl.conf and then reboot or run the command ‘sysctl vm.overcommit_memory=1’ for this to take effect.
这里首先解答下什么是Overcommit?
Linux对大部分申请内存的请求都回复"yes",以便能跑更多更大占用内存的程序。因为申请内存后,并不会马上使用内存。这种技术叫做Overcommit。
当linux发现内存不足时,会发生OOM killer(OOM=out-of-memory)。它会选择杀死一些进程(用户态进程,不是内核线程),以便释放内存。
overcommit_memory的几个主要值的说明:
0:表⽰内核将检查是否有⾜够的可⽤内存供应⽤进程使⽤;如果有⾜够的可⽤内存,内存申请允许;否则,内存申请失败,并把错误返回给应⽤进程;
1: 表⽰内核允许分配所有的物理内存,⽽不管当前的内存状态如何;
2: 表⽰内核允许分配超过所有物理内存和交换空间总和的内存。
这里建议调整为1,相关调整方式:
永久生效:
编辑vim /etc/sysctl.conf ,改vm.overcommit_memory=1,然后sysctl -p 使配置文件生效
临时生效:
echo 1 > /proc/sys/vm/overcommit_memory  
 
上述日志中的Background save代表的是bgsave和bgrewriteaof, 如果当前可用内存不足, 操作系统应该如何处理fork操作呢?
如果vm.overcommit_memory=0, 代表如果没有可用内存, 就申请内存失败, 对应到Redis就是执行fork失败, 在Redis的日志会出现:
Cannot allocate memory
Redis建议把这个值设置为1, 是为了让fork操作能够在低内存下也执行成功。
 
2.操作系统内存大页参数配置
Redis启动给出Warning提示:
WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled.
意思是:你的Redis所在服务器当前使用的是内存大页机制,可能导致Redis访问延迟和内存使用问题。
那什么是内存大页呢?
应用程序向操作系统申请内存空间时,是按内存页为单位进行申请的,默认大小是4KB。不过Linux从 2.6.38内核版本开始,支持了内存大页机制,可以允许向操作系统一次申请 2MB 大小的内存。由于申请的内存单位变大,也意味着申请耗时相对变长。
 
那对于 Redis服务会有什么影响呢?
当 Redis 在执行后台 RDB 和 AOF rewrite 时,采用 fork 子进程的方式来处理。但主进程 fork 子进程后,此时的主进程依旧是可以接收写请求的,而进来的写请求,会采用 Copy On Write(写时复制)的方式操作内存数据。
也就是说,主进程一旦有数据需要修改,Redis 并不会直接修改现有内存中的数据,而是先将这块内存数据拷贝出来,再修改这块新内存的数据,这就是所谓的「写时复制」。
写时复制可以理解为:需要发生写操作哪个Key,就需要先拷贝这个Key,然后再修改。
这里注意,主进程在修改拷贝内存数据时,这个阶段就涉及到新内存的申请。如果此时操作系统开启了内存大页,那么在此期间,应用程序即便只修改 10B 的数据,Redis 在申请内存时也会以 2MB 为单位向操作系统申请,申请内存的耗时变长,进而导致每个写请求的延迟增加,影响到 Redis 性能。
 
所以为了避免过多的内存申请,我们需要关闭内存大页机制:
cat /sys/kernel/mm/transparent_hugepage/enabled
[always] madvise never
如果输出选项是 always,就表示目前开启了内存大页机制,我们需要关掉它:
echo never > /sys/kernel/mm/transparent_hugepage/enabled
 
其实,操作系统提供的内存大页机制,其优势是可以在一定程序上降低应用程序申请内存的次数。
比如针对大数据、对象存储相关的服务来说可能会更好,但是对于 Redis 这种对性能和延迟极其敏感的数据库来说,我们希望 Redis 在每次申请内存时,耗时尽量短,建议关闭这个参数。
 
五、其他影响访问Redis的性能的因素
1.应用程序配置不合理
a.应配置合理的连接数等相关参数,比如jedis,默认MaxActive最大连接数只有8个,在高QPS时就会出现无法获取新连接的提示:
redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool … Caused by: java.util.NoSuchElementException: Timeout waiting for idle object at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:449)
b.驱动版本过低。低版本的Driver连接高版本Redis,除了无法使用最新的特性外,还会经常出现连接不释放、内存泄露、访问缓慢等问题。
2.使用连接池,避免使用短连接模式.特别是使用PHP的应用,频繁的连接创建与销毁,在高QPS访问时网络开销巨大;
3.CPU绑核及主频影响
Redis是单线程模型处理处理用户需求,那么处理的吞吐、效率就会极度依赖CPU的处理能力,所以选型CPU时,如果部署的Redis平时QPS较高,可以采购主频高些的CPU.
另外现在的CPU都是多核处理,为了提高服务性能,降低应用程序在多个 CPU 核心之间的上下文切换带来的性能损耗,通常采用的方案是进程绑定 CPU 的方式提高性能。
但是Redis的绑核操作过于复杂,对于单机多实例的管理挑战过高,再加上Redis 的性能已经足够优秀,不建议绑定 CPU来处理,这里也不做深入说明。
 
最后总结:
本篇为Redis性能诊断的完结篇,通过总结常见的可能导致访问响应延迟、甚至阻塞的问题的各种场景,以及如何定位及分析针对性地提供了解决方案。
但是由于篇幅限制,关于 Redis 的很多细节也无法全部展开,后续也会对Redis使用的各种技巧、架构及内部的工作原理深入分享,欢迎保持关注。
 
如果这篇文章对你有帮助,还请帮忙点赞、在看、转发 一下,你的支持会激励我们输出更多高质量的文章,非常感谢!
如果你还想看更多优质文章,欢迎关注我的公众号「数据库架构师」,提升数据库技能。
 

有关Redis变慢?深入浅出Redis性能诊断系列文章(四)的更多相关文章

  1. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  2. ruby-on-rails - 使用一系列等级计算字母等级 - 2

    这里是Ruby新手。完成一些练习后碰壁了。练习:计算一系列成绩的字母等级创建一个方法get_grade来接受测试分数数组。数组中的每个分数应介于0和100之间,其中100是最大分数。计算平均分并将字母等级作为字符串返回,即“A”、“B”、“C”、“D”、“E”或“F”。我一直返回错误:avg.rb:1:syntaxerror,unexpectedtLBRACK,expecting')'defget_grade([100,90,80])^avg.rb:1:syntaxerror,unexpected')',expecting$end这是我目前所拥有的。我想坚持使用下面的方法或.join,

  3. 【鸿蒙应用开发系列】- 获取系统设备信息以及版本API兼容调用方式 - 2

    在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList​()Obt

  4. 阿里云RDS——产品系列概述 - 2

    基础版云数据库RDS的产品系列包括基础版、高可用版、集群版、三节点企业版,本文介绍基础版实例的相关信息。RDS基础版实例也称为单机版实例,只有单个数据库节点,计算与存储分离,性价比超高。说明RDS基础版实例只有一个数据库节点,没有备节点作为热备份,因此当该节点意外宕机或者执行重启实例、变更配置、版本升级等任务时,会出现较长时间的不可用。如果业务对数据库的可用性要求较高,不建议使用基础版实例,可选择其他系列(如高可用版),部分基础版实例也支持升级为高可用版。基础版与高可用版的对比拓扑图如下所示。优势 性能由于不提供备节点,主节点不会因为实时的数据库复制而产生额外的性能开销,因此基础版的性能相对于

  5. Ruby 的数字方法性能 - 2

    我正在使用Ruby解决一些ProjectEuler问题,特别是这里我要讨论的问题25(Fibonacci数列中包含1000位数字的第一项的索引是多少?)。起初,我使用的是Ruby2.2.3,我将问题编码为:number=3a=1b=2whileb.to_s.length但后来我发现2.4.2版本有一个名为digits的方法,这正是我需要的。我转换为代码:whileb.digits.length当我比较这两种方法时,digits慢得多。时间./025/problem025.rb0.13s用户0.02s系统80%cpu0.190总计./025/problem025.rb2.19s用户0.0

  6. ruby - Ruby 性能中的计时器 - 2

    我正在寻找一个用ruby​​演示计时器的在线示例,并发现了下面的代码。它按预期工作,但这个简单的程序使用30Mo内存(如Windows任务管理器中所示)和太多CPU有意义吗?非常感谢deftime_blockstart_time=Time.nowThread.new{yield}Time.now-start_timeenddefrepeat_every(seconds)whiletruedotime_spent=time_block{yield}#Tohandle-vesleepinteravalsleep(seconds-time_spent)iftime_spent

  7. ruby-on-rails - 如果条件与 &&,是否有任何性能提升 - 2

    如果用户是所有者,我有一个条件来检查说删除和文章。delete_articleifuser.owner?另一种方式是user.owner?&&delete_article选择它有什么好处还是它只是一种写作风格 最佳答案 性能不太可能成为该声明的问题。第一个要好得多-它更容易阅读。您future的自己和其他将开始编写代码的人会为此感谢您。 关于ruby-on-rails-如果条件与&&,是否有任何性能提升,我们在StackOverflow上找到一个类似的问题:

  8. ruby - 从结束值创建一系列字符串 - 2

    我使用irb。下面是我写的代码。“斧头”..“bc”我期待"ax""ay""az""ba"bb""bc"但结果只是“斧头”..“bc”我该如何纠正?谢谢。 最佳答案 >puts("ax".."bc").to_aaxayazbabbbc 关于ruby-从结束值创建一系列字符串,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/7617092/

  9. ruby - 如何找到我的 Ruby 应用程序中的性能瓶颈? - 2

    我编写了一个Ruby应用程序,它可以解析来自不同格式html、xml和csv文件的源中的大量数据。我如何找出代码的哪些区域花费的时间最长?有没有关于如何提高Ruby应用程序性能的好资源?或者您是否有任何始终遵循的性能编码标准?例如,你总是用加入你的字符串吗?output=String.newoutput或者你会使用output="#{part_one}#{part_two}\n" 最佳答案 好吧,有一些众所周知的做法,例如字符串连接比“#{value}”慢得多,但是为了找出您的脚本在哪里消耗了大部分时间或比所需时间更多,您需要进行分

  10. STM32的HAL和LL库区别和性能对比 - 2

    LL库和HAL库简介LL:Low-Layer,底层库HAL:HardwareAbstractionLayer,硬件抽象层库LL库和hal库对比,很精简,这实际上是一个精简的库。LL库的配置选择如下:在STM32CUBEMX中,点击菜单的“ProjectManager”–>“AdvancedSettings”,在下面的界面中选择“AdvancedSettings”,然后在每个模块后面选择使用的库总结:1、如果使用的MCU是小容量的,那么STM32CubeLL将是最佳选择;2、如果结合可移植性和优化,使用STM32CubeHAL并使用特定的优化实现替换一些调用,可保持最大的可移植性。另外HAL和L

随机推荐