多线程update批量更新造成的死锁,问题分析和解决办法

首先我们设想一个情况,然后来阐述今天的问题:现在有若干台服务器,用相同的接口去批量修改一批数据,但是数据中彼此有重复的数据。基于这个问题,出现下面这种情况的死锁-->

 

多线程update批量更新造成的死锁,问题分析和解决办法_第1张图片

问题分析:因为1服务器修改的批次包括abcdef 这个时候刚好修改了abcd所以abcd的索引被锁住了,2服务器修改了efgh,这个时候efgh的索引被锁住了

因为批量修改是一个默认的事务,所以如果没有全部修改完,索引是不会被放开的,所以1服务器的e等待2服务器放开,2服务器的c等待1服务器放开,造成死锁。。。。。

解决办法1:将批量修改通过for循环改成单条修改,但是这个方法对服务器的压力增大

解决办法2:我们在获取数据的时候进行一次筛选,将重复的数据剔除出去,我们用到了redis

再用redis分布式锁,进行批量更新。这样的好处就是解决问题的同时减少对服务器的压力

具体操作看代码


public int batch(SmsReport[] rpts) {
   if (ArrayUtils.isEmpty(rpts)) {
       return 0;
   }
    List list = new ArrayList<>(Arrays.asList(rpts));
    Iterator iterator = list.iterator();
    while(iterator.hasNext()){
       SmsReport rpt = iterator.next();//
            redisTemplate.delete(CACHE_KEY_REPORT_MEG_ID_PREFIX+rpt);
            if(redisTemplate.opsForValue().setIfAbsent(CACHE_KEY_REPORT_MEG_ID_PREFIX,"" )){
redisTemplate.expire(CACHE_KEY_REPORT_MEG_ID_PREFIX, 1 * 60, TimeUnit.SECONDS);
            } else{
                iterator.remove();
            }
        }
        if(list.isEmpty()){
            return 0;
        }
        return sendDataMapper.batch(list.toArray(new SmsReport[list.size()]));

    }

我们将redis的有效时间设为若干分钟,通过key唯一的性质进行数据排重,这样即使是多线程多服务器进行批量修改,也可以整合成一次数据无重复的批量修改,从而解决重复数据的死锁问题。。。。。

 

问题扩展:上面是批量修改造成的死锁,还有就是多线程单条数据造成的死锁,就是两个线程同时修改一条数据,彼此拿到了各自的非主键索引,导致非主键索引不全造成的死锁,这个问题关注我在下一篇文章分享。

你可能感兴趣的:(java框架那点事)