Linux

从操作系统层面去分析慢SQL的一种思路

一 背景说明

两台机器,server1跑数据库和应用;server2向server1的应用执行一个查询请求,并持续监控该请求延迟;

以前,都是从DBA的视角分析和解决问题,定位发现慢SQL,分析原因,执行SQL优化改写,解决问题。

今天,尝试从操作系统层面去分析性能问题的另外一种思路。

server1: 172.16.17.7,4C 8G;

server2: 172.16.11.36,24C 64G;

二 分析步骤和思路

1 server1启动container

# 先下载对应的文件和代码:
# git clone https://github.com/feiskyer/linux-perf-examples
​
[root@guoxin7 ~]# git clone https://github.com/feiskyer/linux-perf-examples
正克隆到 'linux-perf-examples'...
remote: Enumerating objects: 340, done.
remote: Total 340 (delta 0), reused 0 (delta 0), pack-reused 340
接收对象中: 100% (340/340), 476.49 KiB | 0 bytes/s, done.
处理 delta 中: 100% (154/154), done.
[root@guoxin7 ~]# 
​
# cd linux-perf-examples/mysql-slow
[root@guoxin7 ~]# cd linux-perf-examples/mysql-slow/
[root@guoxin7 mysql-slow]# pwd
/root/linux-perf-examples/mysql-slow
[root@guoxin7 mysql-slow]# ll
总用量 36
-rwxr-xr-x 1 root root 2604 3月  22 16:41 app.py
-rw-r--r-- 1 root root  445 3月  22 16:41 dataservice.py
-rw-r--r-- 1 root root  143 3月  22 16:41 Dockerfile
-rw-r--r-- 1 root root  126 3月  22 16:41 Dockerfile.dataservice
-rw-r--r-- 1 root root  189 3月  22 16:41 Dockerfile.mysql
-rw-r--r-- 1 root root  945 3月  22 16:41 Makefile
-rw-r--r-- 1 root root 1336 3月  22 16:41 mysqld.cnf
-rw-r--r-- 1 root root  803 3月  22 16:41 README.md
-rw-r--r-- 1 root root  657 3月  22 16:41 tables.sql
[root@guoxin7 mysql-slow]# 
# make run
[root@guoxin7 mysql-slow]# make run
docker run --name=mysql -itd -p 10000:80 -m 800m feisky/mysql:5.6
22af305e0ae9712e5c0cfb0eeccd172a95824b79f33e5f228b7c2dc3e35fb14c
docker run --name=dataservice -itd --privileged feisky/mysql-dataservice
50e0234b81be4d456b0780d05773328ed16f49f3c89578cb8c37a7d75d6d2ab8
docker run --name=app --network=container:mysql -itd feisky/mysql-slow
e15f238546d93833741f1ae487ad10290842092d246340481126ffe07ac3ef16
[root@guoxin7 mysql-slow]# 
# 确定3个container正常运行
[root@guoxin7 mysql-slow]# docker ps
CONTAINER ID        IMAGE                      COMMAND                  CREATED             STATUS              PORTS                             NAMES
e15f238546d9        feisky/mysql-slow          "python /app.py"         22 seconds ago      Up 17 seconds                                         app
50e0234b81be        feisky/mysql-dataservice   "python /dataservi..."   23 seconds ago      Up 22 seconds                                         dataservice
22af305e0ae9        feisky/mysql:5.6           "docker-entrypoint..."   24 seconds ago      Up 23 seconds       3306/tcp, 0.0.0.0:10000->80/tcp   mysql
[root@guoxin7 mysql-slow]# 
# 验证服务:curl http://127.0.0.1:10000/
[root@guoxin7 mysql-slow]# curl http://127.0.0.1:10000/
Index Page[root@guoxin7 mysql-slow]# 
[root@guoxin7 mysql-slow]# 

2 server1给名为mysql的container初始化数据

