Redis高级应用--阻塞的原因

简介

Redis是单线程架构,如果该线程出现阻塞,将导致Redis不能提供服务。导致阻塞问题的场景大致分为两种原因:

  • 内在原因:不合理使用API或数据库、CPU饱和、持久化阻塞
  • 外在原因:CPU竞争、内存交换和网络问题等

    内在原因

    API或数据结构使用不合理

    Redis可以使用慢查询功能获取慢查询命令,默认超过10毫秒的命令都记录到定长队列(默认128)中。如果命令执行时间是毫秒级,实际OPS只有1000多,因此需要找到慢的查询指令,并进行优化。Redis两个和慢查询日志相关的选项:
    1
    2
    3
    4
    slowlog-log-slower-than    1 # 指定超过1毫秒的命令会被记录到日志上
    slowlog-max-len 128 # 设置保存128条日志(先进先出,超过128条会丢掉前面的日志)

    slowlog get {n} # 获取n条慢查询命令,不适用n则获取全部慢查询日志
    发现慢查询后,可以对命令及时调整:
  • 修改为低时间复杂度的命令,禁用keys、sort等命令
  • 调整大对象:将大对象拆分为多个小对象

CPU饱和

Redis使用单线程,CPU饱和是指Redis将CPU跑到接近100%,其他命令就无法执行,从而造成阻塞。使用top命令,可以看到Redis线程使用CPU达到92.3%。

1
2
3
PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
28 gavin 20 0 582064 537320 1660 R 92.3 6.5 0:39.32 redis-server
44 gavin 20 0 20840 1372 1000 S 0.0 0.0 0:00.03 redis-cli

持久化阻塞

持久化阻塞操作有:fork阻塞、AOF刷盘阻塞和HugePage写操作阻塞三种。

fork阻塞

Redis做RDB和AOF重写和持久化时,需要执行fork操作创建子进程(bgsave操作),子进程不拷贝父进程物理内存空间,但是会复制父进程的空间内存页表(10g的Redis进程,需要复制20MB的内存页表)。因此如果fork操作本身耗时过长,也会导致主线程的阻塞。

AOF刷盘阻塞

开启AOF持久化时,文件刷盘一般每秒一次,后台线程每秒对AOF文件做fsync操作。硬盘压力过大时,fsync操作需要等待,直到写入完成。如果主线程发现距离上次的fsync成功超过两秒,为了数据安全性会阻塞到后台线程fsync操作完成。

HugePage写操作阻塞

子进程在执行重写期间利用Linux写时复制降低内存开销,因此只有写操作是Redis才会复制要修改的内存页。如果开启Transparent HugePage,每次写入命令引起的复制内存页单位由4K变为2MB,放大512倍,拖慢写操作的执行时间。

外在原因

CPU竞争

  • 进程竞争
    如果其他进程过度消耗CPU,会严重影响Redis的吞吐量。
  • 绑定CPU
    部署Redis时,可以将Redis绑定在CPU上,但是一些情况下会出现问题。当Redis父进程创建子进程执行RDB/AOF持久化时,如果绑定了CPU,子进程和父进程有可能会共享一个CPU,影响Redis稳定性。

    内存交换

    内存交换是指操作系统将Redis使用的部分内存换到硬盘上去,造成Redis性能急剧下降。因此需要保证机器有充足的内存,防止内存交换。
    1
    2
    3
    redis-cli info server | grep process_id        #获取Redis进程号
    cat /proc/4476/smaps | grep Swap 87 #根据进程号查询内存交换信息

网络问题

网络问题经常是引起Redis阻塞的问题。常见网络问题有:连接拒接、网络延迟、网卡软中断等