Linux性能优化-SQL慢查询

目录

安装环境

问题分析

后续补充


安装环境

git 下载相关例子代码

git clone https://github.com/feiskyer/linux-perf-examples

案例由三个容器组成,一个MySql数据库应用,一个商品搜索应用,一个数据处理应用
其中商品搜索应用提供了HTTP借口
/,                                  返回Index Page
/db/insert/products/,    插入指定数据量的商品信息
/products/,                      查询指定商品的信息,并返回处理事件

案例的整体结构如下

运行并检查docker容器中的应用


make run
docker run --name=mysql -itd -p 10000:80 -m 800m feisky/mysql:5.6
32f22ce141ba1279ce97cf6f6c00a51c3640edd494e01141bb2ef316ac579110
docker run --name=dataservice -itd --privileged feisky/mysql-dataservice
de3b1b96cabc40275b9a0c0b5773c87def36cb6334bab16d3f374c9312193971
docker run --name=app --network=container:mysql -itd feisky/mysql-slow
83c5dce8e2b20f1470f3b952b2858618d2de56c04d9c3933a5dd270d06825d6d



docker ps
CONTAINER ID        IMAGE                      COMMAND                  CREATED             STATUS              PORTS                             NAMES
83c5dce8e2b2        feisky/mysql-slow          "python /app.py"         3 seconds ago       Up 3 seconds                                          app
de3b1b96cabc        feisky/mysql-dataservice   "python /dataservi..."   4 seconds ago       Up 3 seconds                                          dataservice
32f22ce141ba        feisky/mysql:5.6           "docker-entrypoint..."   4 seconds ago       Up 4 seconds        3306/tcp, 0.0.0.0:10000->80/tcp   mysql

检查MySql是否初始化完成

docker logs -f mysql
2019-01-23 04:54:55 1 [Note] InnoDB: Compressed tables use zlib 1.2.11
2019-01-23 04:54:55 1 [Note] InnoDB: Using Linux native AIO
2019-01-23 04:54:55 1 [Note] InnoDB: Using CPU crc32 instructions
2019-01-23 04:54:55 1 [Note] InnoDB: Initializing buffer pool, size = 5.0M
2019-01-23 04:54:55 1 [Note] InnoDB: Completed initialization of buffer pool
2019-01-23 04:54:55 1 [Note] InnoDB: Highest supported file format is Barracuda.
2019-01-23 04:54:55 1 [Note] InnoDB: 128 rollback segment(s) are active.
2019-01-23 04:54:55 1 [Note] InnoDB: Waiting for purge to start
2019-01-23 04:54:55 1 [Note] InnoDB: 5.6.42 started; log sequence number 1625997
2019-01-23 04:54:55 1 [Note] Server hostname (bind-address): '*'; port: 3306
2019-01-23 04:54:55 1 [Note] IPv6 is available.
2019-01-23 04:54:55 1 [Note]   - '::' resolves to '::';
2019-01-23 04:54:55 1 [Note] Server socket created on IP: '::'.
2019-01-23 04:54:55 1 [Warning] Insecure configuration for --pid-file: Location '/var/run/mysqld' in the path is accessible to all OS users. Consider choosing a different directory.
2019-01-23 04:54:55 1 [Warning] 'proxies_priv' entry '@ root@32f22ce141ba' ignored in --skip-name-resolve mode.
2019-01-23 04:54:55 1 [Note] Event Scheduler: Loaded 0 events
2019-01-23 04:54:55 1 [Note] mysqld: ready for connections.
Version: '5.6.42-log'  socket: '/var/run/mysqld/mysqld.sock'  port: 3306  MySQL Community Server (GPL)

检查商品搜索应用的web请求是否正常

curl http://[IP]:10000
Index Page

执行 make init,初始化数据库,并插入10000条商品信息

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

查询商品

curl http://[IP]:10000/products/geektime
Got data: () in 4.605960845947266 sec 

#为了避免在分析的过程中客户端请求结束,把curl放到一个循环里,每次查询结束后,等待2秒,再开始新查询请求
while true; do curl http://[IP]:10000/products/geektime; sleep5; done

问题分析

通过top可以看出,负载很高了,但CPU使用率并不高,iowait很高

Tasks:  85 total,   2 running,  49 sleeping,   0 stopped,   0 zombie
%Cpu(s):  1.7 us,  4.7 sy,  0.0 ni, 32.1 id, 61.5 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  1008936 total,   186748 free,   578168 used,   244020 buff/cache
KiB Swap:        0 total,        0 free,        0 used.   271684 avail Mem 

  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND                                                                                             
