redis分布式鎖的正確姿勢

來源:互聯網
上載者:User
1.需求分析

    筆者的項目中有個簽到功能:一個使用者每天只能簽到一次;但是在並發的情況下,一個使用者可能一天能簽到數次;在這種情況下,筆者最先想到的就是運用redis的分布式鎖解決該問題 2.redis分布式鎖

①redis中的setnx具備天然的鎖機制;如果redis中存在key,則返回0,儲存失敗;不存在,則返回1,儲存成功;

②擷取redis分布式鎖的正確姿勢

private static final String LOCK_SUCCESS = "OK";private static final String SET_IF_NOT_EXIST = "NX";private static final String SET_WITH_EXPIRE_TIME = "PX";private static final Long RELEASE_SUCCESS = 1L;
public static boolean tryGetDistributedLock(Jedis jedis, String lockKey, String requestId, int expireTime){   String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);   System.out.println("=============擷取分布式鎖結果:"+result);   if (LOCK_SUCCESS.equals(result)) {      return true;   }   return false;}

這裡還設定了redis的expire時間,當linux伺服器崩潰了或者redis崩了,不至於發生死結的情況,key在超過到期時間後自動刪除的。這種情況下key的儲存與到期時間的設定是原子性的操作,有效避免了前面所說的死結。

③擷取分布式鎖的錯誤方式

ShardedJedis shardedJedis = RedisClientUtil.getShardedJedis();long update =  shardedJedis.setnx("goods","66");System.out.println("是否儲存成功:"+update);shardedJedis.expire("goods",30);  //30秒的到期時間shardedJedis.close();

在這種情況下,如果系統發生宕機或者崩潰,那麼分布式鎖很可能成為死結,增添redis的壓力;畢竟這種情況下key的儲存與到期時間的設定是非原子性的操作;

④釋放分布式鎖的正確姿勢

/** * 釋放分布式鎖 * @param jedis * @param lockKey * @param requestId * @return */public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String requestId) {   String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";   Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));   if (RELEASE_SUCCESS.equals(result)) {      return true;   }   return false;}
2.執行個體運用

①在並發的情況下,簽到重複


②分布式鎖正確解決該並發問題

//redis分布式鎖Jedis jedis = RedisClientUtil.getJedis();boolean flag = RedisClientUtil.tryGetDistributedLock(jedis,"singLock:"+userId,userId,1 * 60 * 1000);jedis.close();if(flag){ //擷取分布式鎖成功String registrationId = this.equipmentMapper.findRegistrationId(params.get("userId").toString());//簽到隨機紅包BigDecimal signRedPacketNum = new BigDecimal(RedPacketUtil.randomPacketBySign());//滿勤隨機紅包BigDecimal signFullRedPacket = new BigDecimal(RedPacketUtil.randomPacketBySignFull());int totalDays = DateUtil.getToalDayNow();Integer signDayNum = this.signMapper.findsignDayNum(params);Sign todaySign = this.signMapper.findTodaySign(params);//簽到紅包Map<String,String> pushMap=new HashMap<>();pushMap.put("redpactetId",params.get("uuid"));pushMap.put("packetAmount",String.valueOf(signRedPacketNum));pushMap.put("type", "2018");pushMap.put("packetType","3");pushMap.put("packetBody","簽到紅包");//滿勤紅包Map<String,Object> redpacketFullMaps = new HashMap<>();redpacketFullMaps.put("uuid",params.get("uuid"));redpacketFullMaps.put("userId",params.get("userId"));redpacketFullMaps.put("packetAmount",signFullRedPacket);redpacketFullMaps.put("packetType", 4);redpacketFullMaps.put("packetBody","恭喜你獲得一個簽到滿勤紅包");Map<String,String> pushSignMap=new HashMap<>();pushSignMap.put("redpactetId",params.get("uuid"));pushSignMap.put("packetAmount",Double.toString(signFullRedPacket.doubleValue()));pushSignMap.put("type", "2019");pushSignMap.put("packetType","4");pushSignMap.put("packetBody","滿勤紅包");HashSet<String> registrationIDs = new HashSet<>();registrationIDs.add(registrationId);    if (todaySign != null) {        return ResultJsonUtil.toFailureJSONString(TODAY_IS_SIGNED);    }    //補簽    if (params.get("type").equals("1")) {        Integer repairSignNum = this.signMapper.findRepairSignNum(params);        if (repairSignNum < 2) {            this.signMapper.userSign(params);            return ResultJsonUtil.toSuccessJSONString(uuid);        } else {            return ResultJsonUtil.toFailureJSONString(MONTH_TWO);        }    }    //簽到    String signOrderCode = RandomCodeUtil.getOrderCode(PayTypeConfig.YIVI_USER_SIGN);    //滿勤    String fullOrderCode = RandomCodeUtil.getOrderCode(PayTypeConfig.YIVI_USER_FULL_SIGN);    if (params.get("type").equals("0") && todaySign == null) {        this.signMapper.userSign(params);        this.redPacketMapper.putRedPacket(redpacketMaps);        // 添加系統錢包流水記錄         Double sysRemainAmount = this.accountMapper.findSysAmountByUserId(AlipayUtil.YIVI_SYSTEM_ACCOUNT);        AccountFlow sysFlow = AccountFlow.newBuilder().setUserId(AlipayUtil.YIVI_SYSTEM_ACCOUNT).setOrderCode(signOrderCode)                .setBody("紅包支出").setIsInflow("0").setType("3").setTotal_amount(signRedPacketNum.toString())                .setRemainAmount(sysRemainAmount.toString()).build();        Integer insertSysAccountFlow = this.accountFlowMapper.insertSysFlow(sysFlow.toMap());        try {            PushThreadPool.getInstance().push(PushDataModel.redPacketSend(pushMap, registrationIDs), channal);        } catch (Exception e) {            e.printStackTrace();        }        if (totalDays - signDayNum.intValue() == 1) {            redPacketMapper.putRedPacket(redpacketFullMaps);            AccountFlow sysFlowFull = AccountFlow.newBuilder().setUserId(AlipayUtil.YIVI_SYSTEM_ACCOUNT).setOrderCode(fullOrderCode)                    .setBody("紅包支出").setIsInflow("0").setType("3").setTotal_amount(signFullRedPacket.toString())                    .setRemainAmount(sysRemainAmount.toString()).build();            this.accountFlowMapper.insertSysFlow(sysFlowFull.toMap());            PushThreadPool.getInstance().push(PushDataModel.redPacketSend(pushSignMap, registrationIDs), channal);        }    }}

筆者將使用者的userId作為key, 每個使用者都用一個屬於自己的分布式鎖的key,而且key的到期時間設定為1分鐘,這樣在一分鐘的時間內,並發是不可能發生的,等key失效再進行訪問,系統此時會拋出“今天您已經簽到過”的溫馨提醒

好了,我是張星,歡迎加入博主技術交流群,群號:313145288

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.