性能优化的本质是良好的用户体验和有限的资源之间的矛盾。
核心思路
【1】堆硬件 +优化软件(算法、步骤)
【2】开源(堆机器) + 节流(提高资源利用率,少占资源)
【3】输入、计算、输出
【4】权衡
核心思想
架构优化
架构=组件+交互。
架构可细分为:业务架构、应用架构、技术架构、代码架构、 部署架构。
架构原则:模块化、轻耦合、无共享架构;减少各个组件之前的依懒、注意服务之间依懒所有造成的链式失败及影响等。
DDD提供了在不同的特定领域上下文以及业务功能的基础上拆分组件的指导方法。
如果Redis等中间件在多个业务模块经常使用到且流量压力大,可以考虑单独抽出来做一个Redis服务模块。
高并发场景适当考虑使用全链路NIO异步响应式编程。SpringCloud Gateway、WebFlux、Dubbo、Nacos等。
化整为零
分布式 读写分离:①代理方式:myCat、Atlas;②应用内路由:主从、多数据源;③sharding-jdbc等。
化零为整
批量接口
Buffer
空间换时间(缓存、预写、预留空间、堆硬件)
缓存(浏览器缓存、CDN加速、内存缓存、CPU缓存…)
预写(将一些操作提前准备好) 预留空间(ArrayList 扩容 1.5 倍; HashMap 的扩容机制;Redis String 也会预留空间,避免频繁扩容)
堆硬件(通过添加更多机器,增加集群机器数量,抗更多流量)
提高配置(给更多内存、宽带等)
读写分离(写主读从;COW写时复制)
串行转并行
使用并行 API 将消息发到MQ中,负载均衡给多个消费者,增加消费能力
同步转异步
减少资源利用(压缩、合并、复用、减少输出、减少上下文切换、减少指令)
池化(线程池、连接池、对象池)
压缩、合并、复用、减少输出、减少上下文切换、减少指令
时空局部性
发挥各方面的优势(选择合理的数据结构、合理的中间件等)
比如开发时要用 Set 去重,而不是 List
比如 HashMap 冲突时拉链,链表大于 8个 转为红黑树
使用 Redis,如果适合用 Set 结构的就不要每次都拼接成 String 存储,每次取出来再切割再用。
比如 MySQL 的 InnoDB 选用 B+ 树,主要是层数低,这样IO次数就少,每个叶子节点构成链表,那么范围查找就比较容易。
根据技术特点进行优化(深翻页问题、属性转换问题)
深翻页问题,通过移动 where 条件来实现永远只查第一页,提高性能
手动写 get/set 转换方法或者使用基于字节码增强的属性转换工具,比基于反射的属性转换工具性能好
降低冲突(时间、空间)的范围
分段加锁
COW(Copy-On-Write)
乐观锁
反推产品来优化性能(加限制条件)
比如消息推送即使优化,千万的消息也得发3分钟,产品本来想要提前5分钟发到用户手中。是不是可以和产品商量提前10分钟发? 比如 ES深翻页有性能问题,是不是和产品商量下限制只能搜出2000条?
注意事项
避免过早优化
过早优化是万恶之源。
比如业务发展初期,最重要的是迎合市场,抢占市场份额,那么如果某个功能性能一般,但是不特别影响用户体验,也可以后面再优化。
2. 考虑投入产出比
如果一个功能投入很大,优化效果不好,那么是不是要三思而后行呢?
3. 考虑正确性
性能优化的同时一定要考虑正确性,如果优化之后连正确性都无法保证优化有啥意义呢?
性能优化要伴随着更多的测试来保证正确性。
4. 考虑稳定性
5. 考虑拓展性、可读性、考虑安全性
性能很好,但是可读性很差,可拓展性、安全性很差,那么就容易埋下很多隐患。
比如为什么外国人很少用 fastjson,其中一个重要原因是里面有很多硬编码的东西,而且总是爆出安全漏洞。
等。