31609 systemd+  20   0  771836  48444   4068 S  2.6  4.8   0:16.81 mysqld                                                                                              
31685 root      20   0   24348   5172    644 S  0.6  0.5   0:04.79 python                                                                                              
31764 root      20   0  670152 415096   1708 S  0.6 41.1   3:38.22 python 

观察I/O情况,使用iostat,iotop看磁盘情况
用iostat看,io util已经到100%了
iotop看占用最高的是mysql进程
通过pidstat看也是mysql进程占用很高

iostat -d -x 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
vda               0.00     0.20    0.17    0.31    48.33    35.26   344.42     0.72   66.49   18.94   92.89 1417.13  68.79

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     0.00  241.41    0.00 115830.30     0.00   959.60    11.31   50.23   50.23    0.00   4.19 101.11

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     0.00  121.00    0.00 108988.00     0.00  1801.45     7.55   62.26   62.26    0.00   8.25  99.80



iotop
  419 be/4 root        0.00 B/s    3.66 K/s  0.00 %  0.00 % systemd-journald
  419 be/4 root      105.41 K/s    7.81 K/s  0.00 %  0.03 % systemd-journald
31937 be/4 root        0.00 B/s    0.00 B/s  0.00 %  0.01 % [kworker/0:2-events_power_efficient]
  419 be/4 root      101.28 K/s    3.90 K/s  0.00 %  4.59 % systemd-journald
Total DISK READ :      76.09 M/s | Total DISK WRITE :       0.00 B/s
Actual DISK READ:      76.09 M/s | Actual DISK WRITE:      30.44 K/s
  TID  PRIO  USER     DISK READ  DISK WRITE  SWAPIN     IO>    COMMAND                                                                                                 
31917 be/4 systemd-   76.09 M/s    0.00 B/s  0.00 % 97.14 % mysqld --log_bin=on --sync_binlog=1
32081 be/4 root        3.80 K/s    0.00 B/s  0.00 %  2.69 % python /usr/sbin/iotop
  349 be/3 root        0.00 B/s    0.00 B/s  0.00 %  0.15 % [jbd2/vda1-8]



pidstat -d 1
01:27:24 PM   UID       PID   kB_rd/s   kB_wr/s kB_ccwr/s  Command
01:27:25 PM     0       419      0.00      3.96      0.00  systemd-journal
01:27:25 PM     0      1956      3.96      3.96      0.00  rsyslogd
01:27:25 PM   999     31609 117607.92      0.00      0.00  mysqld
01:27:25 PM     0     31685      0.00      3.96      0.00  python

01:27:25 PM   UID       PID   kB_rd/s   kB_wr/s kB_ccwr/s  Command
01:27:26 PM   999     31609 115846.46      0.00      0.00  mysqld

01:27:26 PM   UID       PID   kB_rd/s   kB_wr/s kB_ccwr/s  Command
01:27:27 PM     0       419      0.00      4.00      0.00  systemd-journal
01:27:27 PM     0     31461  12920.00      0.00      0.00  bash
01:27:27 PM   999     31609  38096.00      0.00      0.00  mysqld
01:27:27 PM     0     32153    636.00      0.00      0.00  sleep

通过strace分析程序,发现mysql有大量的读取操作
通过lsof看,是在不停的读 products.MYD 文件

strace -p 31609 -f
[pid 31917] read(37, "LfkC3vZPjtppqkYw14qPdtJyBNqJrrl2"..., 24576) = 24576
[pid 31917] read(37, "GYCfRdvn30wABejDBdBtSItHqfJXoHeH"..., 131072) = 131072
[pid 31917] read(37, "i95J5XIXsiTO4JtZZuhDrAe66aOfesVq"..., 20480) = 20480
[pid 31917] read(37, "8utFoYnF89gMbLB9bX1oNifFF0IQMcRQ"..., 131072) = 131072
[pid 31917] read(37, "V3x2yLQMscEJHLuh4d6LpZqOApXFJaLO"..., 24576) = 24576



pstree -p 31609
mysqld(31609)─┬─{mysqld}(31834)
              ├─{mysqld}(31835)
              ├─{mysqld}(31836)
              ├─{mysqld}(31837)
              ├─{mysqld}(31838)
              ├─{mysqld}(31839)
              ├─{mysqld}(31840)
              ├─{mysqld}(31841)
              ├─{mysqld}(31842)
              ├─{mysqld}(31843)
              ├─{mysqld}(31845)
              ├─{mysqld}(31846)
              ├─{mysqld}(31847)
              ├─{mysqld}(31848)
              ├─{mysqld}(31849)
              ├─{mysqld}(31850)
              ├─{mysqld}(31851)
              ├─{mysqld}(31852)
              ├─{mysqld}(31853)
              ├─{mysqld}(31854)
              └─{mysqld}(31917)



