Redis4.x(二十三):延迟分析、诊断、测试和优化

star2017 1年前 ⋅ 720 阅读

Redis 服务在生产环境上线运行后,因为使用了不恰当的命令或在大数据集情况下可能出现响应客户端延迟问题;Redis 是单线程快速处理请求,在亚微秒范围内,若现现了延迟则其它所有客户端的请求都将进入等待,这是无法接受的。

本篇对延迟原因进行分析诊断,对服务器环境进行延迟测试并提供优化措施。官方相关资料Redis 延迟问题诊断:Redis latency problems troubleshootingRedis 延迟监控框架:Redis latency monitoring framework

延迟原因分析

网络通信引起的延迟

客户端使用TCP/IP 连接或通过 Unix Socket 连接 Redis。 在 1 Gbit/s网络的典型延迟大约为 200 us,而 Unix 域套接字的延迟可能低至 30 us。这些取决于网络和系统硬件。
在通信本身上,系统也会产生延时,如线程调度、CPU缓存、NUMA 协调等等。 虚拟化环境中的系统引发的延迟明显高于物理机器上的延迟。
即使 Reids 处理大多数命令是亚微秒级别,执行许多往返服务器的客户端命令也秘须为网络和系统相关的延迟付费。因此,高效的客户端将尝试通过多个命令管道化来限制往返次数。这需要客户端的支持。像MSET/MGET这样的聚合命令也是为了达到此目的。

Linux下可使用qperf工具命令来测试网络带宽和网络延迟,使用dstat检查主机的网络利用率:

[root@192 log]# dstat -nt
-net/total- ----system----
 recv  send|     time     
   0     0 |28-12 22:18:39
 120B  342B|28-12 22:18:40
 120B  230B|28-12 22:18:41
 120B  230B|28-12 22:18:42

注: CentOS 可通过yum install qperf|dstat来安装命令工具。

慢速命令引起的延迟

Redis 是单线程处理业务,当处理请求缓慢时,所有其它客户端的请求都将被等待处理。若执行普通命令(get,set,lpush)时,问题不大,因为这类命令的时间复杂度低,可以在恒定(非常短)的时间内执行。但是有些命令可能需要耗费较长的时间来执行,如sort,lrem,sunion,对两个大集合求交集等。

在使用 Redis 命令时,可以记录命令的时间复杂度,如果不熟悉时可以检查它。Redis 官方对命令的使用说明都写明了时间复杂度,Redis 命令-官方,Redis 命令-中文

如果使用的命令有延迟问题,则不应该对由许多元素组成的值使用慢速命令,或者使用 Redis 副本(从实例)运行慢速命令。

重要说明:执行慢速命令产生的非常常见的延迟源是在生产环境中使用 KEYS 命令。而 KEYS 应仅用于调试。 Redis 2.8引入了SCAN, SSCAN, HSCAN, ZSCAN这类带游标特性的命令来迭代 key space 和其他大型集合。

  1. 使用INFO COMMANDSTATS命令检查命令处理相关的统计信息

     127.0.0.1:6379> info commandstats
     # Commandstats
     cmdstat_config:calls=5,usec=133,usec_per_call=26.60
     cmdstat_slowlog:calls=7,usec=344,usec_per_call=49.14
     cmdstat_auth:calls=12,usec=103,usec_per_call=8.58
     cmdstat_command:calls=10,usec=10625,usec_per_call=1062.50
     cmdstat_hmset:calls=2,usec=27140,usec_per_call=13570.00
     cmdstat_hgetall:calls=1,usec=3,usec_per_call=3.00
     cmdstat_set:calls=2,usec=164,usec_per_call=82.00
     cmdstat_keys:calls=1,usec=23,usec_per_call=23.00
     cmdstat_info:calls=423,usec=43386,usec_per_call=102.57
     cmdstat_ping:calls=1,usec=1,usec_per_call=1.00
    
  2. 查看慢速日志,查看具体造成延迟的操作命令,具体参考Redis 4.x系列(二十二):Redis 健康检查与慢速日志

     127.0.0.1:6379> slowlog get 3
    

