JAVA开发规范

前言

本规范的目的是提升代码质量,提升团队协作效率,规范中出现的强制,推荐,参考含义如下:

【强制】:必须严格遵守,如有特殊情况,需架构委员会评审报备。

【推荐】:没特殊情况必须遵守,在开发组长允许下可以不遵守。

【参考】:可以参考,不做严格要求。

后台开发规范

1.1 命名规范

  1. 【强制】驼峰式命名,其他不允许,常量除外。

  2. 【强制】拼音和英文混合不允许。

    正例: alibaba / taobao / youku / hangzhou 等国际通用的名称,可视同英文。
    反例: DaZhePromotion [ 打折 ] / getPingfenByName() [ 评分 ] 
    
  3. 【强制】常量命名大写,单词以下划线隔开,语义尽量表达完整,比如MAX就语义不明确。

     正例: MAX _STOCK _COUNT
     反例: MAX _COUNT
    
  4. 【强制】抽象类使用Abstract开头或者Base结尾,异常类以Exception结尾, 测试类命名以它要测试的类的名称开始,以 Test 结尾。

  5. 【强制】除非业界通用缩写,否则不允许单词缩写。

     反例: AbstractClass “缩写”命名成 AbsClass;condition “缩写”命名成 condi ,此类随意缩写严重降低了代码的可阅读性。 
    
  6. 【推荐】工具类以Utils结尾,帮助类以Helper结尾,帮助类跟工具类的区别在于帮助类是方便业务逻辑使用的,工具类是更通用的。

     正例: 应用工具类包名为 com . yujiahui . common . util 、类名为 MessageUtils( 此规则参考spring 的框架结构 ) 
    
  7. 【推荐】枚举类使用Enum结尾。

  8. 【推荐】如果模块、接口、类、方法使用了设计模式,在命名时体现出具体模式。

     说明:将设计模式体现在名字中,有利于阅读者快速理解架构设计理念。
     正例: public class OrderFactory;
     public class LoginProxy;
     public class ResourceObserver;
    
  9. 【参考】分层命名规范

      1.  DTO命名规范,如果DTO是命令,则Cmd结尾,如果是查询,Query结尾,如果是view object,VO结尾,其他无法归类的DTO结尾。 
      2.  Service层以Service结尾,Dao层以Dao结尾。方法获取单个对象以get开头,获取多个对象以list开头,获取数量已count开头。
          插入以create开头,更新以update开头,删除以delete开头 
      3.  领域层工厂以Factory结尾,领域服务建议以DomainService结尾,实体和值对象不需要后缀,是什么名称就什么名称,比如订单实体就叫Order 
      4.  领域模型层命名尽量与数据表一致,比如表order_detail,命名为OrderDetail,如果表有统一前缀,前缀是否体现到模型对象名上在一个项目内统一。
    
  10. 【推荐】实体里面有些布尔方法如果用is开头容易被框架判断为属性,建议都用iz开头,比如izEasy。

1.2 常量规范

  1. 【强制】魔鬼数字不允许。

  2. 【强制】long 或者 Long 初始赋值时,使用大写的 L ,不能是小写的 l ,小写容易跟数字 1 混淆,造成误解。

    说明: Long a = 2 l; 写的是数字的 21,还是 Long 型的 2?。
    
  3. 【推荐】不要在一个类里面维护所有常量,比如领域模型的常量可以放到领域模型里面,也可以另外建立一个常量类,常量类以Constants结尾

     正例:缓存相关常量放在类 CacheConstants 下 ; 系统配置相关常量放在类 ConfigConstants 下 
    
  4. 【推荐】常量类共享应该按层次放置,层次分为:跨应用共享,应用内共享,模块内共享,类内共享。跨应用共享的常量类放置在一个jar的constant包下,应用内共享的常量类放置下通用模块下的constant包下,模块内共享的常量类放置在本模块的constant包下。

    反例:易懂变量也要统一定义成应用内共享常量,两位攻城师在两个类中分别定义了表示“是”的变量:
     类 A 中: public static final String YES = " yes " ;
     类 B 中: public static final String YES = " y " ;
     A . YES . equals(B . YES) ,预期是 true ,但实际返回为 false ,导致线上问题。
    

