《阿里巴巴开发手册》学习总结(二)——异常日志、单元测试、安全规约、MySQL

二. 异常日志

1. 错误码
  • 错误码为字符串类型,共 5 位,分成两个部分:错误产生来源+四位数字编号

错误产生来源分为 A/B/C,A 表示错误来源于用户,比如参数错误,用户安装版本过低,用户支付超时等问题;B 表示错误来源于当前系统,往往是业务逻辑出错,或程序健壮性差等问题;C 表示错误来源于第三方服务,比如 CDN 服务出错,消息投递超时等问题;

2. 异常处理
  • 事务场景中,抛出异常被 catch 后,如果需要回滚,一定要注意手动回滚事务
  • 不要在 finally 块中使用 return。
  • 在调用 RPC、二方包、或动态生成类的相关方法时,捕捉异常必须使用 Throwable类来进行拦截。
  • 方法的返回值可以为 null,防止 NPE 是调用者的责任
  • 防止 NPE,是程序员的基本修养
3. 日志
  • 应用中不可直接使用日志系统(Log4j、Logback)中的 API,而应依赖使用日志框架(SLF4J、JCL–Jakarta Commons Logging)中的 API,使用门面模式的日志框架,有利于维护和各个类的日志处理方式统一。

private static final Logger logger = LoggerFactory.getLogger(Test.class);

  • 所有日志文件至少保存 15 天,对于当天日志,以“应用名.log”来保存,保存在/home/admin/应用名/logs/目录下,过往日志格式为: {logname}.log.{保存日期},日期格式:yyyy-MM-dd

以 aap 应用为例,日志保存在/home/admin/aapserver/logs/aap.log,历史日志名称为aap.log.2016-08-01

  • 应用中的扩展日志(如打点、临时监控、访问日志等)命名方式:
    appName_logType_logName.log。logType:日志类型,如stats/monitor/access 等;logName:日志描述。
  • 在日志输出时,字符串变量之间的拼接使用占位符的方式(性能更好)。
:logger.debug("Processing trade with id: {} and symbol: {}", id, symbol);
  • 对于 trace/debug/info 级别的日志输出,必须进行日志级别的开关判断
// 如果判断为真,那么可以输出 trace 和 debug 级别的日志
if (logger.isDebugEnabled()) {
 logger.debug("Current ID is: {} and name is: {}", id, getName());
}
  • 异常信息应该包括两类信息:案发现场信息和异常堆栈信息
:logger.error("inputParams:{} and errorMessage:{}", 各类参数或者对象 toString(), e.getMessage(), e);
  • 注意日志输出的级别,error 级别只记录系统逻辑出错、异常或者重要的错误信息。可以使用 warn 日志级别来记录用户输入参数错误的情况,避免用户投诉时,无所适从。

三. 单元测试

  • 单元测试应该是全自动执行的,并且非交互式的。测试用例通常是被定期执行的,执行过程必须完全自动化才有意义。输出结果需要人工检查的测试不是一个好的单元测试。单元测试中不准使用 System.out 来进行人肉验证,必须使用 assert 来验证
  • 对于单元测试,要保证测试粒度足够小,有助于精确定位问题。单测粒度至多是类级别,一般是方法级别
  • 单元测试代码必须写在如下工程目录:src/test/java,不允许写在业务代码目录下。

四. 安全规约

  • 隶属于用户个人的页面或者功能必须进行权限控制校验。
  • 用户请求传入的任何参数必须做有效性验证
  • 用户请求传入的任何参数必须做有效性验证
  • 在使用平台资源,譬如短信、邮件、电话、下单、支付,必须实现正确的防重放的机制,如数量限制、疲劳度控制、验证码校验,避免被滥刷而导致资损。

五. MySQL

