1. 逻辑代码
1.1 基础开发
1.1.1 参数校验
- 入参基本校验:金额不能小于0、不能大于余额、不能大于应还总额等。
-
字符串非空。
-
请求参数用 POJO 接收,POJO 的属性要添加基本校验注解,Controller 方法要用
@Valid
注解开启校验。 -
正则表达式:用正则表达式进行参数校验时,不能写太复杂的,因为 Java 的正则引擎采用的是贪婪匹配模式。
1.1.2 跨系统调用
-
超时限定:为了防止外部服务出现响应缓慢而拖累调用方,调用方必须设置连接超时、读超时等。
-
幂等性,防止重复调用:调用方传递唯一的请求编号、服务方根据请求编号进行幂等性控制;
-
重试:对于重要业务,调用方应该有重试机制,重试一定次数后仍然失败的,有告警出来,人工介入处理。
-
调用链跟踪:
1.2 SQL
-
检查SQL语句的执行计划,关注是否有全表扫描、索引全扫描。
-
尽量把 1+N 次查询改写成 1或2 次查询。
-
不能在字段上应用函数。
-
字段应该单独在比较运算符的左侧。
-
检查是否有隐式类型转换
- 隐式类型转换会对扫描到的每条记录的字段进行转换,然后再进行比较,执行性能较慢,还会导致无法使用被转换列上的索引。
- 比如
varchar(10)
类型的字段用abc=40
则会进行隐式类型转换,abc='40'
则不会。 - MyBatis在处理数据库
Date
类型时只能传TIMESTAMP
。 类型,与Oracle的Date
类型进行比较会导致隐式转换。 - MySQL 里如果比较的两个字符串列的字符集不同也会发生隐式类型转换。
join
的连接条件不要放到 where 子句,连接条件与过滤条件的执行顺序不同。
1.3 事务控制
-
Spring 里内调用 事务方法会导致事务不生效,要通过 Spring 的代理对象来调用。
-
事务内不能访问外部系统,特别是会导致外部系统状态改变的操作。
-
不要混用注解驱动的事务与 JDBC Connection 。
1.4 并发
1.4.1 线程池
-
临时创建的线程池应当在用完后就立即销毁,合理设置线程的空闲等待时间。
-
创建线程池时应该指定
ThreadFactory
的实现,新创建的线程应该有独特的名字,方便定位问题。 -
创建的线程数量要有上限。
-
任务的等待队列要有边界。
-
采用
java.util.concurrent.ThreadPoolExecutor
线程池来执行的任务之间应该是彼此独立、无依赖的。如果任务会派生并等待子任务执行完成的,使用ForkJoinPool
更合适。
1.4.2 善于利用数据库 CAS 操作防止并发
update t_task set status = :newStatus where id = :id and status = :oldStatus
;
检查更新的记录数,如果为 1 表示更新成功、没有并发,否则有其他的操作修改了数据。
该语句应该在执行操作或在事务开始处执行。
2. 数据库设计
2.1 数据库
- 最小权限原则:只给账户分配必须的最小权限。
-
禁止在公网访问数据库。
2.2 表
-
对于MySQL数据库,存储引擎是否是合理,一般用 InnoDB 存储引擎。
-
字符集是否合理,一般用 utf8mb4 。
2.3 字段
-
数据类型选择,满足需求的情况下占用存储空间尽量小。
-
状态值、枚举值的定义是否完备。
-
是否应该有唯一性约束。
-
自增主键、创建时间、更新时间等必须字段是否有。
-
时间类型的字段不要用字符串类型来存储,存储效率不高。
-
不要在表里存储超长字段。
-
字符串长度
- MySQL InnoDB 引擎里,字符串长度对应的是字符集的字符数。
- Oracle 里 varchar2 对应的是字节数。
- 一个字段的类型在所有表里都应该保持一致,比如订单号在订单表的类型是 bigint,那么在运单表的类型也应该是 bigint 。
2.4 索引
-
状态字段是否加了索引,特别是任务类的表。
-
常用查询字段是否有索引。
-
索引合理性检查。
-
加载大量数据时,可以先禁用索引,待数据加载完后再重建索引。
-
一个表的数据大量变更(比如大批插入或删除)后应该重新收集表、索引的统计信息。
-
如果要更改MyQL的主键列,应当先删除二级索引、更改主键列、再创建原二级索引,因为二级索引指向的是主键索引,修改主键列会导致二级索引也修改。
-
Oracle 加索引可以开启并行来提升速度,但加约束只能单线程执行。
-
Oracle 给大表加主键或唯一性约束时,可以先添加唯一性索引,开启并行度,执行完后再把索引的并行度改为 1,然后再加约束。
3. 检查清单-其他
3.1 外部系统交互
-
单独给每个接口设置连接超时时间、读超时时间。
-
被调用接口支持幂等性的情况下,应考虑调用失败时自动重试有限次。
-
重要接口调用失败应该有告警。
3.2 监控
-
对于异步处理的逻辑应该有监控告警,及时发现处理失败的情况。
-
异步任务未处理、处理超时、处理失败等非成功状态都应加以监控。
-
慢 sql 监控。
-
长事务监控:长时间未提交事务监控,特别是交易型系统基本是不存在长事务的。
3.3 日志
-
多实例部署时防止日志互相覆盖,日志文件名加入机器名或IP或时间戳等加以区别。
-
在关键的逻辑点输出日志。
-
日志信息要有关键字段。
-
要打印异常栈,不能只打印信息。
3.单元测试
-
一定要跑所有的单元测试。
-
用断言校验结果。
-
单元测试之间不应有依赖。
<
p>欢迎关注我的微信公众号: coderbee笔记 。