溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

分布式鎖介紹

發(fā)布時(shí)間:2020-05-26 16:02:04 來(lái)源:億速云 閱讀:270 作者:鴿子 欄目:編程語(yǔ)言

1. 什么是分布式鎖

分布式鎖是控制分布式系統(tǒng)之間同步訪問(wèn)共享資源的一種方式。在分布式系統(tǒng)中,常常需要協(xié)調(diào)他們的動(dòng)作。如果不同的系統(tǒng)或是同一個(gè)系統(tǒng)的不同主機(jī)之間共享了一個(gè)或一組資源,那么訪問(wèn)這些資源的時(shí)候,往往需要互斥來(lái)防止彼此干擾來(lái)保證一致性,在這種情況下,便需要使用到分布式鎖。

2. 為什么要使用分布式鎖

為了保證一個(gè)方法或?qū)傩栽诟卟l(fā)情況下的同一時(shí)間只能被同一個(gè)線程執(zhí)行,在傳統(tǒng)單體應(yīng)用單機(jī)部署的情況下,可以使用Java并發(fā)處理相關(guān)的API(如ReentrantLock或Synchronized)進(jìn)行互斥控制。在單機(jī)環(huán)境中,Java中提供了很多并發(fā)處理相關(guān)的API。但是,隨著業(yè)務(wù)發(fā)展的需要,原單體單機(jī)部署的系統(tǒng)被演化成分布式集群系統(tǒng)后,由于分布式系統(tǒng)多線程、多進(jìn)程并且分布在不同機(jī)器上,這將使原單機(jī)部署情況下的并發(fā)控制鎖策略失效,單純的Java API并不能提供分布式鎖的能力。為了解決這個(gè)問(wèn)題就需要一種跨JVM的互斥機(jī)制來(lái)控制共享資源的訪問(wèn),這就是分布式鎖要解決的問(wèn)題!

舉個(gè)例子:

機(jī)器A , 機(jī)器B是一個(gè)集群, A, B兩臺(tái)機(jī)器上的程序都是一樣的, 具備高可用性能.

A, B機(jī)器都有一個(gè)定時(shí)任務(wù), 每天晚上凌晨2點(diǎn)需要執(zhí)行一個(gè)定時(shí)任務(wù), 但是這個(gè)定時(shí)任務(wù)只能執(zhí)行一遍, 否則的話就會(huì)報(bào)錯(cuò), 那A,B兩臺(tái)機(jī)器在執(zhí)行的時(shí)候, 就需要搶鎖, 誰(shuí)搶到鎖, 誰(shuí)執(zhí)行, 誰(shuí)搶不到, 就不用執(zhí)行了!

3. 鎖的處理

  • 單個(gè)應(yīng)用中使用鎖: (單進(jìn)程多線程)

synchronize
分布式鎖控制分布式系統(tǒng)之間同步訪問(wèn)資源的一種方式

分布式鎖是控制分布式系統(tǒng)之間同步同問(wèn)共享資源的一種方式

4. 分布式鎖的實(shí)現(xiàn)

  • 基于數(shù)據(jù)的樂(lè)觀鎖實(shí)現(xiàn)分布式鎖
  • 基于zookeeper臨時(shí)節(jié)點(diǎn)的分布式鎖
  • 基于redis的分布式鎖

5. redis的分布式鎖

  • 獲取鎖:

在set命令中, 有很多選項(xiàng)可以用來(lái)修改命令的行為, 一下是set命令可用選項(xiàng)的基本語(yǔ)法

redis 127.0.0.1:6379>SET KEY VALUE [EX seconds] [PX milliseconds] [NX|XX]

    - EX seconds  設(shè)置指定的到期時(shí)間(單位為秒)
    - PX milliseconds 設(shè)置指定的到期時(shí)間(單位毫秒)
    - NX: 僅在鍵不存在時(shí)設(shè)置鍵
    - XX: 只有在鍵已存在時(shí)設(shè)置

方式1: 推介

    private static final String LOCK_SUCCESS = "OK";
    private static final String SET_IF_NOT_EXIST = "NX";
    private static final String SET_WITH_EXPIRE_TIME = "PX";

        public static boolean getLock(JedisCluster jedisCluster, String lockKey, String requestId, int expireTime) {
        // NX: 保證互斥性
        String result = jedisCluster.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
        if (LOCK_SUCCESS.equals(result)) {
            return true;
        }
        return false;
    }