fork引起的延迟

为了在后台生成 RDB 文件,或启用了 AOF 来持久化为了重写 AOF 文件,Redis 会创建子进程来执行,在大多数 Unix 系统上,fork 是一项耗资源的操作,因为它涉及复制大量链接到进程的对象(复制父进程的空间内存页表),对于虚拟内存机制关联的页表尤其如此(使用虚拟化机术,如 Xen 虚拟机,fork 操作会更耗时)。

例如,在Linux / AMD64系统上,内存分为4 kB页面。 为了将虚拟地址转换为物理地址,每个进程存储一个页表(实际上表示为树),该表至少包含进程地址空间的每页指针。 因此,大型24 GB Redis实例需要24 GB / 4 kB * 8 = 48 MB的页表。
在执行后台保存时,必须 fork 此实例,这涉及分配和复制 48 MB 内,这需要耗费CPU资源和时间,特别是在大型内存块的分配和初始化能很昂贵的虚拟机上。

现代硬件复制页面表非常快,但Xen不是。 Xen的问题不是特定于虚拟化,而是特定于 Xen。 例如,使用VMwareVirtual Box不会导致缓慢的分叉时间。新的基于EC2 HVM的实例在fork时间方面要好得多,几乎与物理服务器配对,因此例如使用m3.medium(或更好)实例将提供良好的结果。

以下是一个表,用于比较不同Redis实例大小的 时间。 获取数据执行BGSAVE并查看 INFO 命令输出中的latest_fork_usec

VMware 6.0GB上的 Linux 强大虚拟机 fork 时间为 77毫秒(每GB 12.8毫秒)。
在物理机器上运行的Linux(未知硬件)6.1GB RSS fork 时间为 80毫秒(每GB 13.1毫秒)
在物理机器上运行的Linux(Xeon @ 2.27Ghz)6.9GB RSS fork 时间 62毫秒(每GB 9毫秒)。
Linux VM on 6sync(KVM)360 MB RSS fork 时间为8.2毫秒(每GB 23.3毫秒)。
Linux VM on EC2,旧实例类型(Xen)6.1GB RSS fork 时间为 1460毫秒(每GB 239.3毫秒)。
Linux VM on EC2,新实例类型(Xen)1GB RSS fork 时间为 10毫秒(每GB 10毫秒)。
Linux VM on Linode(Xen)0.9GB RSS fork 时间为为 382毫秒(每GB 424毫秒)。
从上可以了解到,在旧实例类型(Xen)上运行某些 VM 的性能损失介于一个数量级到两个数量级之间。对于 EC2 用户,建议使用基于 HVM 的现代实例。

fork 延时,可通过检查最后一次fork时间来判断

[root@192 6379]# ./redis-cli -a 123456 info stats |grep fork
latest_fork_usec:0

//或
127.0.0.1:6379> info stats
# Stats
latest_fork_usec:15624

透明大页引起的延迟

Linux 内核默认启用了透明大页来管理内存,Redis 在调用 fork 执行持久化时,会导致大的延迟消耗。原因如下:

调用fork会创建两个具有共享大页面的进程。Reids 持久化并不会一次性复制整个内存, 而是拿到内存页面列表,根据内存页面指向的内存块一个个复制的。在 Redis 繁忙的实例中,一些事件循环运行将导致命令以几千个页面为目标,导致几乎一次就拷贝整个进程内存来持久化,这将会导致大延迟大量内存的使用。

Linux 内存大页功能的开启和关闭,支持实时配置而不用重启系统,可使用如下命令确保禁用内存大页,重新 Redis服务:

echo never > /sys/kernel/mm/transparent_hugepage/enabled

交换分区操作引起的延迟

Linux 系统称之为交换分区交换文件,Windows 系统称之为虚拟内存,是操作系统将内存页保存到磁盘,在需要使用时进行数据交换。Windows 虚拟内存, Linux 交换分区(交换文件)概念见百度词条。

