阿里开发规范

一、编程规约

1.1 命名风格

1、【强制】常量命名全部大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌名字长。
正例:MAX_STOCK_COUNT
反例:MAX_COUNT

2、【强制】抽象类命名使用 Abstract 或 Base 开头;异常类命名使用 Exception 结尾;测试类 命名以它要测试的类的名称开始,以 Test 结尾。

3、【强制】类型与中括号紧挨相连来表示数组。
正例:定义整形数组 int[] arrayDemo;
反例:在 main 参数中,使用 String args[]来定义。

4、【强制】POJO 类中布尔类型的变量,都不要加 is 前缀,否则部分框架解析会引起序列化错误。 反例:定义为基本数据类型Boolean isDeleted的属性,它的方法也是isDeleted(),RPC
框架在反向解析的时候,“误以为”对应的属性名称是 deleted,导致属性获取不到,进而抛
出异常。

5、【强制】包名统一使用小写,点分隔符之间有且仅有一个自然语义的英语单词。包名统一使用 单数形式,但是类名如果有复数含义,类名可以使用复数形式。
正例:应用工具类包名为 com.alibaba.ai.util、类名为 MessageUtils(此规则参考 spring 的框架结构)

6、【推荐】接口类中的方法和属性不要加任何修饰符号(public 也不要加),保持代码的简洁 性,并加上有效的 Javadoc 注释。尽量不要在接口里定义变量,如果一定要定义变量,肯定是 与接口方法相关,并且是整个应用的基础常量。
正例:接口方法签名void commit();
接口基础常量String COMPANY = “alibaba”;
反例:接口方法定义public abstract void f();
说明:JDK8 中接口允许有默认实现,那么这个 default 方法,是对所有实现类都有价值的默 认实现。

7、【参考】枚举类名建议带上 Enum 后缀,枚举成员名称需要全大写,单词间用下划线隔开。
说明:枚举其实就是特殊的类,域成员均为常量,且构造方法被默认强制是私有。 正例:枚举名字为ProcessStatusEnum的成员名称:SUCCESS / UNKNOWN_REASON。

8、【参考】各层命名规约

  • Service/DAO层方法命名规约
  1. 获取单个对象的方法用get做前缀。
  2. 获取多个对象的方法用list做前缀,复数形式结尾如:listObjects。 3) 获取统计值的方法用count做前缀。
  3. 插入的方法用save/insert做前缀。
  4. 删除的方法用remove/delete做前缀。
  5. 修改的方法用update做前缀。
  • 领域模型命名规约
  1. 数据对象:xxxDO,xxx即为数据表名。
  2. 数据传输对象:xxxDTO,xxx为业务领域相关的名称。
  3. 展示对象:xxxVO,xxx一般为网页名称。
  4. POJO是DO/DTO/BO/VO的统称,禁止命名成xxxPOJO。

1.2 OOP规约

2、【强制】POJO 类必须写 toString 方法。使用 IDE 中的工具:source> generate toString 时,如果继承了另一个 POJO 类,注意在前面加一下 super.toString。
说明:在方法执行抛出异常时,可以直接调用 POJO 的 toString()方法打印其属性值,便于排查问题。

3、【推荐】 类内方法定义的顺序依次是:公有方法或保护方法 > 私有方法 > getter/setter 方法。
说明:公有方法是类的调用者和维护者最关心的方法,首屏展示最好;保护方法虽然只是子类 关心,也可能是“模板设计模式”下的核心方法;而私有方法外部一般不需要特别关心,是一个 黑盒实现;因为承载的信息价值较低,所有 Service 和 DAO 的 getter/setter 方法放在类体 最后。
4、【推荐】循环体内,字符串的连接方式,使用 StringBuilder 的 append 方法进行扩展。 说明:下例中,反编译出的字节码文件显示每次循环都会 new 出一个 StringBuilder 对象, 然后进行 append 操作,最后通过 toString 方法返回 String 对象,造成内存资源浪费。 反例:
String str = “start”;
for (int i = 0; i < 100; i++) {
str = str + “hello”; }
5、【推荐】final 可以声明类、成员变量、方法、以及本地变量,下列情况使用 final 关键字: 1) 不允许被继承的类,如:String 类。
2) 不允许修改引用的域对象。
3) 不允许被重写的方法,如:POJO 类的 setter 方法。
4) 不允许运行过程中重新赋值的局部变量。
5) 避免上下文重复使用一个变量,使用 final 描述可以强制重新定义一个变量,方便更好 地进行重构。

1.5 集合的处理

1、【强制】使用集合转数组的方法,必须使用集合的toArray(T[] array),传入的是类型完全
一样的数组,大小就是 list.size()。
说明:使用 toArray 带参方法,入参分配的数组空间不够大时,toArray 方法内部将重新分配
内存空间,并返回新数组地址;如果数组元素个数大于实际所需,下标为[ list.size() ]
的数组元素将被置为 null,其它数组元素保持原值,因此最好将方法入参数组大小定义与集
合元素个数一致。
正例:
List list = new ArrayList(2); list.add(“guan”);
list.add(“bao”);
String[] array = new String[list.size()]; array = list.toArray(array);

5、【参考】利用 Set 元素唯一的特性,可以快速对一个集合进行去重操作,避免使用 List 的 contains 方法进行遍历、对比、去重操作。

二、异常

1、【推荐】防止 NPE,是程序员的基本修养,注意 NPE 产生的场景: 1)返回类型为基本数据类型,return 包装数据类型的对象时,自动拆箱有可能产生 NPE。
反例:public int f() { return Integer 对象}, 如果为 null,自动解箱抛 NPE。
2) 数据库的查询结果可能为null。
3) 集合里的元素即使isNotEmpty,取出的数据元素也可能为null。
4) 远程调用返回对象时,一律要求进行空指针判断,防止NPE。
5) 对于Session中获取的数据,建议NPE检查,避免空指针。
6) 级联调用obj.getA().getB().getC();一连串调用,易产生NPE。
正例:使用 JDK8 的 Optional 类来防止 NPE 问题。

三、MYSQL数据库

这也是我来寻找这篇规范的最初目的…

