首页 > 其他 > 详细

记一次遇到由于重复提交导致的问题

时间:2018-03-24 19:13:56      阅读:152      评论:0      收藏:0      [点我收藏+]

这是新手期间第一个上线功能搞出的bug,同时也明白了一个道理:1. 线上环境总是复杂的,不可预知的,一定要做好各种准备; 2. 重要的功能要做放重复提交;3. 基础要打打牢。

需求

需求其实很简单,就是一个修改密码的入口,用户输入提交表单后,如果之前没有设置过密码,则设置,否则修改。然后验证密码的逻辑就不用说了,查出表里的密码,然后对比。
项目是ibatis+oralce,之前DAO的逻辑如下

...
if (isNotExists()){
    insertPassword;
} else {
    updatePassword;
}

isNotExists()方法就是select看是否存在记录。

public Map queryPassword(Map param) throws DAOException {
    return sqlMapClientTemplate.queryForObject(param);
}

问题

某一天中午突然来了告警日志,发现在queryPassword这个方法这里抛出了too many results异常,很显然,本应该每个用户只存在一条记录的,查出了多条。让运维同事查出数据,果然有一个用户,存在3条记录。查看原代码逻辑,那很显然是在判断是否存在记录这里同时来了三个请求,然后三个线程都发现该用户对应的记录是空的,于是同时插入了三条记录。(后来通过电话回访用户,说当时手机页面卡了,于是瞎点一通,可能页面卡了,等反应后一下子发出去多条请求。)

解决和复盘

  • 首先线上问题紧急的解决方案就是删掉记录就OK了。
  • 然后这种if ... then...的用法是非常危险的,一来一回,在并发情况下就出现问题了。当时想着改个密码对同一个用户哪儿来的多并发,先就这样干吧,事实证明线上环境的复杂是永远超出自己预知的。对于这种场景,最好使用oralce提供的merge方法,把事务处理交给oracle,出错的概率更小一些。

    merge into pwd_table t
    using dual
    on (t.id = #id#)
    when matched then
        update pwd_table t set t.pwd = #pwd#
        where t.id = #id#
    when not matched then
        insert into pwd_table t values(#id#, #pwd#)
  • 做防重复提交限制,考虑到用户的群体,最简单的是在用户提交请求得到响应之前,按钮置为diabled状态
  • 基础一定要打好,当时就是对merge了解不深没有使用,要多学习PL/SQL的东西

记一次遇到由于重复提交导致的问题

原文:https://www.cnblogs.com/dirac/p/8640406.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!