方式2:

public static boolean getLock(String lockKey,String requestId,int expireTime) {
     Long result = jedis.setnx(lockKey, requestId);
     if(result == 1) {
         jedis.expire(lockKey, expireTime);
         return true;
     }
     return false;
 }

注意: 推介方式1, 因?yàn)榉绞?中setnx和expire是兩個(gè)操作, 并不是一個(gè)原子操作, 如果setnx出現(xiàn)問(wèn)題, 就是出現(xiàn)死鎖的情況, 所以推薦方式1

  • 釋放鎖:

方式1: del命令實(shí)現(xiàn)

public static void releaseLock(String lockKey,String requestId) {
    if (requestId.equals(jedis.get(lockKey))) {
        jedis.del(lockKey);
    }
}

方式2: redis+lua腳本實(shí)現(xiàn)  推薦

public static boolean releaseLock(String lockKey, String requestId) {
        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, Collections.singletonList(lockKey),
Collections.singletonList(requestId));
        if (result.equals(1L)) {
            return true;
}
        return false;
    }

6. zookeeper的分布式鎖

6.1 zookeeper實(shí)現(xiàn)分布式鎖的原理

理解了鎖的原理后,就會(huì)發(fā)現(xiàn),Zookeeper 天生就是一副分布式鎖的胚子。

首先,Zookeeper的每一個(gè)節(jié)點(diǎn),都是一個(gè)天然的順序發(fā)號(hào)器。

在每一個(gè)節(jié)點(diǎn)下面創(chuàng)建子節(jié)點(diǎn)時(shí),只要選擇的創(chuàng)建類型是有序(EPHEMERAL_SEQUENTIAL 臨時(shí)有序或者PERSISTENT_SEQUENTIAL 永久有序)類型,那么,新的子節(jié)點(diǎn)后面,會(huì)加上一個(gè)次序編號(hào)。這個(gè)次序編號(hào),是上一個(gè)生成的次序編號(hào)加一

比如,創(chuàng)建一個(gè)用于發(fā)號(hào)的節(jié)點(diǎn)“/test/lock”,然后以他為父親節(jié)點(diǎn),可以在這個(gè)父節(jié)點(diǎn)下面創(chuàng)建相同前綴的子節(jié)點(diǎn),假定相同的前綴為“/test/lock/seq-”,在創(chuàng)建子節(jié)點(diǎn)時(shí),同時(shí)指明是有序類型。如果是第一個(gè)創(chuàng)建的子節(jié)點(diǎn),那么生成的子節(jié)點(diǎn)為/test/lock/seq-0000000000,下一個(gè)節(jié)點(diǎn)則為/test/lock/seq-0000000001,依次類推,等等。

分布式鎖介紹

其次,Zookeeper節(jié)點(diǎn)的遞增性,可以規(guī)定節(jié)點(diǎn)編號(hào)最小的那個(gè)獲得鎖。

一個(gè)zookeeper分布式鎖,首先需要?jiǎng)?chuàng)建一個(gè)父節(jié)點(diǎn),盡量是持久節(jié)點(diǎn)(PERSISTENT類型),然后每個(gè)要獲得鎖的線程都會(huì)在這個(gè)節(jié)點(diǎn)下創(chuàng)建個(gè)臨時(shí)順序節(jié)點(diǎn),由于序號(hào)的遞增性,可以規(guī)定排號(hào)最小的那個(gè)獲得鎖。所以,每個(gè)線程在嘗試占用鎖之前,首先判斷自己是排號(hào)是不是當(dāng)前最小,如果是,則獲取鎖。

第三,Zookeeper的節(jié)點(diǎn)監(jiān)聽(tīng)機(jī)制,可以保障占有鎖的方式有序而且高效。

