1: 单元测试

      提高 Dao 层 方法级别覆盖,包括 insert ,update ,select 。

      Service 层核心方法覆盖,如对外部环境(网络、服务、中间件等)有依赖,引入Mock 实现。

     核心业务、核心应用、核心模块的增量代码确保单元测试通过。

2.    禁止3B:Big Transaction,Big SQL,Big Batch 

2.1.   Big Transaction 

注意点: 

1.      对数据库操作必须使用事务,不能使用自动提交,尽量使用声明式事务; 

2.      让事务尽可能的小,在Service层组装数据,在manager层处理事务; 

3.      不要在事务里调用服务(服务可能阻塞); 

4.      不要在事务里调用Redis; 

5.      在事务中批量更新要排序,确保多事务并发时,避免资源锁等待。  

详解: 

无论是Oracle、SqlServer还是Mysql,大事务是一定要避免的,大事务容易造成锁资源的长时间占用,从而降低并发性能,增大死锁概率。如下是几种大事务的典型场景: 

1)       @Transactional不要打在Class上,这样类中的所有方法均在事务边界内,容易造成大事务,@Transactional应该控制更精细一些,打到方法级; 

2)       在一个事务中要更新多张表,在更新每一张表之前都要处理一堆业务逻辑(查询、运算、调用服务等等),正确的做法应该是将查询、运算和服务调用逻辑提到事务外,事务边界内尽可能只处理表更新操作; 

2.2.   Big SQL 

SQL使用: 

1.      尽量不用表关联,如果使用表关联,不要超过3个表join; 

2.      热点数据尽量使用Redis;(比如基础资料) 

3.       尽量不用子查询,不用Exist,不在条件列上使用函数; 

2.3.   Big Batch 

大批量的查询输出很容易将内存打爆,报表或者打印要分批处理。

3.    禁止全表扫描SQL和select *,update所有列 

3.1.   全表扫描 

什么是全表扫描? 

在数据库中,对无索引表查询或有索引但SQL不能有效利用进行查询的过程称为全表扫描。 

全表扫描会搜寻数据库表中的每一条记录,直到所有符合给定条件的记录返回。 


常见的全表扫描SQL: 

1.      is (not) null

Sql代码  

1.  select ID from t where NUM is null   

解决方式:不允许表中含可空字段,对未赋值的字段设默认值,根据默认值查询。 


2. like “%***%” 和like ‘%***’

Sql代码  

1.  select id from t where name like '%abc%'   

2.  select id from t where name like '%abc’ 

可以用“全文检索”取代“全模糊查询”或“左模糊查询”,右模糊查询可以利用上索引。 


3. where 子句中使用参数 

       Sql代码  

1.  select id from t where num=@num <mailto:num=@num>   

可以改为强制查询使用索引:  

Sql代码  

1.  select id from t with(index(索引名)) where num=@num <mailto:num=@num>   


4. 在where子句中的条件列上进行函数、算术运算或其他表达式运算 

       Sql代码  

1.  select id from t where num/2=100   

应改为:  

Sql代码  

1.  select id from t where num=100*2   


Sql代码  

1.  select id from t where substring(name,1,3)=’abc’–name以abc开头的id   

2.  select id from t where datediff(day,createdate,’2005-11-30′)=0–‘2005-11-30’生成的id   

应改为:  

Sql代码  

1.  select id from t where name like ‘abc%’   

2.  select id from t where createdate>=’2005-11-30′ and createdate<’2005-12-1′   

5. OR条件连接:myisam表能用到索引,innodb不行 

       Sql代码  

1.  select ID from t where NUM=10 or NUM=20   

可以这样查询:  

Sql代码  

1.  select ID from t where NUM=10   

2.  union all   

3.  select ID from t where NUM=20    

3.2.   select * 

查询需要的字段; 

当需要查询全部字段时,写出全部字段名; 

3.3.   update所有列 

1. 不能写通用的update SQL,按业务更新; 

2. 按物理主键或业务主键进行update操作; 

4.  禁止输入参数不做校验及服务直接抛出异常 

4.1.   参数校验        

1. 客户端,服务端参数增加双重校验逻辑

2. 防止数据库注入,跨站点脚本防范(XSS)

4.2.   服务异常 

异常处理是系统内的一种错误处理机制,一般不用于跨系统调用; 

通过定义错误码方式是处理跨系统错误的合适的方式; 

返回错误码,更便于调用方根据不同的返回值进行不同的处理,抛出异常的方式实际上是将底层的实现细节暴露给调用方

5.    禁止服务及UI不做防重入 

5.1.   服务防重 -接口幂等性解决方案 

MQ消息重复: 

在服务端防止重复数据被多次被插入到数据库。 

常用的办法: 

1. MD5防重:对记录取MD5值,收到重复的数据丢弃; 

2. 在数据库加唯一索引; 

3. 根据业务做防重:状态顺序,业务ID; 

5.2.   UI防重入 

在用户操作完成后,应当将界面变为不可操作状态(比如:按钮不可点击等,不再相应enter事件等等)。 


6.    核心场景开发

1:涉及核心场景需求需要提供活动图,序列图。

     修改原有代码逻辑需要提供伪代码图。(如事务开启,提交,回滚,更新逻辑涉及前置条件,sql更新条件 )

     需要review通过后才允许上线。

7 :禁止Worker扫描业务表 

  建议使用分布式任务调度框架;  

7.1.   Worker扫描业务表带来的问题 

1. 增加对业务表的访问压力; 

2. 如果涉及更新,影响并发性能; 

7.2.   正确的做法 

       1. 建立独立的任务表; 

2. 数据量大:基于时间做分区索引; 

3. 处理完成的任务可以删除或转历史,保证任务表数据量比较少 


8: 核心业务在设计阶段考虑支持对账机制

      对账包括实时对账,离线对账。

     8.1  实时对账

      技术方案类似于 “ 旁路监控”   需要双方系统研发支持。

      举例 A系统 B系统 需要对账。A 系统核心流程处理完发出消息 MQ 。 对账系统 C 订阅 A 发出的消息,调用B 系统提供的实时接口,根据查询结果判断业务是否正常。

     8.2  离线对账

     这里主要指 D+1 在数仓上部署脚本核对, 

         

9:  涉及账户,金额类更新场景

       举例在涉及账户额度,更新,退款,优惠券,折扣卡 金额类场景,在设计阶段考虑增加版本号,状态等字段。

       更新时 where 条件 中增加前置状态 或 版本号 条件,更新后dao层返回影响行数,在服务层根据返回行数做业务判断,复杂场景要引入事务保证数据一致性。

      

10 :  KYB,KYC  

              kyb(know your business)  了解你的业务

              kyc(know your customer) 了解你的客户

             业务的核心流程包括那几个节点? 业务风险最高的场景是那块?  我们依赖那些核心系统? 依赖那些核心接口?提供的接口靠谱么?

             写接口支持防重吗?服务端防重还是客户端防重? 是提交实时返回结果还是消息异步通知结果? 消息丢失如何监控数据一致性?

             读接口是实时查询数据库还是查询缓存?数据是否有延迟?业务能接受延迟吗?

             如何及时发现线上问题?

             什么时候做降级?什么时候做限流?