您好,登錄后才能下訂單哦!
這篇文章主要介紹了Redis分布式鎖的原理是什么和怎么實現的相關知識,內容詳細易懂,操作簡單快捷,具有一定借鑒價值,相信大家閱讀完這篇Redis分布式鎖的原理是什么和怎么實現文章都會有所收獲,下面我們一起來看看吧。
之前一人一單的業務使用的悲觀鎖,在分布式系統下,是無法生效的。
理想的情況下是這樣的:一個線程成功獲取互斥鎖,并對查詢訂單并創建訂單,其他線程無法干預。它的原理是會有一個鎖監視器,來監聽是誰獲得了鎖。
但是問題就出現在:
分布式系統下,有多個不同的JVM,不同的JVM的環境下,鎖監聽器是有多個的,就會出現有的線程在別的線程已經拿到鎖的情況下,仍然可以獲取的到鎖。
這個時候,普通的JVM中的鎖就已經不管用了,就需要我們利用分布式鎖 。
就是可以滿足分布式系統或集群模式下多進程可見并且互斥的鎖。
它的實現原理就是,不同的JVM環境,都來共用一個鎖監視器。這樣就不會導致出現多個線程用多把鎖的情況了。
特點:
主要有三種實現方法,我們可以都來進行一個對比。
如下圖:
這里主要講基于Redis的分布式鎖的實現 。
實現Reids分布式鎖的方法主要就下面兩個步驟:
1. 獲取鎖
獲取鎖的方法已經是老朋友了,就是使用Redis String類型方法中的setnx方法(互斥性)。但是,為了預防redis服務器宕機的問題,我們要給鎖設置一個超時時間,避免出現死鎖。(非阻塞)
所以,獲取鎖的方式可以使用如下代碼
SET lock thread1 nx ex 10
lock是鎖的key,thread1 是value,nx就是setnx方法,ex就是設置超時時間
2. 釋放鎖
釋放鎖就簡單了,刪除即可。
del lock
代碼實現:
需求:定義一個接口,利用Redis實現分布式鎖的功能。
代碼如下:
接口代碼:
package com.hmdp.utils; public interface ILock { /** * 嘗試獲取鎖 * @param timeoutSec 鎖的持有時間,過期自動釋放 * @return true代表獲取鎖成功,false代表獲取鎖失敗。 */ boolean tryLock(long timeoutSec); /** * 釋放鎖 */ void unlock(); }
接口實現類:
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); } }
業務層獲取鎖和釋放鎖(優惠券秒殺業務修改)
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); } }
關于“Redis分布式鎖的原理是什么和怎么實現”這篇文章的內容就介紹到這里,感謝各位的閱讀!相信大家對“Redis分布式鎖的原理是什么和怎么實現”知識都有一定的了解,大家如果還想學習更多知識,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。