分布式鎖之redis實現

2021-09-23 08:11:07 字數 4788 閱讀 7229

當我併發測試時:

這tm肯定不行啊,這就超賣了,明明沒這麼多商品,結果還賣出去了。。。

首先,synchronized的確是乙個解決辦法,而且也很簡單,在方法前面加乙個synchronized關鍵字。

但是通過壓測,發現請求變的很慢,因為:

synchronized就用乙個鎖把這個方法鎖住了,每次訪問這個方法,只會有乙個執行緒,所以這就是它導致慢的原因。通過這種方式,保證這個方法中的**都是單執行緒來處理,不會出什麼問題。

同時,使用synchronized還是存在一些問題的,首先,它無法做到細粒度的控制,比如同一時間有秒殺a商品和b商品的請求,都進入到了這個方法,雖然秒殺a商品的人很多,但是秒殺b商品的人很少,但是即使是買b商品,進入到了這個方法,也會一樣的慢。

最重要的是,它只適合單點的情況。如果以後程式水平擴充套件了,弄了個集群,很顯然,負載均衡之後,不同的使用者看到的結果一定是五花八門的。

所以,還是使用更好的辦法,使用redis分布式鎖。

1、兩個redis的命令

setnx key value 簡單來說,setnx就是,如果沒有這個key,那麼就set乙個key-value, 但是如果這個key已經存在,那麼將不會再次設定,get出來的value還是最開始set進去的那個value.

**中還專門講到可以使用!setnx加鎖,如果獲得鎖,返回1,如果返回0,那麼該鍵已經被其他的客戶端鎖定。

並且也提到了如何處理死鎖。

getset key value 這個就更簡單了,先通過key獲取value,然後再將新的value set進去。

2、redis分布式鎖的實現

我們希望的,無非就是這一段**,能夠單執行緒的去訪問,因此在這段**之前給他加鎖,相應的,這段**後面要給它解鎖:

2.1 引入redis依賴

org.springframework.boot

spring-boot-starter-data-redis

2.2 配置redis

spring:

redis:

host: localhost

port: 6379

2.3 編寫加鎖和解鎖的方法

package com.vito.service;

import org.slf4j.logger;

import org.slf4j.logge***ctory;

import org.springframework.beans.factory.annotation.autowired;

import org.springframework.data.redis.core.stringredistemplate;

import org.springframework.stereotype.component;

import org.springframework.util.stringutils;

/** * created by vitoyi on 2018/4/5.

*/@component

public class redislock

//避免死鎖,且只讓乙個執行緒拿到鎖

string currentvalue = redistemplate.opsforvalue().get(key);

//如果鎖過期了

if (!stringutils.isempty(currentvalue) && long.parselong(currentvalue) < system.currenttimemillis())

} return false;

} /**

* 解鎖

* @param key

* @param value

*/ public void unlock(string key, string value)

} catch (exception e) ", e);

} }}

為什麼要有避免死鎖的一步呢?

假設沒有『避免死鎖』這一步,結果在執行到下單**的時候出了問題,畢竟運算元據庫、網路、io的時候拋了個異常,這個異常是偶然丟擲來的,就那麼偶爾一次,那麼會導致解鎖步驟不去執行,這時候就沒有解鎖,後面的請求進來自然也或得不到鎖,這就被稱之為死鎖。

而這裡的『避免死鎖』,就是給鎖加了乙個過期時間,如果鎖超時了,就返回true,解開之前的那個死鎖。

2.4 下單**中引入加鎖和解鎖,確保只有乙個執行緒操作

@autowired

private redislock redislock;

@override

@transactional

public string seckill(integer id)throws runtimeexception

//查庫存

if(product.getstock()==0) throw new runtimeexception("已經賣光");

//寫入訂單表

order order=new order();

order.setproductid(product.getid());

order.setproductname(product.getname());

//減庫存

product.setprice(null);

product.setname(null);

product.setstock(product.getstock()-1);

//解鎖

redislock.unlock(string.valueof(id),string.valueof(time));

return findproductinfo(id);

}

這樣再來跑幾次壓測,就不會超賣了:

封裝-----

* @title: checksoldcountbyredisdate

* @description: 搶購的計數處理(用於處理超賣)

* @param @param key 購買計數的key

* @param @param limitcount 總的限購數量

* @param @param buycount 當前購買數量

* @param @param enddate 搶購結束時間

* @param @param lock 鎖的名稱與undielock方法的lock相同

* @param @param expire 鎖占有的時長(毫秒)

* @param @return 設定檔案

* @return boolean 返回型別

* @throws

private boolean checksoldcountbyredisdate(string key, int limitcount, int buycount, date enddate, string lock, int expire) else else else catch (exception e) {

e.printstacktrace();

return locked;

* @title: getdateaddmillsecond

* @description: (todo)取將來時間

* @param @param date

* @param @param millsecond

* @param @return 設定檔案

* @return date 返回型別

* @throws

public static date getdateadd(date date, int expire, int idate) {

calendar calendar = calendar.getinstance();

if (null != date) {// 預設當前時間

calendar.settime(date);

calendar.add(idate, expire);

return calendar.gettime();

* 刪除對應的value

* @param key

public static void remove(final string key) {

if (exists(key)) {

redistemplate.delete(key);

* 判斷快取中是否有對應的value

* @param key

* @return

public static boolean exists(final string key) {

return stringredistemplate.haskey(key);

private static stringredistemplate stringredistemplate = ((stringredistemplate) springcontextholder.getbean("stringredistemplate"));

分布式鎖實現之redis

利用redis設計分布式鎖主要用下面這三個命令 setnx setnx key val 當且僅當key不存在時,set乙個key為val的字串,返回1 若key存在,則什麼都不做,返回0。expire expire key timeout 為key設定乙個超時時間,單位為second,超過這個時間鎖...

分布式鎖 使用Redis實現分布式鎖

關於分布式鎖的實現,我的前一篇文章講解了如何使用zookeeper實現分布式鎖。關於分布式鎖的背景此處不再做贅述,我們直接討論下如何使用redis實現分布式鎖。關於redis,筆主不打算做長篇大論的介紹,只介紹下redis優秀的特性。支援豐富的資料型別,如string list map set zs...

redis實現分布式鎖

隨便 系統越來越大,各功能模組除了垂直切割以外,同時也得做集群處理,那麼問題來了,在多執行緒情況下對於資源的競爭就需要乙個統一的訪問限制。以選課系統為例子,集群中各節點對課程可選數量同時操作,這裡就需要同步了,否則會導致最後選到的數量比可選的數量大,這裡我們的分布式鎖就派上用場了。利用redis來實...