1、内部原因
(1)redis采用单线程处理请求,reactor是同步IO,需要等待命令执行完成,才会返回执行结果,然后进入下一个请求(队列)
(2)持久化阻塞
- fork阻塞: fork操作发生在rdb和aof重写时,redis主线程调用fork操作产生共享内存的子进程,由子进程完成持久化文件重写工作,若fork操作本身耗时过长,则必会导致主线程阻塞;可执行info stats命令获取到latest_fork_usec指标,表示redis最近一次fork操作耗时,若超过1s,则需要做出优化调整
- aof刷盘阻塞: 当开启aof持久化功能时,文件刷盘的方式一般采用每秒一次,后台线程每秒对aof文件做fsync操作,硬盘压力过大时,fsync操作需要等待,直到写入完成如果主线程发现距离上一次的fsync成功超过2秒,为了数据安全性它会阻塞直到后台线程执行fsync操作完成,这种阻塞行为主要是硬盘压力引起,可查看Redis日志识别出这种情况
- hugepage写操作阻塞:子进程在执行重写期间利用Linux写时复制技术降低内存开销, 因此只有写操作时Redis才复制要修改的内存页。 对于开启Transparent HugePages的操作系统, 每次写命令引起的复制内存页单位由4K变为2MB, 放大了512倍, 会拖慢写操作的执行时间, 导致大量写操作慢查询。 例如简单的incr命令也会出现在慢查询中,因此需要关闭THP功能。
(3)CPU饱和
- 单线程的Redis处理命令时只能使用一个CPU。而CPU饱和是指Redis把单核CPU使用率跑到接近100%。使用top命令很容易识别出对应Redis进程的CPU使用率。CPU饱和是非常危险的,将导致Redis无法处理更多的命令,严重影响吞吐量和应用方的稳定性。
- 应对之策是做好CPU监控报警策略,如果使用的阿里云redis,可以在阿里云后台设置CPU使用阈值预警
2、外部原因
(1)不合理使用API和数据结构(bigkeys、慢查询)
使用大对象
- 将大对象拆分为小对象
- 发现大对象的命令:redis-cli -h {ip} -p {port} --bigkeys ,内部原理采用分段进行scan操作,把历史扫描过的最大对象统计出来便于分析优化。
使用keys取出所有key
- keys [pattern] 一次性全部返回符合条件的key,如果数据量很大将会阻塞其他客户端
- 在数量未知或者数量较大的情况下使用scan遍历来获取所有的key
大量的key同时过期
- 如果有很多key在同一秒内过期,超过了所有key的25%,redis主线程就会阻塞直到过期key比例下降到25%以内
- 因为要避免大量的key同时过期
(2)网络问题(最大连接数、网络延迟)
- redis连接拒绝(超过客户端最大连接数)
- 网络延迟
- 网卡软中断(一般出现在网络高流量吞吐的场景)
(3)内存交换
- 内存交换(swap)对于Redis来说是非常致命的,Redis保证高性能的一个重要前提是所有的数据在内存中。如果操作系统把Redis使用的部分内存换出到硬盘,由于内存与硬盘读写速度差几个数量级,会导致发生交换后的Redis性能急剧下降。