如果内核将Redis页面从内存移动到交换文件,当Redis使用存储在此内存页面中的数据时(例如访问存储在该内存页面中的 KEY),内核将按顺序停止Redis进程将页面移回主内存。这是一个涉及随机I / O的慢速操作(与访问已经在内存中的页面相比),并将导致Redis客户端遇到异常延迟。

三个原因会导致将 Redis 内存数据移到交换分区:

  1. 系统内存不够,正在运行的进程需要使用的物理内存比可用的更多。最简单例子就是 Redis 使用的内存比可用的内存多。
  2. Redis实例数据集或数据集的一部分大部分完全空闲(永远不会被客户端访问),Linux 内核会把该部分数据移到交换分区上。这个问题非常罕见,因为即使是中等访问频率的实例也会经常触及所有内存页面,就会迫使内核持有所有内存页面。
  3. 某些进程正在系统上生成大量读取或写入I / O. 由于文件通常是缓存的,因此往往会对内核施加压力以增加文件系统缓存,从而生成交换活动。 请注意它包括可以生成大文件的Redis RDBAOF 后台线程。

Linux 提供了很好的工具来分析因是否因交换文件导致的延迟:

  1. 首先可以通过free命令看查 Linux 系统有没有使用交换分区,如果Swap0,说明操作系统根据就没有使用交换分区。因为现在操作系统的内存一般都比较大,基本可满足使用。

    root@gj-0001:~# free
                  total        used        free      shared  buff/cache   available
    Mem:        8175084     1442024     1131244        3504     5601816     6396912
    Swap:             0           0           0
    
  2. 获取 Reids 的进程 ID,查看该进程是否使用了交换分区

    //获取实例进程ID
    root@dqx-0001:/usr/local/redis/bin# ./redis-cli -a '123456' info | grep process_id
    process_id:13975
    //通过 ps 获取进程信息
    [root@192 3209]# ps -ef|grep redis
    root       3209      1  0 06:39 ?        00:01:57 ./redis-server *:6379
    
    //查看时程有没有使用交换分区
    root@dqx-0001:/usr/local/redis/bin# cd /proc/13975/
    root@dqx-0001:/proc/13975# cat smaps | grep 'Swap:'
    Swap:                  0 kB
    Swap:                  0 kB
    Swap:                 12 kB
    Swap:                156 kB
    Swap:                  8 kB
    Swap:                  0 kB
    Swap:                  4 kB
    .
    .
    .
    
    //或
    [root@192 log]# awk '/VmSwap/{print $2 " " $3}' /proc/20764/status
    0 kB
    

    在进程 ID 目录下,有一个名为smaps 的文件,它描述了该进程的内存布局,包含该进程映射的非常详细的内存信息。 其中Swap表示交换分区,因为 smaps 文件包含Redis进程的不同内存映射,所以会存在多个Swap信息进程的内存布局比简单的线性页面数组更复杂)。

    如果Swap都是 0 或零星的 4k 项,表示正常。而实际使用过程中,有些 Swap 项会显示更多的交换页面,可以使用命令打印Swap映射的内存大小:

    [root@192 11121]# cat smaps | egrep '^(Swap:|Size)'
    Size:                132 kB
    Swap:                  0 kB
    Size:             720896 kB
    Swap:                 12 kB
    Size:               4096 kB
    Swap:                156 kB
    Size:               4096 kB
    Swap:                  8 kB
    Size:               4096 kB
    Swap:                  0 kB
    

    从上可以看出,有一个 720896 kB 的映射,交换只有 12 kB, 另一个 4096 kB 的映射,交换就比较多为 156 kB。总体上,内存交换量还是非常少的,这样基本不会有什么问题。
    如果在磁盘上交换了相当数量的进程内存,则 Redis 延迟可能与交换有关,可以使用 vmstat 命令进一步分析验证:

    [root@192 11121]# vmstat 1        //1表示延迟1秒输出
    procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
     r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
     1  0   2980 400792   2084 738672    0    0   139    18  166  158  2  2 96  1  0
     0  0   2980 400776   2084 738672    0    0     0     0  305  245  1  1 99  0  0
     0  0   2980 400776   2084 738672    0    0     0     0  250  220  0  1 100  0  0
    

    需要关注的是si 和 so这两项的值,它是计算来源(from)交换文件 到目标(to)交换文件的内存量。如果这两项值非零,表示系统中存在内存交换操作。

    最后,可以使用 iostat 检查系统的全局I/O活动:

    # iostat -xk 1
    Linux 3.10.0-693.21.1.el7.x86_64 (iZwz94fhuwijhzus6n0xltZ)      12/28/2018      _x86_64_        (2 CPU)
    
    avg-cpu:  %user   %nice %system %iowait  %steal   %idle
               0.22    0.00    0.11    0.12    0.00   99.55
    
    Device:         rrqm/s   wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
    vda               0.00     1.71    1.75    1.90    17.92    15.98    18.58     0.01    3.97    1.41    6.34   0.79   0.29
    

    如果延迟问题是由于磁盘上的 Redis 内存交换造成的,则需要降低系统的内存压力,如果 Redis 需要使用的内存比可用的多,则需要添加更多的 RAM,或避免在同一系统中运行其它耗费大量内存的进程(应用)。

