首先我們?cè)O(shè)想一個(gè)情況,然后來(lái)闡述今天的問(wèn)題:現(xiàn)在有若干臺(tái)服務(wù)器,用相同的接口去批量修改一批數(shù)據(jù),但是數(shù)據(jù)中彼此有重復(fù)的數(shù)據(jù)?;谶@個(gè)問(wèn)題,出現(xiàn)下面這種情況的死鎖-->
?
?
?
?
?
問(wèn)題分析:因?yàn)?服務(wù)器修改的批次包括abcdef 這個(gè)時(shí)候剛好修改了abcd所以abcd的索引被鎖住了,2服務(wù)器修改了efgh,這個(gè)時(shí)候efgh的索引被鎖住了
?
因?yàn)榕啃薷氖且粋€(gè)默認(rèn)的事務(wù),所以如果沒(méi)有全部修改完,索引是不會(huì)被放開(kāi)的,所以1服務(wù)器的e等待2服務(wù)器放開(kāi),2服務(wù)器的c等待1服務(wù)器放開(kāi),造成死鎖。。。。。
?
解決辦法1:將批量修改通過(guò)for循環(huán)改成單條修改,但是這個(gè)方法對(duì)服務(wù)器的壓力增大
?
解決辦法2:我們?cè)讷@取數(shù)據(jù)的時(shí)候進(jìn)行一次篩選,將重復(fù)的數(shù)據(jù)剔除出去,我們用到了redis
?
再用redis分布式鎖,進(jìn)行批量更新。這樣的好處就是解決問(wèn)題的同時(shí)減少對(duì)服務(wù)器的壓力
?
具體操作看代碼
?
?
public int batch(SmsReport[] rpts) {
?? if (ArrayUtils.isEmpty(rpts)){
?????? return0;
?? }
??? List<SmsReport>list = new ArrayList<>(Arrays.asList(rpts));
???Iterator<SmsReport> 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的有效時(shí)間設(shè)為若干分鐘,通過(guò)key唯一的性質(zhì)進(jìn)行數(shù)據(jù)排重,這樣即使是多線程多服務(wù)器進(jìn)行批量修改,也可以整合成一次數(shù)據(jù)無(wú)重復(fù)的批量修改,從而解決重復(fù)數(shù)據(jù)的死鎖問(wèn)題。。。。。
?
?