春节参加某Q活动,支持千万级流量。
优化的过程:
根据压测报告和日志分析,不断优化性能短板。
现将本次项目所做的优化点列出来:
1.tomcat连接池
-
设置最大线程数maxThreads,2000 (IO密集型应用) 从linux角度某用户最大线程的限制65530
从jvm角度启动最大线程设置 12G/1M = 12288
最大线程数的配置绝对不是越大越好, cpu在线程切换时消耗的时间随着线程数量的增加越来越大,单次请求的响应时间也会急剧的增加。
最好的做法是:在不断测试的基础上,不断调整、优化,才能得到最合理的配置。
最佳线程数目 = ((线程等待时间+线程CPU时间)/线程CPU时间 )* CPU数目。
线程等待时间(一般指IO等待时间)所占比例越高,需要越多线程。线程CPU时间所占比例越高,需要越少线程。
设置最大连接数maxConnections,10000
如果设的较小,可以保证接受的请求较快相应,但是超出的请求可能就直接被拒绝或者排队中。
如果设的较大,可能就会出现大量的请求超时的情况,因为我们系统的处理能力是一定的。设置最大排队数acceptCount,5000
Tomcat一旦达到了maxConnections,Linux仍然可以接受acceptCount的设置.(就是TCP三次握手完成还没有被应用accept走的socket队列大小。)
可以增加系统的处理容量,但并不会增加tps
2.DB优化(获取连接慢)
- 优化sql,不存在join,or , select出来字段是所需要的字段.
- 优化索引,让每条select都走索引
- 设置连接池的最大连接数,设置的10000/14 = 700, (10000为项目使用的mysql最大连接数,14为机器数)
- 尝试测试不同的连接池,选择性能最佳的,选择的hikari
-
不使用数据库事务,因为数据库操作代码都在消费者中,在代码中做幂等性.
注:查询一条语句性能测试(ms)
3.redis优化
- redis存储数据结构
将db中的数据load保存为redis的hash结构(全表保存) 根据业务优化redis存储结构,减少redis查询次数(例如将phone和券code的领取状态单独存储) - 热点key的处理
redis cpu为单核,进行分片处理
大量查询会成为严重短板,通过hash值进行分片处理,使之近似均匀,
redis能够承受的量是之前的3倍. -
设置redis最大连接数
3*10000/14 = 2100
4.mq优化
- 消息体一般为redis key,可以去redis拿取数据,优化消息存储大小
- 可以按功能不同,拆分多个队列,加快单逻辑处理速度
目前根据业务拆分为5个队列
5.加快消费者消费速度
- 增加消费者数量,20个
根据下游(DB,业务方)TPS多次测试得出,可以利用消费者数量控制下游的负载 - 增加消费者预读取数据数量,50个
减少网络请求次数 - 优化消费逻辑
完善幂等操作(解决消息重复消费问题),db操作,业务查询操作
5.java启动脚本优化
- 设置最大内存为系统的3/4
-
使用G1垃圾回收器,参考RocketMQ启动脚本
6.多台可能和单台tps/消费者消费速度 差不多
- 连接池问题,tomcat连接池,数据库连接池,redis连接池…
- 资源锁的问题,3种方式,我们采用redis控制.
7.http连接
- TCP并发链接(没有及时释放),CLOSE_WAIT过多。
- 设置连接池最大连接数量1000
8.项目初始化
- 将数据库数据先load到redis。可以控制速度,百万数据大概5min load完。
9.日志优化(通过日志分析不出来)
- 异步写日志文件
- 减少日志打印,例如正常请求仅打印入参和出参
- kafka+ELK