lsof -p 31609
。。。
mysqld  31609 systemd-bus-proxy   30uW  REG  253,1     98304  945464 /var/lib/mysql/mysql/slave_master_info.ibd
mysqld  31609 systemd-bus-proxy   31uW  REG  253,1     98304  945466 /var/lib/mysql/mysql/slave_worker_info.ibd
mysqld  31609 systemd-bus-proxy   32uW  REG  253,1     98304  945462 /var/lib/mysql/mysql/slave_relay_log_info.ibd
mysqld  31609 systemd-bus-proxy   33u   REG  253,1      2048  945452 /var/lib/mysql/mysql/event.MYI
mysqld  31609 systemd-bus-proxy   34u   REG  253,1         0  945453 /var/lib/mysql/mysql/event.MYD
mysqld  31609 systemd-bus-proxy   35u  sock    0,9       0t0  696303 protocol: TCPv6
mysqld  31609 systemd-bus-proxy   36u   REG  253,1    105472  945531 /var/lib/mysql/test/products.MYI
mysqld  31609 systemd-bus-proxy   37u   REG  253,1 512440000  945532 /var/lib/mysql/test/mysqld  31609 systemd-bus-proxy   30uW  REG  253,1     98304  945464 /var/lib/mysql/mysql/slave_master_info.ibd
mysqld  31609 systemd-bus-proxy   31uW  REG  253,1     98304  945466 /var/lib/mysql/mysql/slave_worker_info.ibd
mysqld  31609 systemd-bus-proxy   32uW  REG  253,1     98304  945462 /var/lib/mysql/mysql/slave_relay_log_info.ibd
mysqld  31609 systemd-bus-proxy   33u   REG  253,1      2048  945452 /var/lib/mysql/mysql/event.MYI
mysqld  31609 systemd-bus-proxy   34u   REG  253,1         0  945453 /var/lib/mysql/mysql/event.MYD
mysqld  31609 systemd-bus-proxy   35u  sock    0,9       0t0  696303 protocol: TCPv6
mysqld  31609 systemd-bus-proxy   36u   REG  253,1    105472  945531 /var/lib/mysql/test/products.MYI
mysqld  31609 systemd-bus-proxy   37u   REG  253,1 512440000  945532 /var/lib/mysql/test/products.MYD

/var/lib/mysql/test/products.MYD
这个分析前面有个37u,表示mysqld是以读写的方式访问文件的
MYD文件,是MyISAM引擎用来存储表数据的文件
文件名就是数据表的名字
这个文件的父目录,就是数据库的名字
也就是说,mysqld在读取数据库test中的products表
可以执行如下命令,查看mysqld在管理数据库test时的存储文件,由于MySql运行在容器中,需要通过docker exec到容器中查看

docker exec -it mysql ls /var/lib/mysql/test
db.opt  products.MYD  products.MYI  products.frm

/var/lib/mysql/test目录中有四个文件,每个文件的作用分别是
MYD  文件用来存储表的数据
MYI  文件用来存储表的索引
frm  文件用来存储表的元信息(如表结构)
opt  文件用来存储数据库的元信息(如字符集,字符校验规则等)


查看mysql的数据文件,看看这些正在读的是不是mysql正在使用的文件

docker exec -i -t mysql mysql -e 'show global variables like "%datadir%"';
+---------------+-----------------+
| Variable_name | Value           |
+---------------+-----------------+
| datadir       | /var/lib/mysql/ |
+---------------+-----------------+

根据上述路径可以看到,正在读的products.MYD 是mysql正在使用的文件
当然用lsof就可以确认了,这里是用mysql的视角确认的
进入mysql命令行

docker exec -i -t mysql mysql

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
show processlist;
+-----+------+-----------------+------+---------+------+--------------+-----------------------------------------------------+
| Id  | User | Host            | db   | Command | Time | State        | Info                                                |
+-----+------+-----------------+------+---------+------+--------------+-----------------------------------------------------+
| 165 | root | localhost       | NULL | Query   |    0 | init         | show processlist                                    |
| 167 | root | 127.0.0.1:47650 | test | Query   |    2 | Sending data | select * from products where productName='geektime' |
+-----+------+-----------------+------+---------+------+--------------+-----------------------------------------------------+

在以上的输出中
db,     表示数据库的名字
Command,表示SQL类型
Time,   表示执行时间
State,  表示状态
Info,   包含了完整的SQL语句

多执行几次 show processlist命令,可以发现
select * from products where productName='geektime'
这条SQL语句的执行时间比较长

用explain分析执行情况

