从这里你可以看到 , 每隔 10ms 左右 , 就会有一次 fdatasync 调用 , 并且每次调用本身也要消耗 7~8ms 。
不管哪种方式 , 都可以验证我们的猜想 , 配置确实不合理 。这样 , 我们就找出了 Redis 正在进行写入的文件 , 也知道了产生大量 I/O 的原因 。
不过 , 回到最初的疑问 , 为什么查询时会有磁盘写呢?按理来说不应该只有数据的读取吗?这就需
要我们再来审查一下 strace -f -T -tt -p 9085 的结果 。
read(8, "*2rn$3rnGETrn$41rnuuid:53522908-"..., 16384)
write(8, "$4rngoodrn", 10)
read(8, "*3rn$4rnSADDrn$4rngoodrn$36rn535"..., 16384)
write(7, "*3rn$4rnSADDrn$4rngoodrn$36rn535"..., 67)
write(8, ":1rn", 4)
细心的你应该记得 , 根据 lsof 的分析 , 文件描述符编号为 7 的是一个普通文件 /data/appendonly.aof , 而编号为 8 的是 TCP socket 。而观察上面的内容 , 8 号对应的 TCP 读写 , 是一个标准的“请求 - 响应”格式 , 即:
从 socket 读取 GET uuid:53522908-… 后 , 响应 good;
再从 socket 读取 SADD good 535… 后 , 响应 1 。
对 Redis 来说 , SADD 是一个写操作 , 所以 Redis 还会把它保存到用于持久化的 appendonly.aof 文件中 。
观察更多的 strace 结果 , 你会发现 , 每当 GET 返回 good 时 , 随后都会有一个 SADD 操作 , 这也就导致了 , 明明是查询接口 , Redis 却有大量的磁盘写 。
到这里 , 我们就找出了 Redis 写磁盘的原因 。不过 , 在下最终结论前 , 我们还是要确认一下 , 8 号 TCP socket 对应的 Redis 客户端 , 到底是不是我们的案例应用 。
我们可以给 lsof 命令加上 -i 选项 , 找出 TCP socket 对应的 TCP 连接信息 。不过 , 由于 Redis 和 Python 应用都在容器中运行 , 我们需要进入容器的网络命名空间内部 , 才能看到完整的 TCP 连接 。
注意:下面的命令用到的 nsenter 工具 , 可以进入容器命名空间 。如果你的系统没有安装 , 请运行下面命令安装 nsenter:
docker run --rm -v /usr/local/bin:/target jpetazzo/nsenter
还是在终端一中 , 运行下面的命令:
# 由于这两个容器共享同一个网络命名空间 , 所以我们只需要进入app的网络命名空间即可
$ PID=$(docker inspect --format {{.State.Pid}} app)# -i表示显示网络套接字信息$ nsenter --target $PID --net -- lsof -iCOMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAMEredis-ser 9085 systemd-network 6u IPv4 15447972 0t0 TCP localhost:6379 (LISTEN)redis-ser 9085 systemd-network 8u IPv4 15448709 0t0 TCP localhost:6379->localhost:32996 (ESTABLISHED)python 9181 root 3u IPv4 15448677 0t0 TCP *:http (LISTEN)python 9181 root 5u IPv4 15449632 0t0 TCP localhost:32996->localhost:6379 (ESTABLISHED)
这次我们可以看到 , redis-server 的 8 号文件描述符 , 对应 TCP 连接 localhost:6379->localhost:32996 。其中 , localhost:6379 是 redis-server 自己的监听端口 , 自然 localhost:32996 就是 redis 的客户端 。
再观察最后一行 , localhost:32996 对应的 , 正是我们的 Python 应用程序(进程号为 9181) 。
历经各种波折 , 我们总算找出了 Redis 响应延迟的潜在原因 。总结一下 , 我们找到两个问题 。
第一个问题 , Redis 配置的 appendfsync 是 always , 这就导致 Redis 每次的写操作 , 都会触发 fdatasync 系统调用 。今天的案例 , 没必要用这么高频的同步写 , 使用默认的 1s 时间间隔 , 就足够了 。
第二个问题 , Python 应用在查询接口中会调用 Redis 的 SADD 命令 , 这很可能是不合理使用缓存导致的 。
推荐阅读
- 响应速度与智能化如何平衡,携程酒店搜索实践
- 使用Redis轻松实现秒杀系统
- 慢性咽喉炎的症状
- mongodb,redis,hbase,三者都是nosql数据库,他们的最大区别和不同定位是什么?
- Redis、传统数据库、HBase以及Hive的区别
- 聊聊Mysql索引和redis跳表
- Redis:缓存被我写满了,该怎么办?
- 肾结石严重吗?专业人士来回答!
- 老人心肌梗塞严重吗
- ios15.1更新后电池,苹果11升级ios14.4.1耗电严重-