性能测试问题 初步定位方法&样例–DB

2018年10月6日

1.DB服务常见问题

1、慢SQL,查询语句不好,没有优化,如:没有索引或者没有用到索引等
2、I/O吞吐量小,形成了瓶颈效应。
3、内存不足
4、 网络速度慢
5、锁或者死锁(这也是查询慢最常见的问题,是程序设计的缺陷)sp_lock,sp_who,活动的用户查看,原因是读写竞争资源。

 

2. DB监控指标&工具:

  1. 资源层:和其他软件一样
  2. 业务层:
  •               数据库可连接性
  •               监控数据库的连接数 :导致数据库的连接数突然增长可能是出现阻塞(数据库并发请求突增),或像redis这样的缓存失效(可以通过redis命中率监控观察)
  •               数据库吞吐量监控:TPS ,QPS
  •              数据库占用磁盘空间大小(数据大小 + 索引大小)
  •               磁盘IO
  •              死锁情况:
  •              慢SQL统计:

Postgres为例,选择满足如上多纬度实时监控需求的工具—pgwatch2(只支持linux环境的PG监控,Windows上的PG资源监控可以用zabbix+业务层监控pgadmin) :

 

3. DB服务问题定位流程:

3.1  观察慢SQL监控统计,某个时间点运行语句非常慢

3.2   如果这个语句平时也不慢,拿下来一执行几毫秒就完成了。很可能是语句执行的时候被阻塞了

阻塞有两种:硬件的资源等待,或语句资源争用的锁(也是我们常说的锁表/死锁/阻塞)

那我们就会清楚地知道当时是为什么慢? 卡在硬件还是软件的语句上?

如果这个SQL平时也慢,可以直接用EXPLAIN 分析原因;

3.3  通过资源监控查看CPU、内存、IO是否出现瓶颈?  是否有deadlock 出现?

3.4 一般慢SQL会同时出现CPU占用率升高等特征,再通过 EXPLAIN 分析较低效SQL的执行计划分析具体SQL有哪些问题(如果SQL语句无法再优化,可能就需要调整系统资源…)

一般对于测试人员,不要求能完全定位出所有问题根因,能初步定位一些SQL类的常见问题

 

4. 慢SQL 性能优化原则

btree索引使用原则:

 

 

5.  定位慢SQL常用查询语句(postgres)

PostgreSQL提供了pg_stat_statements来存储SQL的运行次数,总运行时间,shared_buffer命中次数,shared_buffer read次数等统计信息

一个数据库中SQL调用频率Top10排序
select s.* from pg_stat_statements s join pg_database d on d.oid = s.dbid and d.datname = current_database() ORDER BY mean_time DESC limit 10

一段时间内的总耗时Top10
select s.* from pg_stat_statements s join pg_database d on d.oid = s.dbid and d.datname = current_database() ORDER BY total_time DESC limit 10

单次最耗时(慢查询) Top10
SELECT query, calls, total_time, (total_time/calls) as average ,rows FROM pg_stat_statements WHERE rows > 0 ORDER BY average DESC LIMIT 10;

单次调用最耗IO SQL TOP 10
select userid,dbid,query,calls,total_time from pg_stat_statements order by (blk_read_time+blk_write_time)/calls desc limit 10;

总最耗IO SQL TOP 10
select userid, dbid, query from pg_stat_statements order by (blk_read_time+blk_write_time) desc limit 10;

单次最耗时(慢查询)SQL Top10 & DB缓存命中情况
SELECT query, calls, total_time, (total_time/calls) as average ,rows,
100.0 * shared_blks_hit /nullif(shared_blks_hit + shared_blks_read, 0) AS hit_percent
FROM pg_stat_statements WHERE rows > 0 ORDER BY average DESC LIMIT 10;

 

6.慢SQL几个案例:

6.1  Postgres –无索引,一次查询大量数据

1.1 现象
添加XX事件后,YY事件和ZZ事件界面全都加载不出来,提示网关超时
1.2 排查
a.  打开pg的pg_stat_statements后,重新点击业主人员页面
b.  通过查询pg_stat_statements视图得到慢查询的SQL列表(同时监控PG服务的CPU占用率飙高)

 SELECT query, calls, total_time, (total_time/calls) as average ,rows FROM pg_stat_statements WHERE rows > 0 ORDER BY average DESC LIMIT 10;
发现几个SQL耗时非常长,需要80+s、60+s、62s返回,其中一个如下:

使用explain分析SQL 执行计划:

EXPLAIN select count(?) from event e inner join XXX_A dc on dc.uuid = e.uuid left join YYY o on o.uuid = dc.org_uuid where ? = ? AND (o.uuid = $1 OR o.path like concat(?, $2,?)) CTE Scan on t (cost=1083521.50..1107521.50 rows=1200000 width=298) (actual time=19756.973..44323.061 rows=1200000 loops=1)

