一次慢请求优化

· Read in about 1 min · (75 Words)
dev golang

前言

最近的项目中,有一个用户数据导入的需求,导入的数据量大致应该是几百条左右。 但是由于每条用户数据都包含有其它子数据,并需要对这些数据进行单一处理,所以 预期处理时间比较长

在完成接口之后,进行测试,大致在2秒左右可以完成300条左右数据导入,基本符合预期

由于最近项目上了opentracing这个性能监控工具,所以想找一找倒底是哪里出现处理时间过长的问题。

性能预估

首先预估了一下可能会出现处理慢的两处地方:数据转换、数据存储。

猜测理由:

  • 数据转换
    因为每一条用户数据可能需要10多次处理,每条处理规则可能不同,所以造成总数据处理时间会很长。

  • 数据存储
    因为数据总量比较大,所以入库时候可能会比较慢。由于在应用层进行了数据拼接,所以入库时间应该不会特别慢。但是,可能会出现SQL过长的问题。

压力测试

首先,对可能出现慢处理的地方做了压力测试。

用户数据转换:

BenchmarkEntry2User   	   20000	     73788 ns/op

可以看到,这里的每一条用户数据转换大概需要的时间为: 73788ns = 73.788µs = 0.073ms,速度非常快。 这里的结果有点出乎意外,本以为会处理比较慢的地方,其实处理非常快。

在相应代码处添加上opentracing的监控代码后,得到请求图如下: convert user

可以看到,请求图上效果与压力测试基本一致,用户转换花费时间并不长。而数据存储入库的时间明显不正常。

猜想优化

由于数据存储时间很长,所以很简单地认为是数据库出现了问题,对数据库进行一系列的优化。 然而,优化之后几乎没有任何效果。由于原数据是拼接而成的,于是猜想可能是SQL过长过大问题导致的。 将SQL改成事务后,依然没有任何积极的效果,反而进一步拉长了存储时间。 事务存储

分析代码

由于对数据库进行优化没有收到任何效果,让我把目光再一次放到了代码上面。 经过仔细分析,发现了一条可能会比较耗时的操作:

user.Salt = utils.GenSalt()
pass := utils.GenPassword()
user.Password = utils.EncryptPassword(pass, user.Salt)

这里会对每一个导入的用户随机生成用户密码和盐,然后还有一次加密处理。

EncryptPassword这个函数进行压测可得:

BenchmarkEncryptPassword   	     500	   3514588 ns/op

由上可知,每一次加密动作耗时为: 3514588ns = 3514.588µs = 3.514588ms。 可知每一次加密需要耗时约3ms,而300个用户耗时可能接近1s。由于测试服务器性能相对于开发机更弱, 所以耗时轻松达到1s以上。至此,问题已然大白。

修正问题

在正确定位问题后,即可对症下药。 经过修改,事务型存储结果如下: SQL事务型

而通过拼接SQL存储结果如下: SQL拼接型

其它

本次优化过程中使用到的相关东西:

Comments