use test;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> explain select * from products where productName='geektime';
+----+-------------+----------+------+---------------+------+---------+------+-------+-------------+
| id | select_type | table    | type | possible_keys | key  | key_len | ref  | rows  | Extra       |
+----+-------------+----------+------+---------------+------+---------+------+-------+-------------+
|  1 | SIMPLE      | products | ALL  | NULL          | NULL | NULL    | NULL | 10000 | Using where |
+----+-------------+----------+------+---------------+------+---------+------+-------+-------------+
1 row in set (0.00 sec)

explain的结果中有几个比较重要的字段
select_type, 表示查询类型,这里的SIMPLE表示此查询不包括UNION查询或子查询
table,       表示数据表的名字,这里是products
type,        表示查询的类型,这里的ALL表示全表查询,但索引查询应该是index类型才对
possible_keys,表示可能选用的索引,这里是NULL
key,          表示确切会使用的索引,这里也是NULL
rows,        表示查询索淼的行数,这里是10000


根据返回的信息可以判断出,查询没有索引,导致每次查询都扫描10000行数据
需要给这个表的相应字段增加索引,首先看下表的结构
 

show create table products;

| products | CREATE TABLE `products` (
  `id` int(11) NOT NULL,
  `productCode` text NOT NULL COMMENT '产品代码',
  `productName` text NOT NULL COMMENT '产品名称',
  `productLine` text NOT NULL COMMENT '产品线',
  `productScale` text NOT NULL,
  `productVendor` text NOT NULL,
  `productDescription` text NOT NULL,
  `quantityInStock` smallint(6) NOT NULL COMMENT '库存',
  `buyPrice` decimal(10,2) NOT NULL COMMENT '价格',
  `MSRP` decimal(10,2) NOT NULL COMMENT '建议零售价',
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC |

给productName字段增加索引,但执行报错了,因为productName是一个BLOB/TEXT类型的字段,需要设置一个长度,所以要给这种字段创建索引,就必须制定一个前缀长度

CREATE INDEX idx_products_name ON products(productName);
ERROR 1170 (42000): BLOB/TEXT column 'productName' used in key specification without a key length

有专门的算法,即通过计算前缀长度的选择性,来确定索引的长度,这里简单使用一个固定的值64,来为这个字段创建索引

CREATE INDEX idx_products_name ON products(productName(64));
Query OK, 10000 rows affected (9.29 sec)
Records: 10000  Duplicates: 0  Warnings: 0

再看curl的那个中端,现在查询时间已经变成1-2毫秒了,所以这次的性能问题就是没有索引导致的慢查询

Got data: () in 0.001811981201171875 sec 
Got data: () in 0.0017011165618896484 sec 
Got data: () in 0.001672983169555664 sec 

后续补充

目前为止,慢查询的问题已经接近了
对于这个案列,除了MySql和商品搜索应用外,还有一个DataService应用,这个应用其实是一个严重影响MySql性能的干扰应用,抛开上述的优化方法,这个案例还有一种优化方法,也就是停止DataService应用
首先删除掉数据库索引,回到原来状态,然后停止DataService应用,看优化效果

docker exec -i -t mysql mysql
use test;
DROP INDEX idx_products_name on products;
Query OK, 10000 rows affected (4.74 sec)
Records: 10000  Duplicates: 0  Warnings: 0

再看curl终端,这次执行速度又变慢了

Got data: () in 4.618531227111816 sec 
Got data: () in 4.709952354431152 sec 

现在停止DataService应用

docker rm -f dataservice
dataservice

docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                             NAMES
83c5dce8e2b2        feisky/mysql-slow   "python /app.py"         20 hours ago        Up 20 hours                                           app
32f22ce141ba        feisky/mysql:5.6    "docker-entrypoint..."   20 hours ago        Up 20 hours         3306/tcp, 0.0.0.0:10000->80/tcp   mysql

根据文章说最终结果是速度变快了,变成0.1秒了,但我这里没看到任何变化
用vmstat看,仍然是有大量的io读取操作,具体原因待查

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  1      0  69208   2072 364072    0    0   142    35    8   12  0  0 99  0  0
 0  1      0  64808   2064 368812    0    0 114496     0  358  452  2  6  0 92  0
 2  0      0  87576   1904 345024    0    0 110520     0  465  503  1  8  0 91  0
 0  0      0  75144   2052 358796    0    0 68800    20  396  500  2  5 19 74  0
 0  1      0  64208   2196 369012    0    0 28512     0  219  432  2  2 80 16  0
 0  1      0  63316   2196 370052    0    0 110672     0  362  498  2  6  0 92  0

猜你喜欢

转载自blog.csdn.net/hixiaoxiaoniao/article/details/86608271