亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

java實(shí)現(xiàn)分布式鎖的常用三種方式

 更新時(shí)間:2024年08月27日 11:13:35   作者:創(chuàng)作小達(dá)人  
本文主要介紹了java實(shí)現(xiàn)分布式鎖,一般有這3種方式,基于數(shù)據(jù)庫(kù)實(shí)現(xiàn)的分布式鎖、基于Redis實(shí)現(xiàn)的分布式鎖和基于Zookeeper實(shí)現(xiàn)的分布式鎖,具有一定的參考價(jià)值,感興趣的可以了解一下

分布式鎖概述

我們的系統(tǒng)都是分布式部署的,日常開(kāi)發(fā)中,秒殺下單、搶購(gòu)商品等等業(yè)務(wù)場(chǎng)景,為了防?庫(kù)存超賣(mài),都需要用到分布式鎖。

分布式鎖其實(shí)就是,控制分布式系統(tǒng)不同進(jìn)程共同訪問(wèn)共享資源的一種鎖的實(shí)現(xiàn)。如果不同的系統(tǒng)或同一個(gè)系統(tǒng)的不同主機(jī)之間共享了某個(gè)臨界資源,往往需要互斥來(lái)防止彼此干擾,以保證一致性。

業(yè)界流行的分布式鎖實(shí)現(xiàn),一般有這3種方式:

  • 基于數(shù)據(jù)庫(kù)實(shí)現(xiàn)的分布式鎖

  • 基于Redis實(shí)現(xiàn)的分布式鎖

  • 基于Zookeeper實(shí)現(xiàn)的分布式鎖

分布式鎖:基于數(shù)據(jù)庫(kù)實(shí)現(xiàn)

主要有兩種方式:

1、悲觀鎖

2、樂(lè)觀鎖

A. 悲觀鎖(排他鎖)

利用select … where xx=yy for update排他鎖

注意:這里需要注意的是where xx=yy,xx字段必須要走索引,否則會(huì)鎖表。有些情況下,比如表不大,mysql優(yōu)化器會(huì)不走這個(gè)索引,導(dǎo)致鎖表問(wèn)題。

核心思想:以「悲觀的心態(tài)」操作資源,無(wú)法獲得鎖成功,就一直阻塞著等待。

注意:該方式有很多缺陷,一般不建議使用。

實(shí)現(xiàn):

創(chuàng)建一張資源鎖表:

