我们在上一节末尾说了一个如果系统缓存被其他应用占用,乃至清理,MyISAM引擎很难利用系统缓存,以至于导致IO性能瓶颈

一般来说,应用系统的性能优化不能完全依靠系统缓存,而是能在应用程序内部分配内存,构建自主控制的缓存,或者使用第三方的缓存,比如Memcached Redis等

Redis是常用的键值存储系统之一,常用于数据库,高速缓存,消息队列代理等,Redis基于内存存储数据,但是为了保证持久化,也是有部分的IO,故还是可能引发IO性能问题

那么我们就来看一个Redis导致的IO性能问题

本次案例有对应的Python Redis构成,Python是一个基于Flask的应用,利用Redis,管理缓存,并提供接口来查询指定值的缓存数据

接下来,我们在第一个终端中,运行两个docker应用

# 注意下面的随机字符串是容器ID,每次运行均会不同,并且你不需要关注它

$ docker run –name=redis -itd -p 10000:80 feisky/redis-server

ec41cb9e4dd5cb7079e1d9f72b7cee7de67278dbd3bd0956b4c0846bff211803

$ docker run –name=app –network=container:redis -itd feisky/redis-app

2c54eb252d0552448320d9155a2618b799a1e71d7289ec7277a61e72a9de5fd0

docker run的过程中,将两个的network namespace进行了整合,之后利用

docker ps命令,确认两个容器处于运行状态

然后进行对应的curl进行相关的测试

curl http://192.1168.0.0:10000/search/xxx

查看返回

发现接口调用有10秒,这个响应时间,说明应用程序有问题

我们接下来,为了避免分析过程中客户端请求结束,循环来进行请求

$ while true;

do curl http://192.168.0.10:10000/get_cache;

done

然后继续进行分析

首先是top命令,查看全局信息

$ top

top – 12:46:18 up 11 days,  8:49,  1 user,  load average: 1.36, 1.36, 1.04

Tasks: 137 total,   1 running,  79 sleeping,   0 stopped,   0 zombie

%Cpu0  :  6.0 us,  2.7 sy,  0.0 ni,  5.7 id, 84.7 wa,  0.0 hi,  1.0 si,  0.0 st

%Cpu1  :  1.0 us,  3.0 sy,  0.0 ni, 94.7 id,  0.0 wa,  0.0 hi,  1.3 si,  0.0 st

KiB Mem :  8169300 total,  7342244 free,   432912 used,   394144 buff/cache

KiB Swap:        0 total,        0 free,        0 used.  7478748 avail Mem

PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND

9181 root      20   0  193004  27304   8716 S   8.6  0.3   0:07.15 python

9085 systemd+  20   0   28352   9760   1860 D   5.0  0.1   0:04.34 redis-server

368 root      20   0       0      0      0 D   1.0  0.0   0:33.88 jbd2/sda1-8

149 root       0 -20       0      0      0 I   0.3  0.0   0:10.63 kworker/0:1H

1549 root      20   0  236716  24576   9864 S   0.3  0.3  91:37.30 python3

在top的输出中可以看出,CPU0的iowait比较高,高达84%,但是CPU使用率不高,对应的python和redis-server只有8%和5%

对应的内存中,总内存只有8GB,剩余内存有7GB多,内存没有问题

综合top的信息,还是从iowait下手吧,看是不是IO的问题

先利用iostat进行查看io性能问题

iostat -d -x 1

图片

对应的sda的磁盘写数据为2.5MB,IO使用率是0,说明没有达到IO的性能瓶颈

那么,虽然没有说明异常,但是应用性能就是存在瓶颈,这是怎么回事呢?

我们还是要考虑从io入手,先查看io读写来自于那些的应用进程

pidstat中观察对应的IO性能

$ pidstat -d 1

12:49:35      UID       PID   kB_rd/s   kB_wr/s kB_ccwr/s iodelay  Command

12:49:36        0       368      0.00     16.00      0.00      86  jbd2/sda1-8

12:49:36      100      9085      0.00    636.00      0.00       1  redis-server

可能看出,IO最多的进程正是PID为9085的redis-server,然后进行查找redis读写的磁盘文件是什么

我们使用strace和lsof组合,查看redis-server正在写什么?

我们利用strace -f -T -tt -p 9085

查看对应的redis进程号

# -f表示跟踪子进程和子线程,-T表示显示系统调用的时长,-tt表示显示跟踪时间

$ strace -f -T -tt -p 9085

[pid  9085] 14:20:16.826131 epoll_pwait(5, [{EPOLLIN, {u32=8, u64=8}}], 10128, 65, NULL, 8) = 1 <0.000055>

[pid  9085] 14:20:16.826301 read(8, “*2\r\n$3\r\nGET\r\n$41\r\nuuid:5b2e76cc-“…, 16384) = 61 <0.000071>

[pid  9085] 14:20:16.826477 read(3, 0x7fff366a5747, 1) = -1 EAGAIN (Resource temporarily unavailable) <0.000063>

[pid  9085] 14:20:16.826645 write(8, “$3\r\nbad\r\n”, 9) = 9 <0.000173>

