分布式Redis解决超卖

客户端请求加锁时,向Redis中写入一个键值对,键为锁的名称值为客户端的唯一标识符,并设置一个过期时间,以防止锁一直被占用而不能释放。

  1. 写入锁的操作需要使用Redis的SETNX命令(SET if Not eXists),该命令只有在锁的名称不存在时才会写入成功。因此,如果多个客户端同时请求加锁,只有一个客户端能够成功写入锁,其他客户端会写入失败。
  2. 加锁成功后,客户端可以开始执行临界区代码,执行完毕后,需要使用DEL命令将锁从Redis中删除,释放锁资源。
  3. 由于存在网络延迟等因素,加锁成功后客户端可能在执行临界区代码时崩溃或超时,导致锁一直没有被释放。为了避免这种情况发生,可以在写入锁时同时设置一个过期时间,当锁过期时会自动被Redis删除。

如果释放锁资源不判断值的唯一标识符,会发生什么?

@RequestMapping("/deduct_stock")
public String deductStock() throws InterruptedException {
	String lockKey = "product_001";
	String clientId = UUID.randomUUID().toString();
	try {
		Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, clientId, 10, TimeUnit.SECONDS);
		if (!result) {
			return "error";
		}

		int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock");
		if (stock > 0) {
			int realStock = stock - 1;
			stringRedisTemplate.opsForValue().set("stock", realStock + "");
			System.out.println("扣减成功,剩余库存:" + realStock + "");
		} else {
			System.out.println("扣减失败,库存不足");
		}

	} finally {
		if (clientId.equals(stringRedisTemplate.get(lockKey))) {
			stringRedisTemplate.delete(lockKey);
		}
	}
}

Redisson

Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid)。它不仅提供了一系列的分布式的Java常用对象,还提供了许多分布式服务

一个基于Redis实现的分布式工具,有基本分布式对象和高级又抽象的分布式服务,为每个试图再造分布式轮子的程序员带来了大部分分布式问题的解决办法。

Untitled

@RequestMapping("/deduct_stock")
public String deductStock() throws InterruptedException {
	String lockKey = "product_001";
	String clientId = UUID.randomUUID().toString();
	RLock redissonLock = redisson.getLock(lockKey);
	try {
		redissonLock.lock(30, TimeUnit.SECONDS);

		int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock");
		if (stock > 0) {
			int realStock = stock - 1;
			stringRedisTemplate.opsForValue().set("stock", realStock + "");
			System.out.println("扣减成功,剩余库存:" + realStock + "");
		} else {
			System.out.println("扣减失败,库存不足");
		}

	} finally {
		redissonLock.unlock();
	}
}

主从切换导致锁失效问题如何解决?