最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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)都是分布式部署的,日常開發(fā)中,秒殺下單、搶購(gòu)商品等等業(yè)務(wù)場(chǎng)景,為了防?庫(kù)存超賣,都需要用到分布式鎖。

分布式鎖其實(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失敗后才能覺察到。

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

實(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í)際代碼中可以寫個(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à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斷開連接后,該節(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斷開連接后,臨時(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)聽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)聽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可開心了,因?yàn)樗恢北O(jiān)聽著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)聽器即可,很適合做分布式鎖。

  • 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)文章

  • SpringBoot實(shí)現(xiàn)日志文件分隔(根據(jù)日期和文件大小)

    SpringBoot實(shí)現(xiàn)日志文件分隔(根據(jù)日期和文件大小)

    文章簡(jiǎn)要介紹了如何在Spring Boot項(xiàng)目中配置日志文件路徑,包括在resource目錄下創(chuàng)建logback-spring.xml文件,并在yml配置文件中設(shè)置logging.file.path屬性來(lái)指定日志文件的輸出路徑
    2026-01-01
  • 基于MyBatis的簡(jiǎn)單使用(推薦)

    基于MyBatis的簡(jiǎn)單使用(推薦)

    下面小編就為大家?guī)?lái)一篇基于MyBatis的簡(jiǎn)單使用(推薦)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-10-10
  • java基于JSON實(shí)現(xiàn)前后端交互(附代碼)

    java基于JSON實(shí)現(xiàn)前后端交互(附代碼)

    本文主要介紹了java基于JSON實(shí)現(xiàn)前后端交互,通過(guò)實(shí)際代碼示例展示了如何前后端JSON交互,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-01-01
  • SpringBoot對(duì)接Spark過(guò)程詳解

    SpringBoot對(duì)接Spark過(guò)程詳解

    這篇文章主要介紹SpringBoot接入Spark的方法的相關(guān)知識(shí),小編通過(guò)實(shí)際案例向大家展示操作過(guò)程,操作方法簡(jiǎn)單快捷,實(shí)用性強(qiáng),希望能幫助大家解決問(wèn)題
    2023-02-02
  • IDEA中調(diào)用方法時(shí),如何同步顯示方法的注釋信息

    IDEA中調(diào)用方法時(shí),如何同步顯示方法的注釋信息

    這篇文章主要介紹了IDEA中調(diào)用方法時(shí),如何同步顯示方法的注釋信息問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2025-03-03
  • des加密解密JAVA與.NET互通實(shí)例

    des加密解密JAVA與.NET互通實(shí)例

    這篇文章主要介紹了des加密解密JAVA與.NET互通實(shí)例,大家參考使用吧
    2013-12-12
  • SpringBoot訪問(wèn)web中的靜態(tài)資源的方式小結(jié)

    SpringBoot訪問(wèn)web中的靜態(tài)資源的方式小結(jié)

    這篇文章主要介紹了SpringBoot訪問(wèn)web中的靜態(tài)資源的方式,本文給大家介紹了兩種方式,通過(guò)代碼示例和圖文講解的非常詳細(xì),具有一定的參考價(jià)值,需要的朋友可以參考下
    2024-10-10
  • SpringBoot導(dǎo)出Excel的四種實(shí)現(xiàn)方式

    SpringBoot導(dǎo)出Excel的四種實(shí)現(xiàn)方式

    近期接到了一個(gè)小需求,要將系統(tǒng)中的數(shù)據(jù)導(dǎo)出為Excel,且能將Excel數(shù)據(jù)導(dǎo)入到系統(tǒng),對(duì)于大多數(shù)研發(fā)人員來(lái)說(shuō),這算是一個(gè)最基本的操作了,本文就給大家總結(jié)一下SpringBoot導(dǎo)出Excel的四種實(shí)現(xiàn)方式,需要的朋友可以參考下
    2024-01-01
  • SpringBoot的ResponseEntity類返回給前端具體講解

    SpringBoot的ResponseEntity類返回給前端具體講解

    這篇文章主要給大家介紹了關(guān)于SpringBoot的ResponseEntity類返回給前端的相關(guān)資料,ResponseEntity是Spring框架中用于封裝HTTP響應(yīng)的類,可以自定義狀態(tài)碼、響應(yīng)頭和響應(yīng)體,常用于控制器方法中返回特定數(shù)據(jù)的HTTP響應(yīng),需要的朋友可以參考下
    2024-11-11
  • MyBatis使用注解開發(fā)實(shí)現(xiàn)步驟解析

    MyBatis使用注解開發(fā)實(shí)現(xiàn)步驟解析

    這篇文章主要介紹了MyBatis使用注解開發(fā)實(shí)現(xiàn)步驟解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-08-08

最新評(píng)論

枞阳县| 察雅县| 桓台县| 梨树县| 玛多县| 武穴市| 罗定市| 天台县| 连云港市| 西藏| 天峻县| 托里县| 连云港市| 瑞金市| 济阳县| 池州市| 大洼县| 巨鹿县| 南丹县| 泸西县| 柘城县| 双桥区| 乐至县| 六安市| 石楼县| 临潭县| 疏附县| 宜黄县| 乐业县| 山东省| 永年县| 鸡东县| 龙里县| 黑山县| 徐汇区| 通州区| 新乐市| 龙井市| 宁远县| 陇西县| 佛山市|