#该步骤可能需要一定的时间完成
[root@guoxin7 mysql-slow]# pwd
/root/linux-perf-examples/mysql-slow
[root@guoxin7 mysql-slow]# make init
docker exec -i mysql mysql -uroot -P3306 < tables.sql
curl http://127.0.0.1:10000/db/insert/products/10000
insert 10000 lines
[root@guoxin7 mysql-slow]# 

3 server2持续向server1发送服务请求,并监控延迟

# 把服务请求放到一个while循环里,并且间隔2秒,持续请求,并打印出服务响应的时间;
[root@centos-master ~]# while true; do time curl http://172.16.17.7:10000/products/geektime;sleep 2; done;
Got data: () in 0.5451030731201172 sec 
​
real    0m0.555s
user    0m0.003s
sys     0m0.005s
Got data: () in 1.4194972515106201 sec 
​
real    0m1.431s
user    0m0.004s
sys     0m0.004s
Got data: () in 1.1215379238128662 sec 
​
real    0m1.132s
user    0m0.006s
sys     0m0.002s
Got data: () in 0.8462660312652588 sec 
​
real    0m0.857s
user    0m0.004s
sys     0m0.005s
Got data: () in 0.9769577980041504 sec 
​
real    0m0.988s
user    0m0.003s
sys     0m0.005s
Got data: () in 0.1878516674041748 sec 
​
real    0m0.199s
user    0m0.006s
sys     0m0.003s
^C
[root@centos-master ~]# 
​

从请求跟踪结果看到,服务的响应快时0.18秒就结束了,而慢的时候则要1秒多,这说明服务的响应时间不够稳定。

4 监控server1的CPU、IO指标

[root@guoxin7 ~]# top
top - 16:57:31 up 1 day,  6:01,  3 users,  load average: 0.75, 0.96, 0.76
Tasks: 220 total,   2 running, 218 sleeping,   0 stopped,   0 zombie
%Cpu0  :  3.7 us,  9.0 sy,  0.0 ni, 51.8 id, 34.8 wa,  0.0 hi,  0.7 si,  0.0 st
%Cpu1  :  1.7 us,  6.7 sy,  0.0 ni, 80.1 id, 11.1 wa,  0.0 hi,  0.3 si,  0.0 st
%Cpu2  :  0.7 us, 10.0 sy,  0.0 ni, 79.6 id,  9.4 wa,  0.0 hi,  0.3 si,  0.0 st
%Cpu3  :  0.7 us,  0.7 sy,  0.0 ni, 92.6 id,  6.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem : 41037092 total, 35861428 free,  1870708 used,  3304956 buff/cache
KiB Swap:  8257532 total,  8257532 free,        0 used. 36242360 avail Mem 
​
  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                                                                                             
14813 polkitd   20   0  833756  59956   8036 S  16.3  0.1   0:20.00 mysqld --log_bin=on --sync_binlog=1                                                                 
14941 root      20   0   24336   7256   2728 S   9.3  0.0   0:39.58 python /dataservice.py                                                                              
    1 root      20   0  191172   4068   2624 S   0.7  0.0   1:37.85 /usr/lib/systemd/systemd --switched-root --system --deserialize 22  

查看系统的IO指标:iostat -dx 1;

