背景
最近,在做一个项目需要对某些数据进行处理并且提供 API 接口查询。该项目每天的数据量,经过压缩后大概是 10G 左右。而且这个项目对于实时性要求很高,不宜直接使用 Mysql 来读写数据。
于是采用了 Redis 的集群,把每天的实时数据存在 Redis 里面,再通过别的方式将旧数据存放在 Mysql 里面做一个备份。
第一次尝试:数据量测试
把存储数据的方式确定为 Redis 后,我们开始了第一次的尝试,对数据的容量做一次测试,对每天的数据都是通过 JSON 的形式存在 Redis 中。本以为 40G 内存的 Redis 完全可以应付每天的数据。结果,一天的数据还没接收完,Redis 就因为可使用的内存不足导致宕机。没办法,可用的内存有限,不得不将数据进行压缩。
使用 Protobuf
经过一次数据容量的测试,发现以 JSON 的数据结构存在 Redis 中太浪费内存了,于是又经过一轮讨论,我们决定使用 Protobuf,将数据通过 Protobuf 序列化存在 Redis 中。改造完后,又进行了一次压力测试,终于 Redis 集群没有再次宕机。并且,将原来每天最少 40G 的数据量,成功的减少到了 10G,也就是说减少了 4 倍的内存占用。
第二次尝试:API 接口的压力测试
搞定了接收数据的程序,并且也搞定了第一版的 API 接口,于是我们对 API 进行了一次压力测试。
第一次测试
API 服务器配置为 8核32G
,压测服务器 8和32G
,使用 wrk
工具进行简单的压力测试
一共有 3 个接口,QPS 测试结果(大致数据):
- 当日数据接口(217/QPS)
- 五日数据接口(30/QPS)
- 详细数据接口(4000+/QPS)
初次测试的结果并不是很理想,特别是当日数据和五日数据接口,都没有达到测试的标准。并且这两个接口每次请求都会对数据进行一次计算,于是继续改造。将需要算的数据,都放在接收数据的程序中,API只是获取数据
。
第二次测试
把 API 计算的部分放在了接收的程序中,让接收程序进行计算,不让 API 每次都进行计算。并且再次进行了一次对当日数据和五日数据接口的压力测试,测试结果如下:
- 当日数据接口(340+/QPS)
- 五日数据接口(300+/QPS)
通过这次的压力测试,可以发现这两个接口的有一定的提升,也就是说这次优化是有效的。但,我依旧不够满意。我发现,这些数据有一个特点,就是 旧的数据它不会变
,那么就可以对旧的数据在本地内存中进行缓存,减少从 Redis 获取数据发起的网络请求。
第三次测试
将 API 接口改为了将历史数据缓存在本地内存中,并且减少了发起网络请求的次数。再次进行压力测试,测试结果如下:
- 当日数据接口(10000+/QPS)
- 五日数据接口(6400+/QPS)
可以发现,对旧的历史数据缓存,减少发起网络请求的优化之后,QPS 有明显的提升。
总结
在对程序性能的分析时,使用了阿里开源的 Arthas
工具,发现最影响程序性能的地方,就是从 Redis 获取数据时发起的网络请求最耗时。
经过这次的优化中,我有几点总结:
- 直接将 JSON 数据存在 Redis 太浪费,并且 API 获取数据时,需要的时间会明显的高于 Protobuf 序列化后存在 Redis 中的数据获取时间。
- 减少 API 对数据计算的次数,最好不计算,把需要计算的数据放在别的地方,并且存起来
- 减少发起网络请求的次数,因为在这次测试的结果中,经过分析后发现最耗时间也最浪费性能的地方就是获取 Redis 数据时,需要发起的网络请求。
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于