日常代码写作规范及优化建议

批量数据处理

每次读取数据时,批量读取,然后在应用中进行分组、分发,例如:

订单列表页涉及到用户、订单、商品等表,批量把当前页的订单、用户、商品一次性读取回来,然后在应用中进行数据的组装,以减少sql请求量,降低数据库压力,加快操作响应速度。

线程池的使用

建议尽量避免使用

1
@Async

注解,因为这个是共享了Spring提供的线程池,大量使用的时候,容易造成线程池溢出,建议使用自定义的线程池,并且估算好以下参数:

  • 核心线程数、最大线程数
  • 存活时长
  • 任务队列及长度
  • 自定义ThreadFactory,指定线程名称
  • 根据业务指定拒绝策略

HashMap不要在多线程环境下使用

应用默认都是多线程环境的,所以在定义HashMap对象的时候一定要清楚的知道当前有没有多线程的问题

控制数据量大小

每次操作数据的时候,一定要清楚的知道当前操作的数据可能的量,并清楚的知道最大的量是多少、未来的增长数据和空间,以防止引起内存不足,我们的系统的内存大部分是4-8个G;

除非能确定数据量有限且不增长,否则都应该分批操作,保证每批数据的有限性,以防随着数据量大后造成系统风险。

对于需要较长时间的业务,比如导入数据、调用第三方接口之类,最好做成任务类型

数据和操作的安全性、可追溯性

  • App端:不允许通过前端传入用户ID来操作对应用户的数据;对用户的私有数据操作,一定要检查当前数据是否属于当前用户,包括但不限于:地址、订单、关注、购物车、评论、个人信息、账户余额等
  • Backend端:后台对用户的操作有权限校验,当前是基于URL做的,所以要确保当前操作的数据一定要在某个url之下,比如菜单url是/ic/item,那么点开这个菜单以后展现的界面里所有的操作对应的url,都要在/ic/item这个url之后(之下),比如/ic/item/to-add,/ic/item/do-add/_ajax
  • 上传到OSS的内容,如果是公司私有的,一定不能使用公有读权限上传,以防造成信息泄露。
  • 关键配置不要直接写在配置文件里,当前我们是写的启动脚本里(配置中心还没有做)
  • 关键数据的操作和修改,一定要有对应的操作日志的记录,以确保可追溯

定时任务

  • 如果是定时执行的,约定只在整点或半点的时候执行,以便大家在发布系统的时候能知道在哪些时间点避免发布;
  • 如果要是对过去一段时间的数据进行定时处理,请确保处理的数据的时间范围大两个(或更长时间)或以上的定时执行时间差内,以便让当前处理的数据能覆盖到上一次定时执行的数据范围,以便上次未执行或执行失败时,能做重试处理
  • App端不做任务定时任务,以确保APP端系统的稳定
  • 根据自身业务情况,可以考虑放到redis队列里去,然后由多台机子一起跑这个队列里的数据,这样的话即使服务器频繁重启也不会中断任务。

配置的内外网之分

为了加速操作的执行速度,在能使用内网的时候,一定使用内网连接,例如:Redis/Mysql/OSS等,请大家在添加或修改线上或测试环境配置的时候,一定要了解当前是否可以使用内网。

外部服务调用

如果有对外部服务的调用,一定要想好如果服务不可用、服务响应时间过长、或者服务响应结果有错,会不会对系统造成压力或影响;尤其是当该调用在事务方法中时,有可能造成事务时间过长,数据库连接被占满,系统不可用。

事务处理

  • 要清楚的理解当前操作是否需要事务,能避免事务操作的就不要使用事务
  • 如果是使用事务,那就需要清楚的知道什么时候可以返回结果,什么时候应该抛出异常,以使事务生效
  • 事务中尽量避免长时操作

延迟或异步给出不必要的数据

对于非必要的数据,可以延迟或异步给出,以加快操作的响应速度。例如之前后台的订单详情页,除了加速订单相关数据外,还会加载订单的物流信息,造成整个订单详情页响应时间超长,后来优化了一下,延迟加载物流信息,整个响应速度就极大的提升了;又比如前端很多页面都有优有推荐数据,为了显示页面,除了要读取必要的数据,还要等待推荐数据的返回才能显示页面,就造成了整个页面的响应过慢。

异步处理,加快响应速度

  • 对于有多个长时操作,但是互相之间没有依赖的,可以让每个操作异步处理,然后在最后再等待所有的操作结果返回,汇总结果,再输出,以加快操作的响应速度。
  • 除此之外,尽量使用消息系统

幂等性

自己写的方法、接口,特别是比较重要的业务,一定要处理好幂等性,不管是消息的消费还是对外暴露的API等等。

缓存的使用

  • 对于耗时的操作,能使用缓存的尽量使用缓存
  • 对于变动不频繁但是使用频繁的,可以使用缓存
  • 对于不会变的数据,可以使用本地缓存,但是要注意缓存数据量的大小,以免造成内存压力

数据库索引

写sql查数据的时候,除了要考虑数据量外,要检查是否有对应的索引使用,防止查询过慢。

一般一个表的索引不要超过6个,索引应建立在离散度比较高且在查询条件中出现频度较高的字段上,索引尽量在建表的时候考虑好,后期数据量大了再加索引的话会比较困难。

其他

  • 使用集合时,估算出可能需要的容量,并以此指定集合的容量,以避免频繁扩容
  • 字符串拼接时,建议使用StringBuilder(Java8中会自动使用)
  • 数据库中存储时间,建议用时间戳(10位,到秒),鉴于历史问题,如果旧表使用的是dateTime,那么后续旧表加时间字段时也用dateTime类型。
  • 读取数据库时间以及和前端交互时,注意时区的问题
  • 数据库中存储金额,都使用分,计算过程涉及取舍时不能精确到分,总和会出现差距
  • 调用方法都检查返回结果;提供方法时尽量避免null结果返回,区分集合和普通对象,处理好返回值为 null 的情况
  • 方法和变量命名是直观且和它所对应的业务意义一至
  • 对于业务逻辑,要写好注释,描述清楚代码所对应的业务逻辑,且注释和代码逻辑要真的一致,对于 map 一定要注释好 key 和 value 分别对应什么
  • 关键的业务要记录好日志,谁,什么时候,做了什么事情,数据变化情况
  • logger.error 注意多个参数,可能丢失堆栈
  • 异常处理:只捕获自己可以处理的异常,不能处理的不要捕获
  • java7 开始,jdk排序算法已改变,需要满足:自反性,传递性,对称性
  • 集合类判断是否为空,使用 CollectionUtil 不要使用 size() > 0

定期review

定期的去APM系统中或Kibana中检查操作的响应速度和耗时处

坚持原创技术分享,您的支持将鼓励我继续创作!