Carte+kettle+mysql性能问题定位分析记录

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: 本文针对一次Carte+kettle+mysql的性能测试,做个简单的问题定位分析记录:
版权声明:本文为博主原创文章,未经博主允许不得转载。欢迎访问我的博客 https://blog.csdn.net/smooth00/article/details/71601446

通过Carte服务对kettle转换进行压力测试,以20并发为基准,通过不断的优化,对比测试结果如下:

20并发测试结果(持续7分钟) 无Redis 加Redis 优化Carte内存
使用配置
加CPU和内存
(压15小时)
Total Throughput (bytes)
总吞吐量
5,079,582 111,814,589 703,321,332  
Average Throughput (bytes/second)
吞吐率
11,624 264,963 1,798,776  
Total Hits 总点击量 1,458 7,245 17,615  
Average Hits per Second
点击率
3.336 17.168 45.051  
Average Response Time
平均响应时间
22.608 4.366 1.645 1.378
90 Percent Response Time
90%请求响应时间
35.614 22.959 3.828 2.351
executeTrans Transaction Pass
交换事务处理量(事务吞吐量)
290 1449 3523  
Carte服务器平均CPU使用率 5.7%
(Carte参数优化前为90%)
10~30% 85%~90%
(压力过大)
45%~70%
Carte服务器内存使用率 70~80%
(饱和)
70~80%
(饱和)
70~80%
(饱和)
50%~60%

MySql 平均CPU使用率

60%
(MySql缓存优化前为100%)
40% 2%
(MySql无压力)
2%

以上测试结果包括的具体优化过程有:Mysql开启查询缓存、Carte日志缓存时间配置、加Redes、优化Carte内存使用配置(将max_log_timeout_minutes和Object_timeout_minutes都缩小配置为1分钟)、加CPU和加内存、优化配置JVM配置调优(根据内存大小重新将spoon.sh文件中的JVM堆内存参数-Xms -Xmx优化设置)。

确认优化结果有效后(能够稳定运行15小时),再以20并发进行更长时间稳定性测试,持续压力测试到20天后出现崩溃,由于崩溃的时间点和引起崩溃原因一时难以定位,重新改变压力测试的策略(测试前开发人员再做一些优化,主要是解除和排除日志中出现的一些异常错误,如转换过程抛出的异常;并同时开启转换使用唯一连接),然后进行100并发持续压力测试(加大并发数可以实现空间换时间,缩短压测时间,以便尽快定位影响稳定性问题的原因),同时开启jvm监控,开启所有服务和数据库监控。

测试脚本日夜不间断跑测,到第二天早上看监控结果,就发现系统真的崩溃了。

一、通过Loadrunner能够看到运行13个小时后出现异常崩溃


二、通过Carte服务的CPU监控,能够看到早上7点钟前出现压力突然上升,并且无法降低


三、获取JVM监控的历史报告(JVM监控已异常断开连接),从连接时间、线程数、堆内存(无法回收内存)的变化情况,也能看出7点左右出现反常情况


四、通过监控MySQL(有两个mysql,一个是资源库,一切正常,一个是交换库出现异常),发现7点左右出现大量断开的连接


五、对交换库的监控,发现快到7点时,压力突然降低,这与上面的异常相符合


六、日志定位问题

通过上面的监控,可以判断,系统在7点前15分钟左右,应该出现故障,这就需要通过日志定位这个时段,以找到具体原因

最后在Carte服务的日志中找到答案,第一次出现连接错误的时间点是06:44:50.273,报的是Too many connections


然后错误越来越频繁,最后导致 jvm内存溢出而崩溃,  

从上面日志能明显看到导致jvm效率下降和内存问题的一个间接原因是 转换步骤中对转换数据库的连接异常:
Error connecting to database: (using class org.gjt.mm.mysql.Driver)
Data source rejected establishment of connection,  message from server: "Too many connections " Error...........

大量异常需要消耗效率和内存,所以之前并发数是20的时候,这个效率下降是缓慢的,现在换成100并发就很快出现问题了。所以即使carte不崩溃,转换也已经不能正常运行,因为db的瓶颈,转换中的sql脚本以及表输入或表输出等操作已经不能连接db 。 

七、优化数据连接

1、开启kettle转换的数据库连接池

对每个测试的转换,将其DB连接全都勾选“使用连接池” (初始大小5,最大25) 。

2、加大MySQL最大连接数,由默认100改为1000(max_connections)

再次测试,这时候压了15分钟就崩溃,通过日志查看,发现不存在数据库数异常的问题了,而且通过监控发现数据库连接正常,达到最大并发连接后没有出现中断连接


但是这次出现了新的现象,通过JVM监控发现有2千多个活动线程(正常峰值也就两百多),包括转换的线程(大部分处于监视状态)。



