有关性能优化的感悟

下半年换了个工作环境,然后有了些新的感悟。在除夕这天,总结一下这几个月为了提高程序性能,而做的那些事。
1. 异步线程 刚入行的时候,很烦面试的时候被考多线程,毕竟当时很少在工作中用到。但工作几年后发现,只要想提高程序的性能,就绕不开它。最常见的场景,就是利用多线程并发执行多个局部事件,缩短总体完成时间。开发中,通常整个项目注册一个线程池的 Bean,各个方法都基于这个线程池来拿线程执行业务,容易针对线程池做参数调优。
有返回参数的异步,可以试试 CompletableFuture,也是 jdk8 提供的神器,比传统的 Feature 要好用,用法参考 CompletableFuture 教程。例如:当接口需要从多个外部系统取数据时,都可以异步执行,然后统一收集返回值。甚至利用 CompletableFuture 中特性,自动执行回调方法。
但要注意,也不是什么时候都要用异步:

  • 机器 CPU 资源有限:单台机器 CPU 的核心数有限,当多线程并发数达到顶点时,再加线程,反倒适得其反。此时可以考虑负载均衡,将并发线程分摊到多台机器上。
  • 同步更快:有些程序原本执行就很快,加了异步后反而慢了,毕竟分配线程资源也是需要时间的。例如总共只有十几条数据,流运算时不用 stream,而是 parallelStream
2. 缓存中间件
Redis
Redis 是最常用的缓存中间件了,它支持的常用数据结构挺多,要根据实际应用场景来选择,我目前用的有String、Hash、Set、List。
原始数据当然落在关系数据库中,但业务上常用的,通常是经过转化的中间数据。例如:员工登录后所属部门、角色权限、积分等数据,来自于不同的微服务,不能每次要用都调各方服务的接口,这些都需要缓存起来。
用缓存很无脑,但刷新缓存才是难题。像刚刚的例子,不能把所有的缓存数据都落到员工的粒度上,要根据业务来分层。例如:机构级别的数据,就每个机构存一份,按照员工所属机构来查;部门级别的数据,就每个部门存一份,按照员工所属部门来查。等等。
Elastic Search
用 ES 做缓存?估计很多人是大大的问号,谁让 Redis 当时不支持 Json 呢。从关系型数据库抽离出来的数据有时比较复杂,例如:人员所属的部门、角色、岗位等,每一个属性往下,背后的数据都不少。有人说 Redis 缓存个部门ID、角色ID,剩下的根据ID查表。但这些可是基础数据,且不说会不会把数据库查出问题,如果想要根据部门属性、角色属性、岗位属性来查人员,这个SQL该咋写。
最好的方式就是将这些数据从关系数据库中抽出来,以 Json 文档的形式缓存下来,MongoDB、ES 都能干这事。但为了应对后续可能千奇百怪的查询场景,还是 ES 保险一些。
可 ES 毕竟不是专业的缓存中间件,我很看好最近新出来的 RedisJson。完全满足日常工作需求,而且据说查询性能是 ES 的上百倍。但毕竟刚出来怕有坑,等稳定一段时间再入手吧。
3. 消息中间件 这里不说 MQ 的其他特性,就聊优化性能吧。前面说单台机器的 CPU 资源有限,可以考虑将并发线程分摊到多台机器上,具体怎么做呢?
将需要处理任务通过消息投递到 MQ 中,消费者监听到 MQ 的消息后实际执行。倘若有1000个任务,单台机器能处理任务的最大并发线程数是100,那么10台机器就够了。
4. 减少系统交互 内存运算是最快的,要尽量减少和外部系统的交互次数。
例如:在查询一批数据时,每条数据需要通过外键,查询匹配的其他系统信息。如果其他系统信息种类不多的话,是不是通过查一次SQL或调用一次接口,一次性全拿过来。然后写代码做数据拼接。而不是遍历出每条数据,都去外部系统查一遍。
还有一些插入 MySQL、Redis 的一批数据,或给 MQ 发一批消息的,如果可以的话,就在程序内部处理好,然后调用批量插入、发送的方法,性能绝对能省不少。
5. Jvm 调优 这是老生常谈啦!我之前文章有写过,网上资料更多,这就不说了。只说一点,尽量少怀疑 Jvm 调优能提高你的程序性能,多从方案设计和代码上反思。
【有关性能优化的感悟】新年快乐~

    推荐阅读