AOF 和磁盘 I/O 引起的延迟

开启了 AOF 持久化也可能是延迟的原因。AOF 基本上使用两个系统调用来完成其工作。一个是write(2),用于将数据写入AOF文件,另一个是fdatasync(2),用于刷新磁盘上的内核文件缓冲区,以确保用户指定的持久性级别。

write(2)fdatasync(2)调用都可能是延迟的来源。例如:write(2)可能导致系统范围的同步时阻塞,或者在输出缓冲区已满并且内核需要刷新磁盘以便接受新的写入时阻塞。
fdatasync(2)调用是一个更糟糕的延迟源,因为所使用的内核和文件系统的许多组合可能需要从几毫秒到几秒的时间来完成,尤其是在某些其他进程执行I/O的情况下。因此,在可能的情况下,Redis 在 Redis 2.4之后的另一个线程中执行fdatasync(2)调用。

Reids 配置选择的持久化的类型也会影响处理命令的延迟,通常需要在持久性延迟/性能上进行权衡,可参考:AOF 配置
AOF 持久化有三种方式可配置,下面分析使用 AOF 持久化时可能导致的延迟:

  1. appendfsync no:方式,不执行fsync,由操作系统决定何时将数据写入磁盘,可以减少 I/O 压力和延迟峰值风险。
    此配置下,唯一的延迟源可能是write(2)。这种情况发生一般没有解决办法,因为磁盘的不I/O速度远远跟不上 Redis 接收数据的速度,但如果磁盘的I/O不被其他进程严重减速,这种情况也不常见。
  2. appendfsync everysec:方式,Redis 每秒执行一次fsync
    执行fsync使用的是不同的线程,如果fsync在执行中,Redis 使用缓冲区将write(2)调用延迟两秒(因为如果fsync正在对同一文件进行中,则写会在Linux上阻塞),如果fsync花费的时间太长,Redis最终将执行write(2)调用,即使fsync仍在进行中,这可能是延迟的原因。
    可配置no-appendfsync-on-rewrite yes,确保在重写期间不调用fsync, 以降低 I/O 压力。建议使用此配置。
  3. appendfsync always:方式,在使用OK码回复客户端之前,每次写入操作都会执行fsync(实际上,Redis 将尝试将同时执行的许多命令集群到一个fsync中)。
    在这种模式下,通常性能非常低,强烈建议使用快速磁盘和可以在短时间内执行fsync的文件系统实现。不建议在生产中使用。

大多数Redis用户将使用appendfsync配置指令的noeverysec设置。 对于最小延迟的建议是避免其它进程在同一系统中执行I/O操作。使用SSD磁盘也可以有所帮助,但是如果磁盘是空闲的,那么即使非SSD磁盘也可以很好执行文件追加操作(AOF持久化),因为 Redis AOF方式持久化是只追加而不执行任何查询操作。