[pid  9085] 14:20:16.826907 epoll_pwait(5, [{EPOLLIN, {u32=8, u64=8}}], 10128, 65, NULL, 8) = 1 <0.000032>

[pid  9085] 14:20:16.827030 read(8, “*2\r\n$3\r\nGET\r\n$41\r\nuuid:55862ada-“…, 16384) = 61 <0.000044>

[pid  9085] 14:20:16.827149 read(3, 0x7fff366a5747, 1) = -1 EAGAIN (Resource temporarily unavailable) <0.000043>

[pid  9085] 14:20:16.827285 write(8, “$3\r\nbad\r\n”, 9) = 9 <0.000141>

[pid  9085] 14:20:16.827514 epoll_pwait(5, [{EPOLLIN, {u32=8, u64=8}}], 10128, 64, NULL, 8) = 1 <0.000049>

[pid  9085] 14:20:16.827641 read(8, “*2\r\n$3\r\nGET\r\n$41\r\nuuid:53522908-“…, 16384) = 61 <0.000043>

[pid  9085] 14:20:16.827784 read(3, 0x7fff366a5747, 1) = -1 EAGAIN (Resource temporarily unavailable) <0.000034>

[pid  9085] 14:20:16.827945 write(8, “$4\r\ngood\r\n”, 10) = 10 <0.000288>

[pid  9085] 14:20:16.828339 epoll_pwait(5, [{EPOLLIN, {u32=8, u64=8}}], 10128, 63, NULL, 8) = 1 <0.000057>

[pid  9085] 14:20:16.828486 read(8, “*3\r\n$4\r\nSADD\r\n$4\r\ngood\r\n$36\r\n535″…, 16384) = 67 <0.000040>

[pid  9085] 14:20:16.828623 read(3, 0x7fff366a5747, 1) = -1 EAGAIN (Resource temporarily unavailable) <0.000052>

[pid  9085] 14:20:16.828760 write(7, “*3\r\n$4\r\nSADD\r\n$4\r\ngood\r\n$36\r\n535″…, 67) = 67 <0.000060>

[pid  9085] 14:20:16.828970 fdatasync(7) = 0 <0.005415>

[pid  9085] 14:20:16.834493 write(8, “:1\r\n”, 4) = 4 <0.000250>

看出对应的系统调用比较频繁,其中可能涉及io的就是write和fdatasync导致的

然后查看lsof输出的系统调用的操作对象

lsof -p 9085

图片

在这些输出中,显示出只有7号文件涉及了磁盘写,对应的路径是

/data/appendonly.aof,调用有write和fdatasync

这个文件是,redis用于持久化的文件,那么是否是持久化的配置不合理,导致的磁盘写升高

执行命令 reids-cli config get ‘append*’

来查看对应的配置

发现appendfsync配置的是always,appendonly配置的是yes

redis提供了不同的持久化方式

一种是快照,一种是追加文件

快照对应的是按照指定的时间间隔,来生成数据的快照,保存在文件中,这个保存是利用的fork的子进程,但是由于数据量大,所以fork需要大量内存

还有就是追加文件,文件末尾追加记录,进行持久化,支持的配置是always everysec no等

always 表示 每个操作都会执行一次fsync

everysec 表示 每秒钟都调用一次fsync,保证1秒一次的保存

no 表示交给操作系统处理

appendfsync配置的是always,每次写数据,都有一次的fsync,导致磁盘压力过大,此外,还有strace,观察整个系统的执行情况,通过-e选项指定fdatasync后,得到下面结果

图片

这就是每10ms,都有fdatasync的问题

Redis的配置有问题,但是我们只进行了查询,也会触发fsync吗?

在每次的触发了磁盘写的时候,还会有一个编号为8的 TCP socket,观察上面的内容,8号对应的TCP读写,是一个标准的请求-响应 格式

从socket读取 GET uuid

然后读取的是SADD 命令

SADD是一个写操作,于是触发了持久化

观察这些结果,会发现GET返回good的时候,都会有一个SADD操作,导致明明是查询接口,却又大量的磁盘写

我们继续观察一下,上面的TCP链接,是不是链接到了Redis上

这需要我们进入容器的网络链接内部,查看完整的TCP链接

使用nsenter,可以进入容器命名空间

安装命令如下

docker run –rm -v /usr/local/bin:/target jpetazzo/nsenter

下述的命令为

nsenter –target $PID –net — lsof -i

在其中显示,TCP链接就是localhost:6379 -> localhost:32996

正是 redis -> python

reids延迟的原因可以发现了

1.reids配置文件,appendfsync是always,导致Redis每次的写操作,都会触发fdatasync的调用

2.python查询中会调用Redis的SADD命令,这是不合理的调用

首先我们将appendfsync设置为默认的everysec

docker exec -it redis redis-cli config set appendfsync everysec

然后是修改源码

图片

python将reids作为临时文件缓存临时存储的数据了,导致数据调用拥堵.完全没必要不是

基于这个思路,我们将代码修改了

导致性能平稳下来了

总结一下,本次还是利用了pidstat strace lsof nsenter等一系列的工具,找到了潜在问题,一个Redis的配置不合理,一个是Python对应的redis的滥用,找到瓶颈后,优化就简单了

发表评论

邮箱地址不会被公开。 必填项已用*标注