补充说明:关于五种状态的线程说明

运行(Running):我们最喜欢的状态。说明该线程正在执行代码,没有问题。
休眠(Sleeping):调用了Thread.sleep后的状态,说明线程正停在某个Thread.sleep处
等待(Wait):手动调用了wait方法,或者某些IO操作,在阻塞中等待数据。
驻留(Resident):常驻线程,相当于守护线程。
监视(Monitor):这里就是我想找的问题了。它表示线程想执行一段synchronized中的代码,但是发现已经有其它线程正在执行,自己被block了,只能无奈地等待。如果这种状态多,说明程序需要好好优化。

重现一下测试过程,发现到崩溃前,线程活动数突然间大量增长,如下所示:


通过threaddump分析,发现jetty 服务出现问题,有大量的线程死锁,经过开发人员的分析,是因为连接池出现大量争用,最后导致死锁,如下:

"qtp1238013097-258267 - /kettle/executeTrans/" - Thread t@258267
   java.lang.Thread.State: WAITING
	at java.lang.Object.wait(Native Method)
	- waiting on <50979aad> (a java.lang.Thread)
	at java.lang.Thread.join(Thread.java:1245)
	at java.lang.Thread.join(Thread.java:1319)
	at org.pentaho.di.trans.Trans.prepareExecution(Trans.java:1075)
	at org.pentaho.di.www.ExecuteTransServlet.executeTrans(ExecuteTransServlet.java:447)
	at org.pentaho.di.www.ExecuteTransServlet.doGet(ExecuteTransServlet.java:354)
	at org.pentaho.di.www.BaseHttpServlet.doPost(BaseHttpServlet.java:103)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:595)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:668)
	at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:684)
	at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:503)
	at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:229)
	at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1086)
	at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:429)
	at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:193)
	at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1020)
	at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:135)
	at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:255)
	at org.eclipse.jetty.server.handler.HandlerList.handle(HandlerList.java:52)
	at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:522)
	at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:116)
	at org.eclipse.jetty.server.Server.handle(Server.java:366)
	at org.eclipse.jetty.server.AbstractHttpConnection.handleRequest(AbstractHttpConnection.java:494)
	at org.eclipse.jetty.server.AbstractHttpConnection.content(AbstractHttpConnection.java:982)
	at org.eclipse.jetty.server.AbstractHttpConnection$RequestHandler.content(AbstractHttpConnection.java:1043)
	at org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:865)
	at org.eclipse.jetty.http.HttpParser.parseAvailable(HttpParser.java:240)
	at org.eclipse.jetty.server.AsyncHttpConnection.handle(AsyncHttpConnection.java:82)
	at org.eclipse.jetty.io.nio.SelectChannelEndPoint.handle(SelectChannelEndPoint.java:696)
	at org.eclipse.jetty.io.nio.SelectChannelEndPoint$1.run(SelectChannelEndPoint.java:53)
	at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:608)
	at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:543)
	at java.lang.Thread.run(Thread.java:745)

   Locked ownable synchronizers:
	- None

"init of Get  film categories.0 (Thread-245354)" - Thread t@258266
   java.lang.Thread.State: BLOCKED
	at org.pentaho.di.trans.steps.databasejoin.DatabaseJoin.init(DatabaseJoin.java:216)
	- waiting to lock <376dc4ad> (a org.pentaho.di.trans.Trans) owned by "init of Film.0 (Thread-245334)" t@258246
	at org.pentaho.di.trans.step.StepInitThread.run(StepInitThread.java:69)
	at java.lang.Thread.run(Thread.java:745)

   Locked ownable synchronizers:
	- None

"init of dim_film_actor_bridge.0 (Thread-245353)" - Thread t@258265
   java.lang.Thread.State: BLOCKED
	at org.pentaho.di.trans.steps.insertupdate.InsertUpdate.init(InsertUpdate.java:476)
	- waiting to lock <376dc4ad> (a org.pentaho.di.trans.Trans) owned by "init of Film.0 (Thread-245334)" t@258246
	at org.pentaho.di.trans.step.StepInitThread.run(StepInitThread.java:69)
	at java.lang.Thread.run(Thread.java:745)

   Locked ownable synchronizers:
	- None

"init of Lookup dim_actor.0 (Thread-245351)" - Thread t@258263
   java.lang.Thread.State: BLOCKED
	at org.pentaho.di.trans.steps.databaselookup.DatabaseLookup.connectDatabase(DatabaseLookup.java:637)
	- waiting to lock <376dc4ad> (a org.pentaho.di.trans.Trans) owned by "init of Film.0 (Thread-245334)" t@258246
	at org.pentaho.di.trans.steps.databaselookup.DatabaseLookup.init(DatabaseLookup.java:577)
	at org.pentaho.di.trans.step.StepInitThread.run(StepInitThread.java:69)
	at java.lang.Thread.run(Thread.java:745)

   Locked ownable synchronizers:
	- None

