1. Git 使用避坑指南
1)切分支出错
master 主分支,即生产版本,xx_test 分支对应测试环境分支,请基于 xx_test 分支拉功能分支开发。比如两个新需求同时开发,项目管理人员此时需基于 xx_test 拉出两个功能分支,分别是 feature-a 分支和 feature-b 分支。开发人员检出对应的功能分支,并在其上开发。
粗心的开发人员忘了切换分支,直接在检出的 master (xx_test 分支)开发、合并或提交。容易参数代码混乱。
2)测试完过早合并至 master 主分支
如 feature-a 分支 和 feature-b 分支对应两个功能需求,需求 feature-b 功能先开发测试完,然后合并至 master 主分支,这时产品和项目经理确定发版内容为 feature-a 分支功能,由于开发人员疏忽,导致 feature-b 也上线了,容易出现生产事故。
开发完合并至 xx_test 分支,测试环境测试完成,待发版时,再合并到 master 主分支。
3)merge 代码冲突解决不当
feature-a 分支 或 feature-b 分支开发完成,git merge 到 xx_test 分支时,如果涉及其他开发人员提交的内容,且不确定,错误删除了别人的代码。
请与相关需求对应的开发人员一同解决代码冲突问题。
4)遵循一个分支只做一件事
feature-a 分支只开发新的需求 a,此时开发人员看到某个方法觉得编码不太规范,顺手优化一下,结果优化完,没有和测试人员或项目经理说,等上线完,线上出现问题,为时已晚。如果有修改非新需求的代码,请告知测试或产品进行回归测试相关系统的一切功能。
建议优化代码时,另拉出一个 optimize-a 分支进行优化或重构。
5)同一个功能开发人员 commit 多次,不利于阅读,合并提交记录
为了使分支提交记录更清晰,需要合并多次提交为一次提交。
①第一种方式交互式,主要涉及的 git 命令如下:
# 得到需要合并提交记录的前一个提交记录的 commitId
git log
# 进入交互式修改,以其中一个 pick 为基准,其他需要合并的 commitId 前的pick 修改为 squash(简写 s),保存修改并退出即可
git rebase -i [commitId]
# 推送至远程仓库
git push
# 或强制推送至远程仓库(谨慎使用)
git push -f
②第二种方式回滚,主要涉及的 git 命令如下:
git log
# 比如合并前三个,commitId 是前第四个的提交记录
git reset [commitId]
# 添加至暂存区
git add .
# 提交至本地仓库
git commit -m “commit msg”
git push 或 git push -f
功能分支的代码合并至 master 分支后,提交记录更合理清晰,方便其他开发人员了解或是 code review。
2. 数据库避坑指南
1)业务上唯一特性的字段(或组合字段)请建立唯一键约束
避免出现诡异现象或是导致业务上出现错误,增加排查的难道或是编码复杂。
很多人认为,保证唯一性,“先查后插”。其实高并发场景下,如果没有进行同步操作,两个事务同时开启,查数据库没有,然后导致数据库插入了两条重复的数据(即产生垃圾数据)。
可能的影响有:Dao 层出现 sql 执行异常,业务逻辑层处理与实际不符等等。
2)delete 操作时请注意带上 where 条件
开发人员,在写 delete 语句时,请先带上 where 条件查询数据库,看数据是否符合删除的逻辑,然后再写 delete 语句删除相应条件下的垃圾或是废弃数据。
delete ... where条件很重要
3)由于业务需要某旧表新增字段,设置 NOT NULL 请谨慎!
可能影响其他接口业务逻辑插入该表,没有插入非空字段,导致线上系统接口异常。
如果新增字段为空,请检查相关接口,或是设置默认值。
4)新增字段考虑是否创建索引
大多数人在建新表时,有意识的新增索引,但是在旧表新增字段时,却忘记创建索引。后期因为数据量大或是并发高,导致数据库性能下降。
如果新增字段是 where 查询条件,请考虑创建索引或是组合索引,避免出现数据库查询性能问题
5)使用 insert into 语句注意字段对应关系
强制使用 insert into table_name (field1, field2, ...) values (value1, value2, ...);
禁止使用 insert into table_name values (value1, value2, ...);
如何新增字段,可能导致其他接口服务报错(sql 语法错误)
6)分页查询条数限制
在数据库分页查询时,mysql 中 select * from table_name limit m,n; 注意对 n 参数校验,防止每页查询的数据量过大,导致内存溢出;oracle 中 select * from (select * from (select rownum rn, t1.* from (select * from page_table_name) t1) where rownum <= currentPage * pageSize) where rn > (currentPage - 1) * pageSize; 注意对 pageSize 参数校验,防止每页查询的数据量过大,导致内存溢出。
分页查询需对每页条数参数校验,防止发生线上系统出现OOM
7)避免数据库长事务发生
批量入库操作时,循环结束后再提交可能引起长事务发生,注意每多少条 sql 执行一次提交;多个 sql 执行顺序、执行时机按业务逻辑和性能调到最优。
长事务易触发数据库机制导致kill进程或是数据库阻塞等,合理事务范围及事务耗时。
8)大表创建索引或 DDL 避免高峰期执行,或是升级停库时执行
大表创建索引或是执行 DDL 时,引起数据库表锁表,对高峰期业务接口响应影响较大。
创建索引或执行 DDL 时停机执行。
9)where 条件少写 (field != 'X' 或者 field <> 'X')
可能会出现结果集不符合预期。比如数据库字段 field 不是 NOT NULL,where 条件如果是 field != 'X' 此时查询的结果集不全(不包含 is null),所以 where 条件应该是 field is null or field <> 'X'。
一般建议创建新表定义字段时,添加 not null 约束。另外查询条件不建议使用 != 或 <>,这样索引可能失效,尽量使用等值或范围查询。
10)单表或多表关联分页
如果执行计划出现 SORT ORDER BY,一般这种分页查询的 sql 是有问题的。
利用索引的有效性,等值查询,创建组合索引(等值过滤条件与排序字段优先组合、非等值过滤条件放在后面,其中等值过滤条件能过滤掉大量数据的放在最前面)等;
多表关联分页,走嵌套循环,如果驱动表返回的数据是有序的,那么关联之后的数据集也具备有序性。
让参与排序的表作为嵌套循环的驱动表,其他关联表对应的连接列创建索引。如果存在外连接,选择主表列作为排序列。语句中不能存在 distinct、group by、max、min、avg、union、union all。
11)oracle 分页 sql 写法误区
select * from (select t.*, rownum rn from(select * from tmp) t) where rn >= 1 and rn <= 10;
上面的分页查询 sql 是错误的写法。
这种写法没有使用到 oracle 的 COUNT STOPKEY 特性,即获取到分页的结果集后 sql 立即停止运行。
正确的写法应该如下:
select * from (select t.*, rownum rn from (select * from tmp) t where rownum <= 10) where rn >= 1;
走索引排序。使用 COUNT STOPKEY 特性。如果有过滤字段,可以考虑组合索引,如果过滤条件能够过滤大部分数据,排序列可以不包含在索引中。
3. Java 避坑指南
技术原理理解不到位带来的性能问题或坑。可参考《阿里巴巴Java开发手册》
举例说明:
1)创建 ArrayList 或 HashMap 时,合理设置集合初始化容量
避免集合扩容带来的性能消耗。
集合都有默认初始化容量和扩容机制,多次扩容会引起性能问题或接口响应变慢等。
2)谨慎使用 ArrayList 集合的 subList 方法
subList 方法返回的子类是 ArrayList 内部类 SubList,是原 ArrayList 的地址引用(和数据库视图有点类似)。
对subList返回的结果操作会反映在原ArrayList集合上,而对原集合进行结构变化,会触发并发修改异常
3)合理使用 Executors 构造线程池,最好使用 ThreadPoolExecutor(7参)构造
避免 Executors 构造的无限线程或是无界队列造成 OOM 异常。
根据任务是 I/O 密集性还是 CPU 密集性,合理设置线程池参数(核心线程数、最大线程数、任务队列、拒绝策略等),使线程池发挥最大功效。
4)开启事务时,注意事务隔离级别、回滚条件、传播策略、事务超时设置
MySQL 数据库默认事务隔离级别是RR(可重复读);Orcale 数据库默认事务隔离级别是RC(读已提交)。针对RC,会出现幻读,不可重复读。事务回滚条件设置(捕获程序异常时注意)。传播策略默认是当前有事务直接加入该事务,没有事务新建事务。设置合理的事务超时时间(数据库管理系统内置有相关的参数设置)。
养成好的习惯很重要!
举例说明:
1)开发前没有仔细梳理需求、绘制 UML、时序图、活动图的习惯(针对开发人员、技术经理);
2)开发时没有自我 code review 的习惯(针对开发人员、技术经理);
3)测试时没有看 error log 的习惯(针对开发人员、测试人员);
4)上线前没有检查各种 properties file 的习惯(针对开发人员、技术经理);
5)上线过程中和上线后没有关注线上系统日志和系统监控的习惯(针对开发人员、项目经理)。
编码规范很重要!可参考《阿里巴巴Java开发手册》和《Google 开发编码规范》
举例说明:
1)int a,b,c = 0; 命名不规范,debug 不方便(建议定义变量时一行一个);
2)定义变量,作用域最小化,遵循就近原则,对象引用范围最小化(比如:静态成员变量中引用了其他类对象);
3)合理使用强引用、弱引用、软引用、虚引用。
4)规范日志记录,使用占位符(减少拼接字符串的性能消耗);
不规范:log.info(“programme_id:” + programmeId + “, appl_no:” + applNo);
规范:log.info(“programme_id:{}, appl_no:{}”, programmeId, applNo);
5)Controller 控制层严格接口参数校验,Service 业务逻辑层处理业务操作并减少与数据库交互次数(合理使用事务),Dao 层操作数据库;
6)sql 编写规范,where 条件要求走索引(索引优化、组合索引)等。
多交流、多讨论、多思考、多学习、多积累、多实践
1)持续学习、积累项目经验;
2)业余时间多研究框架源码;
3)积极参与需求讨论,合理设计解决方案。
4)积极思考业务场景,简化优化流程,提高用户体验;
5)多看别人的优秀代码并讨论,减少不必要的开发和踩先人以前的坑;
6)养成持续优化持续重构的意识。
加油
# 精彩推荐 #
分布式系统「全链路日志追踪」实战之 RestTemplate & Feign
小白都能看得懂的服务调用链路追踪设计与实现
[三步法] 可视化分析定位线上 JVM 问题
从 Java 代码如何运行聊到 JVM 和对象的创建-分配-定位-布局-垃圾回收
深入了解 Spring 中的事务(从核心注解和类入手)
"在看"吗,赶快分享和收藏吧