1. 建表规约
  • 表达是与否概念的字段,必须使用 is_xxx 的方式命名数据类型是 unsigned tinyint(1 表示是,0 表示否)。
  • 表名、字段名必须使用小写字母或数字,禁止出现数字开头,禁止两个下划线中间只出现数字。
  • 表名不使用复数名词
  • 主键索引名为 pk_字段名;唯一索引名为 uk_字段名;普通索引名则为 idx_字段名。
  • 小数类型为 decimal,禁止使用 float 和 double(否则有精度损失的风险)。
  • 表必备三字段:id, create_time, update_time

其中 id 必为主键,类型为 bigint unsigned、单表时自增、步长为 1。create_time, update_time的类型均为 datetime 类型,前者现在时表示主动式创建,后者过去分词表示被动式更新。

  • 表的命名最好是遵循“业务名称_表的作用”。库名与应用名称尽量一致。
  • 单表行数超过 500 万行或者单表容量超过 2GB,才推荐进行分库分表
2. 索引规约
  • 业务上具有唯一特性的字段,即使是组合字段,也必须建成唯一索引。(即使在应用层做了非常完善的校验控制,只要没有唯一索引,根据墨菲定律,必然有脏数据产生。)
  • 超过三个表禁止 join。需要 join 的字段,数据类型保持绝对一致;多表关联查询时,保证被关联的字段需要有索引
  • varchar 字段上建立索引时,必须指定索引长度,没必要对全字段建立索引,根据实际文本区分度决定索引长度。
  • 利用延迟关联或者子查询优化超多分页场景

MySQL 并不是跳过 offset 行,而是取 offset+N 行,然后返回放弃前 offset 行,返回 N 行,那当offset 特别大的时候,效率就非常的低下,要么控制返回的总页数,要么对超过特定阈值的页数进行 SQL改写。

// 先快速定位需要获取的 id 段,然后再关联:
SELECT t1.* FROM 表 1 as t1, (select id from 表 1 where 条件 LIMIT 100000,20 ) as t2 where t1.id=t2.id
  • SQL 性能优化的目标:至少要达到 range 级别,要求是 ref 级别,如果可以是 consts最好
3. SQL语句
  • 不要使用 count(列名)或 count(常量)来替代 count(*)
  • count(distinct col) 计算该列除 NULL 之外的不重复行数,注意 count(distinct col1, col2) 如果其中一列全为 NULL,那么即使另一列有不同的值,也返回为 0。
  • 当某一列的值全是 NULL 时,count(col)的返回结果为 0,但 sum(col)的返回结果为NULL,因此使用 sum()时需注意 NPE 问题
SELECT IFNULL(SUM(column), 0) FROM table;
  • 使用 ISNULL()来判断是否为 NULL 值, NULL 与任何值的直接比较都为 NULL。
  • 代码中写分页查询逻辑时,若 count 为 0 应直接返回,避免执行后面的分页语句
  • 对于数据库中表记录的查询和变更,只要涉及多个表,都需要在列名前加表的别名(或表名)进行限定
  • SQL 语句中表的别名前加 as,并且以 t1、t2、t3、…的顺序依次命名。
  • in 操作能避免则避免,若实在避免不了,需要仔细评估 in 后边的集合元素数量,控制在 1000 个之内。
4. ORM映射
  • 在表查询中,一律不要使用 * 作为查询的字段列表,需要哪些字段必须明确写明。
  • 不要用 resultClass 当返回参数,即使所有类属性名与数据库字段一一对应,也需要定义<resultMap>;反过来,每一个表也必然有一个<resultMap>与之对应。
  • sql.xml 配置参数使用:#{},#param# 不要使用${} 此种方式容易出现 SQL 注入。
  • 更新数据表记录时,必须同时更新记录对应的 update_time 字段值为当前时间。
  • @Transactional 事务不要滥用。事务会影响数据库的 QPS,另外使用事务的地方需要考虑各方面的回滚方案,包括缓存回滚、搜索引擎回滚、消息补偿、统计修正等。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值