1.3 格式规范

  1. 【强制】第一个大括号不换行,单行字符120个,其他采用IDE默认格式。
  2. 【推荐】不同业务逻辑或者不同语义的代码之间需要有空行。
  3. 【强制】IDE设置文件编码为UTF-8。
  4. 【强制】一行不允许定义多个变量。

1.4 Java规范

  1. 【强制】所有覆写的方法都必须加上@Override。

  2. 【推荐】equals方法容易报空指针异常,常量放前面或者使用Objects.equals(jdk7引入)。

     正例:" test " .equals(object);
     反例: object.equals( " test " );
    
  3. 【强制】包装类的相等比较用equals,不能用==。

  4. 【推荐】基本类型和包装类型的使用标准:

      1. pojo类型的属性用包装类型
      2. RPC方法的参数和返回值用包装类型
      3. 局部变量使用基本类型
    
  5. 【强制】领域模型类必须实现toString方法

  6. 【推荐】类内方法定义的顺序是:公有方法》保护方法》私有方法》getter,setter。

  7. 【推荐】类的方法的访问控制从严。类的方法只在内部使用必须是private,只对继承类开放,必须是protected,变量跟方法类似。

  8. 【强制】不要在类里面使用静态变量存储数据,如果需要,使用线程安全的数据结构。

  9. 【强制】不能在foreach循环中删除集合元素,删除元素使用迭代器。

    	// 正面案例
        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()) {
                String item = iterator.next();
                if (删除元素的条件) {
                        iterator.remove();
                }
        }
    
    	// 反面案例
         List<String> a = new ArrayList<String>();
            list.add("1");
            list.add("2");
            for (String item : list) {
                    if ("1".equals(item)) {
                            list.remove(item);
                    }
            }
    
  10. 【强制】SimpleDateFormat线程不安全不要定义为static变量。

     	// 正例:注意线程安全,使用 DateUtils 。亦推荐如下处理:
        private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() {
                @ Override
                protected DateFormat initialValue() {
                        return new SimpleDateFormat("yyyy-MM-dd");
                }
        };
    
  11. 【推荐】高并发时,考虑锁的性能,尽量用无锁数据结构,能锁区块就不要锁整个方法,能锁对象及不要锁整个类。

  12. 【强制】有并发修改同一个对象的场景,需要加锁,并发修改的概率大于20%,使用悲观锁,否则使用乐观锁, 乐观锁根据业务场景考虑重试次数。

  13. 【推荐】有返回值的函数尽量不要修改入参。

  14. 【推荐】尽量少用else,使用卫语句。比如:if(condition) {return obj;} 其他逻辑; 如果实在if-else多,采用状态模式。

        //  正例:超过 3 层的 if-else 的逻辑判断代码可以使用卫语句、策略模式、状态模式等来实现,
        public void today() {
            if (izBusy()) {
                System.out.println(“change time.”);
                return;        
            }
            if (izFree()) {
                System.out.println(“go to travel.”);
                return;
            }
            return;
        }
    