Aggregate (cost=2756804.06..2756804.07 rows=1 width=8)
-> Hash Join (cost=6393.00..2666798.62 rows=36002174 width=0)
Hash Cond: ((e.resource_uuid)::text = (dc.uuid)::text)
-> Append (cost=0.00..1601669.73 rows=36002174 width=33)
-> Seq Scan on event e (cost=0.00..0.00 rows=1 width=82)
-> Seq Scan on event_201805 e_1 (cost=0.00..10.40 rows=40 width=33)
-> Seq Scan on event_201806 e_2 (cost=0.00..10.20 rows=20 width=82)
-> Seq Scan on event_201807 e_3 (cost=0.00..10.20 rows=20 width=82)
-> Seq Scan on event_201808 e_4 (cost=0.00..10.20 rows=20 width=82)
-> Seq Scan on event_201809 e_5 (cost=0.00..10.20 rows=20 width=82)
-> Seq Scan on event_201810 e_6 (cost=0.00..10.20 rows=20 width=82)
-> Seq Scan on event_20180403 e_7 (cost=0.00..266965.04 rows=6001004 width=33)
-> Seq Scan on event_20180402 e_8 (cost=0.00..266963.04 rows=6001004 width=33)
-> Seq Scan on event_20180401 e_9 (cost=0.00..266918.14 rows=6000014 width=33)
-> Seq Scan on event_20180404 e_10 (cost=0.00..266921.82 rows=5999982 width=33)
-> Seq Scan on event_20180405 e_11 (cost=0.00..266920.16 rows=6000016 width=33)
-> Seq Scan on event_20180406 e_12 (cost=0.00..266920.13 rows=6000013 width=33)
-> Hash (cost=3971.00..3971.00 rows=100000 width=66)
-> Seq Scan on XXX_A dc (cost=0.00..3971.00 rows=100000 width=66) 

问题分析:有6张600W的表 使用的是Seq Scan 全表扫描…,没有索引

 

6.2  mysql  –锁表

现象:在XX平台 并发用户查询 YY 信息,Z min后速度将会越来越慢,需要>10s

存储过程循环30次更新操作

/*30次更新操作*/ 
BEGIN DECLARE v1 INT DEFAULT 30; 
WHILE v1 > 0 DO 
     update jx_attach set complete=1,attach_size=63100 where cycore_file_id='56677142da502cd8907eb58f'; 
     SET v1 = v1 - 1; 
END WHILE; 
END

时间: 29.876s

执行结果(持续一段时间后速度越来越慢,出现等待锁)

# Time: 151208 22:41:24 
# User@Host: zmduan[zmduan] @ [192.168.235.1] Id: 2 
# Query_time: 1.848644 Lock_time: 0.780778 Rows_sent: 0 Rows_examined: 393382 SET timestamp=1449643284; 
update jx_attach set complete=1,attach_size=63100 where cycore_file_id='56677142da502cd8907eb58f'; 
......... 
........ 
# User@Host: zmduan[zmduan] @  [192.168.235.1]  Id:     2
# Query_time: 2.868598  Lock_time: 1.558542 Rows_sent: 0  Rows_examined: 393382
SET timestamp=1449643805;
update jx_attach set complete=1,attach_size=63100 where cycore_file_id='56677142da502cd8907eb58f';
[root@localhost log]# tail -f slow_query.log 
# User@Host: zmduan[zmduan] @  [192.168.235.1]  Id:    19
# Query_time: 1.356797  Lock_time: 0.000169 Rows_sent: 1  Rows_examined: 393383
SET timestamp=1449643805;
SELECT *
FROM jx_attach ja,jx_feed jf
where ja.feed_id=jf.feed_id and ja.cycore_file_id='56677146da502cd8907eb5b7';
# User@Host: zmduan[zmduan] @  [192.168.235.1]  Id:     2
# Query_time: 2.868598  Lock_time: 1.558542 Rows_sent: 0  Rows_examined: 393382
SET timestamp=1449643805;
update jx_attach set complete=1,attach_size=63100 where cycore_file_id='56677142da502cd8907eb58f';

也可以通过使用命令查看是否有连接上的进程状态:

show processlist;

下图显示有一个连接上有锁表:

 

分析

CREATE TABLE `jx_attach` (
  `attach_id` int(11) NOT NULL AUTO_INCREMENT,
  `feed_id` int(11) DEFAULT NULL ,
  `attach_name` varchar(255) NOT NULL,
  `cycore_file_id` varchar(255) DEFAULT NULL , 
  `attach_size` bigint(20) NOT NULL DEFAULT '0',
  `complete` smallint(6) NOT NULL DEFAULT '0' ,
  PRIMARY KEY (`attach_id`),
  KEY `jx_trend_attach_FK` (`feed_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=394160 DEFAULT CHARSET=utf8;

使用innodb存储引擎,支持行级锁:innodb的行锁是通过给索引项加锁实现的,只有通过索引条件检索数据时,innodb才使用行锁,否则使用表锁。

根据当前的数据更新语句:

update jx_attach set complete=1,attach_size=63100 where cycore_file_id=’56677142da502cd8907eb58f’;

该条件字段cycore_file_id并没有添加索引,所以导致数据表被锁

解决方案:为cycore_file_id添加索引,改表级索引为行级索引

 最终效果:(30次更新操作)时间: 0.094s ,比原值29.876s 提升了几个数量级

 

没有评论

发表评论

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