在Linux下,可以使用 strace 命令检测与仅 AO F相关的延迟问题:

sudo strace -p $(pidof redis-server) -T -e trace=fdatasync

上面的命令将显示 Redis 在主线程中执行的所有fdatasync(2)系统调用。 使用上面的命令,当appendfsync config选项设置为everysec时,您将看不到后台线程执行的fdatasync系统调用(另一个线程),只需将-f开关添加到strace即可。

还可以使用以下命令同时查看 fdatasync 和 write 系统调用:

sudo strace -p $(pidof redis-server) -T -e trace=fdatasync,write

[root@192 ~]# sudo strace -p $(pidof redis-server) -T -e trace=fdatasync,write
strace: Process 11121 attached
write(9, "11121:M 28 Dec 16:30:29.199 - DB"..., 71) = 71 <0.000828>
write(9, "11121:M 28 Dec 16:30:29.206 - 0 "..., 82) = 82 <0.000339>
write(9, "11121:M 28 Dec 16:30:34.403 - DB"..., 71) = 71 <0.000338>
write(9, "11121:M 28 Dec 16:30:34.409 - 0 "..., 82) = 82 <0.000558>

strace是 Linux 系统调用的跟踪器,性能开销较大,在生产环境中应谨慎使用。

AOF 延迟可通过检查aof_delayed_fsync指标是否在增加,并检查aof_pending_bio_fsync来判断是否由于 AOF后台重写而存在挂起的fsync任务:

[root@192 6379]# ./redis-cli -a 123456 info persistence |grep fsync
aof_pending_bio_fsync:0
aof_delayed_fsync:0

//或
127.0.0.1:6379> info persistence
# Persistence
loading:0
aof_pending_bio_fsync:0
aof_delayed_fsync:0

搜索 Redis 运行日志来判断是否存在缓慢的AOF fsync

[root@192 log]# less 6379.log
[24734] 07 Jul 10:54:41.006 * Asynchronous AOF fsync is taking too long (disk is busy?). 
Writing the AOF buffer without waiting for fsync to complete, this may slow down Redis.

另外: RDB 快照方式持久化,可根据配置的保存策略触发,也需要在持久性延迟/性能上进行权衡。

键过期处理引起的延迟

Redis 清除过期的键有两种方式:

  1. LAZY(惰性删除),键在被请求访问时进行过期检查,发现已过期就删除处理,也称为访问过期策略。
  2. ACTIVE(主动删除),每 100 毫秒检查过期的键并处理,称为定期过期策略。
    主动过期被设计为自适应的(在CPU空闲时执行,不影响主进程业务)。每隔100毫秒(每秒10次)循环执行,并将执行以下两步操作:
    • ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP 包含的过期键会被全部清除。
    • 如果发现过期超过 25%,重复执行。

Redis 底层定义了ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP,默认值是 20 个键,并且每秒执行 10 次,即每秒可以处理 200 个过期的键。即使已经过期的KEY有较长时间不被访问,也足以被快速清理。同时每少只处理 200 个过期键就 Redis 实例的延迟几乎没有影响。

主动过期策略算法是自适应的,是一个基于概率的简单随机抽样算法,是从整个 DB 中随机抽取 20 KEY,如果在该组抽样的键中发现过期的键超过 25%,则该算法将循环执行。 这个过程每秒会执行 10 次,意味着随机样本中超过 25% 的键至少在 1 秒内过期。意味着若很多KEY在同一秒内过期,过期KEY数量超过 KEY 总量的 25%,Redis 就会阻塞来处理过期的 KEY,直到降到 25% 以下。

使用此策略是为了避免过期的KEY占用过多的内存,这方式几乎不会有不良影响。因为大量的键同时到期是一种少见的现象,但如果用户对大量的KEY使用EXPIREAT命令指定同一个Unix时间为过期时间也不是不可能。

也就是说,大量的键在同一时刻过期也可能导致延迟

延迟测试与诊断

