2. 2 被测程序分析
2.1 影响客户申领性能的主要因素
涉及的数据库表及数据量、索引情况、访问方式
在 CRM 的性能测试中,功能涉及的数据库表、数据库表的数据量、索引使用情况至关
重要。客户申领涉及的主要数据库表如下:
表名 数据量 访问方式
Tb_customer 456989 读、写
Tb_follow_assign 334629 读、写
在对索引的使用中,主要是查看索引是否缺失、索引的区分度。尤其是线下构造大量数
据时, 需要将索引的区分度与线上进行对比。 以防止数据的不合理造成索引效率不高等
问题呢。
s
客户申领的个数
客户申领的个数在大并发下可能会对性能产生较大影响。
了 SQL 语句筛选、对记录加锁的范围。如下语句所示,申领个数为 3 时,会锁定 3 条
记录 (这个说法不太准确, 根据 INNODB 行锁实现方式,
lu 从实现上看,
具体锁定记录数可能会大于 3)
申领的个数决定
。
ha
以下是涉及到的核心 SQL 语句:
select f.follow_assign_id follow_assign_id,f.cust_id cust_id from tb_follow_assign
f,tb_customer c where c.cust_id=f.cust_id and c.pose_id=11 and f.follow_id=0 and
f.cust_id not in (53041) and c.black_flag =1 and c.follow_audit=0 limit 3 for update;
p
ce
并发请求数
不难理解,并发请求是影响性能的主要因素之一,并发数越大,系统负载越大。
bu
2.2 程序实现的主要逻辑
尽量清楚程序实现的主要逻辑是测试准备的主要功课之一。 在知道逻辑的基础上,可以对程
序处理数据的方式、使用资源的方式有了比较清楚的把握, 了解那些因素会更多的影响性能。
主要涉及如下几方面:
访问的主要数据库表
是否可能存在大量的频繁的堆内存分配(如,记录导出)
是否可能存在大量的 SQL 拼装(如,各种报表)
其他更多的和性能相关的因素
2.3 线上日志分析
对于已经上线的系统,分析系统负载特征的最权威的数据就是访问日志。通过访问日志,我
们可以得到:
哪些功能点是访问的最频繁的
beiyu95@hotmail.com
3. 各个主要功能点在系统中访问比例是多大
各功能点访问的响应时间状况如何
客户申领功能是典型的集中使用功能,会在 10 点 30 分、14 点 28 分存在集中访问。根据线
上日志分析,在 10:30 分,一秒之内,申领客户资料的并发请求达到 10。20s 之内,总请求
数达到 123。平均申领个数为 3,平均响应时间在 5s 左右。
s
lu
图 1 45 秒内访问情况统计
ha
3 测试场景设计
p
ce
根据本次性能测试的目的。场景设计主要是采用不同用户数(20,40,60,80,100,120) (下图的
interactive schedule graph 显示了不同时段用户数)并发申请 3 个客户资料。观察响应时间及
服务器资源使用。 得到不同并发用户数下程序的响应时间, 并进一步确定可接受的并发极限。
bu
beiyu95@hotmail.com
4. 图 2 LoadRunner 的压力场景设计
4 测试结果分析
结果分析是性能测试中难度较大的一环,涉及的因素众多。但也并非无迹可寻,下面以客户
申领的性能数据为例,给出了需要分析的各个方面。供大家参考,主要包括:
响应时间
服务器工作状态
每个工作模块压力下的负载
压力下模块间的想好影响
异常/日志分析
深入工作模块内部/底层
操作系统
Web 服务器内部
s
数据库服务器内部
4.1 响应时间分析
lu
ha
如图所示,在 10 用户、20 用户、40 用户、60 用户、80 用户、100 用户并发情况下,随着
并发用户数的增加,响应时间略有增加,但响应时间均在 10s 以下。当 120 用户并发时,事
务响应时间迅速增加,达到 100s 以上。
p
从响应时间上分析,可以初步认为,系统支持的最大并发数为 100。 (当然,如果想更准确
ce
知道是多少,可以进一步测试,比如 105。不过意义不大。 )
bu
图 3 响应时间与并发用户数的关系
beiyu95@hotmail.com
5. 4.2 Web 服务器与数据库服务器资源分析
对 Web 服务器和数据库服务器操作系统级资源的监控,主要包括 CPU、内存、网络相关指
标。需要重点关注什么指标和系统的应用有关,比如计算密集型的是以消耗 CPU 资源为主、
而有些应用可能是以 IO 为主。此处,我们可以直接通过分析 CPU 使用来得到系统基本的性
能特点。
s
lu
ha
图 4 不同并发用户数下 web 服务器与数据库服务器 CPU 使用率对比
p
从图中可以看出,在 100 用户以下的并发场景测试中,数据库服务器(944)的 CPU 使用率
一直低于 web 服务器的 CPU 使用率, 考虑到响应时间很快(结合响应时间图表数据), 可以认
ce
为,系统整体运行良好,web 服务器和 DB 服务器协同工作正常,没有明显的瓶颈。
而在 120 用户并发情况下, 数据库服务器 CPU 使用率要大大高于 web 服务器 CPU 使用率(正
常是要低于),而响应时间大大变长。因此,基本说明数据库服务器是系统的瓶颈。
bu
如果要进一步定位出现瓶颈的原因, 需要进一步获取数据库的内部状态, 如果从数据库内部
的监控仍然不能确定原因,可能需要修改代码给出更多的调试信息,以定位问题的原因。
4.3 Tomcat busy threads 分析
Tomcat busy threads,即某一时刻 tomcat 中 busy 的线程数。比 jvm 中的线程数更精确的反
映了 tomcat 的繁忙程度与工作状态。Tomcat 中通常包括两线程池:
http 线程池
tomcat 直接提供 web 访问服务时使用的线程池。
ajp 线程池
apache 和 tomcat 之间通过 AJP 协议协同工作(通过 mod_jk) ,AJP 线程池中的 busy 线
程数,准确的反映了 tomcat 的繁忙程度。
我们可以通过 jconsole 监控 jvm 来获取 tomcat busy threads 相关数据,如下图:
beiyu95@hotmail.com
6. s
lu
图 4 Jconsole 提供的 MBEANS 数据
通过查看线程状态,可以进一步确定是哪些线程在工作。比如在 tomcat 的 manager 提供的
状态查看页面中可以查看当前 busy 的现场发送的 POST/GET 请求具体内容。当然,也可以通
ha
过分析日志得到,只是不太直观。
4.4 JVM 资源分析
p
ce
JVM 是 web 应用服务器运行的基础,对 jvm 堆内存的监控对于 web 应用程序非常重要,一
般监控如下几个方面即可:
内存使用的效率
bu
GC 频率
阈值配置
收集策略
是否存在堆内存泄露的隐患
主要包括以下几个堆内存
Eden space(年轻代)
Survivor space(中年代)
Old space(老年代)
更多 jvm 内存结构及垃圾收集机制的信息可以参考 java 虚拟机手册。下图是以正常运行中
的 JVM 内存状态的例子,包含了丰富的内存状态信息。
beiyu95@hotmail.com
7. s
lu
图 5 Jconsole 提供的 jvm 内存数据状态
4.5 慢查询分析
ha
Mysql 提供了慢查询日志,可以在 my.cnf 中打开。通过 long_query_time 来设置慢查询的阈
p
值。高于该阈值的 SQL 语句将被打印到慢查询日志中。信息格式如下:
# Time: 100513 15:48:24
ce
# User@Host: crm[crm] @ [10.81.11.237]
# Query_time: 2 Lock_time: 0 Rows_sent: 3 Rows_examined: 27517
select f.follow_assign_id follow_assign_id,f.cust_id cust_id from tb_follow_assign f,tb_customer c
bu
where c.cust_id=f.cust_id and c.pose_id=11 and f.follow_id=0 and f.cust_id not in
(45064,606760,658815,664398) and c.black_flag =1 and c.follow_audit=0 limit 3 for update;
目前,mysql 支持设置的阈值最小为 1s,如果想设置更小的阈值,需要打额外的补丁。
慢查询是数据库应用性能问题的头号杀手,在 CRM 类型的应用中更是如此。因此,分析慢
查询的原因,找到优化方案是性能优化的重要途径之一。
一般而言,慢查询出现的主要原因可能有如下几个:
缺少索引
索引使用不当
数据库参数设置不当
SQL 语句的实现导致性能低下
当然,还有很多其他的原因,比如数据库设计、压力过大、数据存储等等,此处不做展开。
对于慢查询语句的分析,我们可以用 explain、profiler 等数据库工具进行进一步的分析,查
找症结所在。
beiyu95@hotmail.com
8. 4.6 数据库内部状态
细心的同学,可以在图 web 服务器与数据库服务器 CPU 使用率对比 中发现,虽然可以判
断问题出在数据库上,但数据库本身的 CPU 使用率并不高。事实上,通过对数据库操作系
统 CPU、内存、磁盘 IO 等的分析后,并没有发现瓶颈的存在。因此,需要了解数据库内部
状态,获取压力下数据库层面的性能数据。主要包括:
慢查询
INNODB 状态
数据库的逻辑读写
数据库的物理读写
SQL 执行速率
锁竞争情况
排序状况
临时表
缓存
s
当然,数据库的状态参数和配置参数还有很多,可以参考专门的资料。
下图是一个 mysql 状态概要数据,通过数据可以看出,mysql 的工作负载不是很重,物理 IO
lu
也比较小。因此,需要进一步分析 INNO DB 的内部状态
ha
p
ce
bu
图 6 Spotlight 提供的数据库活动概要信息
上图中各个区分别表明了各类 SQL 执行的统计、行数据访问的状态、session 的状态、缓存
的状态、innodb 的逻辑读写、数据库的物理读写状态。透过这些性能参数我们可以大致了
解数据库的“热点”所在。找到热点后,可以通过更细的指标来查看热点部分的内部工作状
态,如排序、锁竞争、mutex 竞争等。
beiyu95@hotmail.com
9. 4.7 INNODB 状态分析
通过 show innodb status 命令,可以查询 INNODB 的内部状态,mysql 提供了异常丰富的信息。
通过这些信息,可以掌握 innodb 内部事务状态、锁状态等重要的性能信息,来辅助性能问
题的分析。关于该命令的输出,可以查看 mysql 手册。
下面是本次测试锁竞争和死锁的例子。
4.7.1 锁竞争情况监控
在大并发下,mysql 的锁竞争几乎是不可避免的。锁竞争会导致响应时间大幅变长,系统资
源无法充分利用。
从下面部分输出中可以看出,事务锁定了较多的表,会导致较多的等待,引起系统响应时
间变长。
在发生严重的锁竞争时,mysql 处理能力大幅下降,会造成用户响应较慢,从而引起 tomat
线程数飙升,对系统的稳定性和服务的稳定性造成严重威胁。
s
锁竞争的例子
index read
mysql tables in use 10, locked 10
lu
‐‐‐TRANSACTION 0 94611044, ACTIVE 4 sec, process no 28350, OS thread id 1168296288 starting
ha
LOCK WAIT 2 lock struct(s), heap size 1216
MySQL thread id 147818, query id 5386420 10.81.11.237 crm Sending data
select f.follow_assign_id follow_assign_id from tb_follow_assign f,tb_customer c where
p
c.cust_id=f.cust_id and c.pose_id=11 and f.follow_id=0 and c.quit_user not like '%1981172%'
and c.black_flag =1 and c.follow_audit=0 limit 10 for update
ce
Trx read view will not see trx with id >= 0 94611045, sees < 0 94611020
‐‐‐‐‐‐‐ TRX HAS BEEN WAITING 4 SEC FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 0 page no 579897 n bits 1192 index `Index_follow_assign_follow_id` of
bu
table `crm_sale/tb_follow_assign` trx id 0 94611044 lock_mode X waiting
Record lock, heap no 636 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
0: len 4; hex 00000000; asc ;; 1: len 4; hex 00015635; asc V5;;
4.7.2 死锁情况监控
在客户申领的测试过程中,启动了从晚上 9 点到早上 9 点共 12 个小时的压力。在晚上 4 点
11 分的时候,置换放弃定时任务启动(Run_Quit_Replacement_Job),由于与客户申领功能
发生死锁,导致大量死锁出现,共 33 次。
死锁是 web 系统中较为严重的一类问题,主要的危害有以下几种:
死锁会使进程得不到正确的结果。 因为处于死锁状态的进程得不到所需的资源, 不能向
前推进,故得不到结果。
死锁会使资源的利用率降低。 因为处于死锁状态的进程不释放已占有的资源, 以至于这
些资源不能被其他进程利用,故系统资源利用率降低。
死锁还会导致产生新的死锁。 其它进程因请求不到死锁进程已占用的资源而无法向前推
beiyu95@hotmail.com
10. 进,所以也会发生死锁。
4.7.2.1LoadRunner 给出的错误信息
当后台发生严重错误时(一般是 500 错误),LoadRunner controller 中会有错误信息输出。如
下图所示。
s
图 7 压力工具的错误信息
lu
ha
系统提示共发生了 33 次 500 错误,经排查,和后台的 33 次死锁信息完全吻合。从此处也可
以看出,我们要对发生的 500 错误,高度重视,找出错误原因,消除潜在的性能隐患。
p
4.7.2.2INNODB 的死锁信息
ce
发生死锁时,Mysql 会根据自己的策略,选择一个事务作为牺牲品,回滚该事务。如果程序
健壮性不好,可能会造成功能的错误或者用户页面的错误。
bu
值得注意的是,show innodb status 只能显示 innodb 内部死锁的信息,对于多个引擎间产生
的死锁无能为力。
下面是 INNODB 输出的一个死锁信息的例子(部分) :
LATEST DETECTED DEADLOCK
100512 15:51:23
*** (1) TRANSACTION:
TRANSACTION 0 95431538, ACTIVE 3 sec, process no 28350, OS thread id 1164482912 updating
or deleting
mysql tables in use 6, locked 6
LOCK WAIT 21 lock struct(s), heap size 3024, undo log entries 54
MySQL thread id 164143, query id 13937709 10.81.11.237 crm Updating
update tb_customer set cust_stat_1=3, cust_stat_2=30, cust_his_stat_1=8, cust_his_stat_2=80,
upd_user=1832883, upd_time=’2010‐05‐12 15:51:22’ where cust_id=65069
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 0 page no 127542 n bits 976 index `Index_customer_cust_stat_2` of table
beiyu95@hotmail.com
11. `crm_sale/tb_customer` trx id 0 95431538 lock_mode X locks gap before rec insert intention
waiting
Record lock, heap no 5 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
0: len 4; hex 0000000b; asc ;; 1: len 1; hex 1e; asc ;; 2: len 4; hex 0000feff; asc ;;
*** (2) TRANSACTION:
TRANSACTION 0 95431550, ACTIVE 2 sec, process no 28350, OS thread id 1179937120 fetching
rows, thread declared inside InnoDB 497
mysql tables in use 10, locked 10
4685 lock struct(s), heap size 522224
MySQL thread id 164142, query id 13937303 10.81.11.237 crm Sending data
select f.follow_assign_id follow_assign_id,f.cust_id cust_id from tb_follow_assign f,tb_customer c
where c.cust_id=f.cust_id and c.pose_id=11 and f.follow_id=0 and f.cust_id not in (52355,)
and c.black_flag =1 and c.follow_audit=0 limit 10 for update
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 0 page no 127542 n bits 976 index `Index_customer_cust_stat_2` of table
s
`crm_sale/tb_customer` trx id 0 95431550 lock_mode X
….
*** WE ROLL BACK TRANSACTION (2) lu
4.8 应用程序中的异常
ha
在实际的性能测试过程中,我们也需要关注应用程序的日志,检查有无异常抛出。以确认压
p
力下系统运行的稳定性与功能的正确性。
ce
死锁异常示例
在上面的访问中,我们从 LoadRunner 的错误信息,可以查到 web 服务器的错误日志,通过
日志的信息,也可以确认是后台发生了死锁。
bu
[ERROR] 2010‐05‐12 15:51:20,550 [TP‐Processor58] [localhost].[/ht‐callout].[default]
(StandardWrapperValve.java:253) –Servlet.service() for servlet default threw exception
com.mysql.jdbc.exceptions.MySQLTransactionRollbackException: Deadlock found when trying to
get lock; try restarting transaction
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1041)
(以下省略……)
并发访问异常示例
在有些时候, 程序的开发者可能没有考虑到并发访问可能性的存在, 对一些不支持互斥访问
的数据管理不善, 会导致并发访问时产生异常。 下面是日志中给出的一个并发访问错误的信
息。
beiyu95@hotmail.com
12. 图 8 压力下的并发错误日志
5 小结
通过 4.7.1 的分析可以看出,在大并发下,严重的锁竞争导致了瓶颈的出现。测试中使用的
是 mysql 5.0.25,并发性能方面比较弱。在 5.0.47 之后并发性能有了较大提高。在 CRM 的应
用中,100 并发已经基本可以满足需要,因此暂时可以接受这样的性能结果。
通过 4.7.2 的分析,可以看出系统中存在死锁,并导致了事务失败回滚,给应用程序造成异
常。改死锁是由表访问顺序不一致造成的。在修改了相关事务逻辑,使不同事务对表的访问
顺序一致后解决。
s
lu
pha
ce
bu
beiyu95@hotmail.com