上周五项目组成员报了一个问题。有支业务在测试的过程中出现压力持续一段时间后,TPS就下降的情况。如下所示:
据描述,这个情况每过一段时间就固定出现。并且服务器的CPU降得很低,看似是没有压力一样。
前面的线程数比较多,有100以,我尝试把线程数降到10个线程,也还是会出现,并且还是1分钟左右出现一次。甚至我把压力降到1个线程,也还是会出现,只是时间长了一些。
看起来和压力的大小无关。
既然这么规则的出现,那就查一下像FGC这样有规律的动作。
从图上看,并没有FGC。
同时也查了有没有固定的批量等进程,也是没有。
初步排查完了之后,那就先画一个拓扑图来看看。
在这样的逻辑关系中,先连主机1、2(LB),再连主机3、4(LB),再连数据库。数据库是典型的互联网数据库结构,从网关到proxy,proxy从ZK上取状态,再把sql发给数据库集群。
反复查了主机1、2、3、4上的状态,都没发现有什么问题。所有资源都正常。连数据量都不大。
name server和渠道也都正常,因为还有其他人在测试的过程中用到这两个部分,并且用得很正常。
数据库我查过processlist/innodb_trx/innodb_locks/innodb_lock_waits也都没有抓到慢的点。所以判断没有慢sql。
另外,如果数据有问题的话,应该不会这么规则的TPS降低,应该是一直都很低。
基于这些考虑,对数据库没有再深入查下去。
到这里再往前查,就是jmeter了。因为压力机的问题,我一般是不愿意多花时间在上面的,所以就直接换了一个压力机,问题也仍然没有得到解决。
也尝试了把脚本和数据copy到别人的压力机上,也是没有解决。
问题再回到主机1、2上。因为这两个主机是跟jmeter直连的。因为TPS下降了,所以响应时间肯定就上升了。那就找时间的消耗在哪里。
在jmeter和主机1、2上抓了几个数据包,发现主机1、2并没有给jmeter返回。
既然没返回,那就问题在1、2主机后面,因为之前为了让性能更快,把日志调到error级别,根本没有应用层的正常日志可查,也不知道时间是在1、2上还是在3、4上,还是在db上。
只能根据经验来一层层判断了。
前面说了GC是正常的,tomcat层也查了没问题。那还能有什么呢?网络层?对简单的方式就是先拿一个正常的主机的参数做个比对。
还真有几个不一样的参数值。但是瞄来瞄去,似乎这几个参数都和这个问题无关呀。
那怎么办呢?
还是要锁定数据库了。
把慢日志捞出来再看一遍。
# Profile
# Rank Query ID Response time Calls R/Call V/M Item
# ==== ================== ================= ===== ======= ===== ==========
# 1 0xB5DEC0207094BA2F 117365.8906 44.9% 14120 8.3120 8.46 SELECT
# 2 0xFF8A1413823E401F 62050.0287 23.7% 12078 5.1374 2.78 SELECT
# 3 0xC861142E667B5663 36004.3209 13.8% 21687 1.6602 0.13 SELECT
# 4 0xFB7DBC1F41799DDD 32413.9030 12.4% 19615 1.6525 0.09 SELECT
# 5 0xC065900AEAC5717F 11056.5444 4.2% 9304 1.1884 0.02 SELECT
# 9 0x6422DFBA813FC194 202.4342 0.1% 54 3.7488 1.83 INSERT
# 11 0x197C9DCF5DB927C8 137.4273 0.1% 36 3.8174 1.14 INSERT
# 13 0x1A9D64E72B53D706 97.9536 0.0% 31 3.1598 2.65 UPDATE
# 36 0x3B44178A8B9CE1C3 20.1134 0.0% 16 1.2571 0.04 INSERT
# 39 0x370753250D9FB9EF 14.5224 0.0% 11 1.3202 0.04 INSERT
# MISC 0xMISC 2152.2442 0.8% 151 14.2533 0.0 <72 ITEMS>
确实有四个SQL消耗了更多的时间,并且时间还不短。这是明显的性能问题,但是我考虑是另外的问题,不是我现在要定位的问题。因为我把SQL都拿出来执行过一遍,并且单个执行都不慢。
而我之前把用户降到一个用户的时候也照样会出现TPS下降的情况。
那还有什么呢?顿时觉得生无可恋了。
不管怎么说,前面主机1、2、3、4上都查过了,我还是对自己查的结果有信心的,那只能是在数据库层面。之前我是直接自己写语句查的,查得不是很彻底。
于是我让数据中心的人,把DB所有的proxy日志拿出来,做一次过滤。
先cat printf了一下time cost大于200ms以上的。结果真的找到在压力的那个TPS下降的点上出现了time cost大于200ms的SQL,并且就在是前面慢日志中的3、4两个SQL。我奇怪了。数据我也换了好几次了,查出来并不慢呀,基本上是在十几ms完成。
这是我之前执行的。看执行计划,似乎也没什么呀。
可是为什么抓得出来这几个SQL呢。既然慢,就用抓出来的来执行吧。
哟,同结构的SQL,差距怎么这么大呢?14倍的时间差!
下面就看下用的数据有什么不同了。
比对了一下之后,发现只有数据不一样。那明显的就是一个查出来的多。于是group了一下,看根据查询的ID看看数据是不是分布均匀。
客户ID, 数量
'这一列只是客户id,无它', '91307'
'这一列只是客户id,无它', '69865'
'这一列只是客户id,无它', '55075'
'这一列只是客户id,无它', '54990'
'这一列只是客户id,无它', '54975'
'这一列只是客户id,无它', '54962'
'这一列只是客户id,无它', '54899'
'这一列只是客户id,无它', '54898'
'这一列只是客户id,无它', '54874'
'这一列只是客户id,无它', '54862'
'这一列只是客户id,无它', '54849'
'这一列只是客户id,无它', '54847'
'这一列只是客户id,无它', '54844'
'这一列只是客户id,无它', '54836'
'这一列只是客户id,无它', '54833'
'这一列只是客户id,无它', '54827'
'这一列只是客户id,无它', '54819'
'这一列只是客户id,无它', '54819'
'这一列只是客户id,无它', '54791'
'这一列只是客户id,无它', '54783'
'这一列只是客户id,无它', '54783'
'这一列只是客户id,无它', '54749'
'这一列只是客户id,无它', '54749'
'这一列只是客户id,无它', '54749'
'这一列只是客户id,无它', '54731'
'这一列只是客户id,无它', '54726'
'这一列只是客户id,无它', '54711'
'这一列只是客户id,无它', '54703'
'这一列只是客户id,无它', '54693'
'这一列只是客户id,无它', '54693'
'这一列只是客户id,无它', '54687'
'这一列只是客户id,无它', '54686'
'这一列只是客户id,无它', '54686'
'这一列只是客户id,无它', '54667'
'这一列只是客户id,无它', '54667'
'这一列只是客户id,无它', '54658'
'这一列只是客户id,无它', '54657'
'这一列只是客户id,无它', '54647'
'这一列只是客户id,无它', '54645'
'这一列只是客户id,无它', '54639'
'这一列只是客户id,无它', '54629'
'这一列只是客户id,无它', '54612'
'这一列只是客户id,无它', '54606'
'这一列只是客户id,无它', '54585'
'这一列只是客户id,无它', '54584'
'这一列只是客户id,无它', '54565'
'这一列只是客户id,无它', '54563'
'这一列只是客户id,无它', '54563'
'这一列只是客户id,无它', '54554'
'这一列只是客户id,无它', '54552'
'这一列只是客户id,无它', '54551'
'这一列只是客户id,无它', '54546'
'这一列只是客户id,无它', '54542'
'这一列只是客户id,无它', '54536'
'这一列只是客户id,无它', '54523'
'这一列只是客户id,无它', '54511'
'这一列只是客户id,无它', '54503'
'这一列只是客户id,无它', '54498'
'这一列只是客户id,无它', '54498'
'这一列只是客户id,无它', '54496'
'这一列只是客户id,无它', '54484'
'这一列只是客户id,无它', '54480'
'这一列只是客户id,无它', '54480'
'这一列只是客户id,无它', '54465'
'这一列只是客户id,无它', '54463'
'这一列只是客户id,无它', '54461'
'这一列只是客户id,无它', '54457'
'这一列只是客户id,无它', '54457'
'这一列只是客户id,无它', '54451'
'这一列只是客户id,无它', '54447'
'这一列只是客户id,无它', '54446'
'这一列只是客户id,无它', '54428'
'这一列只是客户id,无它', '54428'
'这一列只是客户id,无它', '54424'
'这一列只是客户id,无它', '54418'
'这一列只是客户id,无它', '54415'
'这一列只是客户id,无它', '54401'
'这一列只是客户id,无它', '54399'
'这一列只是客户id,无它', '54368'
'这一列只是客户id,无它', '54365'
'这一列只是客户id,无它', '54365'
'这一列只是客户id,无它', '54359'
'这一列只是客户id,无它', '54358'
'这一列只是客户id,无它', '54356'
'这一列只是客户id,无它', '54356'
'这一列只是客户id,无它', '54351'
'这一列只是客户id,无它', '54348'
'这一列只是客户id,无它', '54324'
'这一列只是客户id,无它', '54323'
'这一列只是客户id,无它', '54315'
'这一列只是客户id,无它', '54309'
'这一列只是客户id,无它', '54303'
'这一列只是客户id,无它', '54278'
'这一列只是客户id,无它', '54250'
'这一列只是客户id,无它', '54245'
'这一列只是客户id,无它', '54238'
'这一列只是客户id,无它', '54198'
'这一列只是客户id,无它', '54192'
'这一列只是客户id,无它', '54111'
'这一列只是客户id,无它', '54013'
'这一列只是客户id,无它', '187'
'这一列只是客户id,无它', '186'
'这一列只是客户id,无它', '185'
'这一列只是客户id,无它', '185'
'这一列只是客户id,无它', '183'
'这一列只是客户id,无它', '183'
'这一列只是客户id,无它', '183'
'这一列只是客户id,无它', '183'
'这一列只是客户id,无它', '182'
'这一列只是客户id,无它', '182'
'这一列只是客户id,无它', '182'
'这一列只是客户id,无它', '181'
'这一列只是客户id,无它', '180'
'这一列只是客户id,无它', '180'
'这一列只是客户id,无它', '180'
'这一列只是客户id,无它', '180'
'这一列只是客户id,无它', '180'
'这一列只是客户id,无它', '179'
'这一列只是客户id,无它', '179'
'这一列只是客户id,无它', '179'
'这一列只是客户id,无它', '179'
'这一列只是客户id,无它', '179'
'这一列只是客户id,无它', '179'
'这一列只是客户id,无它', '179'
'这一列只是客户id,无它', '179'
'这一列只是客户id,无它', '179'
'这一列只是客户id,无它', '178'
'这一列只是客户id,无它', '178'
'这一列只是客户id,无它', '178'
'这一列只是客户id,无它', '177'
'这一列只是客户id,无它', '177'
'这一列只是客户id,无它', '177'
'这一列只是客户id,无它', '177'
'这一列只是客户id,无它', '177'
'这一列只是客户id,无它', '177'
'这一列只是客户id,无它', '177'
'这一列只是客户id,无它', '176'
'这一列只是客户id,无它', '176'
'这一列只是客户id,无它', '176'
'这一列只是客户id,无它', '176'
'这一列只是客户id,无它', '175'
'这一列只是客户id,无它', '175'
'这一列只是客户id,无它', '175'
'这一列只是客户id,无它', '175'
'这一列只是客户id,无它', '175'
'这一列只是客户id,无它', '175'
'这一列只是客户id,无它', '175'
'这一列只是客户id,无它', '175'
'这一列只是客户id,无它', '175'
'这一列只是客户id,无它', '174'
'这一列只是客户id,无它', '174'
'这一列只是客户id,无它', '174'
'这一列只是客户id,无它', '173'
'这一列只是客户id,无它', '173'
'这一列只是客户id,无它', '173'
'这一列只是客户id,无它', '173'
'这一列只是客户id,无它', '173'
'这一列只是客户id,无它', '173'
'这一列只是客户id,无它', '173'
'这一列只是客户id,无它', '173'
'这一列只是客户id,无它', '172'
'这一列只是客户id,无它', '172'
'这一列只是客户id,无它', '172'
'这一列只是客户id,无它', '172'
'这一列只是客户id,无它', '172'
'这一列只是客户id,无它', '172'
'这一列只是客户id,无它', '172'
'这一列只是客户id,无它', '172'
'这一列只是客户id,无它', '172'
'这一列只是客户id,无它', '171'
'这一列只是客户id,无它', '171'
'这一列只是客户id,无它', '171'
'这一列只是客户id,无它', '171'
'这一列只是客户id,无它', '171'
'这一列只是客户id,无它', '171'
'这一列只是客户id,无它', '171'
'这一列只是客户id,无它', '170'
'这一列只是客户id,无它', '170'
'这一列只是客户id,无它', '170'
'这一列只是客户id,无它', '170'
'这一列只是客户id,无它', '170'
'这一列只是客户id,无它', '170'
'这一列只是客户id,无它', '170'
'这一列只是客户id,无它', '170'
'这一列只是客户id,无它', '169'
'这一列只是客户id,无它', '169'
'这一列只是客户id,无它', '169'
'这一列只是客户id,无它', '169'
'这一列只是客户id,无它', '168'
'这一列只是客户id,无它', '168'
'这一列只是客户id,无它', '168'
'这一列只是客户id,无它', '168'
'这一列只是客户id,无它', '168'
'这一列只是客户id,无它', '167'
'这一列只是客户id,无它', '167'
'这一列只是客户id,无它', '167'
'这一列只是客户id,无它', '167'
'这一列只是客户id,无它', '167'
'这一列只是客户id,无它', '167'
'这一列只是客户id,无它', '167'
'这一列只是客户id,无它', '167'
'这一列只是客户id,无它', '167'
'这一列只是客户id,无它', '167'
'这一列只是客户id,无它', '167'
'这一列只是客户id,无它', '167'
'这一列只是客户id,无它', '167'
'这一列只是客户id,无它', '167'
'这一列只是客户id,无它', '167'
'这一列只是客户id,无它', '166'
'这一列只是客户id,无它', '166'
'这一列只是客户id,无它', '166'
'这一列只是客户id,无它', '166'
'这一列只是客户id,无它', '166'
'这一列只是客户id,无它', '166'
'这一列只是客户id,无它', '166'
'这一列只是客户id,无它', '166'
'这一列只是客户id,无它', '166'
'这一列只是客户id,无它', '165'
'这一列只是客户id,无它', '165'
'这一列只是客户id,无它', '165'
'这一列只是客户id,无它', '165'
'这一列只是客户id,无它', '165'
'这一列只是客户id,无它', '165'
'这一列只是客户id,无它', '165'
'这一列只是客户id,无它', '165'
'这一列只是客户id,无它', '165'
'这一列只是客户id,无它', '165'
'这一列只是客户id,无它', '165'
'这一列只是客户id,无它', '165'
'这一列只是客户id,无它', '165'
'这一列只是客户id,无它', '164'
'这一列只是客户id,无它', '164'
'这一列只是客户id,无它', '164'
'这一列只是客户id,无它', '164'
'这一列只是客户id,无它', '164'
'这一列只是客户id,无它', '164'
'这一列只是客户id,无它', '164'
'这一列只是客户id,无它', '164'
'这一列只是客户id,无它', '164'
'这一列只是客户id,无它', '164'
'这一列只是客户id,无它', '164'
'这一列只是客户id,无它', '163'
'这一列只是客户id,无它', '163'
'这一列只是客户id,无它', '163'
'这一列只是客户id,无它', '163'
'这一列只是客户id,无它', '163'
'这一列只是客户id,无它', '163'
'这一列只是客户id,无它', '163'
'这一列只是客户id,无它', '163'
'这一列只是客户id,无它', '163'
'这一列只是客户id,无它', '163'
'这一列只是客户id,无它', '163'
'这一列只是客户id,无它', '162'
'这一列只是客户id,无它', '162'
'这一列只是客户id,无它', '162'
'这一列只是客户id,无它', '162'
'这一列只是客户id,无它', '162'
'这一列只是客户id,无它', '162'
'这一列只是客户id,无它', '162'
'这一列只是客户id,无它', '162'
'这一列只是客户id,无它', '162'
'这一列只是客户id,无它', '161'
'这一列只是客户id,无它', '161'
'这一列只是客户id,无它', '161'
'这一列只是客户id,无它', '161'
'这一列只是客户id,无它', '161'
'这一列只是客户id,无它', '161'
'这一列只是客户id,无它', '161'
'这一列只是客户id,无它', '161'
'这一列只是客户id,无它', '160'
'这一列只是客户id,无它', '160'
明显数据量分布的太不均匀了呀。查160条和查9万多条,那肯定是不一样喽。
到这里,问题基本上就明确了,查了下参数化的数据,里面有10万条。而取到6万左右的时候,才出现了响应时间长的问题,所以才有前面的用户量大的时候,一分钟一次,用户量越少,出现的间隔就越长。那就说明,前面的5万条,都是数据量少的ID,而6万左右ID中有不少一部分是数据量大的。遇到这些数据量大的,主机1、2、3、4都得等着DB返回了数据之后才返回给jmeter。所以从jmeter上来看,就是在等主机1、2给数据。
而我之前的执行的SQL,恰好试了多次都是数据量少的。
悲催的人,今天适合买彩票!
下面怎么办呢?先做个最规矩的实验,把5万条往后的数据全都删掉!场景再执行一遍。
顿时人生又有了希望!
我在培训的时候说到参数化的时候,都是在强调说,参数化的数据要做到:模拟真实环境的数据分布。
而在这个例子中,这些通过脚本造出来的数据,本来应该分布均匀的,并且还是我在前面开会的时候强调过多次的。但是还是没把数据造均匀了。项目就是这样一步步做歪了的。哈哈。幸好现在发现了这个问题,还有纠正的余地。
细想一下,其实不是造的数据也是会出现这种情况的。并且还不在少数。
从这个例子来看,查数据还是要看趋势图、统计图。层层的排查,有些容易漏掉的点是意想不到的。但是找到问题之后,又觉得“哎呀,这问题怎么这么简单!”。
再次强调,性能分析还是要有明确的思路。