CREATE TABLE `resource_lock` (
  `id` int(4) NOT NULL AUTO_INCREMENT COMMENT '主鍵',
  `resource_name` varchar(64) NOT NULL DEFAULT '' COMMENT '鎖定的資源名',
  `owner` varchar(64) NOT NULL DEFAULT '' COMMENT '鎖擁有者',
  `desc` varchar(1024) NOT NULL DEFAULT '備注信息',
  `update_time` timestamp NOT NULL DEFAULT '' COMMENT '保存數(shù)據(jù)時(shí)間,自動(dòng)生成',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uidx_resource_name` (`resource_name `) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='鎖定中的資源';

注意:resource_name 鎖資源名稱必須有唯一索引

使用事務(wù)查詢更新:

@Transaction
public void lock(String name) {
   ResourceLock rlock = exeSql("select * from resource_lock where resource_name = name for update");
     if (rlock == null) {
           exeSql("insert into resource_lock(reosurce_name,owner,count) values (name, 'ip',0)");
     }
}

使用 for update 鎖定的資源。如果執(zhí)行成功,會(huì)立即返回,執(zhí)行插入數(shù)據(jù)庫(kù),后續(xù)再執(zhí)行一些其他業(yè)務(wù)邏輯,直到事務(wù)提交,執(zhí)行結(jié)束;如果執(zhí)行失敗,就會(huì)一直阻塞著。

可以在數(shù)據(jù)庫(kù)客戶端工具上測(cè)試出來(lái)這個(gè)效果,當(dāng)在一個(gè)終端執(zhí)行了 for update,不提交事務(wù)。在另外的終端上執(zhí)行相同條件的 for update,會(huì)一直卡著

雖然也能實(shí)現(xiàn)分布式鎖的效果,但是會(huì)存在性能瓶頸。

優(yōu)點(diǎn):

簡(jiǎn)單易用,好理解,保障數(shù)據(jù)強(qiáng)一致性。

缺點(diǎn):

1)在 RR 事務(wù)級(jí)別,select 的 for update 操作是基于間隙鎖(gap lock) 實(shí)現(xiàn)的,是一種悲觀鎖的實(shí)現(xiàn)方式,所以存在阻塞問(wèn)題。

2)高并發(fā)情況下,大量請(qǐng)求進(jìn)來(lái),會(huì)導(dǎo)致大部分請(qǐng)求進(jìn)行排隊(duì),影響數(shù)據(jù)庫(kù)穩(wěn)定性,也會(huì)耗費(fèi)服務(wù)的CPU等資源。

當(dāng)獲得鎖的客戶端等待時(shí)間過(guò)長(zhǎng)時(shí),會(huì)提示:

[40001][1205] Lock wait timeout exceeded; try restarting transaction

高并發(fā)情況下,也會(huì)造成占用過(guò)多的應(yīng)用線程,導(dǎo)致業(yè)務(wù)無(wú)法正常響應(yīng)。

3)如果優(yōu)先獲得鎖的線程因?yàn)槟承┰?,一直沒(méi)有釋放掉鎖,可能會(huì)導(dǎo)致死鎖的發(fā)生。

4)鎖的長(zhǎng)時(shí)間不釋放,會(huì)一直占用數(shù)據(jù)庫(kù)連接,可能會(huì)將數(shù)據(jù)庫(kù)連接池?fù)伪?,影響其他服?wù)。

5)MySql數(shù)據(jù)庫(kù)會(huì)做查詢優(yōu)化,即便使用了索引,優(yōu)化時(shí)發(fā)現(xiàn)全表掃效率更高,則可能會(huì)將行鎖升級(jí)為表鎖,此時(shí)可能就更悲劇了。

6)不支持可重入特性,并且超時(shí)等待時(shí)間是全局的,不能隨便改動(dòng)。

B. 樂(lè)觀鎖 

所謂樂(lè)觀鎖與悲觀鎖最大區(qū)別在于基于CAS思想,表中添加一個(gè)時(shí)間戳或者是版本號(hào)的字段來(lái)實(shí)現(xiàn),update xx set version=new_version where xx=yy and version=Old_version,通過(guò)增加遞增的版本號(hào)字段實(shí)現(xiàn)樂(lè)觀鎖。

不具有互斥性,不會(huì)產(chǎn)生鎖等待而消耗資源,操作過(guò)程中認(rèn)為不存在并發(fā)沖突,只有update version失敗后才能覺(jué)察到。

搶購(gòu)、秒殺就是用了這種實(shí)現(xiàn)以防止超賣(mài)。

實(shí)現(xiàn):

創(chuàng)建一張資源鎖表:

CREATE TABLE `resource` (
  `id` int(4) NOT NULL AUTO_INCREMENT COMMENT '主鍵',
  `resource_name` varchar(64) NOT NULL DEFAULT '' COMMENT '資源名',
  `share` varchar(64) NOT NULL DEFAULT '' COMMENT '狀態(tài)',
  `version` int(4) NOT NULL DEFAULT '' COMMENT '版本號(hào)',
  `desc` varchar(1024) NOT NULL DEFAULT '備注信息',
  `update_time` timestamp NOT NULL DEFAULT '' COMMENT '保存數(shù)據(jù)時(shí)間,自動(dòng)生成',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uidx_resource_name` (`resource_name `) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='資源';

為表添加一個(gè)字段,版本號(hào)或者時(shí)間戳都可以。通過(guò)版本號(hào)或者時(shí)間戳,來(lái)保證多線程同時(shí)間操作共享資源的有序性和正確性。

偽代碼實(shí)現(xiàn):

Resrouce resource = exeSql("select * from resource where resource_name = xxx");
boolean succ = exeSql("update resource set version= 'newVersion' ... where resource_name = xxx and version = 'oldVersion'");

if (!succ) {
    // 發(fā)起重試
}

實(shí)際代碼中可以寫(xiě)個(gè)while循環(huán)不斷重試,版本號(hào)不一致,更新失敗,重新獲取新的版本號(hào),直到更新成功。

優(yōu)點(diǎn):

實(shí)現(xiàn)簡(jiǎn)單,復(fù)雜度低

保障數(shù)據(jù)一致性

缺點(diǎn):

性能低,并且有鎖表的風(fēng)險(xiǎn)

可靠性差

非阻塞操作失敗后,需要輪詢,占用CPU資源

長(zhǎng)時(shí)間不commit或者是長(zhǎng)時(shí)間輪詢,可能會(huì)占用較多的連接資源

分布式鎖:基于Redis實(shí)現(xiàn)

原理與實(shí)現(xiàn)

Redis提供了多種命令支持實(shí)現(xiàn)分布式鎖,其中最常用的是SETNX(Set if Not eXists)和GETSET結(jié)合使用,或者使用更高級(jí)的SET命令配合NX(Only set the key if it does not already exist)和PXEX(為key設(shè)置過(guò)期時(shí)間)選項(xiàng)。

優(yōu)點(diǎn):

  • 性能高效,Redis本身為內(nèi)存數(shù)據(jù)庫(kù),操作速度快。

  • 實(shí)現(xiàn)簡(jiǎn)單,通過(guò)幾個(gè)命令即可完成鎖的獲取與釋放。

  • 支持自動(dòng)過(guò)期,降低死鎖風(fēng)險(xiǎn)。

缺點(diǎn)

  • 單點(diǎn)問(wèn)題,依賴單一Redis實(shí)例可能成為瓶頸。

  • 網(wǎng)絡(luò)分區(qū)可能導(dǎo)致鎖的不一致?tīng)顟B(tài)。