1.5 业务代码规范

  1. 【强制】方法的参数不允许超过5个。

  2. 【推荐】参数和返回值不要用Map这种泛化参数。

  3. 【强制】方法的大括号层级不允许超过4层。

  4. 【推荐】一个方法只做一件事情,方法不超过30行。

  5. 【强制】方法不能有副作用,比如查询类方法,不允许改变入参的属性值。

  6. 【推荐】类不能超过500行。

  7. 【推荐】不允许大量重复代码。

  8. 【强制】批量操作必须分组,比如批量插入一千条数据,分为500一组。

 正例:List groups = Lists.partition(list,500);
  1. 【强制】业务查询返回数据过多必须分页,比如不能超过5000条返回数据。

  2. 【推荐】工具类先看项目中是否有提供,不允许随意添加,如果碰到项目中和jar内有同名工具类,优先使用项目中的类,比如StringUtils在多个jar中有,先使用本项目的StringUtils,满足不了要求,再用其他jar的类或者移植方法到本项目的StringUtils。

  3. 【推荐】重要业务流程必须有业务日志,采用BizLog注解,记录新旧值。

  4. 【推荐】Service层提供的都是业务逻辑方法,不要放大量查询方法,有多种查询的业务模型抽取出Query类,比如OrderQueryService,也可以采用CQRS模式,命令和查询分离。

  5. 【强制】业务上存在并发操作的场景,考虑方法的幂等性,或者使用乐观锁。

  6. 【推荐】时刻进行代码重构,避免代码腐化,去掉下次再改的心态(潜台词:永不再改)

  7. 【强制】业务上涉及订单、用户信息、金额等安全敏感数据文件导出并上传阿里云OSS时,必须使用文件服务的/api/file/uploadPrivate接口上传到阿里云OSS。

  8. 【强制】业务逻辑尽量避免跨数据库事务操作,严禁事务中穿插执行不同数据库的sql语句,必要时要考虑失败场景补偿方案和告警机制。

    反例:
    执行A数据库更新逻辑X
    执行B数据库更新逻辑Y
    执行A数据库更新逻辑X
    执行B数据库更新逻辑Y
    

1.6 异常规范

  1. 【推荐】不允许对大段代码进行try-catch。
  2. 【强制】对于有核心功能的死循环线程必须try-catch整个循环体,防止任何异常导致循环线程退出,如果有性能问题,可以酌情优化
  3. 【推荐】不允许捕获异常后不做任何处理,如果不想处理,抛出去。
  4. 【推荐】方法可能返回null,调用方要做非空判断防止NPE问题。
  5. 【推荐】避免直接抛RuntimeException,使用业务异常,比如BusinessException,ApplicatinException,DomainException。

1.7 日志规范

  1. 【推荐】应用的扩展日志文件命名格式 logName.时间.logType.log。logType:日志类型,比如json表示结构化日志,app表示普通日志,logName:日志名称。
  2. 【推荐】错误日志和业务日志分开。
  3. 【推荐】异常日志必须包含堆栈信息和现场参数。
  4. 【强制】严禁输出大量无效日志,比如在大循环中输出日志。

1.8 注释规范

  1. 【参考】类、类属性、类方法的注释使用 Javadoc 规范,使用/**内容*/格式,避免使用// xxx 方式。

  2. 【推荐】所有的抽象方法 ( 包括接口中的方法 ) 必须要用 Javadoc 注释、除了返回值、参数、异常说明外,还必须指出该方法做什么事情,实现什么功能。

 说明:对子类的实现要求,或者调用注意事项,请一并说明。
  1. 【推荐】所有的枚举类型字段要有注释,说明每个数据项的用途。

  2. 【推荐】代码修改的同时,注释也要进行相应的修改,尤其是参数、返回值、异常、核心逻辑等的修改。

  3. 【参考】谨慎注释掉代码。在上方详细说明,而不是简单的注释掉。如果无用,则删除。

 说明:代码被注释掉有两种可能性:
  1. 后续会恢复此段代码逻辑。
  2. 永久不用。前者如果没有备注信息,难以知晓注释动机。后者建议直接删掉 ( 代码仓库保存了历史代码 ) 。
  1. 【参考】对于注释的要求:第一、能够准确反应设计思想和代码逻辑 ; 第二、能够描述业务含义,使别的程序员能够迅速了解到代码背后的信息。完全没有注释的大段代码对于阅读者形同天书,注释是给自己看的,即使隔很长时间,也能清晰理解当时的思路 ; 注释也是给继任者看的,使其能够快速接替自己的工作。

  2. 【参考】好的命名、代码结构是自解释的,注释力求精简准确、表达到位。避免出现注释的一个极端:过多过滥的注释,代码的逻辑一旦修改,修改注释是相当大的负担。