每個(gè)線程搶占鎖之前,先搶號(hào)創(chuàng)建自己的ZNode。同樣,釋放鎖的時(shí)候,就需要?jiǎng)h除搶號(hào)的Znode。搶號(hào)成功后,如果不是排號(hào)最小的節(jié)點(diǎn),就處于等待通知的狀態(tài)。等誰(shuí)的通知呢?不需要其他人,只需要等前一個(gè)Znode 的通知就可以了。當(dāng)前一個(gè)Znode 刪除的時(shí)候,就是輪到了自己占有鎖的時(shí)候。第一個(gè)通知第二個(gè)、第二個(gè)通知第三個(gè),擊鼓傳花似的依次向后。

Zookeeper的節(jié)點(diǎn)監(jiān)聽(tīng)機(jī)制,可以說(shuō)能夠非常完美的,實(shí)現(xiàn)這種擊鼓傳花似的信息傳遞。具體的方法是,每一個(gè)等通知的Znode節(jié)點(diǎn),只需要監(jiān)聽(tīng)linsten或者 watch 監(jiān)視排號(hào)在自己前面那個(gè),而且緊挨在自己前面的那個(gè)節(jié)點(diǎn)。 只要上一個(gè)節(jié)點(diǎn)被刪除了,就進(jìn)行再一次判斷,看看自己是不是序號(hào)最小的那個(gè)節(jié)點(diǎn),如果是,則獲得鎖。

為什么說(shuō)Zookeeper的節(jié)點(diǎn)監(jiān)聽(tīng)機(jī)制,可以說(shuō)是非常完美呢?

一條龍式的首尾相接,后面監(jiān)視前面,就不怕中間截?cái)鄦幔勘热?,在分布式環(huán)境下,由于網(wǎng)絡(luò)的原因,或者服務(wù)器掛了或則其他的原因,如果前面的那個(gè)節(jié)點(diǎn)沒(méi)能被程序刪除成功,后面的節(jié)點(diǎn)不就永遠(yuǎn)等待么?

其實(shí),Zookeeper的內(nèi)部機(jī)制,能保證后面的節(jié)點(diǎn)能夠正常的監(jiān)聽(tīng)到刪除和獲得鎖。在創(chuàng)建取號(hào)節(jié)點(diǎn)的時(shí)候,盡量創(chuàng)建臨時(shí)znode 節(jié)點(diǎn)而不是永久znode 節(jié)點(diǎn),一旦這個(gè) znode 的客戶端與Zookeeper集群服務(wù)器失去聯(lián)系,這個(gè)臨時(shí) znode 也將自動(dòng)刪除。排在它后面的那個(gè)節(jié)點(diǎn),也能收到刪除事件,從而獲得鎖。

說(shuō)Zookeeper的節(jié)點(diǎn)監(jiān)聽(tīng)機(jī)制,是非常完美的。還有一個(gè)原因。

Zookeeper這種首尾相接,后面監(jiān)聽(tīng)前面的方式,可以避免羊群效應(yīng)。所謂羊群效應(yīng)就是每個(gè)節(jié)點(diǎn)掛掉,所有節(jié)點(diǎn)都去監(jiān)聽(tīng),然后做出反映,這樣會(huì)給服務(wù)器帶來(lái)巨大壓力,所以有了臨時(shí)順序節(jié)點(diǎn),當(dāng)一個(gè)節(jié)點(diǎn)掛掉,只有它后面的那一個(gè)節(jié)點(diǎn)才做出反映。

###6.2 zookeeper實(shí)現(xiàn)分布式鎖的示例

zookeeper是通過(guò)臨時(shí)節(jié)點(diǎn)來(lái)實(shí)現(xiàn)分布式鎖.

import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.junit.Before;
import org.junit.Test;

/**
 * @ClassName ZookeeperLock
 * @Description TODO
 * @Author lingxiangxiang
 * @Date 2:57 PM
 * @Version 1.0
 **/
public class ZookeeperLock {
    // 定義共享資源
    private static int NUMBER = 10;

    private static void printNumber() {
        // 業(yè)務(wù)邏輯: 秒殺
        System.out.println("*********業(yè)務(wù)方法開(kāi)始************\n");
        System.out.println("當(dāng)前的值: " + NUMBER);
        NUMBER--;
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("*********業(yè)務(wù)方法結(jié)束************\n");

    }