示例代碼(偽代碼):

import redis.clients.jedis.Jedis;

public class RedisDistributedLock {

    private Jedis jedis;
    private static final String LOCK_SUCCESS = "OK";
    private static final Long RELEASE_SUCCESS = 1L;

    public RedisDistributedLock(Jedis jedis) {
        this.jedis = jedis;
    }

    public boolean lock(String lockKey, int expireTime) {
        String result = jedis.set(lockKey, "locked", "NX", "PX", expireTime * 1000);
        return LOCK_SUCCESS.equals(result);
    }

    public boolean unlock(String lockKey) {
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        Object result = jedis.eval(script, 1, lockKey, "locked");
        return RELEASE_SUCCESS.equals(result);
    }
}
  • 代碼注解:上例中,lock方法嘗試使用NX(只在鍵不存在時(shí)設(shè)置)和PX(設(shè)置過(guò)期時(shí)間,單位毫秒)參數(shù)設(shè)置鎖,返回OK表示成功獲取鎖。unlock方法使用Lua腳本確保解鎖操作的原子性,只有當(dāng)鎖的持有者與當(dāng)前客戶端匹配時(shí)才執(zhí)行刪除操作。

分布式鎖:基于Zookeeper實(shí)現(xiàn)

在學(xué)習(xí)Zookeeper分布式鎖之前,我們復(fù)習(xí)一下Zookeeper的節(jié)點(diǎn)哈。

Zookeeper的節(jié)點(diǎn)Znode有四種類型:

  • 持久節(jié)點(diǎn):默認(rèn)的節(jié)點(diǎn)類型。創(chuàng)建節(jié)點(diǎn)的客戶端與zookeeper斷開(kāi)連接后,該節(jié)點(diǎn)依舊存在。

  • 持久節(jié)點(diǎn)順序節(jié)點(diǎn):所謂順序節(jié)點(diǎn),就是在創(chuàng)建節(jié)點(diǎn)時(shí),Zookeeper根據(jù)創(chuàng)建的時(shí)間順序給該節(jié)點(diǎn)名稱進(jìn)行編號(hào),持久節(jié)點(diǎn)順序節(jié)點(diǎn)就是有順序的持久節(jié)點(diǎn)。

  • 臨時(shí)節(jié)點(diǎn):和持久節(jié)點(diǎn)相反,當(dāng)創(chuàng)建節(jié)點(diǎn)的客戶端與zookeeper斷開(kāi)連接后,臨時(shí)節(jié)點(diǎn)會(huì)被刪除。

  • 臨時(shí)順序節(jié)點(diǎn):有順序的臨時(shí)節(jié)點(diǎn)。

Zookeeper分布式鎖實(shí)現(xiàn)應(yīng)用了臨時(shí)順序節(jié)點(diǎn)。這里不貼代碼啦,來(lái)講下zk分布式鎖的實(shí)現(xiàn)原理吧。

 zk獲取鎖過(guò)程

當(dāng)?shù)谝粋€(gè)客戶端請(qǐng)求過(guò)來(lái)時(shí),Zookeeper客戶端會(huì)創(chuàng)建一個(gè)持久節(jié)點(diǎn)locks。如果它(Client1)想獲得鎖,需要在locks節(jié)點(diǎn)下創(chuàng)建一個(gè)順序節(jié)點(diǎn)lock1.如圖

接著,客戶端Client1會(huì)查找locks下面的所有臨時(shí)順序子節(jié)點(diǎn),判斷自己的節(jié)點(diǎn)lock1是不是排序最小的那一個(gè),如果是,則成功獲得鎖。

這時(shí)候如果又來(lái)一個(gè)客戶端client2前來(lái)嘗試獲得鎖,它會(huì)在locks下再創(chuàng)建一個(gè)臨時(shí)節(jié)點(diǎn)lock2

客戶端client2一樣也會(huì)查找locks下面的所有臨時(shí)順序子節(jié)點(diǎn),判斷自己的節(jié)點(diǎn)lock2是不是最小的,此時(shí),發(fā)現(xiàn)lock1才是最小的,于是獲取鎖失敗。獲取鎖失敗,它是不會(huì)甘心的,client2向它排序靠前的節(jié)點(diǎn)lock1注冊(cè)Watcher事件,用來(lái)監(jiān)聽(tīng)lock1是否存在,也就是說(shuō)client2搶鎖失敗進(jìn)入等待狀態(tài)。

此時(shí),如果再來(lái)一個(gè)客戶端Client3來(lái)嘗試獲取鎖,它會(huì)在locks下再創(chuàng)建一個(gè)臨時(shí)節(jié)點(diǎn)lock3

同樣的,client3一樣也會(huì)查找locks下面的所有臨時(shí)順序子節(jié)點(diǎn),判斷自己的節(jié)點(diǎn)lock3是不是最小的,發(fā)現(xiàn)自己不是最小的,就獲取鎖失敗。它也是不會(huì)甘心的,它會(huì)向在它前面的節(jié)點(diǎn)lock2注冊(cè)Watcher事件,以監(jiān)聽(tīng)lock2節(jié)點(diǎn)是否存在。

釋放鎖 

我們?cè)賮?lái)看看釋放鎖的流程,Zookeeper的客戶端業(yè)務(wù)完成或者發(fā)生故障,都會(huì)刪除臨時(shí)節(jié)點(diǎn),釋放鎖。如果是任務(wù)完成,Client1會(huì)顯式調(diào)用刪除lock1的指令

如果是客戶端故障了,根據(jù)臨時(shí)節(jié)點(diǎn)得特性,lock1是會(huì)自動(dòng)刪除的

lock1節(jié)點(diǎn)被刪除后,Client2可開(kāi)心了,因?yàn)樗恢北O(jiān)聽(tīng)著lock1。lock1節(jié)點(diǎn)刪除,Client2立刻收到通知,也會(huì)查找locks下面的所有臨時(shí)順序子節(jié)點(diǎn),發(fā)下lock2是最小,就獲得鎖。

