背景:
近期做了一个话务管理的功能,就是调用大唐电信的接口完成与客户的电话沟通,并将通话信息记录在数据表中。该功能已经上线了两个多月,一直运行正常,但是前天我自己在查询生产数据时发现了一个问题:我们的通话记录中很多字段的值都为null。而这些字段应该是在挂断电话时大唐回调我们的接口更新到我们的数据表中的,所以一开始我怀疑大唐在某些情况下挂断电话是不走回调的,就与大唐的同事沟通,他们的反馈是任何情况下的电话挂断都会回调我们的接口。按说此时我本应该意识到是我们的接口在更新数据时出现了问题,但我竟然没有第一时间去测试我们的接口,而是继续忙其他的事情,直到一天后其他部门的同事反应电话打通之后没有查询到通话记录,此时我才开始测试自己的接口,发现是大唐在回调我们的接口时某个字段的值变长了,我们的数据表的长度不足以存储变长后的数据导致在UPDATE时失败了。关于此,我应该做自我检讨:
1、遇到生产问题时的敏感度不够强,既然是生产问题,就应该第一时间解决,不应该在发现了问题还置之不理,因为生产问题是最重要的问题,很可能造成严重的事故;
2、在遇到问题时应该首先考虑是不是自己的问题并进行测试,在测试完之后再决定是应该修正自己还是去询问别人,而不应该首先想到或许是别人的问题,就像这一次我应该首先测试一下自己的接口,这样也可以避免一些无效的沟通,当然这次大唐修改接口的返回值没有知会我方也有点不地道;
3、自己编码的习惯问题:不要盲目的相信自己,任何人的代码都有可能在不同的时间点出现问题,所以要记得把代码中可能出现异常的地方给出前台提示或打印后台日志,方便进行跟踪,尤其是在SQL的INSERT和UPDATE操作的时候,还有就是在try...catch...的时候要注意将捕获的异常信息进行打印,或者给前台提示,或者再抛出,而不要捕获到异常之后什么都不做;
目的:
将丢失的大唐通话记录数据恢复至我们系统的数据表中;
方案:
想办法获取到这两天的大唐电信通话记录数据,根据坐席、时间进行排序,将大唐数据更新至我方数据表中;
过程:
首先,联系大唐的同事将5号和6号的全部通话记录数据拿到(只有这两天的数据有问题),发现他们的数据比我们多出了20多条,而且数据的时间有偏差。
我开始觉得该数据是无法恢复的,因为在插入通话记录时(我们的通话记录是在振铃时插入表中的,在电话挂断时大唐回调我们的接口将通话的其他具体信息更新至我们的表中)没有将任何与大唐直接关联的字段插入表中,这就导致了我们无法根据任何一个字段与大唐的数据进行关联,只能根据数据产生的时间,但我们记录的时间和大唐记录的时间又存在偏差,数据量也不一致。这该如何是好?但我忽略了一点(小组长的提醒,方案也是他想出来的):在插入数据时是有这条数据的所有者(我们的系统用户ID)的,而我们的坐席(大唐提供)配置是与此ID关联的,通过用户ID就可以关联到坐席,这样终于找到了我们系统和大唐之间的关联关系,剩下的任务就是怎么匹配数据的问题了。
思路:建立临时表,将大唐的数据全部导入临时表中(注意:若是呼入的情况,需将被叫和主叫的位置进行调换,因为我方在初始化坐席时均是按照主叫初始化的,也只能如此,在最后数据恢复时根据呼叫类型判断一下再进行恢复);对双方的表数据分别先根据坐席排序,再按照时间正序进行排序(由于双方记录的时间不一致,因此给双方的两个表都增加一个字段groupNum,用来记录每个坐席打出的第n个电话——每个坐席在同一个时间点只能有一次通话);比对双方每个坐席的第n个电话的时间,将数据多的那一方的时间偏差较大的数据剔除,然后再次根据坐席进行组内排序和比对数据,如此反复,直到双方的数据条数相等和时间一致为止;
操作:
1、据我方的表复制出一个空的临时表,并将大唐的数据全部导入;
2、在我方的数据表和刚创建的临时表中都新加一个字段group_num,用来记录每个坐席打出的第n个电话,同时两个表中都新加一个字段flag,并将值初始化为1,用来标记该条记录是否有效;
3、将大唐的通话记录数据进行组内排序:
UPDATE recores_temp t1
LEFT JOIN (
SELECT id,caller,start_time,
@row_num := CASE WHEN @caller = caller THEN @row_num + 1 ELSE 1 END AS group_num,
@caller := caller AS caller_temp -- 注意给@caller变量赋值的时机不能在生成组号之前
FROM recores_temp WHERE flag = 1
ORDER BY caller,start_time ASC
) t2
ON t1.id = t2.id
SET t1.group_num = t2.group_num ;
注意变量的赋值时机:@caller的赋值不能在@row_num的赋值之前,原因很简单,在给@row_num赋值时用到了一个条件@caller = caller,若在@row_num之前就给@row_num赋值则@caller=caller的条件永远成立,得到的序号就不再是组内的排序序号了,而是全部的排序序号
4、将我们的通话记录数据进行组内排序:
UPDATE sys_telephone_recoreds t1
LEFT JOIN (
SELECT id,call_main_seat,create_time,
@row_num := CASE WHEN @call_main_seat = call_main_seat THEN @row_num + 1 ELSE 1 END AS group_num,
@call_main_seat := call_main_seat AS call_main_seat_temp
FROM sys_telephone_recoreds WHERE flag = 1
ORDER BY call_main_seat,create_time ASC
)t2
ON t1.id = t2.id
SET t1.group_num = t2.group_num;
5、筛选出通话记录条数不一致的坐席,便于后面剔除数据:
SELECT t1.seatNo,t1.count1,t2.count2 FROM (
SELECT call_main_seat seatNo,COUNT(1) count1
FROM sys_telephone_recoreds
WHERE flag = '1'
GROUP BY call_main_seat
) t1
LEFT JOIN (
SELECT caller seatNo,COUNT(1) count2
FROM recores_temp
WHERE flag = '1'
GROUP BY caller
) t2
ON t1.seatNo = t2.seatNo
WHERE t1.count1 != t2.count2;
6、比对通话记录条数不一致的坐席的通话记录的通话时间,在比对时可采用二分法:
SELECT t1.id,t2.id tempId,t1.create_time,t2.start_time,t1.call_main_seat seatNo,t2.caller
FROM sys_telephone_recoreds t1
LEFT JOIN recores_temp t2 ON t1.call_main_seat = t2.caller
AND t1.group_num = t2.group_num
WHERE t1.flag = 1 AND t2.flag = 1
AND t1.call_main_seat IN ('7034')
ORDER BY t1.create_time;
查询结果如下图:可以看出删掉圈出的记录后时间就可以对上了,因此需要删除tempId为49的记录
7、剔除数据:更改偏差较大的数据的flag和group_num值,使其不再参与排序和比对
UPDATE recores_temp SET flag = 9,group_num = -1 WHERE id IN('49');
每更改一次状态都需要对双方的数据进行重新排序(仅将更改数据的那一方的数据进行重排序即可),即接着执行3、4、5、6、7步骤,如此往复直至在第5步时查不出数据为止
8、最后一步,数据迁移:
UPDATE sys_telephone_recoreds str
LEFT JOIN recores_temp rt ON str.call_main_seat = rt.caller AND str.group_num = rt.group_num
SET str.call_sec = rt.callee,...
WHERE str.flag = 1 AND rt.flag = 1 AND rt.session_type = 1;
对于呼入的情况,另行处理,迁移完成之后再将新加的flag、group_num字段删除即可
心得:
1、端正自己的工作态度,要对遇到的问题敏感;
2、解决问题时,不要首先觉得不可能,这会打压自己解决问题的积极性,没有什么是不可能的;首先应该认真寻找解决问题的突破口,不放过任何一个细节,将解决问题的资源关联起来,就算是不可能也应该在努力尝试之后;