3.1 建表规约

  1. 【强制】表达是与否概念的字段,必须使用 is_xxx 的方式命名,数据类型是 unsigned tinyint (1 表示是,0 表示否)。
    说明:任何字段如果为非负数,必须是 unsigned。
    注意:POJO 类中的任何布尔类型的变量,都不要加 is 前缀,所以,需要在设置 从 is_xxx 到 Xxx 的映射关系。数据库表示是与否的值,使用 tinyint 类型,坚持 is_xxx 的 命名方式是为了明确其取值含义与取值范围。
    正例:表达逻辑删除的字段名 is_deleted,1 表示删除,0 表示未删除。

  2. 【强制】表名、字段名必须使用小写字母或数字,禁止出现数字开头,禁止两个下划线中间只 出现数字。数据库字段名的修改代价很大,因为无法进行预发布,所以字段名称需要慎重考虑。 说明:MySQL 在 Windows 下不区分大小写,但在 Linux 下默认是区分大小写。因此,数据库名、 表名、字段名,都不允许出现任何大写字母,避免节外生枝。 正例:aliyun_admin,rdc_config,level3_name 反例:AliyunAdmin,rdcConfig,level_3_name

  3. 【强制】表名不使用复数名词。 说明:表名应该仅仅表示表里面的实体内容,不应该表示实体数量,对应于 DO 类名也是单数 形式,符合表达习惯。

  4. 【强制】禁用保留字,如 desc、range、match、delayed 等,请参考 MySQL 官方保留字。

  5. 【强制】主键索引名为 pk_字段名;唯一索引名为 uk_字段名;普通索引名则为 idx_字段名。
    说明:pk_ 即 primary key;uk_ 即 unique key;idx_ 即 index 的简称。

  6. 【强制】小数类型为 decimal,禁止使用 float 和 double。
    说明:float 和 double 在存储的时候,存在精度损失的问题,很可能在值的比较时,得到不 正确的结果。如果存储的数据范围超过 decimal 的范围,建议将数据拆成整数和小数分开存储。

  7. 【强制】如果存储的字符串长度几乎相等,使用 char 定长字符串类型。

  8. 【强制】varchar 是可变长字符串,不预先分配存储空间,长度不要超过 5000,如果存储长 度大于此值,定义字段类型为 text,独立出来一张表,用主键来对应,避免影响其它字段索 引效率。

  9. 【强制】表必备三字段:id, gmt_create, gmt_modified。 说明:其中id必为主键,类型为bigint unsigned、单表时自增、步长为1。gmt_create, gmt_modified 的类型均为 datetime 类型,前者现在时表示主动创建,后者过去分词表示被 动更新。

  10. 【推荐】表的命名最好是加上“业务名称_表的作用”。
    正例:alipay_task / force_project / trade_config

  11. 【推荐】库名与应用名称尽量一致。

  12. 【推荐】如果修改字段含义或对字段表示的状态追加时,需要及时更新字段注释。

  13. 【推荐】字段允许适当冗余,以提高查询性能,但必须考虑数据一致。冗余字段应遵循: 1)不是频繁修改的字段。
    2)不是 varchar 超长字段,更不能是 text 字段。
    正例:商品类目名称使用频率高,字段长度短,名称基本一成不变,可在相关联的表中冗余存 储类目名称,避免关联查询。

  14. 【推荐】单表行数超过 500 万行或者单表容量超过 2GB,才推荐进行分库分表。 说明:如果预计三年后的数据量根本达不到这个级别,请不要在创建表时就分库分表。

3.2 索引规约

  1. 【推荐】建组合索引的时候,区分度最高的在最左边。
    正例:如果 where a=? and b=? ,如果 a 列的几乎接近于唯一值,那么只需要单建 idx_a 索引即可。 说明:存在非等号和等号混合时,在建索引时,请把等号条件的列前置。如:where c>? and d=? 那么即使 c 的区分度更高,也必须把 d 放在索引的最前列,即索引 idx_d_c。

  2. 【推荐】利用延迟关联或者子查询优化超多分页场景。
    说明:MySQL 并不是跳过 offset 行,而是取 offset+N 行,然后返回放弃前 offset 行,返回 N 行,那当 offset 特别大的时候,效率就非常的低下,要么控制返回的总页数,要么对超过 特定阈值的页数进行 SQL 改写。
    正例:先快速定位需要获取的 id 段,然后再关联:

SELECT a.* FROM1 a,
(select id from1 where 条件 LIMIT 100000,20) b 
where a.id=b.id
  1. 【参考】创建索引时避免有如下极端误解: 1)宁滥勿缺。认为一个查询就需要建一个索引。 2)宁缺勿滥。认为索引会消耗空间、严重拖慢更新和新增速度。 3)抵制惟一索引。认为业务的惟一性一律需要在应用层通过“先查后插”方式解决。

