Redis开发与运维之第七章Redis的噩梦:阻塞

发现阻塞

·当Redis阻塞时,线上应用服务应该最先感知到,这时应用方会收到大量Redis超时异常。常规做法是在应用方加入异常统计并通过邮件/微信/短信报警,以便及时发现通知问题。开发人员需要处理如何统计异常以及触发报警的时机。何时报警根据应用的并发量决定。由于Redis调用API会分散在项目的多个地方,每个地方都监听异常并加入监控代码必然难以维护。这可以借助于日志系统,使用logback或者log4j.当异常发生时,异常信息最终会被日志系统收集到Appender,默认的Appender一般是具体的日志文件,开发人员可以自定义一个Appender,用于专门统计异常和触发报警逻辑。

 另外一个问题,开发人员接到异常报警后,通常会去线上服务器查看错误日志细节。这时如果应用操作的是多个Redis节点,如何决定是哪个节点超时还是所有的节点超时呢?这是线上很常见的需求,但绝大多数的客户端类库并没有在异常信息中打印IP和PORT信息,导致无法快速定位是哪个Redis节点超时。

不过修改Redis客户端成本很低,比如Jedis只需要修改Connection类下的connect、sendCommand、readProtocalWithCheckintBroken 方法专门捕获连接,发送命令,协议读取事件的异常。由于客户端类库都会保存IP和PORT信息,当异常发生时很容易打印出对应节点的IP和PORT信息。

也可以借助Redis监控系统监控的关键指标,命令耗时,慢查询,持久化阻塞,连接拒绝、CPU、内存、网络、磁盘使用过载的问题等。

内在原因

API或者数据结构使用不合理

比如hgetall 数据量比较大且命令算法复杂度是O(n) 这条命令执行速度必然很慢。

CPU饱和的问题

redis-cli --stat    #查看CPU使用率

垂直层面的命令优化很难达到效果,这时就需要水平扩展来分摊OPS压力。如果只有几百或者几千OPS的Redis实例就接近CPU饱和是很不正常的,有可能使用了高算法复杂度的命令。

持久化相关阻塞问题

1.fork阻塞

fork操作发生在RDB和AOF重写时,Redis主线程调用fork操作产生共享内存的子进程,由子进程完成持久化文件重写工作。如果fork操作本身耗时过长,必然会导致主线程的阻塞。

可以执行info stats 获取latest_fork_usec指示,表示Redis最近一次fork操作耗时,如果耗时很大,比如超过1秒,则需要作出优化调整,如避免使用过大的内存实例和规避fork缓慢的操作系统等

2.AOF刷盘阻塞

文件刷盘采用每秒一次,后台线程每秒对AOF文件做fsync操作。当硬盘压力过大时候,fsync操作需要等待,直到写入完成。如果主线程发现距离数十年该一次的fsync成功超过2秒,为了数据安全性它会阻塞直到后台线程执行fsync操作完成。这种阻塞行为主要是硬盘压力引起,可以查看Redis日志识别出这种情况。

查看指令info persistence 统计aof_delayed_fsync指标,每次发生fdatasync阻塞主线程时会累加。

3.HugePage 写操作阻塞

子进程在执行重写期间利用Linux写时复制技术降低内存开销,因此只有写操作时候,Redis才复制要修改的内存页。

外在原因

CPU竞争

进程竞争:Redis是典型的CPU密集型应用,不建议和其他多核CPU密集型服务部署在一起。当其他进程过度消耗CPU时候,将严重影响Redis吞吐量。可以通过top/sar等命令定位到CPU消耗的时间点和具体进程,这个问题比较容易发现,需要调整服务之间部署结构。

绑定CPU:部署Redis时候为了充分利用多核CPU,通常一台机器部署多个实例。常见的一种优化是把Redis进程绑定CPU上,用于降低CPU频繁上下文切花的开销。

内存交换

Redis保证高性能的一个重要前提是所有数据在内存中,若果操作系统把Redis使用的部分内存换出到硬盘,由于内存与硬盘读写速度差几个数量级,会导致发生交换后的Redis性能急剧下降。

网络问题

1.连接拒绝

1.网络闪断

一般发生在网络割接或者带宽耗尽的情况,对于网络闪断的识别比较困难。

2.Redis连接拒接。

Redis通过maxclients参数控制客户端最大连接数,默认1000. 当Redis连接拒绝。Redis通过maxclients参数控制客户端最大连接数。Redis使用多路复用IO模型可支撑大量连接,但是不代表可以无限连接。客户端访问Redis时候尽量采用NIO长连接或者连接池的方式。

3.连接溢出。

操作系统或者Redis客户端在连接时的问题。这个原因比较多,下面就分别介绍两种原因:

进程限制,backlog 队列溢出。

1、进程限制

客户端想成功连接上Redis服务需要操作系统和Redis的限制都通过才可以

操作系统一般都会对进程使用资源做限制,其中一项是对进程可打开最大文件数操作,通过ulimit -n 查看,通常默认1024。

由于Linux系统对TCP连接也定义一个文件句柄,因此对于支撑大量连接的Redis来说需要增大这个值,ulimit -n 65535

2. backlog队列溢出

系统对于特定端口的TCP连接使用backlog队列保存Redis默认的长度为511,通过tcp-backlog参数设置。如果Redis用于高并发场景为了防止缓慢连接占用,可适当增大这个设置,但必须大于操作系统允许值才能生效。

网络延迟问题

取决于客户端到Redis服务器之间的网络环境。主要包括他们之间的物理拓步和带宽占用情况。常见的物理拓步按网络延迟由快到慢可分为:同物理机>同机架>跨机架>同机房>同城机房>异地机房

容灾性正好相反,同物理机容灾性最低而异地机房容灾性最高。Redis提供了测试机器之间网络延迟的工具,

redis-cli -h {host} -p {port}

--latency: 持续进行延迟测试 分别统计:最小值、最大值、平均值、采样次数。

--latency-history:统计结果同--latency,但默认每15秒完成一行统计,

可通过-i参数控制采样时间。

--latency-dist:使用统计图的形式展示延迟统计,每1秒采样一次。

带宽瓶颈体现在

机器网卡带宽

机架交换机带宽

机房之间专线带宽

网卡软中断

由于单个网卡队列只能使用一个CPU,高并发下网卡数据交互都几种在同一个CPU,导致无法充分利用多核CPU情况。网卡中断瓶颈一般出现在网络高流量吞吐的场景

top+数字1

猜你喜欢

转载自blog.csdn.net/cuiwei1026522829/article/details/86501342