[root@guoxin7 ~]# iostat -dx 1
Device:         rrqm/s   wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
sda               0.00     0.00 1013.00    5.00 466224.00   164.00   916.28    10.22    9.84    9.62   54.60   0.77  78.40
scd0              0.00     0.00    0.00    0.00     0.00     0.00     0.00     0.00    0.00    0.00    0.00   0.00   0.00
dm-0              0.00     0.00 1025.00    6.00 472368.00   172.00   916.66    10.22    9.72    9.51   45.50   0.76  78.40
dm-1              0.00     0.00    0.00    0.00     0.00     0.00     0.00     0.00    0.00    0.00    0.00   0.00   0.00
​
Device:         rrqm/s   wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
sda               0.00     0.00  383.00   18.00 152032.00   188.00   759.20     8.07   20.64   20.60   21.44   2.30  92.40
scd0              0.00     0.00    0.00    0.00     0.00     0.00     0.00     0.00    0.00    0.00    0.00   0.00   0.00
dm-0              0.00     0.00  372.00   17.00 145896.00   180.00   751.03     8.07   21.27   21.20   22.71   2.38  92.40
dm-1              0.00     0.00    0.00    0.00     0.00     0.00     0.00     0.00    0.00    0.00    0.00   0.00   0.00
​
Device:         rrqm/s   wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
sda               0.00     0.00 1349.00    0.00 69604.00     0.00   103.19     4.32    3.20    3.20    0.00   0.20  27.10
scd0              0.00     0.00    0.00    0.00     0.00     0.00     0.00     0.00    0.00    0.00    0.00   0.00   0.00
dm-0              0.00     0.00 1348.00    0.00 69596.00     0.00   103.26     4.32    3.21    3.21    0.00   0.20  27.10
dm-1              0.00     0.00    0.00    0.00     0.00     0.00     0.00     0.00    0.00    0.00    0.00   0.00   0.00
​
Device:         rrqm/s   wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
sda               0.00     0.00  767.00    0.00 392704.00     0.00  1024.00     9.10   11.79   11.79    0.00   1.06  81.60
scd0              0.00     0.00    0.00    0.00     0.00     0.00     0.00     0.00    0.00    0.00    0.00   0.00   0.00
dm-0              0.00     0.00  783.00    0.00 400896.00     0.00  1024.00     9.10   11.54   11.54    0.00   1.04  81.60
dm-1              0.00     0.00    0.00    0.00     0.00     0.00     0.00     0.00    0.00    0.00    0.00   0.00   0.00
​
Device:         rrqm/s   wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
sda               0.00     0.00  211.00    0.00 107728.00     0.00  1021.12     3.32   16.04   16.04    0.00   1.36  28.80
scd0              0.00     0.00    0.00    0.00     0.00     0.00     0.00     0.00    0.00    0.00    0.00   0.00   0.00
dm-0              0.00     0.00  195.00    0.00 99536.00     0.00  1020.88     3.32   17.36   17.36    0.00   1.48  28.80
dm-1              0.00     0.00    0.00    0.00     0.00     0.00     0.00     0.00    0.00    0.00    0.00   0.00   0.00
​
Device:         rrqm/s   wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
sda               0.00     0.00    1.00    1.00     4.00     4.00     8.00     0.03   13.00    8.00   18.00  13.00   2.60
scd0              0.00     0.00    0.00    0.00     0.00     0.00     0.00     0.00    0.00    0.00    0.00   0.00   0.00
dm-0              0.00     0.00    1.00    1.00     4.00     4.00     8.00     0.03   13.00    8.00   18.00  13.00   2.60
dm-1              0.00     0.00    0.00    0.00     0.00     0.00     0.00     0.00    0.00    0.00    0.00   0.00   0.00
​
Device:         rrqm/s   wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
sda               0.00     0.00  978.00    0.00 500432.00     0.00  1023.38     6.43    6.58    6.58    0.00   0.63  61.20
scd0              0.00     0.00    0.00    0.00     0.00     0.00     0.00     0.00    0.00    0.00    0.00   0.00   0.00
dm-0              0.00     0.00  978.00    0.00 500432.00     0.00  1023.38     6.42    6.57    6.57    0.00   0.63  61.20
dm-1              0.00     0.00    0.00    0.00     0.00     0.00     0.00     0.00    0.00    0.00    0.00   0.00   0.00
​
^C
[root@guoxin7 ~]# 

跟踪mysqld进程的IO指标:pidstat -d -l -p 14813