// 反例 put elephant into fridge
put(elephant, fridge);
方法名 put ,加上两个有意义的变量名 elephant 和 fridge ,已经说明了这是在干什么,语义清晰的代码不需要额外的注释。
  1. 【推荐】及时清理不再使用的代码段或配置信息。

     说明:对于垃圾代码或过时配置,坚决清理干净,避免程序过度臃肿,代码冗余。
     正例:对于暂时被注释掉,后续可能恢复使用的代码片断,在注释代码上方,统一规定使用三个斜杠(///)来说明注释掉代码的理由。
    

接口规范

  1. 【强制】Rest接口返回值必须是BaseResult对象及其子类对象,封装了错误码,错误描述,isSuccess,data信息。data放业务数据。业务错误码自己定义,推荐优先使用英文,避免使用通用错误码,通用错误码参考ExceptionEnum。

比如{code:”0”,msg:”操作成功”,data:{XX}}。

  1. 【推荐】Rest接口必须标明请求的content_type。比如content_type=applicaton/json

  2. 【强制】接口提供方必须考虑幂等性,防止重复调用导致严重的业务灾难。

  3. 【强制】查询接口如果数据量过多,需要分页返回。

  4. 【推荐】接口必须标明字段的类型,长度,是否必填,文字说明必须准确,反例:person:人员。

  5. 正例:person:人员编码。

  6. 【推荐】Rest接口返回值需要综合考虑实际功能、安全和性能需求,精细化按需返回业务数据。

    1. 比如,移动端接口需要考虑性能问题,要避免返回无效字段;对于中台服务移动端接口返回的多余字段场景,需要业务应用封装处理后再返回给移动端。

      PC端接口返回值要求 移动端接口返回值要求
      中台服务 满足实际功能、安全即可 满足实际功能、安全即可。性能要求需要业务应用封装处理。
      业务应用 满足实际功能、安全即可 除了满足实际功能、安全需求之外,要考虑性能,避免返回无效字段
  7. 【推荐】Feign Api提供方禁止在给下游使用方的jar中引入AutoConfiguration等影响启动的配置类

数据库规范

1. 建表相关规范

  1. 库名、表名、字段名,使用小写和下划线 _ 分割
  2. 库名、表名、字段名,不超过12个字符。默认支持64个字符。
  3. 库名、表名、字段名,见名知意,建议使用名词而不是动词。
  4. 使用 InnoDB 存储引擎。支持;事务、锁、高并发 性能好。
  5. 推荐使用 utf8mb4 可以存emoji
  6. 单表字段数,建议不超过40个

2. 字段相关规范

  1. 整型定义中不显示设置长度,如使用 INT,而不是INT(4)
  2. 存储精度浮点数,使用 DECIMAL 替代 FLOAT、DOUBLE
  3. 所有字段,都要有 Comment 描述
  4. 所有字段应定义为 NOT NULL
  5. 超过2038年,用DATETIME存储
  6. 短数据类型 0~80 选用 TINYINT 存储
  7. UUID 有全局唯一统一字段属性,适合做同步ES使用。
  8. IPV4,用无符号 INT 存储
  9. IPV6,用VARBINARY存储
  10. JSON MySql 8.x 新增特性
  11. update_time 设置 on update 更新属性

3. 索引相关规范

  1. 要求有自增ID作为主键,不要使用随机性较强的 order_id 作为主键,会导致innodb内部page分裂和大量随机I/O,性能下降。

  2. 单表索引建议控制在5个以内,单索引字段数不超过5个。注意:已有idx(a, b)索引,又有idx(a)索引,可以把idx(a)删了,浪费空间,降低更新、写入性能。* 单个索引中,每个索引记录的长度不能超过64KB

  3. 利用覆盖索引来进行查询操作,避免回表。另外建组合索引的时候,区分度最高的在最左边。

  4. select(count(distinct(字段)))/count(id) = 1 的区分度,更适合建索引。在一些低区分度的字段,例如type、status上建立独立索引几乎没意义,降低更新、写入性能。

  5. 防止因字段不同造成的隐式转换,导致索引失效。

  6. 更新频繁的字段,不要建索引。

4. 使用相关规范

  1. 单表数据量不超过500万行,ibc 文件大小不超过 2G

  2. 水平分表用取模,日志、报表类,可以用日期

  3. 单实例表数目小于 500

  4. alter表之前,先判断表数据量,对于超过100W行记录的表进行alter table,必须在业务低峰期执行。因为alter table会产生表锁,期间阻塞对于该表的所有写入

  5. SELECT语句必须指定具体字段名称,禁止写成 “*”select * 会将不需要读的数据也从MySQL里读出来,造成网卡压力,数据表字段一旦更新,但model层没有来得及更新的话,系统会报错

  6. insert语句指定具体字段名称,不要写成 `insert into t1 values(…)``

  7. ``insert into…values(XX),(XX),(XX)..` 这里XX的值不要超过5000个,值过多会引起主从同步延迟变大。

  8. union allunion,不要超过5个子句,如果没有去重的需求,使用union all性能更好。

  9. in 值列表限制在500以内,例如 select… where userid in(….500个以内…),可以减少底层扫描,减轻数据库压力。

  10. 除静态表或小表(100行以内),DML语句必须有where条件,且尽量使用索引查找

  11. 生产环境禁止使用 hint,如 sql_no_cache,force index,ignore key,straight join等。 要相信MySQL优化器。hint是用来强制SQL按照某个执行计划来执行,但随着数据量变化我们无法保证自己当初的预判是正确的。

  12. where条件里,等号左右字段类型必须一致,否则会造成隐式的类型转化,可能导致无法使用索引

  13. 生产数据库中强烈不推荐在大表执行全表扫描,查询数据量不要超过表行数的25%,否则可能导致无法使用索引

  14. where子句中禁止只使用全模糊的LIKE条件进行查找,如like ‘%abc%’,必须有其他等值或范围查询条件,否则可能导致无法使用索引

  15. 索引列不要使用函数或表达式,如 where length(name)=10where user_id+2=1002,否则可能导致无法使用索引

  16. 减少使用or语句 or有可能被 mysq l优化为支持索引,但也要损耗 mysql 的 cpu 性能。可将or语句优化为union,然后在各个where条件上建立索引。如 where a=1 or b=2 优化为 where a=1… union …where b=2, key(a),key(b) 某些场景下,也可优化为 in

  17. 分页查询,当limit起点较高时,可先用过滤条件进行过滤。如 select a,b,c from t1 limit 10000,20; 优化为 select a,b,c from t1 where id>10000 limit 20;

  18. 同表的字段增删、索引增删等,合并成一条DDL语句执行,提高执行效率,减少与数据库的交互。

  19. replace intoinsert on duplicate key update 在并发环境下执行都可能产生死锁(后者在5.6版本可能不报错,但数据有可能产生问题),需要catch异常,做事务回滚,具体的锁冲突可以关注next key lockinsert intention lock

  20. TRUNCATE TABLE 比 DELETE 速度快,且使用的系统和事务日志资源少,但 TRUNCATE 无事务且不触发 trigger ,有可能造成事故,故不建议在开发代码中使用此语句。说明: TRUNCATE TABLE 在功能上与不带 WHERE 子句的 DELETE 语句相同。

安全规范

接口安全

  1. 【强制】涉及大批量敏感数据的接口增加限流和内网访问保护
  2. 【强制】涉及小批量敏感数据的接口增加内网访问保护
  3. 【强制】所有不宜公开数据都必须添加接口权限

代码安全

  1. 【强制】新加的jar要经过技术中台组安全检测评估才能使用
  2. 【强制】敏感字段禁止明文存储,需要使用统一工具类进行加密处理
  3. 【强制】mybatis中能用#{}时不要用${},#{}能防止SQL注入。禁止SQL拼接不安全参数。

密码安全

  1. 【强制】所有使用中的业务系统(包括测试环境)密码必须由大写字母+小写字母+数字+特殊字符四种组合,字符数不少于8位。

技术文档的写作规范

  1. 设计文档需要的核心要素有业务架构 ,应用架构,领域模型(数据架构),技术架构,。
  2. 业务架构描绘系统的业务流程和功能
  3. 应用架构描绘系统之间的关系
  4. 数据架构是指领域模型的关系
  5. 技术架构描绘系统实现,我们都是比较统一的技术架构,可以省略

Java开发编程军规

一、禁止循环中查询数据库,尽量在循环外一次查询

  • 说明

系统性能瓶颈很大一部分都是指向了数据库,而循环中查询数据库非常耗资源。

  • 案例

展示少量树结构数据时,循环内查询数据后进行数据组装。导致服务器在测试环境就频繁宕机。

使用java.util.Comparator#compare方法调用数据库查询接口,导致线上性能极低。

二、禁止把redis这种缓存当数据库用

  • 说明

缓存无法完全符合事务特性ACID原则,数据存在不可使用的风险比较大。

  • 案例

会员数据直接存储到redis缓存中,数据量也比较大,经常会丢数据。

三、禁止循环中创建新线程,尽量使用线程池

四、死循环必须有退出机制

  • 说明

死循环中最好有休眠语句存在,另外还要退出机制。

  • 案例

订单同步应用请求第三方平台数据时,平台方没有翻页的结束标志,同时代码中没有退出机制直接导致该平台订单同步异常。

五、共享变量必须考虑线程安全

  • 说明

尽量避免使用共享变量,无法避免时必须考虑线程安全。

  • 案例

微信抽奖功能中,每次中奖都是同一个,原因是对共享变量进行了修改操作,后面的逻辑获取的是脏数据。

六、浮点计算必须使用BigDecimal

  • 说明

如果需要精确计算,非要用String来够造BigDecimal不可。

七、批量操作必须考虑合理分组

  • 说明

数据量大时须批量操作,而批量操作必须分组,避免一次操作耗时过久导致连锁反应。

  • 案例

订单历史迁移数据时,分组为5000,导致数据库删除操作没有走索引。建议分组数量在100~500之间。

八、禁止单点部署

  • 说明

增加一台服务器部署可以降低50%的服务不可用风险。

九、禁止大表的全表扫描不加限流

  • 说明

全表扫描已经很耗数据库资源了,频繁处理请求不加限流就更雪上加霜。

  • 案例

售后问题跟踪单的导出,时间索引没有控制范围,导致全表扫描。导出数据接口没有加限流加剧服务资源消耗。

十、读写分离架构,必须考虑读到过期数据

  • 说明

读写分离在业务数据更新写入后再重现读取时会存在延迟问题,导致读到脏数据。

  • 案例

A. 双十一开启读写分离,主从同步有延迟,导致业务事件重复发送,原因是读取到历史 脏数据。

B. 会员积分服务创建数据后其他服务应用马上查询,结果是查询到空数据。

十一、事务内有外部调用,必须考虑外部不稳定和性能问题

  • 说明

事务本身是很耗资源,极易产生超时的问题,要避免再引入外部不稳定因素。

  • 案例

个人中心服务,事务内远程查询美丽分享官的积分,导致性能极低。外部接口调用需要设置超时和最长等待时间。

十二、接口提供方和Xxljob定时任务必须考虑幂等性,防止重复调用导致严重的业务灾难

  • 说明

根据墨菲定律,接口重复调用是会必现的线上问题。

  • 案例

订单付款接口,幂等逻辑不严谨导致重复付款问题。apollo报表中心由于xxljob一秒内重复调度任务,导致统计数据重复,严重影响管理层的决策判断。

十三、禁止资源操作(IO等)后未释放

十四、嵌套事务的默认传播属性是Propagation.REQUIRED,如果需要开启新事务,必须手动设置事务传播属性为Propagation.REQUIRES_NEW。尽量不要使用嵌套事务。

  • 说明

使用事务注解或者编程式事务时,需要考虑默认的事务传播属性,根据需要决定是并入同一个事务还是开启新事务。

  • 案例

OMS异步操作任务,调用第三方接口时,修改状态为确认中状态,需要先提交事务更新,后面的逻辑操作成功则需要修改为已确认,失败则修改为待审核。当第三方接口没有返回明确的成功或失败时,状态应该保持确认中不变。如果调用接口前不开启新事务,会导致后面回滚的数据有误。

十五、覆写对象的equals()方法时必须同时覆写hashCode()方法

  • 说明

equals和hashCode方法是对象在hash容器内高效工作的基础,正确的覆写这两个方法才能保证在hash容器内查找对象的正确性,同时一个好的hashCode方法能大幅提升hash容器效率。

十六、禁止含事务的循环内加线程同步锁

  • 说明

循环上层包含事务,使用synchronized锁,会导致MySQL事务锁和JVM同步锁互相等待死锁问题。

  • 案例
@Transactional(rollbackFor = Exception.class) 
public void storeData(List<Order> orderList) { 
    /**
    order_id = {1,2,3}
    线程A更新order_id为1后进入下一轮循环,事务锁还未释放,同步锁需要重新获取。
    同时线程B已获取同步锁,需要更新order_id=1的事务操作。结果就是线程A等待线程B持有的
    JVM同步锁,线程B等待线程A持有的事务锁。
    */
    for(Order order : orderList) {
        synchronized (LOCK) {
            updateOrderId(order);
        }
    }
}

十七、使用线程池时,必须设置合理的大小,禁止不加限制动态批量创建线程

  • 说明

不加限制的批量创建线程会抢占大量系统的资源,引发OOM等连锁异常,最终导致宕机

  • 案例

将new MapReduce<>(xxx)创建线程池的代码放在API接口实现方法中没有加其他限制,导致引发OOM宕机,中间触发了Redis连接超时、Kafka重复消费等异常

十八、所有公共代码(比如api包、通用工具类)或公共服务(中台服务、业务应用自身服务)的改动,必须考虑向下兼容

  • 说明·

对多个系统项目都有依赖的公共代码进行修改时,需要考虑兼容历史逻辑,除非确认所有使用方都能够接受功能修改产生的影响

  • 案例

项目A的开发人员将公共jar包中逻辑进行了修改(该修改需要开启一个新配置才和原逻辑一致),同时deploy jar包进行测试,新配置只在测试环境进行了操作。依赖了公共jar包的项目B这时进行线上发版,但是没有进行配置(而且也不知道有这个配置),导致线上事故。

十九、禁止在新建表或者增加修改表字段时设置字符集和排序规则

  • 说明·

字符集和排序规则后期修改需要耗费巨大的资源,影响业务稳定性,为了保持schema-表-表字段三者的字符集和排序规则一致,禁止在新建表或者增加修改表字段时设置字符集和排序规则。

  • 案例

A项目的某一个表的字段设置了字符集和排序规则,导致与表-schema的排序规则不一致,在联合查询时这个表作为关联字段,报关联字段排序规则不一致的错误,无法进行关联查询,只能修改,如果是一张大表修改会非常耗时,占用大量的io会影响业务。

二十、新建表或者增加修改表字段时使用TEXT/BLOD字段需要评估必要性

  • 说明·

TEXT/BLOD的大字段会产生磁盘临时表,而且不能使用全文索引,各种操作的代价都非常高昂,在业务中最好不要使用,如果实在是要使用,也要独立出一张表专门用于存储,不得跟业务表中使用。

  • 案例

A项目前期的一张表中使用了一个TEXT存储json大字段,导致这张表占用了600多G的空间,其中那一个大字段就占用90%的表空间,后续的查询,迁移,碎片整理都非常的耗资源。

posted @ 2024-11-14 16:12  DOONDO  阅读(228)  评论(0编辑  收藏  举报