Redis 是单线程处理请求,为实现大量查询的高速处理,对客户端和服务器之间的响应时间的要求是苛核的。因此,对于 Redis 在线服务来说, 高延迟是最为致命名问题。

延迟检测

若出现了延尽,可进行延迟测试来定位问题的具体发生位置。可以在应用端对请求的响应时间进行测试,可以使用redis-cli在 Redis 服务端检测 Redis 服务器处理命令的延迟(单位:毫秒)。

  1. redis-cli测试 Redis 服务器的延迟,包含了网络延迟

    //实时测量延迟
    ./redis-cli --latency -h <host> -p <port>
    
  2. 使用 Redis 内部延迟监控子系统
    从Redis 2.8.13开始,Redis提供了延迟监视功能,能够对不同的执行路径进行采样,以了解服务器阻塞的位置。使得调试延迟问题更简单,建议启用延迟监控。

  3. 延迟基线(固有延迟)
    延迟基线:指运行 Redis 实例的环境本身的一种延迟,如操作系统内核产延迟,采用虚拟化技术产生的延迟。这些延迟是无法消除的,称之为基线,即 Redis 的延迟不可能比环境的延迟更低。
    环境延迟也称为内在延迟redis-cliRedis 版本2.8.7开始就可以对它进行检测。
    ./redis-cli --intrinsic-latency 100:参数100是测试执行秒数。运行测试的时间越多,越有可能发现延迟峰值。100秒测试时长是比较合适的。

    # ./redis-cli --intrinsic-latency 100
    Max latency so far: 1 microseconds.
    Max latency so far: 3 microseconds.
    Max latency so far: 9 microseconds.
    Max latency so far: 23 microseconds.
    Max latency so far: 1271 microseconds.
    Max latency so far: 1673 microseconds.
    
    1509521491 total runs (avg latency: 0.0662 microseconds / 66.25 nanoseconds per run).
    Worst run took 25254x longer than the average latency.
    

    注意:使用 redis-cli 测试延迟,需要在运行或计划运行Redis的服务器中运行,而不是在客户端运行。 在这种特殊模式下,redis-cli 根本没有连接到Redis服务器,它只会尝试测量内核在不向redis-cli进程本身提供的CPU运行时间的下最大时间(延迟)。

    在上面试示例中,系统的固有延迟为 1.955毫秒(1955微秒),但该值会随系统的负载而改变。虚拟化环境则可能延迟更长,如下例:

    # ./redis-cli --intrinsic-latency 100
    Max latency so far: 1 microseconds.
    Max latency so far: 32 microseconds.
    Max latency so far: 45 microseconds.
    Max latency so far: 109 microseconds.
    Max latency so far: 1052 microseconds.
    Max latency so far: 1649 microseconds.
    Max latency so far: 3237 microseconds.
    Max latency so far: 4018 microseconds.
    Max latency so far: 4039 microseconds.
    Max latency so far: 4178 microseconds.
    Max latency so far: 5840 microseconds.
    Max latency so far: 8014 microseconds.
    Max latency so far: 8021 microseconds.
    
    1920296241 total runs (avg latency: 0.0521 microseconds / 52.08 nanoseconds per run).
    Worst run took 154027x longer than the average latency.
    
  4. 还可以通过./redis-cli --latency-history-i选项来监控Redis服务器的 PING 延迟实现。还可以进一步使用第三方工具来收集延迟日志并报警,如LogstashFlume工具。

    [root@192 6379]# ./redis-cli --latency-history -i 10 >>latency.log &
    [root@192 6379]# tail -n 10 latency.log 
    0 3 0.57 733
    0 3 0.57 734
    0 3 0.56 735
    0 3 0.56 736
    0 3 0.56 737
    0 3 0.56 738
    0 3 0.56 739
    0 3 0.56 740
    0 3 0.56 741
    

    日志内容包含4个描述延迟的字段内容,分别是最小值,最大值,平均值,Redis 响应 PING 探测序列的时间