[root@guoxin7 ~]# pidstat -d -l -p 14813 1
Linux 3.10.0-1160.59.1.el7.x86_64 (guoxin7)     2022年03月22日  _x86_64_        (4 CPU)
​
17时04分51秒   UID       PID   kB_rd/s   kB_wr/s kB_ccwr/s  Command
17时04分52秒   999     14813      0.00      0.00      0.00  mysqld --log_bin=on --sync_binlog=1 
17时04分53秒   999     14813 242946.53      0.00      0.00  mysqld --log_bin=on --sync_binlog=1 
17时04分54秒   999     14813 168004.00      0.00      0.00  mysqld --log_bin=on --sync_binlog=1 
17时04分55秒   999     14813      0.00      0.00      0.00  mysqld --log_bin=on --sync_binlog=1 
17时04分56秒   999     14813  87024.00      0.00      0.00  mysqld --log_bin=on --sync_binlog=1 
17时04分57秒   999     14813      0.00      0.00      0.00  mysqld --log_bin=on --sync_binlog=1 
17时04分58秒   999     14813  97792.00      0.00      0.00  mysqld --log_bin=on --sync_binlog=1 
17时04分59秒   999     14813 402640.00      0.00      0.00  mysqld --log_bin=on --sync_binlog=1 
17时05分00秒   999     14813      0.00      0.00      0.00  mysqld --log_bin=on --sync_binlog=1 
17时05分01秒   999     14813 396800.00      0.00      0.00  mysqld --log_bin=on --sync_binlog=1 
17时05分02秒   999     14813 103632.00      0.00      0.00  mysqld --log_bin=on --sync_binlog=1 
17时05分03秒   999     14813      0.00      0.00      0.00  mysqld --log_bin=on --sync_binlog=1 
17时05分04秒   999     14813 380416.00      0.00      0.00  mysqld --log_bin=on --sync_binlog=1 
17时05分05秒   999     14813 120016.00      0.00      0.00  mysqld --log_bin=on --sync_binlog=1 
17时05分06秒   999     14813      0.00      0.00      0.00  mysqld --log_bin=on --sync_binlog=1 
17时05分07秒   999     14813 302592.00      0.00      0.00  mysqld --log_bin=on --sync_binlog=1 
^C
平均时间:   999     14813 143928.29      0.00      0.00  mysqld --log_bin=on --sync_binlog=1 
[root@guoxin7 ~]# 

其实,到这里的话,从DBA的视角来分析,此时,看到这些信息,大概能看出来,mysqld进程在执行一些慢SQL了,可以进入MySQL数据库里,执行show full processlist;查看慢SQL并分析了。

但是,这里,我们可以从操作系统的角度,继续分析一下该问题了。

5 跟踪mysqld进程的读写文件

strace -fp 14813;加上-f选项,可以跟踪看到进程里的线程的内核调用信息:

[root@guoxin7 ~]# strace -fp 14813
strace: Process 14813 attached with 22 threads
[pid 15869] futex(0x55de811f6044, FUTEX_WAIT_PRIVATE, 371, NULL <unfinished ...>
[pid 15653] rt_sigtimedwait([HUP QUIT ALRM TERM TSTP], NULL, NULL, 8 <unfinished ...>
[pid 15652] restart_syscall(<... resuming interrupted futex ...> <unfinished ...>
...
[pid 15869] read(37, "WVyYSro50pa8VH3knzlPDp5U51YZMsnX"..., 24576) = 24576
[pid 15869] read(37, "h8eOUppxAgkNCezmYTYZxw1cgv7A\10\0000q"..., 131072) = 131072
[pid 15869] read(37, "r3T249CjU2StnOLyHJdr0jBe0jAsb2WM"..., 20480) = 20480
[pid 15869] read(37, "YpdGvDesdyiaBBNMYcUqADdTRk92Shol"..., 131072) = 131072
[pid 15869] read(37, "A4RLFrKXd7LiD8S2Zss972xxklYt3YYk"..., 24576) = 24576
[pid 15869] read(37, "am0cVxqqD9WCU9QRV2Ad6QJ0v6PF67qP"..., 131072) = 131072
[pid 15869] read(37, "X7wofeSpni1jVpNDOvzlFSTkS2GAojGP"..., 20480) = 20480
[pid 15869] read(37, ^C"JZ31V0lkO5bnrXwDW3EoRPjvUwXQivG3"..., 131072) = 131072
strace: Process 15634 detached
...
strace: Process 15869 detached
[root@guoxin7 ~]# 

看到mysqld进程14813在读写37号文件。

lsof -p 14813;查看进程打开file descriptor文件句柄信息:

[root@guoxin7 ~]# lsof -p 14813
COMMAND   PID    USER   FD   TYPE DEVICE  SIZE/OFF      NODE NAME
mysqld  14813 polkitd  cwd    DIR  253,0       239  12182228 /var/lib/mysql
mysqld  14813 polkitd  rtd    DIR   0,37        28 201445261 /
...
mysqld  14813 polkitd   34u   REG  253,0         0 201453422 /var/lib/mysql/mysql/event.MYD
mysqld  14813 polkitd   36u   REG  253,0    105472     42706 /var/lib/mysql/test/products.MYI
mysqld  14813 polkitd   37u   REG  253,0 512440000     42714 /var/lib/mysql/test/products.MYD
[root@guoxin7 ~]# 

MySQL在读写/var/lib/mysql/test/products.MYD,熟悉MySQL数据库的话,就可以看出个大概了。

6 跟踪分析系统缓存情况

使用vmstat跟踪系统的内存情况:

[root@guoxin7 ~]# vmstat 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      0 36238072      0 2928668    0    0  6804   484   95   21 11  2 85  2  0
 0  1      0 36237476      0 2929080    0    0   512     0 1725  667  0  0 98  2  0
 1  1      0 36453100      0 2713112    0    0 288204    68 3588 1869  2 11 78 10  0
 0  0      0 36450888      0 2715296    0    0  1816   144 2480 1248  1  1 92  6  0
 1  0      0 36279616      0 2886488    0    0 171520     0 1817 1106  1  2 87 10  0
 1  0      0 36137124      0 3029224    0    0 328840     4 2733 1692  1  8 78 13  0
 0  0      0 36326932      0 2838596    0    0 125616     0 4977 4699  3  4 80 13  0
 0  1      0 36147444      0 3018600    0    0 179712     0 2079  982  1  3 92  4  0
 0  0      0 35828540      0 3339104    0    0 320720   428 2956 1629  1  4 81 14  0
 0  0      0 36454524      0 2712964    0    0     4     4 1979  706  0  6 94  0  0
 0  1      0 36323364      0 2845236    0    0 132404     0 2287  996  1  2 90  8  0
 0  0      0 35953572      0 3214948    0    0 369848     0 3118 1645  2  4 90  4  0
 0  0      0 36454432      0 2712992    0    0     4     4 1940  725  0  6 93  0  0
 1  0      0 35958036      0 3207912    0    0 495104     0 3362 1890  1  6 85  8  0
^C
[root@guoxin7 ~]# free -m
              total        used        free      shared  buff/cache   available
Mem:          40075        1823       35107        2436        3143       35395
Swap:          8063           0        8063
[root@guoxin7 ~]# free -m
              total        used        free      shared  buff/cache   available
Mem:          40075        1824       35589        2444        2661       35437
Swap:          8063           0        8063
[root@guoxin7 ~]# 

从上,我们可以看到系统缓存page cache信息的明显变化,cache列的系统缓存,io列中的bi变动非常大。

free -m看到的结果中,buff/cache抖动也非常明显。

说明系统缓存不断的在剧烈变化。

7 查看docker容器具体内容

[root@guoxin7 ~]# docker ps
CONTAINER ID        IMAGE                      COMMAND                  CREATED             STATUS              PORTS                             NAMES
e15f238546d9        feisky/mysql-slow          "python /app.py"         38 minutes ago      Up 38 minutes                                         app
50e0234b81be        feisky/mysql-dataservice   "python /dataservi..."   38 minutes ago      Up 38 minutes                                         dataservice
22af305e0ae9        feisky/mysql:5.6           "docker-entrypoint..."   38 minutes ago      Up 38 minutes       3306/tcp, 0.0.0.0:10000->80/tcp   mysql
[root@guoxin7 ~]# docker exec -it dataservice /bin/bash
root@50e0234b81be:/# ls
bin  boot  dataservice.py  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
root@50e0234b81be:/# cat dataservice.py 
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
import time
filename = "/tmp/dataservice"
​
​
def write():
    with open(filename, "w+") as f:
        f.write("data")
​
​
def read():
    with open(filename) as f:
        return f.read()
​
​
def clean_cache():
    with open("/proc/sys/vm/drop_caches", "w") as f:
        f.write("1")
​
if __name__ == "__main__":
    while True:
        write()
        clean_cache()
        read()
        time.sleep(3)
root@50e0234b81be:/# 

原来dataservice这个容器在清理系统缓存。

8 停掉该container

[root@guoxin7 ~]# docker stop dataservice
dataservice
[root@guoxin7 ~]# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                             NAMES
e15f238546d9        feisky/mysql-slow   "python /app.py"         40 minutes ago      Up 40 minutes                                         app
22af305e0ae9        feisky/mysql:5.6    "docker-entrypoint..."   40 minutes ago      Up 40 minutes       3306/tcp, 0.0.0.0:10000->80/tcp   mysql
[root@guoxin7 ~]# vmstat 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      0 35807660      0 3370024    0    0  6931   482   97   23 11  2 85  3  0
 0  0      0 35807660      0 3370092    0    0     0     0 1386  607  0  0 100  0  0
 0  0      0 35807536      0 3370092    0    0     0     0 1686  760  1  4 95  0  0
 0  0      0 35806592      0 3370096    0    0     0     0 3575 3717  3  2 96  0  0
 0  0      0 35806664      0 3370100    0    0     0     0 1774  809  1  5 94  0  0
 0  0      0 35806664      0 3370100    0    0     0     0 1618  605  0  0 100  0  0
 0  0      0 35806664      0 3370224    0    0   124     0 1707  780  1  4 94  2  0
 0  0      0 35806568      0 3370224    0    0     0     0 1366  570  0  0 99  0  0
 0  0      0 35806664      0 3370224    0    0     0     0 1779  770  1  4 95  0  0
 0  0      0 35806600      0 3370224    0    0     0     0 1438  648  0  0 100  0  0
^C
[root@guoxin7 ~]# 

停掉dataservice这个container之后,看到系统缓存基本维持不变。同时,客户端的服务请求也维持在稳定的响应延迟了:

[root@centos-master ~]# while true; do time curl http://172.16.17.7:10000/products/geektime;sleep 2; done;
Got data: () in 0.17266535758972168 sec 
​
real    0m0.184s
user    0m0.002s
sys     0m0.006s
Got data: () in 0.17386388778686523 sec 
​
real    0m0.186s
user    0m0.007s
sys     0m0.002s
Got data: () in 0.1739194393157959 sec 
​
real    0m0.185s
user    0m0.004s
sys     0m0.005s
^C
[root@centos-master ~]# 

三小结和收获

从DBA的视角来看这个性能问题的话,从top和iostat以及pidstat -d -l -p mysql_pid 1;看出确实是数据库的进程“慢”的话,就会从数据库层面去分析并优化这个case了。

但是,这里提供了另外一个思路,从操作系统层面,通过strace -fp mysqld_pid;lsof -p mysqld_pid;以及vmstat 继续跟踪分析系统内存和mysqld进程在内核内部的系统调用和读写文件来分析问题。

当然,最后还可以继续优化那个SQL,给where条件中的查询字段加上index,这里略过并没有包含进来。

四 参考

倪朋飞老师的Linux性能优化实战,28 | 案例篇:一个SQL查询要15秒,这是怎么回事?

只是,我这里的演示环境,并不像倪朋飞老师环境的,查询需要15秒那么明显。