Redis(1.18)redis阻塞分析
读书笔记《redis开发与运维》
1. 发现阻塞
客户端记录redis相关日志时,需要具体到redis节点,在出现连接相关异常时能定位的具体节点。
服务器端应利用相关工具加强对redis集群的监控,发现不正常指标时应进行报警,并快速反应。主要监控指标为慢查询、持久化阻塞、连接拒绝、CPU内存网络磁盘使用过载。
阻塞出现的原因主要包括内在原因和外在原因2方面。
2. 内在原因
2.1 API或数据结构使用不合理
通常redis执行指令的速度非常快,但有些指令如hgetall在遇到大并发且大对象时,执行速度会变慢。因此在高并发场景,避免在大对象上执行算法复杂度高的指令。
如何发现慢查询?如何发现大对象?
slowlog get {n} 获取最近的N条慢查询指令。
redis-cli -h{ip} -p{port} --bigkeys 指令,汇总大对象的键以及不同类型数据结构的使用情况。
解决方案:
避免使用复杂度高的指令
根据业务合理调整大对象,将大对象拆分成多个小对象
如果使用管道,减少管道指令数量,如果使用批量操作,减少批量操作的数量
2.2 CPU饱和
redis是一个单线程架构,处理命令只会用一个CPU,可以使用
redis-cli -h{ip} -p{port} --stat
获取redis使用情况。如下图所示:
上图的并发量达到6w+,优化余地不大,可以考虑扩容。如果只有几百几千的并发量redis的CPU就接近饱和是很不正常的。
有可能是因为过度的内存优化,导致CPU执行指令变慢,如使用 info commandstats 统计指令的开销时间,如下所示:
hset指令平均执行时间为135毫秒,肯定不合理,有可能是redis过度放宽了ziplist的使用,如调大了 hash-max-ziplist-entries和hash-max-ziplist-value配置,ziplist编码省空间,但执行效率会比hashtable低。
2.3 持久化阻塞
fork阻塞:在RDB和AOF重写时,redis主线程会开启一个fork子进程,由子进程完成持久化操作,可以通过info stats获取到latest_fork_usec指标,如果超过1秒,则需要作出优化调整。
AOF刷盘阻塞:开启AOF后,后台线程每秒对AOF文件做同步,如果同步超过2秒,redis会阻塞等待。也可以查看info persistence统计中的aof_delayed_fsync指标。
hugepage写操作阻塞:具体见学习笔记8
fork问题
一般是内存配置和使用问题
fork 应该是每GB数据20毫秒左右的消耗,具体可以在 Info stats 统计中查看 latest_fork_usec 指标获取最近一次fork操作的耗时,单位毫秒。
如何改善:
(1)使用物理机或高效的支持redis的虚拟化技术,避免使用xmen等低效虚拟化技术
(2)控制redis 单实例的最大可用内存,fork耗时和内存数据量大小成正比
(3)合理分配内存,要比较大,避免内存不足 fork 执行失败
(4)控制好fork的频率,比如 rdp 的频率,aof 的同步频率,避免不必要的全量复制。
Aof 追加阻塞、刷盘阻塞
一般是磁盘IO负载问题
通过 info persistence 统计,指标 aof_delayed_fsync 指标累加
3. 外在原因
3.1 CPU竞争
redis是CPU密集型应用,不建议和其它CPU密集服务部署在一起,因为其它CPU密集服务可能会与redis竞争CPU,可通过top、sar等命令定位CPU消耗的时间点和具体进程。
如果把redis进程绑定到CPU上,可用于降低CPU上下文切换,但如果开启了持久化,那么fork进程会和redis进程竞争CPU,因此对于开启了持久化或参与复制的节点不要绑定CPU。
3.2 内存交换
redis高性能的保证是所有数据都在内存中,如果redis的部分内存是虚拟内存(磁盘),那么性能急速下降,可通过如下办法查询:
获取redis进程号: redis-cli -p 6383 info server | grep process_id
根据进程号查询内存交换信息:cat /proc/4476/smaps | grep Swap
如果为0,则表示内存没有被交换,预防方法如下:
保证机器有足够的可用内存。
设置redis实例最大可用内存即maxmemory,防止redis内存不可控的增长
降低系统使用swap优先级
3.3 网络问题
3.3.1 连接拒绝
网络问题:尽量避免让redis和客户端异地跨机房调用。
连接数控制:通过redis的maxclients控制客户端最大连接数,info stats指令的 rejected_connections 指标记录被拒绝的连接,默认最大连接是10000,超过最大连接数新连接会被拒绝。
连接溢出--进程限制:使用Linux的ulimit -n查看系统对请求并发的控制,一般这个值需要调大,如ulimit -n 65535
Linux操作系统请求队列限制:backlog队列溢出,操作系统对于特定端口的TCP连接使用backlog队列保存。
修改redis的 tcp-backlog 参数,默认为511,这个参数可以理解为redis在操作系统层面能接收到的请求队列长度。可以使用指令 netstat -s |grep overflowed 查看是否出现队列溢出情况
要修改tcp-backlog 也要同时修改系统的 /proc/sys/net/core/somaxconn 参数
3.3.2 网络延迟、流量监控
主要是指带宽不能满足高并发的要求,如机器网卡带宽、交换机带宽、专线带宽等。
带宽占用主要根据当时使用率是否达到瓶颈有关,如频繁操作Redis的大对象对于redis所在的机器很容易达到网卡瓶颈,因此需要重点监控机器流量。
网络延迟可以通过,redis-cli -h {host} -p {port}
--latency:持续进行延迟测试,分别统计:最小值、最大值、平均值、采样次数
--latency-history:统计结果同 --latency,但默认每15秒完成一行统计,可以通过 -i 控制采样时间
--latency-dist:使用统计图的形式展示延迟统计,每1S一次。
3.3.3 网卡软中断
网卡软中断是指由于单个网卡队列只能使用一个CPU,高并发下网卡数,据交互都集中在同一个CPU,导致无法充分利用多核CPU的情况。
网卡软中断瓶颈一般出现在网络高流量吞吐的场景,如下使用“top+数字1”命令可以很明显看到CPU1的软中断指标(si)过高: