记一次服务端系统性能优化

记一次服务端系统性能优化

首先简单介绍一下业务场景,物联网设备,关注公众号,免费领取环保袋。

12月8号,也就是昨天上午,突然接到大量客户投诉反馈下单界面点击下单一直在“转圈”,最后超时。紧急排查!

第一步查看网络,服务器ping值正常,然后查询服务器带宽占用率正常。

第二步,查看应用服务器负载,很低,基本没问题。

第三步,重点来了,检查数据库性能。

show processlist; 发现连接数维持在97-99,怀疑,是否是受到最大连接数限制,导致新的查询在排队,查询得知,最大连接数设置为800,所以排除连接数限制问题。又扫了一眼processlist列表,发现大量耗时很长的查询,初步定位问题。把sql拎出来看一下,发现该查询没有建索引,系统上线时间不长,业务发展迅猛,问题一下子暴露出来,建索引完事。

索引建完之后,再次检查数据库,发现有很多警告,CPU占用率一直居高不下,高峰期直接100%,这个问题就比较严重了。首先查看了慢查询日志,发现慢查询的时间阈值还停留在10秒,这肯定不行,于是设置为4秒,改为4秒之后发现,依然没有慢查询,再看了一下sql执行情况,高峰期,qps 为1000左右,tps大概40+,比较高了,但读请求明显多于写请求。决定,再次对系统进行优化。

1. 分析了一下sql执行日志,对一段时间内执行的sql按执行次数进行了一个排序,过了一遍所有的sql,进行了少量优化,但优化空间不大。

2. 维护设备在线状态的模块,分布在各地的设备每分钟或每30秒会发一个心跳包,心跳包用于维持设备的在线状态,现在规定是5分钟内没有收到心跳包则认为设备离线,收到心跳包后每次都会去更新设备最后心跳时间字段。开始想把设备在线状态维护完全放到redis里面,直接砍掉这部分的数据库IO,后来分析了一下,发现业务不允许,因此查询的时候需要按照设备在线状态来查询。最后解决方案,由于设备每分钟会发送1-2次心跳包,每次都去更新数据库,而业务允许5分钟的掉线状态延迟,因此,利用redis缓存过滤一下,在5分钟内,仅仅更新一次数据库也可以达到同样的效果。最后看了一下优化效果,发现,好像不太理想,首先,因为该update操作本来就执行的很快,资源占用很小,基于看不出CPU占用率曲线有明显变化。

3. 下一步,继续寻找优化点。接受前面优化的教训,接下来寻找优化的点的时候,从耗时长的操作入手,这样达到的效果应该是最好的。首先把慢查询时间阈值改为2秒,这时,一个新的慢查询sql出现了,就是在每次创建订单时,需要先查询一下该公众号当前已经成功吸粉的数量,因为业务规定达到吸粉数量目标之后需要停止吸粉,这查询操作进行了全表扫描,而且sql没有可优化空间了。但这里很明显可以通过缓存去优化,将公众号当前已吸粉数缓存起来,当订单完结时,对缓存执行+1操作,+1操作如果直接使用redis的incr操作,会有问题:想象一下,缓存过期,这时恰好执行了incr,由于incr当key不存在时,会创建key,并初始化为0再+1,而且该key永不过期,这样就达不到限制吸粉数量的效果了。因此通过lua脚本来进行+1操作,只有当key存在时,才执行+1,代码如下:

local exists = redis.call('exists', KEYS[1]); if (exists == 1) then     return redis.call('incr', KEYS[1]); end return nil;
View Code

发布之后,再次查看优化效果,震惊,CPU占用率曲线断崖式下跌,从100%掉到了10%以下,至此,本次优化取得圆满成功,又可以撑一段时间了。

推荐阅读