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

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

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

分布式鎖概述

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

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

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

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

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

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

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

主要有兩種方式:

1、悲觀鎖

2、樂觀鎖

A. 悲觀鎖(排他鎖)

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

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

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

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

實現(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ù)時間,自動生成',
  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í)行成功,會立即返回,執(zhí)行插入數(shù)據(jù)庫,后續(xù)再執(zhí)行一些其他業(yè)務(wù)邏輯,直到事務(wù)提交,執(zhí)行結(jié)束;如果執(zhí)行失敗,就會一直阻塞著。

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

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

優(yōu)點:

簡單易用,好理解,保障數(shù)據(jù)強一致性。

缺點:

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

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

當(dāng)獲得鎖的客戶端等待時間過長時,會提示:

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

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

3)如果優(yōu)先獲得鎖的線程因為某些原因,一直沒有釋放掉鎖,可能會導(dǎo)致死鎖的發(fā)生。

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

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

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

B. 樂觀鎖 

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

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

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

實現(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 '版本號',
  `desc` varchar(1024) NOT NULL DEFAULT '備注信息',
  `update_time` timestamp NOT NULL DEFAULT '' COMMENT '保存數(shù)據(jù)時間,自動生成',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uidx_resource_name` (`resource_name `) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='資源';

為表添加一個字段,版本號或者時間戳都可以。通過版本號或者時間戳,來保證多線程同時間操作共享資源的有序性和正確性。

偽代碼實現(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ā)起重試
}

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

優(yōu)點:

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

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

缺點:

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

可靠性差

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

長時間不commit或者是長時間輪詢,可能會占用較多的連接資源

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

原理與實現(xiàn)

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

優(yōu)點:

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

  • 實現(xiàn)簡單,通過幾個命令即可完成鎖的獲取與釋放。

  • 支持自動過期,降低死鎖風(fēng)險。

缺點

  • 單點問題,依賴單一Redis實例可能成為瓶頸。

  • 網(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è)置)和PX(設(shè)置過期時間,單位毫秒)參數(shù)設(shè)置鎖,返回OK表示成功獲取鎖。unlock方法使用Lua腳本確保解鎖操作的原子性,只有當(dāng)鎖的持有者與當(dāng)前客戶端匹配時才執(zhí)行刪除操作。

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

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

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

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

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

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

  • 臨時順序節(jié)點:有順序的臨時節(jié)點。

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

 zk獲取鎖過程

當(dāng)?shù)谝粋€客戶端請求過來時,Zookeeper客戶端會創(chuàng)建一個持久節(jié)點locks。如果它(Client1)想獲得鎖,需要在locks節(jié)點下創(chuàng)建一個順序節(jié)點lock1.如圖

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

這時候如果又來一個客戶端client2前來嘗試獲得鎖,它會在locks下再創(chuàng)建一個臨時節(jié)點lock2

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

此時,如果再來一個客戶端Client3來嘗試獲取鎖,它會在locks下再創(chuàng)建一個臨時節(jié)點lock3

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

釋放鎖 

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

如果是客戶端故障了,根據(jù)臨時節(jié)點得特性,lock1是會自動刪除的

lock1節(jié)點被刪除后,Client2可開心了,因為它一直監(jiān)聽著lock1。lock1節(jié)點刪除,Client2立刻收到通知,也會查找locks下面的所有臨時順序子節(jié)點,發(fā)下lock2是最小,就獲得鎖。

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

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

  • Zookeeper作為分布式鎖也缺點:如果有很多的客戶端頻繁的申請加鎖、釋放鎖,對于Zookeeper集群的壓力會比較大。

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

相關(guān)文章

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

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

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

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

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

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

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

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

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

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

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

    Java實用工具之StringJoiner詳解

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

    Javafx實現(xiàn)國際象棋游戲

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

    JDK的一個Bug監(jiān)聽文件變更的初步實現(xiàn)思路

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

    詳解maven BUILD FAILURE的解決辦法

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

    java更改圖片大小示例分享

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

最新評論