The pessimistic lock used in the previous one-person-one-order business cannot take effect in a distributed system.
In the most ideal case, if one thread can successfully acquire the mutex lock and query and create the order, other threads will not be able to interfere. The principle is that there will be a lock monitor to monitor who has obtained the lock.
But the problem arises:
In a distributed system, there are multiple different JVMs. In different JVM environments, the lock listener is If there are multiple threads, some threads can still obtain the lock even when other threads have already obtained the lock.
At this time, the locks in the ordinary JVM no longer work, so we need to use distributed locks.
It is a lock that can satisfy the requirements of visibility and mutual exclusion of multiple processes in a distributed system or cluster mode .
Its implementation principle is that different JVM environments share a lock monitor. This will prevent multiple threads from using multiple locks.
Features:
There are three main implementation methods, We can all make a comparison.
As shown below:
Here we mainly talk about the implementation of distributed lock based on Redis.
The method of implementing Reids distributed lock mainly involves the following two steps:
1. Obtain the lock
Use the setnx method of String type in Redis (guaranteeing mutual exclusivity) It is already a well-known way to obtain locks. In order to prevent the redis server from crashing, we need to set a timeout for the lock to avoid deadlock. (Non-blocking)
So, you can use the following code to obtain the lock
SET lock thread1 nx ex 10
lock is the key of the lock, thread1 is the value, nx is the setnx method, ex is to set the timeout
2. Release the lock
It is simple to release the lock, just delete it.
del lock
Code implementation:
Requirements: Define an interface and use Redis to implement the distributed lock function.
The code is as follows:
Interface code:
package com.hmdp.utils; public interface ILock { /** * 尝试获取锁 * @param timeoutSec 锁的持有时间,过期自动释放 * @return true代表获取锁成功,false代表获取锁失败。 */ boolean tryLock(long timeoutSec); /** * 释放锁 */ void unlock(); }
Interface implementation class:
package com.hmdp.utils; import org.springframework.data.redis.core.StringRedisTemplate; import java.util.concurrent.TimeUnit; /** * @Version 1.0 */ public class SimpleRedisLock implements ILock { //Redis private StringRedisTemplate stringRedisTemplate; //业务名称,也就是锁的名称 private String name; public SimpleRedisLock(StringRedisTemplate stringRedisTemplate, String name) { this.stringRedisTemplate = stringRedisTemplate; this.name = name; } //key的前缀 private static final String KEY_PREFIX = "lock:"; @Override public boolean tryLock(long timeoutSec) { //获取线程id,当作set的value long threadId = Thread.currentThread().getId(); Boolean success = stringRedisTemplate.opsForValue() .setIfAbsent(KEY_PREFIX + name, threadId+"", timeoutSec, TimeUnit.SECONDS); return Boolean.TRUE.equals(success); } //释放锁 @Override public void unlock() { //删除key stringRedisTemplate.delete(KEY_PREFIX+name); } }
The business layer acquires the lock and Release lock (coupon flash sale business modification)
package com.hmdp.service.impl; import com.hmdp.dto.Result; import com.hmdp.entity.SeckillVoucher; import com.hmdp.entity.VoucherOrder; import com.hmdp.mapper.VoucherOrderMapper; import com.hmdp.service.ISeckillVoucherService; import com.hmdp.service.IVoucherOrderService; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.hmdp.utils.RedisIdWorker; import com.hmdp.utils.SimpleRedisLock; import com.hmdp.utils.UserHolder; import org.springframework.aop.framework.AopContext; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; import java.time.LocalDateTime; /** * <p> * 服务实现类 * </p> * */ @Service public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService { @Resource private ISeckillVoucherService iSeckillVoucherService; @Resource private RedisIdWorker redisIdWorker; @Resource private StringRedisTemplate stringRedisTemplate; @Override public Result seckillVoucher(Long voucherId) { //1.获取优惠券信息 SeckillVoucher voucher = iSeckillVoucherService.getById(voucherId); //2.判断是否已经开始 if (voucher.getBeginTime().isAfter(LocalDateTime.now())){ Result.fail("秒杀尚未开始!"); } //3.判断是否已经结束 if (voucher.getEndTime().isBefore(LocalDateTime.now())){ Result.fail("秒杀已经结束了!"); } //4.判断库存是否充足 if (voucher.getStock() < 1) { Result.fail("库存不充足!"); } //5.扣减库存 boolean success = iSeckillVoucherService.update() .setSql("stock = stock-1").eq("voucher_id",voucherId).gt("stock",0) .update(); if (!success){ Result.fail("库存不充足!"); } Long userId = UserHolder.getUser().getId(); //1.创建锁对象 SimpleRedisLock lock = new SimpleRedisLock(stringRedisTemplate, "order:" + userId); //2.尝试获取锁 boolean isLock = lock.tryLock(1200); if (!isLock){ //获取锁失败 return Result.fail("一个用户只能下一单!"); } try { IVoucherOrderService proxy = (IVoucherOrderService) AopContext.currentProxy(); return proxy.createVoucherOrder(voucherId); } finally { //释放锁 lock.unlock(); } } @Transactional public Result createVoucherOrder(Long voucherId) { Long userId = UserHolder.getUser().getId(); //6.根据优惠券id和用户id判断订单是否已经存在 //如果存在,则返回错误信息 int count = query().eq("user_id", userId).eq("voucher_id", voucherId).count(); if (count > 0) { return Result.fail("用户已经购买!"); } //7. 创建订单 VoucherOrder voucherOrder = new VoucherOrder(); //7.1添加订单id Long orderId = redisIdWorker.nextId("order"); voucherOrder.setId(orderId); //7.2添加用户id voucherOrder.setUserId(userId); //7.3添加优惠券id voucherOrder.setVoucherId(voucherId); save(voucherOrder); //8.返回订单id return Result.ok(orderId); } }
The above is the detailed content of What is the principle of Redis distributed lock and how to implement it. For more information, please follow other related articles on the PHP Chinese website!