3.3 SQL语句

  1. 【强制】不要使用 count(列名)或 count(常量)来替代 count(),count()是 SQL92 定义的 标准统计行数的语法,跟数据库无关,跟 NULL 和非 NULL 无关。 说明:count(*)会统计值为 NULL 的行,而 count(列名)不会统计此列为 NULL 值的行。

  2. 【强制】count(distinct col) 计算该列除 NULL 之外的不重复行数,注意 count(distinct col1, col2) 如果其中一列全为NULL,那么即使另一列有不同的值,也返回为0。

  3. 【强制】当某一列的值全是 NULL 时,count(col)的返回结果为 0,但 sum(col)的返回结果为 NULL,因此使用 sum()时需注意 NPE 问题。 正例:可以使用如下方式来避免sum的NPE问题:SELECT IF(ISNULL(SUM(g)),0,SUM(g)) FROM table;

  4. 【强制】使用 ISNULL()来判断是否为 NULL 值。 说明:NULL 与任何值的直接比较都为 NULL。
    1) NULL<>NULL的返回结果是NULL,而不是false。
    2) NULL=NULL的返回结果是NULL,而不是true。
    3) NULL<>1的返回结果是NULL,而不是true。

  5. 【强制】 在代码中写分页查询逻辑时,若 count 为 0 应直接返回,避免执行后面的分页语句。

  6. 【强制】不得使用外键与级联,一切外键概念必须在应用层解决。 说明:以学生和成绩的关系为例,学生表中的 student_id 是主键,那么成绩表中的 student_id 则为外键。如果更新学生表中的 student_id,同时触发成绩表中的 student_id 更新,即为 级联更新。外键与级联更新适用于单机低并发,不适合分布式、高并发集群;级联更新是强阻 塞,存在数据库更新风暴的风险;外键影响数据库的插入速度。

  7. 【强制】禁止使用存储过程,存储过程难以调试和扩展,更没有移植性。

3.3 数据库查询优化

1 使用子查询优化

这种方式先定位偏移位置的 id,然后往后查询,这种方式适用于 id 递增的情况。

select * from orders_history where type=8 and 
id>=(select id from orders_history where type=8 limit 100000,1) 
limit 100;

2 使用 id 限定优化(前提:id是连续递增,删除过记录不符合)

这种方式假设数据表的id是连续递增的,则我们根据查询的页数和查询的记录数可以算出查询的id的范围,可以使用 id between and 来查询:

select * from orders_history where type=2 
and id between 1000000 and 1000100 limit 100;
select * from orders_history where id >= 1000001 limit 100;

3 使用临时表优化(数据量上千万的时候

对于使用 id 限定优化中的问题,需要 id 是连续递增的,但是在一些场景下,比如使用历史表的时候,或者出现过数据缺失问题时,可以考虑使用临时存储的表来记录分页的id,使用分页的id来进行 in 查询。这样能够极大的提高传统的分页查询速度,尤其是数据量上千万的时候。

在这里插入图片描述
在这里插入图片描述

  • 主要原因在于 exists这个部分,因为 table3有200W的数据,并且循环式和外表扫描查询,并且这里的like是不会走索引的,只能全扫描,所以慢就很明显了,由于是动态语句,并在存储过程中,所以优化就是拆解EXISTS这部分
  • 主要思路就是 先从200W+ 的table3中查出来order_no 然后把order_no插入临时表,然后再使用in 临时表查询减少关联扫描次数就能极大的优化查询时间

一般只在确定不能用索引的时候才使用临时表,或者在存储过程中某些固定数据使用次数非常多的时候使用临时表,其他时候我一般不建议使用

四、关于数据表的id说明

一般情况下,在数据库中建立表的时候,强制为每一张表添加 id 递增字段,这样方便查询。

如果像是订单库等数据量非常庞大,一般会进行分库分表。这个时候不建议使用数据库的 id 作为唯一标识,而应该使用分布式的高并发唯一 id 生成器来生成,并在数据表中使用另外的字段来存储这个唯一标识。

使用先使用范围查询定位 id (或者索引),然后再使用索引进行定位数据,能够提高好几倍查询速度。
即先 select id,然后再 select *

五、MySQL 数据量很大的情况下查询优化方法:

一、不要用

1,where后边用like模糊查询,不走索引
2,in 和 not in 不走索引(尽量用between and)
3,索引的最左匹配原则
4,!= 不走索引
5,where中使用函数操作,不走索引
6,where中使用参数,不走索引
7,order by的时候,如果没有select该字段,也不走索引!
8,不用游标,因为当数据量到1万的时候,游标效率很差

二、要用

1,先select id,再select *
2,使用between and 而不是用 in和not in
3,适当情况下可用存储过程,减少数据库的IO
4,建立索引
5,数据量特别大,差多1000万时,用临时表存储Id,再做in操作

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值