国产亚洲精品福利在线无卡一,国产精久久一区二区三区,亚洲精品无码国模,精品久久久久久无码专区不卡

當(dāng)前位置: 首頁 > news >正文

北京b2c網(wǎng)站開發(fā)抖音廣告代運營

北京b2c網(wǎng)站開發(fā),抖音廣告代運營,網(wǎng)站視頻管理系統(tǒng),南京做企業(yè)網(wǎng)站的公司四、分布式鎖 4.1 基本原理和實現(xiàn)方式對比 分布式鎖:滿足分布式系統(tǒng)或集群模式下多進(jìn)程可見并且互斥的鎖。 分布式鎖的核心思想就是讓大家都使用同一把鎖,只要大家使用的是同一把鎖,那么我們就能鎖住線程,不讓線程進(jìn)行&#xf…

四、分布式鎖

4.1 基本原理和實現(xiàn)方式對比

分布式鎖:滿足分布式系統(tǒng)或集群模式下多進(jìn)程可見并且互斥的鎖。

分布式鎖的核心思想就是讓大家都使用同一把鎖,只要大家使用的是同一把鎖,那么我們就能鎖住線程,不讓線程進(jìn)行,讓程序串行執(zhí)行,這就是分布式鎖的核心思路。

外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳

分布式鎖滿足的條件

可見性:多個線程都能看到相同的結(jié)果。注意:這個地方說的可見性并不是并發(fā)編程中指的內(nèi)存可見性,只是說多個進(jìn)程之間都能感知到變化的意思。

互斥:互斥是分布式鎖最基本的條件,使得程序串行執(zhí)行。

高可用:程序不易崩潰,時時刻刻都保證較高的可用性。

高性能:由于加鎖本身就讓性能降低,所以對于分布式鎖本身需要它有較高的加鎖性能和釋放鎖性能。

安全性:安全是程序中必不可少的一環(huán)。

常見的三種分布式鎖

  • MySQL:mysql本身就帶有鎖機制,但是由于mysql性能本身一般,所以采用分布式鎖的情況下,其實使用mysql作為分布式鎖比較少見。
  • Redis:redis作為分布式鎖是非常常見的一種使用方式,現(xiàn)在企業(yè)級開發(fā)中基本都使用redis或zookeeper作為分布式鎖,利用setnx這個方法,如果插入key成功,則表示獲得了鎖,如果有人插入成功,其他人插入失敗則表示無法獲得到鎖,利用這套邏輯來實現(xiàn)分布式鎖。
  • zookeeper:zookeeper也是企業(yè)級開發(fā)中較好的一個實現(xiàn)分布式鎖的方案。

外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳

4.2 Redis分布式鎖的實現(xiàn)核心思路

實現(xiàn)分布式鎖時需要實現(xiàn)的兩個基本方法:

  • 獲取鎖:

    • 互斥:確保只能有一個線程獲取鎖
    • 非阻塞:嘗試一次,成功返回true,失敗返回false

    外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳

  • 釋放鎖:

    • 手動釋放

    • 超時釋放:獲取鎖時添加一個超時時間

      外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳

核心思路:

我們利用redis的setNx方法,當(dāng)有多個線程進(jìn)入時,我們就利用該方法,第一個線程進(jìn)入時,redis中就有這個key了,返回了1,如果結(jié)果是1表示他搶到了鎖,那么他去執(zhí)行業(yè)務(wù),然后再刪除鎖,退出鎖邏輯,如果沒有搶到鎖,等待一定時間后重試即可

4.3 實現(xiàn)分布式鎖版本一

鎖的基本接口

public interface ILock {/*** 嘗試獲取鎖** @param timeoutSec 鎖持有的超時時間,過期后自動釋放* @return true代表獲取鎖成功;false代表獲取鎖失敗*/boolean tryLock(long timeoutSec);/*** 釋放鎖*/void unlock();
}

SimpleRedisLock

利用setnx方法進(jìn)行加鎖,同時增加過期時間,防止死鎖,此方法可以保證加鎖和增加過期時間,具有原子性