同理,Client2獲得鎖之后,Client3也對(duì)它虎視眈眈,啊哈哈~

  • Zookeeper設(shè)計(jì)定位就是分布式協(xié)調(diào),簡(jiǎn)單易用。如果獲取不到鎖,只需添加一個(gè)監(jiān)聽(tīng)器即可,很適合做分布式鎖。

  • Zookeeper作為分布式鎖也缺點(diǎn):如果有很多的客戶端頻繁的申請(qǐng)加鎖、釋放鎖,對(duì)于Zookeeper集群的壓力會(huì)比較大。

到此這篇關(guān)于java實(shí)現(xiàn)分布式鎖的常用三種方式的文章就介紹到這了,更多相關(guān)java 分布式鎖內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Spring?Boot?+?Mybatis?Plus實(shí)現(xiàn)樹(shù)狀菜單的方法

    Spring?Boot?+?Mybatis?Plus實(shí)現(xiàn)樹(shù)狀菜單的方法

    這篇文章主要介紹了Spring?Boot?+?Mybatis?Plus實(shí)現(xiàn)樹(shù)狀菜單,包括實(shí)體類中添加子菜單列表和集合及構(gòu)建菜單樹(shù)的詳細(xì)代碼,代碼簡(jiǎn)單易懂,需要的朋友可以參考下
    2021-12-12
  • 詳解在idea 中使用Mybatis Generator逆向工程生成代碼

    詳解在idea 中使用Mybatis Generator逆向工程生成代碼

    這篇文章主要介紹了在idea 中使用Mybatis Generator逆向工程生成代碼,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-12-12
  • 解決SpringCloud Gateway配置自定義路由404的坑

    解決SpringCloud Gateway配置自定義路由404的坑

    這篇文章主要介紹了解決SpringCloud Gateway配置自定義路由404的坑,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • 關(guān)于spring data jpa一級(jí)緩存的問(wèn)題

    關(guān)于spring data jpa一級(jí)緩存的問(wèn)題

    這篇文章主要介紹了關(guān)于spring data jpa一級(jí)緩存的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-11-11
  • Java中八大包裝類舉例詳解(通俗易懂)

    Java中八大包裝類舉例詳解(通俗易懂)

    這篇文章主要介紹了Java中的包裝類,包括它們的作用、特點(diǎn)、用途以及如何進(jìn)行裝箱和拆箱,包裝類還提供了許多實(shí)用方法,如轉(zhuǎn)換、獲取基本類型值、比較和類型檢測(cè),文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2025-02-02
  • Java實(shí)用工具之StringJoiner詳解

    Java實(shí)用工具之StringJoiner詳解

    這篇文章主要介紹了Java實(shí)用工具之StringJoiner詳解,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java的小伙伴們有很好地幫助,需要的朋友可以參考下
    2021-05-05
  • Javafx實(shí)現(xiàn)國(guó)際象棋游戲

    Javafx實(shí)現(xiàn)國(guó)際象棋游戲

    這篇文章主要為大家詳細(xì)介紹了Javafx實(shí)現(xiàn)國(guó)際象棋游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-05-05
  • JDK的一個(gè)Bug監(jiān)聽(tīng)文件變更的初步實(shí)現(xiàn)思路

    JDK的一個(gè)Bug監(jiān)聽(tīng)文件變更的初步實(shí)現(xiàn)思路

    這篇文章主要介紹了JDK的一個(gè)Bug監(jiān)聽(tīng)文件變更要小心了,本篇文章就帶大家簡(jiǎn)單實(shí)現(xiàn)一個(gè)對(duì)應(yīng)的功能,并分析一下對(duì)應(yīng)的Bug和優(yōu)缺點(diǎn),需要的朋友可以參考下
    2022-05-05
  • 詳解maven BUILD FAILURE的解決辦法

    詳解maven BUILD FAILURE的解決辦法

    這篇文章主要介紹了詳解maven BUILD FAILURE的解決辦法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03
  • java更改圖片大小示例分享

    java更改圖片大小示例分享

    這篇文章主要介紹了java更改圖片大小示例,方法中指定路徑 ,舊文件名稱 ,新文件名稱,n 改變倍數(shù)就可以完成更改圖片大小,需要的朋友可以參考下
    2014-03-03

最新評(píng)論