    // 這里使用@Test會(huì)報(bào)錯(cuò)
    public static void main(String[] args) {
        // 定義重試的側(cè)策略 1000 等待的時(shí)間(毫秒) 10 重試的次數(shù)
        RetryPolicy policy = new ExponentialBackoffRetry(1000, 10);

        // 定義zookeeper的客戶端
        CuratorFramework client = CuratorFrameworkFactory.builder()
                .connectString("10.231.128.95:2181,10.231.128.96:2181,10.231.128.97:2181")
                .retryPolicy(policy)
                .build();
        // 啟動(dòng)客戶端
        client.start();

        // 在zookeeper中定義一把鎖
        final InterProcessMutex lock = new InterProcessMutex(client, "/mylock");

        //啟動(dòng)是個(gè)線程
        for (int i = 0; i <10; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        // 請(qǐng)求得到的鎖
                        lock.acquire();
                        printNumber();
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                        // 釋放鎖, 還鎖
                        try {
                            lock.release();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }).start();
        }

    }
}

7. 基于數(shù)據(jù)的分布式鎖

我們?cè)谟懻撌褂梅植际芥i的時(shí)候往往首先排除掉基于數(shù)據(jù)庫(kù)的方案,本能的會(huì)覺(jué)得這個(gè)方案不夠“高級(jí)”。從性能的角度考慮,基于數(shù)據(jù)庫(kù)的方案性能確實(shí)不夠優(yōu)異,整體性能對(duì)比:緩存 > Zookeeper、etcd > 數(shù)據(jù)庫(kù)。也有人提出基于數(shù)據(jù)庫(kù)的方案問(wèn)題很多,不太可靠。數(shù)據(jù)庫(kù)的方案可能并不適合于頻繁寫(xiě)入的操作.

下面我們來(lái)了解一下基于數(shù)據(jù)庫(kù)(MySQL)的方案,一般分為3類:基于表記錄、樂(lè)觀鎖和悲觀鎖。

7.1 基于表記錄

要實(shí)現(xiàn)分布式鎖,最簡(jiǎn)單的方式可能就是直接創(chuàng)建一張鎖表,然后通過(guò)操作該表中的數(shù)據(jù)來(lái)實(shí)現(xiàn)了。當(dāng)我們想要獲得鎖的時(shí)候,就可以在該表中增加一條記錄,想要釋放鎖的時(shí)候就刪除這條記錄。

為了更好的演示,我們先創(chuàng)建一張數(shù)據(jù)庫(kù)表,參考如下:

CREATE TABLE `database_lock` (
    `id` BIGINT NOT NULL AUTO_INCREMENT,
    `resource` int NOT NULL COMMENT '鎖定的資源',
    `description` varchar(1024) NOT NULL DEFAULT "" COMMENT '描述',
    PRIMARY KEY (`id`),
    UNIQUE KEY `uiq_idx_resource` (`resource`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='數(shù)據(jù)庫(kù)分布式鎖表';
  • 獲得鎖

我們可以插入一條數(shù)據(jù):

INSERT INTO database_lock(resource, description) VALUES (1, 'lock');

因?yàn)楸韉atabase_lock中resource是唯一索引, 所以其他請(qǐng)求提交到數(shù)據(jù)庫(kù), 就會(huì)報(bào)錯(cuò), 并不會(huì)插入成功, 只有一個(gè)可以插入. 插入成功, 我們就獲取到鎖

  • 刪除鎖
INSERT INTO database_lock(resource, description) VALUES (1, 'lock');

這種實(shí)現(xiàn)方式非常的簡(jiǎn)單,但是需要注意以下幾點(diǎn):

這種鎖沒(méi)有失效時(shí)間,一旦釋放鎖的操作失敗就會(huì)導(dǎo)致鎖記錄一直在數(shù)據(jù)庫(kù)中,其它線程無(wú)法獲得鎖。這個(gè)缺陷也很好解決,比如可以做一個(gè)定時(shí)任務(wù)去定時(shí)清理。
這種鎖的可靠性依賴于數(shù)據(jù)庫(kù)。建議設(shè)置備庫(kù),避免單點(diǎn),進(jìn)一步提高可靠性。
這種鎖是非阻塞的,因?yàn)椴迦霐?shù)據(jù)失敗之后會(huì)直接報(bào)錯(cuò),想要獲得鎖就需要再次操作。如果需要阻塞式的,可以弄個(gè)for循環(huán)、while循環(huán)之類的,直至INSERT成功再返回。
這種鎖也是非可重入的,因?yàn)橥粋€(gè)線程在沒(méi)有釋放鎖之前無(wú)法再次獲得鎖,因?yàn)閿?shù)據(jù)庫(kù)中已經(jīng)存在同一份記錄了。想要實(shí)現(xiàn)可重入鎖,可以在數(shù)據(jù)庫(kù)中添加一些字段,比如獲得鎖的主機(jī)信息、線程信息等,那么在再次獲得鎖的時(shí)候可以先查詢數(shù)據(jù),如果當(dāng)前的主機(jī)信息和線程信息等能被查到的話,可以直接把鎖分配給它。