public class SimpleRedisLock implements ILock {private String name;private StringRedisTemplate stringRedisTemplate;public SimpleRedisLock(String name, StringRedisTemplate stringRedisTemplate) {this.name = name;this.stringRedisTemplate = stringRedisTemplate;}private static final String KEY_PREFIX = "lock:";@Overridepublic boolean tryLock(long timeoutSec) {//獲取線程標(biāo)識long threadId = Thread.currentThread().getId();//獲取鎖Boolean success = stringRedisTemplate.opsForValue().setIfAbsent(KEY_PREFIX + name, threadId + "", timeoutSec, TimeUnit.SECONDS);return Boolean.TRUE.equals(success);}@Overridepublic void unlock() {//釋放鎖stringRedisTemplate.delete(KEY_PREFIX+name);}
}

修改seckillVoucher業(yè)務(wù)代碼

@Autowired
private StringRedisTemplate stringRedisTemplate;public Result seckillVoucher(Long voucherId) {//1.查詢優(yōu)惠券信息SeckillVoucher voucher = seckillVoucherService.getById(voucherId);//2.判斷秒殺是否開始if (voucher.getBeginTime().isAfter(LocalDateTime.now())) {return Result.fail("秒殺尚未開始!");}//3.判斷秒殺是否已經(jīng)結(jié)束if (voucher.getEndTime().isBefore(LocalDateTime.now())) {return Result.fail("秒殺已經(jīng)結(jié)束!");}//4.判斷庫存是否充足if (voucher.getStock() < 1) {return Result.fail("庫存不足!");}Long userId = UserHolder.getUser().getId();//創(chuàng)建鎖對象SimpleRedisLock lock = new SimpleRedisLock("order:" + userId, stringRedisTemplate);//獲取鎖boolean isLock = lock.tryLock(1200);//判斷釋放獲取鎖成功if (!isLock) {//獲取鎖失敗,返回錯誤或重試return Result.fail("不允許重復(fù)下單!");}try {//獲取代理對象(事務(wù))IVoucherOrderService proxy = (IVoucherOrderService) AopContext.currentProxy();return proxy.createVoucherOrder(voucherId);} finally {//釋放鎖lock.unlock();}
}

4.4 Redis分布式鎖誤刪情況說明

邏輯說明:

持有鎖的線程在鎖的內(nèi)部出現(xiàn)了阻塞,導(dǎo)致它的鎖超時自動釋放,線程2來嘗試獲得鎖,拿到了這把鎖,然后線程2在持有鎖執(zhí)行過程中,線程1繼續(xù)執(zhí)行,而線程1執(zhí)行過程中,走到了刪除鎖邏輯,此時就會把本應(yīng)該屬于線程2的鎖進(jìn)行刪除。

外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳

解決方案:在每個線程釋放鎖的時候,去判斷一下當(dāng)前這把鎖是否屬于自己。假設(shè)還是上面的情況,線程1卡頓,鎖超時自動釋放,線程2進(jìn)入到鎖的內(nèi)部執(zhí)行邏輯,此時線程1反映過來,然后刪除鎖,但是線程1一看當(dāng)前這把鎖不是屬于自己,于是不進(jìn)行刪除鎖邏輯,當(dāng)線程2走到刪除鎖邏輯時,如果沒有卡過自動釋放鎖的時間點,則判斷當(dāng)前這把鎖是屬于自己的,于是刪除這把鎖。

外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳

4.5 解決Redis分布式鎖誤刪問題

需求:修改之前的分布式鎖實現(xiàn),滿足:在獲取鎖時存入線程標(biāo)識(可以用UUID表示),在釋放鎖時先獲得鎖的線程標(biāo)示,判斷是否與當(dāng)前線程標(biāo)識一致。

  • 如果一致則釋放鎖
  • 如果不一致則不釋放鎖

核心邏輯:在存入鎖時,放入自己線程的標(biāo)識,在刪除鎖時,判斷當(dāng)前這把鎖的標(biāo)識是不是自己存入的,如果是,則進(jìn)行刪除,如果不是,則不進(jìn)行刪除。

private static final String ID_PREFIX = UUID.randomUUID().toString(true) + "-";@Override
public boolean tryLock(long timeoutSec) {//獲取線程標(biāo)識String threadId = ID_PREFIX + Thread.currentThread().getId();//獲取鎖Boolean success = stringRedisTemplate.opsForValue().setIfAbsent(KEY_PREFIX + name, threadId, timeoutSec, TimeUnit.SECONDS);return Boolean.TRUE.equals(success);
}@Override
public void unlock() {//獲取線程標(biāo)識String threadId = ID_PREFIX + Thread.currentThread().getId();//獲取鎖中的標(biāo)識String id = stringRedisTemplate.opsForValue().get(KEY_PREFIX + name);//判斷標(biāo)識是否一致if (threadId.equals(id)) {//釋放鎖stringRedisTemplate.delete(KEY_PREFIX + name);}
}

有關(guān)代碼實操說明:

在我們修改完此處代碼后,我們重啟工程,然后啟動兩個線程,第一個線程持有鎖后,手動釋放鎖,第二個線程 此時進(jìn)入到鎖內(nèi)部,再放行第一個線程,此時第一個線程由于鎖的value值并非是自己,所以不能釋放鎖,也就無法刪除別人的鎖,此時第二個線程能夠正確釋放鎖,通過這個案例初步說明我們解決了鎖誤刪的問題。

4.6 分布式鎖的原子性問題

更為極端的誤刪邏輯說明:

線程1現(xiàn)在持有鎖之后,在執(zhí)行業(yè)務(wù)邏輯過程中,它正準(zhǔn)備刪除鎖,而且已經(jīng)走到了條件判斷的過程中,比如它已經(jīng)拿到了當(dāng)前這把鎖確實是屬于他自己的,正準(zhǔn)備刪除鎖,但是此時它的鎖到期了,那么此時線程2進(jìn)來,但是線程1他會接著往后執(zhí)行,當(dāng)線程1執(zhí)行到刪除鎖那行代碼時,相當(dāng)于條件判斷并沒有起到作用,這就是刪鎖時的原子性問題,之所以有這個問題,是因為線程1的拿到鎖,比較鎖,刪除鎖實際上不是一個原子性的,我們要防止剛才的情況發(fā)生。

外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳

4.7 Lua腳本解決多條命令原子性問題

Redis提供了Lua腳本功能,在一個腳本中編寫多條Redis命令,確保多條命令執(zhí)行時的原子性。

Lua是一種編程語言,它的基本語法大家可以參考網(wǎng)站:https://www.runoob.com/lua/lua-tutorial.html,這里重點介紹Redis提供的調(diào)用函數(shù),我們可以使用Lua去操作redis,又能保證它的原子性,這樣就可以實現(xiàn)拿鎖、比較鎖和刪除鎖是一個原子性動作了。

這里重點介紹Redis提供的調(diào)用函數(shù),語法如下:

redis.call('命令名稱','key','其他參數(shù)',...)

例如,我們要執(zhí)行set name jack,則腳本是這樣的:

# 執(zhí)行 set name jack
redis.call('set','name','jack')

例如,我們要先執(zhí)行set name Rose,再執(zhí)行g(shù)et name,則腳本如下:

# 先執(zhí)行 set name jack
redis.call('set','name','Rose')
# 再執(zhí)行 get name
local name=redis.call('get','name')
# 返回
return name

寫好腳本以后,需要用Redis命令來調(diào)用腳本,調(diào)用腳本的常見命令如下:

例如,我們要執(zhí)行 redis.call(‘set’, ‘name’, ‘jack’) 這個腳本,語法如下:

#調(diào)用腳本
EVAL "return redis.call('set','name','jack')" 0

如果腳本中的key、value不想寫死,可以作為參數(shù)傳遞。key類型參數(shù)會放入KEYS數(shù)組,其它參數(shù)會放入ARGV數(shù)組,在腳本中可以從KEYS和ARGV數(shù)組獲取這些參數(shù):

#調(diào)用腳本
EVAL "return redis.call('set',KEYS[1],ARGV[1])" 1 name Rose

使用Lua腳本實現(xiàn)釋放鎖的流程

--這里的KEYS[1]就是鎖的key,這里的ARGV[1]就是當(dāng)前線程標(biāo)識
--獲取鎖中的標(biāo)識,判斷是否與當(dāng)前線程標(biāo)識一致
if(redis.call('GET',KEYS[1])==ARGV[1]) then-- 一致,則刪除鎖return redis.call('DEL',KEYS[1])
end
--不一致,則直接返回
return 0

4.8 利用Java代碼調(diào)用Lua腳本改造分布式鎖

在RedisTemplate中,可以利用execute方法去執(zhí)行l(wèi)ua腳本,參數(shù)對應(yīng)關(guān)系如圖所示

外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳

private static final DefaultRedisScript<Long> UNLOCK_SCRIPT;static {UNLOCK_SCRIPT = new DefaultRedisScript<>();UNLOCK_SCRIPT.setLocation(new ClassPathResource("unlock.lua"));UNLOCK_SCRIPT.setResultType(Long.class);}public void unlock() {// 調(diào)用lua腳本stringRedisTemplate.execute(UNLOCK_SCRIPT,Collections.singletonList(KEY_PREFIX + name),ID_PREFIX + Thread.currentThread().getId());
}

經(jīng)過以上改造,我們就可以實現(xiàn)拿鎖、比較鎖、刪除鎖的原子性操作了。

測試邏輯

第一個線程進(jìn)來,得到了鎖,手動刪除鎖,模擬鎖超時了,其他線程會來搶鎖,當(dāng)?shù)谝粋€線程利用lua刪除鎖時,lua能保證他不能刪除別人的鎖,第二個線程刪除鎖時,利用lua同樣可以保證不會刪除別人的鎖,同時還能保證原子性。

4.9 總結(jié)

基于Redis的分布式鎖實現(xiàn)思路:

  • 利用set nx ex 獲取鎖,并設(shè)置過期時間,保存線程標(biāo)識
  • 釋放鎖時先判斷標(biāo)識是否與自己一致,一致則刪除鎖
    • 特性:
      • 利用set nx滿足互斥性
      • 利用set ex保證故障時鎖依然能釋放,避免死鎖,提高安全性
      • 利用Redis集群保證高可用和高并發(fā)特性

一路走來,利用添加過期時間,防止死鎖問題的發(fā)生,但是有了過期時間之后,可能出現(xiàn)誤刪別人鎖的問題,這個問題開始是利用刪之前拿鎖、比較鎖、刪除鎖這個邏輯來解決的,也就是刪之前判斷這把鎖是否是屬于自己的,但是現(xiàn)在還有一個原子性問題,我們無法保證拿鎖、比較鎖和刪除鎖是一個原子性動作,最后通過lua表達(dá)式解決了這個問題。

http://m.aloenet.com.cn/news/40947.html

相關(guān)文章:

  • 做窗簾的效果圖網(wǎng)站快推達(dá)seo
  • 柳河縣做網(wǎng)站谷歌paypal下載
  • 做網(wǎng)站哪些技術(shù)資源優(yōu)化排名網(wǎng)站
  • 怎么做網(wǎng)站注冊登入頁面搜狗指數(shù)官網(wǎng)
  • 做易拉寶設(shè)計的網(wǎng)站衡陽seo優(yōu)化報價
  • 一站式做網(wǎng)站哪家強寧波網(wǎng)站推廣哪家公司好
  • 山西省政府網(wǎng)站建設(shè)北京建站
  • 音樂介紹網(wǎng)站怎么做的商業(yè)軟文代寫
  • wordpress 獲取縮略圖路徑全網(wǎng)關(guān)鍵詞優(yōu)化公司哪家好
  • 真正免費的網(wǎng)站建站平臺排名中山seo
  • 直播的網(wǎng)站開發(fā)中國最新領(lǐng)導(dǎo)班子
  • 黃頁 網(wǎng)站模板微信營銷的方法7種
  • nba網(wǎng)站開發(fā)畢業(yè)論文廣州seo外包
  • 哪個網(wǎng)站可以免費看小說不收費百度網(wǎng)絡(luò)營銷推廣
  • 寧波網(wǎng)站建設(shè)模板下載免費宣傳軟文模板
  • 萬戶網(wǎng)絡(luò)網(wǎng)站建設(shè)軟文營銷的經(jīng)典案例
  • 網(wǎng)站建設(shè)方案書 備案最近發(fā)生的熱點新聞事件
  • 大型門戶網(wǎng)站建設(shè)一般多少錢北京云無限優(yōu)化
  • 武漢建設(shè)局網(wǎng)站南寧百度seo排名公司
  • 深圳網(wǎng)站設(shè)計興田德潤放心百度關(guān)鍵詞優(yōu)化多少錢一年
  • 環(huán)球網(wǎng)今日疫情消息網(wǎng)站優(yōu)化招聘
  • 網(wǎng)站建設(shè)與管理教案電商數(shù)據(jù)統(tǒng)計網(wǎng)站
  • 做網(wǎng)站的公司術(shù)語國際新聞最新消息中國
  • wordpress網(wǎng)站搭建教程視頻山東濰坊疫情最新消息
  • 長沙網(wǎng)站制作電話百度seo新算法
  • 做抖音風(fēng)的網(wǎng)站怎么優(yōu)化關(guān)鍵詞排名優(yōu)化
  • 專門做家教的網(wǎng)站營銷網(wǎng)站搭建
  • 怎么做谷歌這樣的網(wǎng)站網(wǎng)站如何做seo排名
  • 免費推廣網(wǎng)站大全網(wǎng)如何做線上銷售和推廣
  • 如何為網(wǎng)站做優(yōu)化培訓(xùn)機構(gòu)招生方案