"init of Get film_actor.0 (Thread-245348)" - Thread t@258260
   java.lang.Thread.State: BLOCKED
	at org.pentaho.di.trans.steps.databasejoin.DatabaseJoin.init(DatabaseJoin.java:216)
	- waiting to lock <376dc4ad> (a org.pentaho.di.trans.Trans) owned by "init of Film.0 (Thread-245334)" t@258246
	at org.pentaho.di.trans.step.StepInitThread.run(StepInitThread.java:69)
	at java.lang.Thread.run(Thread.java:745)

   Locked ownable synchronizers:
	- None
AI 代码解读

猜测可能是由于转换勾选了“使用唯一连接”


开发给的解释为:这个问题是由于死锁造成的,jetty线程的锁是表面现象,实际是转换步骤线程死锁,每个转换使用唯一连接的情况下,当每个转换的步骤往下进行时,不断索取连接资源。如果有时刻T刚好所有转换都在进行,并且刚好每个转换下面还有步骤需要获取连接,(这种情况不难出现),线程X占用连接B,但索要连接A, 线程Y占用连接A, 但要连接B,(可以想象多个线程是互相交叉的)就会等待对方线程释放资源,就出现死锁状态,jetty服务器于是出现阻塞。
如果转换中出现连接两个数据库时,还需要研究一下怎么才能使得资源最大化。

这个问题必须后续分析和解决(这也说明前面的优化跟最后的开启连接池优化存在冲突),这个问题留到下周来进行验证。

八、优化失败回滚部分设置

按照上周开发的解释,将转换中的“使用唯一连接”勾选去掉,再次执行100并发,这次发现不到100用户时(大概80多并发时)点击率就直线下降,到100并发时直接崩溃,性能比上周的结果还差。对比了一下使用唯一连接和不使用唯一连接的线程数分布情况如下:


图8.1 转换使用唯一连接后测试崩溃统计的线程数


图8.2 转换不使用唯一连接后测试崩溃统计的线程数

可以发现使用唯一连接后总线程要多的多(因为运行的时间比后者长),而且大部分的活动线程是被阻塞的,这与不使用唯一连接的测试结果不一样(不使用唯一连接后大部分的活动进程 处在等待中,极少部分是被阻塞,而且系统只运行了8分钟后就崩溃了,这比前者还早了7分钟,可以看出不勾选“使用唯一连接”效果更差)。

这也说明在当前配置情况下,转换中勾选唯一连接和不勾选唯一连接的结果都一样会让系统很快崩溃,实质上会有些差别(线程状态数分布不同),但这已不重要了,重要的是步骤七针对数据连接的优化是无效的,并带来了副作用,继续优化下去可能进入死胡同,因此需要进行测试回滚(回到第七步、优化数据连接),将优化数据连接的策略做一下更改,改为只加大MySQL最大连接数(设为1500),不再开启转换的连接池(完全依靠mysql自身的连接控制策略),同时保留转换使用唯一连接(好处是一个转换过程只开启一个连接,避免转换中的每个步骤都开启连接,会导致过快的消耗DB连接数)。

然后进行100并发执行测试,目前运行良好(各项指标正常,carte服务器的CPU 30%左右,内存 69%左右;交换库CPU 16%~75%,内存 21%),需要继续观察(计划运行30天)。从目前JVM运行情况来看,比较稳定:


另外我们可能也需要换个思路来提升该数据交换平台的稳定性和性能了,留待后续思考。

九、分析网络稳定性问题

经过以上有效调优后,系统运行一个晚上,发现Loadrunner还是报出了少量错误(No Route to Host):

Action.c(22): Error -27796: Failed to connect to server "172.17.2.89:8081": [10065] No Route to Host cartekettle Action 22   2017/6/3 2:21:01 3493 CarteKettle_2:310 localhost 

猜测可能是跟网络延迟有关,通过优化连接数,加大Carte的连接超时时间,以及将loadrunner的超时错误时间放大,都无法避免这个错误。只能通过监控客户端与服务端的ping丢包情况(通过统制ping.vbs 脚本,以cscript ping.vbs 172.17.2.89 -t -l 1000 -w 5000>ping89.txt输出ping错误日志),结果发现真是由网络中断引起的,说明测试环境的网络是不稳定的。



说明在网络稳定性方面,需要做好重连接,这得由调用carte服务的客户端来决定,之后这块逻辑可能需要在调度上去考虑。

十、后续思考:

由于本次测试过程,已经对mysql进行了优化(加了redis缓存,提升了读写速度,减少了IO操作),同时对carte和kettle也进行了JVM配置调优,修改carte配置文件(将max_log_timeout_minutes和Object_timeout_minutes配置为最小值,及时回收内存,减少内存溢出的概率),同时对转换也进行了优化(使用唯一连接,减少转换中多步骤的DB连接资源占用)。但这些手段虽然提升了当前数据交换系统的性能和稳定性,但面对DB本身的瓶颈还是需要后续进一步的优化和性能提升。

下一步就要考虑mysql的集群化部署,包括通过mycat集群(提升mysql的高可用+读写分离)应用,并且在业务层面上,适当的开展分表分库以减轻mysql的单节点压力,具体对于mycat的配置和测试参见另一篇文章:http://blog.csdn.net/smooth00/article/details/71082046

接着就是要考虑部署Carte服务集群,进一步提升数据交换平台的高可用和高稳定性,以满足长时间的稳定运行和数据的大批量实时交换要求。


相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
MySQL索引策略与查询性能调优实战
在实际应用中,需要根据具体的业务需求和查询模式,综合运用索引策略和查询性能调优方法,不断地测试和优化,以提高MySQL数据库的查询性能。
429 66
【YashanDB知识库】Kettle迁移MySQL到YashanDB
本文介绍了使用Kettle将MySQL数据库中的中文数据迁移到YashanDB的方法,解决因YMP不支持Latin1字符集导致的乱码问题。提供了Windows和Linux两种环境下的操作步骤,包括配置JAVA环境、解压作业包、设置数据库连接(MySQLInput与YashanOutput)、修改表列表配置文件及运行迁移任务。Windows环境支持图形界面便于调试,Linux环境网络性能更优。通过详细的操作指南,确保数据迁移成功并可重试无冲突。
ThinkPHP框架show columns引发mysql性能问题
ThinkPHP框架的show columns引发mysql性能问题,结尾有关闭方式。
44 13
无缝集成 MySQL,解锁秒级 OLAP 分析性能极限,完成任务可领取三合一数据线!
通过 AnalyticDB MySQL 版、DMS、DTS 和 RDS MySQL 版协同工作,解决大规模业务数据统计难题,参与活动完成任务即可领取三合一数据线(限量200个),还有机会抽取蓝牙音箱大奖!
MySQL事务日志-Undo Log工作原理分析
事务的持久性是交由Redo Log来保证,原子性则是交由Undo Log来保证。如果事务中的SQL执行到一半出现错误,需要把前面已经执行过的SQL撤销以达到原子性的目的,这个过程也叫做"回滚",所以Undo Log也叫回滚日志。
137 7
MySQL事务日志-Undo Log工作原理分析
mysql慢查询每日汇报与分析
通过启用慢查询日志、提取和分析慢查询日志,可以有效识别和优化数据库中的性能瓶颈。结合适当的自动化工具和优化措施,可以显著提高MySQL数据库的性能和稳定性。希望本文的详解和示例能够为数据库管理人员提供有价值的参考,帮助实现高效的数据库管理。
70 11
无缝集成 MySQL,解锁秒级数据分析性能极限
在数据驱动决策的时代,一款性能卓越的数据分析引擎不仅能提供高效的数据支撑,同时也解决了传统 OLTP 在数据分析时面临的查询性能瓶颈、数据不一致等挑战。本文将介绍通过 AnalyticDB MySQL + DTS 来解决 MySQL 的数据分析性能问题。
MySQL原理简介—4.深入分析Buffer Pool
本文介绍了MySQL的Buffer Pool机制,包括其作用、配置方法及内部结构。Buffer Pool是MySQL用于缓存磁盘数据页的关键组件,能显著提升数据库读写性能。默认大小为128MB,可根据服务器配置调整(如32GB内存可设为2GB)。它通过free链表管理空闲缓存页,flush链表记录脏页,并用LRU链表区分冷热数据以优化淘汰策略。此外,还探讨了多Buffer Pool实例、chunk动态调整等优化并发性能的方法,以及如何通过`show engine innodb status`查看Buffer Pool状态。关键词:MySQL内存数据更新机制。
MySQL 窗口函数详解:分析性查询的强大工具
MySQL 窗口函数从 8.0 版本开始支持,提供了一种灵活的方式处理 SQL 查询中的数据。无需分组即可对行集进行分析,常用于计算排名、累计和、移动平均值等。基本语法包括 `function_name([arguments]) OVER ([PARTITION BY columns] [ORDER BY columns] [frame_clause])`,常见函数有 `ROW_NUMBER()`, `RANK()`, `DENSE_RANK()`, `SUM()`, `AVG()` 等。窗口框架定义了计算聚合值时应包含的行。适用于复杂数据操作和分析报告。
217 11
【深入了解MySQL】优化查询性能与数据库设计的深度总结
本文详细介绍了MySQL查询优化和数据库设计技巧,涵盖基础优化、高级技巧及性能监控。
638 0