Redis分布式锁的实践

   首先说一下场景,不根据实际场景讲的技术都是吹流弊,没人反对吧,咳咳

   医院挂号,需要尽量高效的显示最新的数据,根据不同的科室设置不同的缓存时间,因为科室的热门程度也不一样嘛,这里本次只分享我学习的一些心得.

    思路:号源的缓存是30分钟,然后在第25的时候,如果还有患者访问某个部门的号源,就开启一条异步线程直接查询最新的号源,重新放入缓存中.如果没有,就直接让缓存失效,这样就区别出来了热点科室和冷门科室

   工具:思路就是自定义一个key,当前线程的name存为value,然后设置过期时间,调用 distributeLock 方法加锁,调用 releaseLock释放锁. 其中加锁的思路其实就是redis的setnx命令,每个线程都尝试加锁,因为 setnx就是一个原子性的操作,只有一个线程可以设置成功

  /**
     * 异步重新号源信息
     * @param queryParms
     */
	public void asynReservationsByDoctorsByThread(final QueryParms queryParms){
		final String lockKey = new String("lock#co#asynReservationsByDoctorsByThread");
		final String clientId = Thread.currentThread().getName();
		boolean lockFlag = distributeLock(lockKey, clientId, JIUYUAN_LOCK_TIME);
		if (lockFlag){
				new Thread(new Runnable() {
					@Override
					public void run() {
						logger.info("开始一个新的线程加载最新号源!_____star");
						try {
							for (String doctorId : queryParms.getDoctorIds()) {
								if (queryParms.getConverted()) {
									queryParms.setMyDoctorId(doctorId);
								} else {
									queryParms.setDoctorId(doctorId);
								}
								logger.info("getReservationsByDoctors  queryParms is =>" + queryParms);
								List l = getReservationsByDoctorIdDays(queryParms);
							}
						}catch (BusinessException e){
							throw new BusinessException("异步加载信息异常:" + e.getMessage() + "请求参数:" + queryParms);
						}finally {
							releaseLock(lockKey, clientId);
							logger.info("开始一个新的线程加载最新号源!___end");
						}
					}
				}).start();
			};
    }


	private static final String SET_IF_NOT_EXIST = "NX";

	private static final String SET_WITH_EXPIRE_TIME = "PX";//毫秒 如果是秒的话就是EX

/**
	 * 这是一个分布式锁,用于获取key锁,如果抢占成功,并强行设置当前锁的过期时间
	 *
	 * @param key
	 * @param value
	 * @param seconds
	 * @return
	 */
	public boolean distributeLock(final String key, final String value, final long seconds) {
		return (Boolean)redisTemplate.execute(new RedisCallback<Object>() {
			@Override
			public Object doInRedis(RedisConnection connection) throws DataAccessException {
				Jedis jedis = (Jedis) connection.getNativeConnection();
				String result = jedis.set(key, value, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, seconds);
				if (LOCK_SUCCESS.equals(result)) {
					return Boolean.TRUE;
				}
				return Boolean.FALSE;
			}
		});
	}


	//Lua脚本
	private static final String RELEASE_LOCK_SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";


	/**
	 * 调用Lua脚本释放锁
	 * @param key
	 * @param clientId
	 * @return
	 */
	public boolean releaseLock(final String key, final String clientId) {
		return redisTemplate.execute(new RedisCallback<Boolean>() {
			@Override
			public Boolean doInRedis(RedisConnection redisConnection) throws DataAccessException {
				Jedis jedis = (Jedis) redisConnection.getNativeConnection();
				Object result = jedis.eval(RELEASE_LOCK_SCRIPT, Collections.singletonList(key), Collections.singletonList(clientId));
				if (RELEASE_SUCCESS.equals(result)) {
					return Boolean.TRUE;
				}
				return Boolean.FALSE;
			}
		});
	}

    

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值