Redis锁保障线上订单不重复(redis锁防止重复下单)

2023-05-07 19:27:13 重复 线上 下单

操作

Redis是支持多种数据类型的高性能NoSQL内存数据库,其中也包括支持分布式锁功能,可以大大提高系统的可用性。网站应用在做电子商务或者是支付类应用时,常见的经典场景是,多个用户同时下单争抢同一个库存商品,此时需要设计一套可靠的方案来保障订单不重复操作,就可以用Redis锁来解决。

下面通过实例来介绍Redis的锁功能,建议读者使用Java实现,这样可以更好地结合原生redis命令行。

1.可重入锁:

用户请求下单时首先使用的是可重入锁,也就是说,同一个线程/进程调用同一把锁,调用次数可以超过1次,会累积计算,而不会发生死锁,代码如下:

jedis = new Jedis(“localhost”);

String key = “order_” + orderId;

String requestId = UUID.randomUUID().toString();

// SET NX PX 10: 这个命令是Set if Not Exists,带上10毫秒的超时值以保证获取锁的及时性

String result = jedis.set(key, requestId, “NX”, “PX”, 10);

if (“OK”.equals(result)) {

// 获取锁成功,执行自己的逻辑

}

// 解锁:判断当前锁是否属于自己,如果是,再次使用set操作释放锁,如果不是,怎不做任何操作

String script = “if redis.call(‘get’, KEYS[1]) == ARGV[1] then return redis.call(‘del’, KEYS[1]) else return 0 end”;

Object obj = jedis.eval(script, Collections.singletonList(key), Collections.singletonList(requestId));

2.自旋锁:

当可重入锁(非阻塞)获取锁失败,返回错误时,用户可以选择使用自旋锁(阻塞)来重新获取锁,如下所示:

boolean locked = false;

String key = “order_” + orderId;

String requestId = UUID.randomUUID().toString();

while (!locked) {

// 自旋锁:一旦获取锁失败,立刻重试

locked = jedis.set(key, requestId, “NX”, “PX”, 10) == “OK”;

}

// 获取锁成功,执行自己的逻辑

// 解锁:判断当前锁是否属于自己,如果是,再次使用set操作释放锁,如果不是,怎不做任何操作,此时不允许多线程对同一把锁进行删除,以免出现脏读

String script = “if redis.call(‘get’, KEYS[1]) == ARGV[1] then return redis.call(‘del’, KEYS[1]) else return 0 end”;

Object obj = jedis.eval(script, Collections.singletonList(key), Collections.singletonList(requestId));

以上便是使用Redis锁来保证系统订单不重复操作的一种常用方案,基本上也是目前比较好的解决方案。当然,由于Redis不适合所有的场景,当多结点分布式部署时,建议使用更加稳定并且支持分布式集群的ZooKeeper,来进行订单锁定。

相关文章