Skip to content

一些优化心得.

代码层面优化

  • 实际开发中所有的实体类中的字段应当都是包装类型,例如数据库某个字段可以为null,int的默认值是0,Integer默认值是null可以方便的自动映射。

  • 所有包装类的比较都必须实用equals。

  • 使用局部变量缓存,减少一些循环中的调用。

  • 当成员变量值无需改变时,尽量定义为静态常量。静态常量在类加载时初始化,且只有一份实例,可节省内存。同时,不可变的静态常量和成员变量,尽量使用非线程安全类,因为它们不存在线程安全问题,无需额外的同步开销。

  • 包装类尽量使用.valueOf(Object)方法,因为所有的包装类都有一定范围预定义的缓存,而new则会创建一个新的对象。

  • if中不应该有太过于复杂的逻辑,如果有应当提取出来。

  • 假如需要把db中某个表的数据取出来转为树返回给前端,这时候应当直接查出部分或者所有的数据, 然后在业务里去处理,不能递归去查数据库,应当减少数据库的IO,数据库永远都是瓶颈。

  • 使用局部变量缓存,减少一些循环中的调用。

  • 在并发编程中,应当权衡是使用锁,还是直接使用并发集合。

java
Map<String, Integer> map = new HashMap<>();

public synchronized void put(String key, Integer value) {
    map.put(key, value);
}

ConcurrentMap<String, Integer> cMap = new ConcurrentHashMap<>();

public void cPut(String key, Integer value) {
    cMap.put(key, value);
}
  • 根据数据量大小和集合的类型结合业务来权衡是使用普通for还是增强for
java
// 在数据量小于1000的情况下,传统for和增强for并没有太大的性能差别
for (int i = 0; i < 1000; i++) {
    // 循环体
}
// 普通for是通过索引访问元素,如果是LinkedList,通过索引访问时,需要从头节点开始依次遍历链表,时间复杂度为O(n);
// 而使用迭代器访问 LinkedList 元素时,迭代器可以直接移动到下一个节点,时间复杂度为O(1);
// 所以实际需要根据集合的类型和业务数据量大小来权衡。
  • 根据业务比如查找,遍历选择合适的集合。

  • 和数据库交互时尽量去进行批操作。

业务层面优化

  1. 根据数据量大小,以及数据的读写情况来判断某个对象是否需要缓存。

数据库优化

  1. explain可以模拟优化器执行sql语句,从而看到索引使用情况,和预估查询的行数。
  2. 在设计表的时候需要将物理主键和逻辑主键分开,方便后续拓展。
  3. 合理建立索引。
    • 出现在 SELECT、UPDATE、DELETE 语句的 WHERE 从句中的列。
    • 包含在 ORDER BY、GROUP BY、DISTINCT 中的字段。
    • 不要将符合 1 和 2 中的字段的列都建立一个索引,通常将 1、2 中的字段建立联合索引效果更好。
    • 多表 join 的关联列。
  4. 索引列的顺序。
    • 区分度最高的列放在联合索引的最左侧:这是最重要的原则。区分度越高,通过索引筛选出的数据就越少,I/O 操作也就越少。
    • 最频繁使用的列放在联合索引的左侧:这符合最左前缀匹配原则。将最常用的查询条件列放在最左侧,可以最大程度地利用索引。
    • 字段长度:字段长度对联合索引非叶子节点的影响很小,因为它存储了所有联合索引字段的值。
  5. 对于频繁的查询,优先考虑使用覆盖索引(包含了所有查询字段where、select、order by、group by的字段的索引),这样可以避免回表,还可以把随机io转为顺序io。
  6. 外键,尽量少用外键,数据完整性可以在业务端实现,键会影响父表和子表的写操作从而降低性能。
  7. 尽量不在数据库做运算,复杂运算需移到业务应用里完成。
  8. 通过查看慢sql日志优化对性能影响最大的语句。
  9. 如果子查询在in中,并且子查询为简单sql,可以考虑把子查询转化为关联查询进行优化,因为子查询的结果集无法使用索引,通常子查询的结果集会被存储到临时表中,不论是内存临时表还是磁盘临时表都不会存在索引,所以查询性能会受到一定的影响。
  10. 用in代替or,因为in能利用索引,or不行。
  11. where从句禁止使用函数转换或计算,where date(create_time)='20190101',因为这样无法使用索引。
  12. 大sql考虑拆分成小sql,因为一个sql只能使用一个cpu去执行,无法利用多核服务器优势,并且大SQL逻辑上比较复杂,需要占用大量 CPU 进行计算。
  13. 大批量的插入,5000行以上(可以根据实际业务调整)需要拆分成批量多次操作。
  14. 如果有经常连接的列,可以考虑冗余字段。

部署注意事项

  1. nginx必须开启gzip,如果不开启gzip,打开网页时的js文件太大会很占用带宽,访问人多了会很容易让服务阻塞

  2. 前端项目打包的时候必须加上版本号,方便后续查看项目的js是否更新

  3. 如果nginx已经开启了gzip,但是项目的js还是很大,并且带宽不足的情况下,可以考虑将js文件放到oss里,减少带宽压力

事务问题

在实际开发中很多时候图方便都是直接使用注解式事务,但是当一个业务的调用链太长或者业务的数据量太大的时候应该避免使用大事务, 可以尝试 1. 在需要执行事务的地方将事务方法拆分出来 2. 当业务方法太大并且拆分困难的情况下考虑编程式事务(推荐)