延迟诊断

  1. 对服务器进行基准延迟测试,包括固有延迟网络往返延迟。固有延迟是延迟基线,难以改变,网络延迟可通过 Linux 相关命令工具检测。

  2. 通过redis-cli将延迟指标收集到日志。结果显示最小、最大平均延迟,以及 Redis 响应 PING 探测序列的时间。

  3. 使用INFO COMMANDSTATS命令检查命令处理的相关统计信息。该命令提供了一个有关请求处理统计信息的大致情况。

  4. 使用SLOWLOG命令检查慢日志以定位具体哪个命令处理慢。如果发现了问题,就需要考虑使用时间复杂度低的非阻塞命令。

  5. 检查CPU使用率,如果redis-server进程的 CPU 利用率接近 100%。意味着 Redis 实例已超负荷了。CPU 处理性能出现了瓶颈。
    查看CPU使用率:ps aux|head -1; ps aux|grep redis-server

    [root@192 6379]# ps aux|head -1; ps aux|grep redis-server 
    USER        PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
    root      20764  2.3  0.1 145308  2436 ?        Ssl  21:13   0:43 ./redis-server *:6379
    root      21192  0.0  0.0 112704   964 pts/0    S+   21:43   0:00 grep --color=auto redis-server
    

    高 CPU 使用率分析:

    • 其它进程占用了大量的 CPU 资源,导致redis-serverCPU资源不足。
    • 对大数据集进行排序可能导致 CPU 满负荷。
    • 大量的客户端连接也可能导致 CPU 非常高的占用率。
  6. 检查客户端的总连接数指标total_connections_received,如果在短时间急剧增加,说明用户没有正确管理客户端与服务器之间的连接。
    频繁的连接也会导致客户端和服务端的延迟,也会导致redis-server进程的CPU使用率过高。

    127.0.0.1:6379> info stats
    # Stats
    total_connections_received:6
    
    //或者
    [root@192 6379]# ./redis-cli -a 123456 info stats|grep total_connections_received
    total_connections_received:7
    
  7. 检查持久化的开销是否引起了延迟问题。Redis 持久化可能导致较高的磁盘I/O延迟。

    • fork 引起的延迟,RDB 后台存储和 AOF 重写操作将在主进程中创建子进程导致延迟。保存了大数据集(大于16G)可能会遇到较高的fork延迟。对于 EC2 用户,确保使用现代的 EC2 实例,例如 m3.medium。否则 fork() 非常慢。
    • AOF fsync引起的延迟,可从aof_delayed_fsync(同步延迟)aof_pending_bio_fsync(延迟BIO等待)指标看出是否延迟;还可通过strace跟踪fdatasync函数;还可通过操作系统的命令iostat工具查看是否有 I/O等待,若存在表示磁盘 I/O存在延迟。
      AOF持久化,还可配置no-appendfsync-on-rewrite yes来平衡,保存正在重写时不会对 AOF 文件进行fsync操作。但也意味着在重写过程中,若redis-server进程意外退出可能会有丢失数据的风险。若该选项帮助不大,可设为none
  8. 网络拥塞可使用操作系统的命令工具进行检测,如ping, dstat, qperf命令。

  9. 检查 redis-server 进程是否使用了交换空间。一个需要交换空间的进程意味着等待交换空间的内存页从交换空间移动到内存会被内核阻塞。

延迟优化

  1. 如果负担得起,可以优先选择物理服务器来托管 Redis 服务,而不是虚拟机。
  2. 如果负担得起,选择 SSD 做为持久化磁盘会有较好表现。
  3. 保持长连接,而不是频繁的创建/断开 服务器连接,对于基于 Web 的应用尤其重要。
  4. 如果客户端与服务器在同于主机上,尽量使用 Unix Socket连接。
  5. 能够使用聚合命令(MSET/MGET)或具有可变参数的命令就不用管道操作。
  6. Redis 最后不要部署在单核CPU的服务器上,Redis fork 会创建子进程,是非常耗 CPU 资源的任务,如bgsaveAOF重写,不同与主进程同在一核心上运行,充分利用多核的优势来降低 fork 可能导致的延迟。
  7. 建议使用数据集较小的实例(<10G),在一台系统服务器上可根据 CPU 资源部署多个实例。
  8. 建议单个键的数据集控制在一定范围,可对键进行分片存储。
  9. 如果可以,在往返序列中尽可能使用管道操作。
  10. Redis 还支持 Lua 服务器端脚本,以支持不适合使用管道的情况。
  11. 尽量避免使用会导致阻塞的慢速命令(keys,sort,lrem,sunion)。可以在慢速命令日志功能监视。
  12. 还可以使用操作系统的命令来快速检查 Reids 进程的 CPU消耗,如top, htop, prstat等。如果占用资源很高,而实际操作不多,通常是因为使用了慢速命令
  13. 在生产环境中禁用KEYS命令的执行,KEYS命令仅用于调试目的。

