java使用redis分布式锁

1、背景

目前在查询时并发量较高,qps 6000左右,数据查出来后缓存在redis中,有效期5分钟,为防止redis失效的瞬间有太多到达数据库,给数据库造成冲击,在查询时使用redis分布式锁,保证一种查询条件只有一个查询能请求到数据库。

2、使用

2.1引入jedis

		<!--jedis-->
		<dependency>
			<groupId>redis.clients</groupId>
			<artifactId>jedis</artifactId>
			<version>3.4.1</version>
		</dependency>

2.2 redis分布式锁工具类

package com.soul.open.portal.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.params.SetParams;

import javax.annotation.Resource;
import java.util.Collections;

/**
 * @Description redis实现分布式锁
 * @Author liujunzhong@soulapp.cn
 * @Date 2021/7/14 下午8:49
 **/
@Component
public class RedisDistributedLock {

    private static Logger logger = LoggerFactory.getLogger(RedisDistributedLock.class);

    /**
     * 锁超时时间,1000ms
     */
    public static final Integer LOCK_EXPIRE_TIME = 1000;

    private static final String LOCK_SUCCESS = "OK";

    private static final Long RELEASE_SUCCESS = 1L;

    /**
     * 分布式锁前缀
     */
    public static final String LOCK_PREFIX = "lock:prefix:";

    @Resource(name = "mediaSourceJedisPool")
    private JedisPool mediaSourceJedisPool;

    /**
     * 获取分布式锁
     *
     * @param lockKey    锁
     * @param requestId  请求标识
     * @param expireTime 超期时间
     * @return 是否获取成功
     */
    public boolean tryGetDistributedLock(String lockKey, String requestId, long expireTime) {
        try (Jedis jedis = mediaSourceJedisPool.getResource()) {
            SetParams params = new SetParams().px(expireTime).nx();
            String result = jedis.set(lockKey, requestId, params);
            logger.info("tryGetDistributedLock result:{},lockKey:{},requestId:{}", result, lockKey, requestId);
            if (LOCK_SUCCESS.equals(result)) {
                return true;
            }
        } catch (Exception e) {
            logger.error("tryGetDistributedLock failed,lockKey:{},requestId:{}", lockKey, requestId, e);
        }
        return false;
    }


    /**
     * 释放锁
     *
     * @param lockKey   锁
     * @param requestId 请求标识
     * @return 是否释放成功
     */
    public boolean releaseDistributedLock(String lockKey, String requestId) {
        try (Jedis jedis = mediaSourceJedisPool.getResource()) {
            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));
            logger.info("releaseDistributedLock result:{},lockKey:{},requestId:{},", result, lockKey, requestId);
            if (RELEASE_SUCCESS.equals(result)) {
                return true;
            }
        } catch (Exception e) {
            String errMsg = "releaseDistributedLock failed,lockKey:" + lockKey + ",requestId:" + requestId;
            logger.error(errMsg, e);
        }
        return false;
    }

}

2.3使用分布式锁

    /**
     * 使用分布式锁,查询数据库
     */
    private SourceDataResult getDataByDistributedLock(String mediaKey, String requestId, AppSourceParam param) {
        String lockKey = RedisDistributedLock.LOCK_PREFIX + mediaKey;

        //1、获取锁,从数据库查询
        if (redisDistributedLock.tryGetDistributedLock(lockKey, requestId, RedisDistributedLock.LOCK_EXPIRE_TIME)) {
            try {
                List<SourceGroupVO> sourceGroups = getDataFromDb(param);

                SourceDataResult result = sourceListConvertMap(sourceGroups);
                try (Jedis jedis = mediaSourceJedisPool.getResource()) {
                    jedis.setex(mediaKey, SourceConstants.SOURCE_EXPIRE_TIME, JSONObject.toJSONString(result));
                    mediaSourceCache.put(mediaKey, result);
                } catch (Exception e) {
                    logger.error("set data to redis error, param:{}", JSONObject.toJSONString(param), e);
                }

                return result;
            } catch (Exception e) {
                logger.error("get data from db by distributed lock error, param:{}", JSONObject.toJSONString(param), e);
            } finally {
                redisDistributedLock.releaseDistributedLock(lockKey, requestId);
            }
        }

        //2、未获取锁,最多循环等待3次
        int count = 0;
        do {
            try {
                Thread.sleep(30);
            } catch (InterruptedException e) {
                logger.error("thread interrupted", e);
            }
            SourceDataResult dataResultFromRedis = getDataResultFromRedis(mediaKey);
            if (dataResultFromRedis != null) {
                logger.info("loop wait for data success,count:{}", count);
                return dataResultFromRedis;
            }
            count++;
        } while (count < 3);

        return new SourceDataResult();
    }

3、缺点

这种锁比较重,会同时锁住多台机器,在这里是为了防止太多请求同时查询数据库,如果有几个请求同时请求也是可以容忍的,比如一台机器请求一次,可以替换成java 本地的锁,不至于锁住整个集群,下篇文章介绍。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值