大背景介绍:项目A之前从产品-开发-测试全是外包进行开发的,后来由于部门的一些人事变动,外包全被裁了。很不幸,需要负责该项目
用户反馈问题:审批流程节点,审批人点击查看详情时,报服务器内部错误。
处理步骤:
1.查询后端日志:grep 'exception' xxx.log
nested exception is org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 2
Caused by: org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 2
grep -C 20 'exception' xxx.log 查看上下文
出错代码行
2.查看对应SQL,此处不得不吐槽代码风格,mybatis的查询里面封装了一个大的查询逻辑,所有的query 语句都用一个查询逻辑,通过 if 来判断对应过滤是否生效。
通过和前端同学确认,获取前端该接口传参,针对SQL进行二次过滤处理,发现只是根据orderNo来查询的,然后居然能查询出两条语句!!!
3.本来应该查询线上数据库确认,但是部门内部因为项目数据的敏感性,查询线上数据需要发起审批流程,而且需要三个审批节点,遂打消该想法。
4.orderNo,一般是唯一的,都会创建唯一索引,通过查看test数据库的索引,发现,之前被创建成普通索引,唉
5.此处讲一下orderNo的生成逻辑,因为线上服务是双实例的,所以我们用redis来递增生成这个唯一编码,所以当我发现这个问题,突然想起来之前由于IT机房断电,DBA给了一台新的redis给我们上线。
由于我们的orderNo是带日期的,通过报错编号发现日期确实是切换redis的那天。
6.也怪我,当时因为项目是内部项目,觉得晚上21:30之后没人会用了,就上线了,结果晚上23:00有人提单了。
问题复盘:
1.数据库该用唯一索引的地方,一定要用唯一索引
2.这种利用redis做递增id(类似)时,如果切换redis:a. 让DBA把旧redis的数据同步到新redis 或 b.等到半夜12点上线。
后续处理:看看流程那边是否可以撤回审批,让用户重新发起申请。
----------------------------
后续处理:还是申请了线上读库的权限,定位了数据的id,再阅读前人代码里“作废”的逻辑,按照“作废”的逻辑更新了对应申请的状态;同时通知流程的同学帮忙终止了对应的流程。