Redis 软看门狗

Redis 2.6 引入了Redis软件看门狗,这是一种调试工具,用于跟踪那些由于某种原因而未能使用常规工具进行分析的延迟问题。

Redis 软件看门狗是一个实验性功能。虽然它设计可用于生产环境,但在继续操作之前应注意备份数据库,因为它可能会与Redis服务器的正常执行产生意外交互。

当无法通过其他方式跟踪问题时,可使用 Redis 软件看门狗功能来作为最后的手段:

  1. 使用CONFIG SET命令启用软件看门狗。
  2. Redis 开启对自身的持续监视。
  3. 如果 Redis 检测到服务器被阻塞到一些返回速度不够快的操作时,这可能是延迟问题的根源,则将关于服务器被阻塞位置的低级报告转储到日志文件中。
  4. 用户可以在 Redis Google Group 写消息(邮件)联系开发人员,邮件包括看门狗的报告(watchdog report)。

注意: Redis 软件看门狗是无法在 redis.conf 文件中配置启用,仅在运行的实例中通过命令开启,仅用于调试目的。

Redis 启用看门狗操作:

config set watchdog-period 500

上面例子,当服务器检测到 500毫秒 或理长的延迟时长记录延迟问题。最小可配置为 200毫秒。当使用完软件看门狗后,可以关闭它,将watchdog-period参数设置为0
重要说明:实例保持看门狗打开的时间比所需的时间长,这通常不是一个好主意。

以下是一旦软件看门狗检测到延迟超过配置的延迟,您将在日志文件中看到的内容示例:

[8547 | signal handler] (1333114359)
--- WATCHDOG TIMER EXPIRED ---
/lib/libc.so.6(nanosleep+0x2d) [0x7f16b5c2d39d]
/lib/libpthread.so.0(+0xf8f0) [0x7f16b5f158f0]
/lib/libc.so.6(nanosleep+0x2d) [0x7f16b5c2d39d]
/lib/libc.so.6(usleep+0x34) [0x7f16b5c62844]
./redis-server(debugCommand+0x3e1) [0x43ab41]
./redis-server(call+0x5d) [0x415a9d]
./redis-server(processCommand+0x375) [0x415fc5]
./redis-server(processInputBuffer+0x4f) [0x4203cf]
./redis-server(readQueryFromClient+0xa0) [0x4204e0]
./redis-server(aeProcessEvents+0x128) [0x411b48]
./redis-server(aeMain+0x2b) [0x411dbb]
./redis-server(main+0x2b6) [0x418556]
/lib/libc.so.6(__libc_start_main+0xfd) [0x7f16b5ba1c4d]
./redis-server() [0x411099]
------

注意:在上面示例中,使用DEBUG SLEEP命令来阻塞服务器。 如果服务器在不同的上下文中阻塞,则堆栈跟踪会有所不同。

相关参考

  1. Redis数据过期策略详解
  2. Redis数据过期和淘汰策略详解
  3. Redis过期键删除策略
  4. Redis 响应延迟问题排查
  5. Linux/Centos检测网络带宽与延迟
  6. linux 下使用 tc 模拟网络延迟和丢包
  7. Linux sysbench 基准和性能测试
更多内容请访问:IT源点

相关文章推荐

全部评论: 0

    我有话说: