千家信息网

怎么解决java并发请求下数据插入重复问题

发表于:2025-11-07 作者:千家信息网编辑
千家信息网最后更新 2025年11月07日,本篇内容介绍了"怎么解决java并发请求下数据插入重复问题"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所
千家信息网最后更新 2025年11月07日怎么解决java并发请求下数据插入重复问题

本篇内容介绍了"怎么解决java并发请求下数据插入重复问题"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

前言

前段时间发现数据库里经常会存在两条相同的用户数据,导致数据查询异常。查了原因,发现前端微信小程序在授权登录时,有时会出现同时发送了两条一模一样的请求(也就是常说的并发)。虽然后端代码有做防重复的判断,但是避免不了并发时候的重复性操作。于是就开始考虑并发的解决方案,解决方案有很多,从拦截请求到数据库层面都可以入手。

我们采用了对请求报文生成摘要信息+Redis分布式锁的方案。运行了一段时间,功能很可靠,代码也很简洁。于是上来做下记录以便后续参考。

解决方案说明:

系统架构用的Spring boot,定义一个Filter过滤器对请求进行过滤,然后对请求报文生成摘要信息并设置Redis分布式锁。通过摘要和锁判断是否为同一请求。

分布式锁工具类

public class ContextLJ {                private static final Integer JD = 0;                  /**           * 上锁 使用redis 为分布式项目 加锁           * @param sign           * @param tiD           * @return           * @throws Exception           */          public static boolean lock(String sign, String tiD) {            synchronized (JD) { // 加锁                    Cache cache = CacheManager.getCommonCache(sign);                    if(cache == null || StringUtils.isBlank(cache.getValue())) {                            CacheManager.putCommonCacheInfo(sign, tiD, 10000);                            return true;                        }                    return false;            }         }                   /**           * 锁验证           * @param sign           * @param tiD           * @return           */          public static boolean checklock(String sign, String tiD){                  Cache cache = CacheManager.getCommonCache(sign);                  String uTid = StringUtils.replace(cache.getValue(), "\"", "");                  return tiD.equals(uTid);          }                   /**           * 去掉锁           * @param sign           * @param tiD           */          public static void clent (String sign, String tiD){                    if (checklock(sign, tiD)) {                            CacheManager.clearOnly(sign);                    }          }                   /**           * 获取摘要           * @param request           */          public static String getSign(ServletRequest request){            // 此工具是将 request中的请求内容 拼装成 key=value&key=value2 的形式 源码在线面            String sign = null;            try {                    Map map =  getRequstMap((HttpServletRequest) request);                    // 生成摘要                    sign = buildRequest(map);            } catch (Exception e) {                    e.printStackTrace();            }            return sign;          }                    public static Map getRequstMap(HttpServletRequest req) throws Exception{               Map params = new HashMap();               params.put("uri", req.getRequestURI());                    Map requestParams = req.getParameterMap();                    for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext();) {                      String name = (String) iter.next();                      String[] values = (String[]) requestParams.get(name);                      String valueStr = "";                      for (int i = 0; i < values.length; i++) {                        valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";                      }                      params.put(name, valueStr);                    }                    return params;        }                   private static String buildRequest(Map map) {                 List signList = new ArrayList<>();                 for(Entry entry : map.entrySet()) {                         signList.add(entry.getKey() + "=" + entry.getValue());                 }                 String sign = StringUtils.join(signList, "&");                 return DigestUtils.md5Hex(sign);        }        }

在过滤器实现请求拦截

/** * 过滤频繁请求 */@Slf4j@Componentpublic class MyFilter implements Filter{                @Override        public void init(FilterConfig filterConfig) throws ServletException {                        }        @Override        public void doFilter(ServletRequest request, ServletResponse myResp, FilterChain chain) throws IOException, ServletException {                HttpServletRequest req = (HttpServletRequest) request;                Boolean isDict = StringUtils.contains(req.getRequestURI(), "/dict/getDatas");                Boolean isFile = StringUtils.contains(req.getRequestURI(), "/files/file");                if(isDict || isFile) {                        chain.doFilter(request, myResp); // 查询数据字典或者文件,直接放行                        return;                }                String sign = "sign_" + ContextLJ.getSign(request); // 生成摘要            String tiD = RandomUtils.randomCode(3) + "_" + Thread.currentThread().getId(); // 当前线程的身份            try {                     if (!ContextLJ.lock(sign, tiD)) {                            Map map = ContextLJ.getRequstMap((HttpServletRequest)request);                            log.warn("放弃相同并发请求【" + sign+ "】【" + tiD+"】"+JSON.toJSONString(map));                            frequentlyError(myResp);                            return;                    }                    if (!ContextLJ.checklock(sign, tiD)) {                            Map map = ContextLJ.getRequstMap((HttpServletRequest)request);                              log.warn("加锁验证失败 【" + sign+ "】【" + tiD+"】"+JSON.toJSONString(map));                              frequentlyError(myResp);                              return;                    }                    chain.doFilter(request, myResp); // 放行            } catch (Exception e) { // 捕获到异常 进行异常过滤                      log.error("", e);                      myResp.getWriter().write(JSON.toJSONString(ApiRs.asError("服务器繁忙,请重试")));            } finally {                    ContextLJ.clent(sign, tiD);            }        }        @Override        public void destroy() {                        }                /**         * 频繁请求         */        private void frequentlyError(ServletResponse myResp) throws IOException {          ((HttpServletResponse) myResp).setHeader("Content-type", "text/html;charset=UTF-8");          myResp.getWriter().write(JSON.toJSONString(ApiRs.asError("稍安勿躁,不要频繁请求")));        }}

"怎么解决java并发请求下数据插入重复问题"的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注网站,小编将为大家输出更多高质量的实用文章!

0