7.2 樂(lè)觀鎖

顧名思義,系統(tǒng)認(rèn)為數(shù)據(jù)的更新在大多數(shù)情況下是不會(huì)產(chǎn)生沖突的,只在數(shù)據(jù)庫(kù)更新操作提交的時(shí)候才對(duì)數(shù)據(jù)作沖突檢測(cè)。如果檢測(cè)的結(jié)果出現(xiàn)了與預(yù)期數(shù)據(jù)不一致的情況,則返回失敗信息。

分布式鎖介紹

樂(lè)觀鎖大多數(shù)是基于數(shù)據(jù)版本(version)的記錄機(jī)制實(shí)現(xiàn)的。何謂數(shù)據(jù)版本號(hào)?即為數(shù)據(jù)增加一個(gè)版本標(biāo)識(shí),在基于數(shù)據(jù)庫(kù)表的版本解決方案中,一般是通過(guò)為數(shù)據(jù)庫(kù)表添加一個(gè) “version”字段來(lái)實(shí)現(xiàn)讀取出數(shù)據(jù)時(shí),將此版本號(hào)一同讀出,之后更新時(shí),對(duì)此版本號(hào)加1。在更新過(guò)程中,會(huì)對(duì)版本號(hào)進(jìn)行比較,如果是一致的,沒(méi)有發(fā)生改變,則會(huì)成功執(zhí)行本次操作;如果版本號(hào)不一致,則會(huì)更新失敗。

為了更好的理解數(shù)據(jù)庫(kù)樂(lè)觀鎖在實(shí)際項(xiàng)目中的使用,這里也就舉了業(yè)界老生常談的庫(kù)存例子。一個(gè)電商平臺(tái)都會(huì)存在商品的庫(kù)存,當(dāng)用戶進(jìn)行購(gòu)買(mǎi)的時(shí)候就會(huì)對(duì)庫(kù)存進(jìn)行操作(庫(kù)存減1代表已經(jīng)賣(mài)出了一件)。如果只是一個(gè)用戶進(jìn)行操作數(shù)據(jù)庫(kù)本身就能保證用戶操作的正確性,而在并發(fā)的情況下就會(huì)產(chǎn)生一些意想不到的問(wèn)題:
??比如兩個(gè)用戶同時(shí)購(gòu)買(mǎi)一件商品,在數(shù)據(jù)庫(kù)層面實(shí)際操作應(yīng)該是庫(kù)存進(jìn)行減2操作,但是由于高并發(fā)的情況,第一個(gè)用戶購(gòu)買(mǎi)完成進(jìn)行數(shù)據(jù)讀取當(dāng)前庫(kù)存并進(jìn)行減1操作,由于這個(gè)操作沒(méi)有完全執(zhí)行完成。第二個(gè)用戶就進(jìn)入購(gòu)買(mǎi)相同商品,此時(shí)查詢出的庫(kù)存可能是未減1操作的庫(kù)存導(dǎo)致了臟數(shù)據(jù)的出現(xiàn)【線程不安全操作】,通常如果是單JVM情況下使用JAVA內(nèi)置的鎖就能保證線程安全,如果在多JVM的情況下,使用分布式鎖也能實(shí)現(xiàn)【后期會(huì)補(bǔ)】,而本篇著重的去講數(shù)據(jù)庫(kù)層面的。
針對(duì)上面的問(wèn)題,數(shù)據(jù)庫(kù)樂(lè)觀鎖也能保證線程安全,通常哎代碼層面我們都會(huì)這樣做:

select goods_num from goods where goods_name = "小本子";
update goods set goods_num = goods_num -1 where goods_name = "小本子";

上面的SQL是一組的,通常先查詢出當(dāng)前的goods_num,然后再goods_num上進(jìn)行減1的操作修改庫(kù)存,當(dāng)并發(fā)的情況下,這條語(yǔ)句可能導(dǎo)致原本庫(kù)存為3的一個(gè)商品經(jīng)過(guò)兩個(gè)人購(gòu)買(mǎi)還剩下2庫(kù)存的情況就會(huì)導(dǎo)致商品的多賣(mài)。那么數(shù)據(jù)庫(kù)樂(lè)觀鎖是如何實(shí)現(xiàn)的呢?
首先定義一個(gè)version字段用來(lái)當(dāng)作一個(gè)版本號(hào),每次的操作就會(huì)變成這樣:

select goods_num,version from goods where goods_name = "小本子";
update goods set goods_num = goods_num -1,version =查詢的version值自增 where goods_name ="小本子" and version=查詢出來(lái)的version;

其實(shí),借助更新時(shí)間戳(updated_at)也可以實(shí)現(xiàn)樂(lè)觀鎖,和采用version字段的方式相似:更新操作執(zhí)行前線獲取記錄當(dāng)前的更新時(shí)間,在提交更新時(shí),檢測(cè)當(dāng)前更新時(shí)間是否與更新開(kāi)始時(shí)獲取的更新時(shí)間戳相等。

7.3 悲觀鎖

除了可以通過(guò)增刪操作數(shù)據(jù)庫(kù)表中的記錄以外,我們還可以借助數(shù)據(jù)庫(kù)中自帶的鎖來(lái)實(shí)現(xiàn)分布式鎖。在查詢語(yǔ)句后面增加FOR UPDATE,數(shù)據(jù)庫(kù)會(huì)在查詢過(guò)程中給數(shù)據(jù)庫(kù)表增加悲觀鎖,也稱排他鎖。當(dāng)某條記錄被加上悲觀鎖之后,其它線程也就無(wú)法再改行上增加悲觀鎖。

悲觀鎖,與樂(lè)觀鎖相反,總是假設(shè)最壞的情況,它認(rèn)為數(shù)據(jù)的更新在大多數(shù)情況下是會(huì)產(chǎn)生沖突的。

在使用悲觀鎖的同時(shí),我們需要注意一下鎖的級(jí)別。MySQL InnoDB引起在加鎖的時(shí)候,只有明確地指定主鍵(或索引)的才會(huì)執(zhí)行行鎖 (只鎖住被選取的數(shù)據(jù)),否則MySQL 將會(huì)執(zhí)行表鎖(將整個(gè)數(shù)據(jù)表單給鎖住)。

在使用悲觀鎖時(shí),我們必須關(guān)閉MySQL數(shù)據(jù)庫(kù)的自動(dòng)提交屬性(參考下面的示例),因?yàn)镸ySQL默認(rèn)使用autocommit模式,也就是說(shuō),當(dāng)你執(zhí)行一個(gè)更新操作后,MySQL會(huì)立刻將結(jié)果進(jìn)行提交。

mysql> SET AUTOCOMMIT = 0;
Query OK, 0 rows affected (0.00 sec)

這樣在使用FOR UPDATE獲得鎖之后可以執(zhí)行相應(yīng)的業(yè)務(wù)邏輯,執(zhí)行完之后再使用COMMIT來(lái)釋放鎖。

我們不妨沿用前面的database_lock表來(lái)具體表述一下用法。假設(shè)有一線程A需要獲得鎖并執(zhí)行相應(yīng)的操作,那么它的具體步驟如下:

STEP1 - 獲取鎖:SELECT * FROM database_lock WHERE id = 1 FOR UPDATE;。
STEP2 - 執(zhí)行業(yè)務(wù)邏輯。
STEP3 - 釋放鎖:COMMIT

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI