自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

面試官必問的分布式鎖面試題,你答得上來嗎?

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

一、面試聊聊-分布式鎖,如何回答?

要分析分布式鎖這個(gè)問題,我們根據(jù)黃金圈法則來分析。

黃金圈法則是由美國(guó)營(yíng)銷顧問西蒙·斯涅克(Simon Sinek)提出的一種思維模型,用于幫助人們更好地理解和傳達(dá)信息。黃金圈法則由三個(gè)圈組成,分別是:

  • 為什么(Why):為什么要做這件事?這是黃金圈的核心,是一切的起點(diǎn)。
  • 怎么做(How):怎么做這件事?這是黃金圈的中間部分,是實(shí)現(xiàn)目標(biāo)的方法。
  • 做什么(What):做什么這件事?這是黃金圈的外圍部分,是具體的行為。

使用3w分析問題思路來分析分布式鎖,可以從以下幾個(gè)方面進(jìn)行分析:

What:分布式鎖是什么?

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

回答時(shí):先說一下概念。分布式鎖是用于在分布式系統(tǒng)中控制對(duì)共享資源的訪問,以避免數(shù)據(jù)競(jìng)爭(zhēng)和并發(fā)問題。

How:如何實(shí)現(xiàn)分布式鎖?

分布式鎖的實(shí)現(xiàn)方法有很多,常見的有以下幾種:

  • 數(shù)據(jù)庫(kù)鎖:使用數(shù)據(jù)庫(kù)中的行鎖或表鎖來實(shí)現(xiàn)分布式鎖。
  • 文件鎖:使用文件來實(shí)現(xiàn)分布式鎖。
  • Zookeeper鎖:使用Zookeeper來實(shí)現(xiàn)分布式鎖。
  • Redis鎖:使用Redis來實(shí)現(xiàn)分布式鎖。
  • 消息隊(duì)列鎖:使用消息隊(duì)列來實(shí)現(xiàn)分布式鎖。

回答時(shí):說一下分布式鎖以上的實(shí)現(xiàn)方式。

Why:為什么需要分布式鎖?

一些需要分布式鎖的場(chǎng)景:

  • 分布式數(shù)據(jù)庫(kù)事務(wù):在分布式系統(tǒng)中,通常需要使用分布式事務(wù)來保證數(shù)據(jù)的一致性。在分布式事務(wù)中,通常會(huì)使用分布式鎖來保證事務(wù)的執(zhí)行順序。
  • 分布式資源分配:在分布式系統(tǒng)中,通常需要使用分布式鎖來分配共享資源。例如,在搶購(gòu)場(chǎng)景中,需要使用分布式鎖來保證同一商品只能被一個(gè)用戶購(gòu)買。
  • 分布式數(shù)據(jù)同步:在分布式系統(tǒng)中,通常需要使用分布式鎖來保證數(shù)據(jù)的同步。例如,在訂單系統(tǒng)中,需要使用分布式鎖來保證同一訂單只能被一個(gè)系統(tǒng)修改。

回答時(shí):說一下分布式鎖的應(yīng)用場(chǎng)景。

二、深入聊聊-分布式鎖在項(xiàng)目中的應(yīng)用

1、Redisson實(shí)現(xiàn)分布式鎖

Redisson 是一個(gè)基于 Redis 的 Java 分布式框架。Redisson 提供了豐富的功能,包括分布式鎖、分布式集合、分布式隊(duì)列等。

以下是使用 Redisson 實(shí)現(xiàn)分布式鎖的示例:

@Autowired
    private RedissonClient redissonClient;

    public String lock() {
        // 獲取鎖
        RLock lock = redissonClient.getLock("lock");
        boolean acquired = lock.tryLock(10,-1,TimeUnit.SECONDS);
        if (acquired) {
            // 獲取鎖成功,執(zhí)行業(yè)務(wù)邏輯
            return "獲取鎖成功,執(zhí)行業(yè)務(wù)邏輯...";
        } else {
            // 獲取鎖失敗,重試
            return "獲取鎖失敗,重試...";
        }
    }

    public String unlock() {
        // 釋放鎖
        RLock lock = redissonClient.getLock("lock");
        lock.unlock();
        return "釋放鎖成功...";
    }

另外,redisson支持鎖續(xù)期。即在鎖鍵值過期后任務(wù)還沒執(zhí)行完成,此時(shí)需要把鎖鍵值的時(shí)間自動(dòng)延長(zhǎng)。

Redisson提供了的續(xù)期機(jī)制,只要客戶端加鎖成功,就會(huì)啟動(dòng)一個(gè)Watch Dog。可以看到源代碼的實(shí)現(xiàn)leaseTime不設(shè)置為-1時(shí)開啟監(jiān)聽。如果任務(wù)沒完成就調(diào)用scheduleExpirationRenewal續(xù)期方法。

tryLock() 方法用于嘗試獲取分布式鎖,該方法有三個(gè)參數(shù):

  • key:鎖的鍵值。
  • waitTime:等待獲取鎖的時(shí)間,單位為毫秒。
  • leaseTime:鎖的過期時(shí)間,單位為毫秒。

waitTime 參數(shù)表示客戶端最多等待多長(zhǎng)時(shí)間來獲取鎖。如果在 waitTime 時(shí)間內(nèi)沒有獲取到鎖,則會(huì)返回 false。

leaseTime 參數(shù)表示鎖的過期時(shí)間。如果鎖在 leaseTime 時(shí)間內(nèi)沒有被釋放,則會(huì)自動(dòng)釋放。如果 leaseTime 設(shè)置為 -1,則表示鎖的過期時(shí)間由 renew() 方法來控制。這樣,在業(yè)務(wù)邏輯執(zhí)行過程中,可以定期調(diào)用 lock.renew() 方法來續(xù)期鎖的過期時(shí)間。

tryLock() 方法的返回值是一個(gè) Boolean 值,表示是否成功獲取到鎖。如果成功獲取到鎖,則返回 true。否則,返回 false。

2、Springboot+Lettuce

在 SpringBoot 2.7 中,可以通過spring-boot-starter-data-redis 默認(rèn)依賴是Lettuce。那么Lettuce是如何實(shí)現(xiàn)分布式鎖呢

  • 在 Spring Boot 項(xiàng)目中添加 spring-boot-starter-data-redis 依賴。
  • 定義一個(gè) RedisLock 類來封裝分布式鎖的相關(guān)操作。
  • 在 ServiceImpl類中使用 RedisLock 類來獲取和釋放分布式鎖。

以下是 SpringBoot+Lettuce 實(shí)現(xiàn)分布式鎖的完整代碼:

@Component
public class RedisLock {

    private static final String LOCK_SCRIPT = "if redis.call('exists', KEYS[1]) == 0 then\n" +
    "    redis.call('set', KEYS[1], ARGV[1], 'NX', 'PX', ARGV[2]);\n" +
    "    return 1\n" +
    "else\n" +
    "    return 0\n" +
    "end";

    private final RedisTemplate<String, String> redisTemplate;

    public RedisLock(RedisTemplate<String, String> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    //獲得鎖
    public boolean acquireLock(String key, long timeout) {
        String uuid = UUID.randomUUID().toString();
        Object result = redisTemplate.execute(new DefaultRedisScript(LOCK_SCRIPT, Long.class), Arrays.asList(key), uuid, timeout);
        return result != null && (long) result == 1;
    }
	//釋放鎖
    public void releaseLock(String key, String uuid) {
        redisTemplate.delete(key);
    }
}
@Service
public class TestServcieImpl implements TestServcie{

  @Autowired
    private StringRedisTemplate stringRedisTemplate;

    // 加鎖腳本
    private static final String LOCK_SCRIPT = "if redis.call ('setnx', KEYS[1], ARGV[1]) == 1 then return redis.call ('expire', KEYS[1], ARGV[2]) else return 0 end";

    // 解鎖腳本
    private static final String UNLOCK_SCRIPT = "if redis.call ('get', KEYS[1]) == ARGV[1] then return redis.call ('del', KEYS[1]) else return 0 end";

    // 加鎖方法
    public boolean lock(String key, String value, Long expire) {
        RedisScript<Long> redisScript = new DefaultRedisScript<>(LOCK_SCRIPT, Long.class);
        Long result = stringRedisTemplate.execute(redisScript, Collections.singletonList(key), value, String.valueOf(expire));
        return result.equals(Long.valueOf(1));
    }

    // 解鎖方法
    public boolean unlock(String key, String value) {
        RedisScript<Long> redisScript = new DefaultRedisScript<>(UNLOCK_SCRIPT, Long.class);
        Long result = stringRedisTemplate.execute(redisScript, Collections.singletonList(key), value);
        return result.equals(Long.valueOf(1));
    }
}

在上述 demo 中,我們使用 RedisLock 類來封裝分布式鎖的相關(guān)操作。acquireLock() 方法用于獲取分布式鎖,releaseLock() 方法用于釋放分布式鎖。

在 ServcieImpl 類中,我們使用 RedisLock 類來獲取和釋放分布式鎖。lock() 方法用于獲取鎖,unlock() 方法用于釋放鎖。不像Redisson封裝好了相應(yīng)的方法,Lettuuce如果要實(shí)現(xiàn)鎖續(xù)期就需要自己寫監(jiān)聽器及相應(yīng)的lua腳本。

三、小結(jié)

1、什么是Lua?

可以看到不論是Redisson還是Lettuce實(shí)現(xiàn)分布式鎖都使用的Lua腳本,那我們先來了解一下什么是Lua腳本語(yǔ)言。

Lua 是一個(gè)小巧的腳本語(yǔ)言,由巴西里約熱內(nèi)盧天主教大學(xué)(Pontifical Catholic University of Rio de Janeiro)里的一個(gè)研究小組于 1993 年開發(fā)的。Lua 使用標(biāo)準(zhǔn) C 語(yǔ)言編寫并以源代碼形式開放,幾乎在所有操作系統(tǒng)和平臺(tái)上都能編譯運(yùn)行。Lua 腳本可以調(diào)用 C/C++ 的函數(shù),也可以被 C/C++ 代碼調(diào)用,所以 Lua 在應(yīng)用程序中可以被廣泛應(yīng)用。

Lua 的特點(diǎn)如下:

  • 小巧靈活:Lua 的核心只有 200K,非常小巧,可以方便地嵌入到應(yīng)用程序中。Lua 的語(yǔ)法也非常簡(jiǎn)單,易于學(xué)習(xí)和使用。
  • 可擴(kuò)展性:Lua 可以調(diào)用 C/C++ 的函數(shù),也可以被 C/C++ 代碼調(diào)用,所以 Lua 可以很容易地?cái)U(kuò)展到應(yīng)用程序的其他部分。
  • 高性能:Lua 的運(yùn)行效率非常高,可以滿足大多數(shù)應(yīng)用程序的需求。

Lua 在游戲開發(fā)、Web 開發(fā)、嵌入式系統(tǒng)等領(lǐng)域都有廣泛的應(yīng)用。

以下是 Lua 的一些典型應(yīng)用:

  • 游戲開發(fā):Lua 常用于游戲中的腳本編寫,用于實(shí)現(xiàn)游戲的邏輯和特效。
  • Web 開發(fā):Lua 可以用于 Web 開發(fā),用于實(shí)現(xiàn)動(dòng)態(tài)頁(yè)面和游戲。
  • 嵌入式系統(tǒng):Lua 可以用于嵌入式系統(tǒng)的開發(fā),用于實(shí)現(xiàn)控制邏輯和用戶界面。

Lua 是一款非常實(shí)用的腳本語(yǔ)言,在眾多領(lǐng)域都有廣泛的應(yīng)用。

2、為什么使用Lua?

  • Redis 實(shí)現(xiàn)分布式鎖中,獲取鎖、釋放鎖為什么要使用 Lua 腳本?
  • 使用 Lua 腳本的主要原因是為了保證操作的原子性,避免出現(xiàn)并發(fā)問題或誤解鎖的情況。
  • 使用 setnx 命令獲取鎖,然后使用 expire 命令設(shè)置過期時(shí)間,這兩個(gè)命令之間可能會(huì)發(fā)生網(wǎng)絡(luò)延遲或者其他異常,導(dǎo)致鎖沒有正確設(shè)置過期時(shí)間,從而造成死鎖。
  • 使用 del 命令釋放鎖,需要先判斷鎖是否屬于當(dāng)前客戶端,否則可能會(huì)誤解其他客戶端的鎖。

使用 Lua 腳本可以將判斷和刪除鎖的操作合并為一個(gè)原子操作,避免了這些問題。Lua 腳本在 Redis 服務(wù)器端執(zhí)行,不會(huì)受到網(wǎng)絡(luò)延遲或者客戶端故障的影響,也不會(huì)被其他命令打斷,因此可以保證操作的原子性。

  • 為什么說 Redis 命令沒有原子性?
  • Redis 命令本身是單線程執(zhí)行的,所以單個(gè)命令是具有原子性的。
  • 但是如果要實(shí)現(xiàn)分布式鎖的功能,通常需要多個(gè)命令組合起來執(zhí)行,例如 setnx + expire 或者 get + del。
  • 這些命令組合在執(zhí)行過程中可能會(huì)被其他客戶端發(fā)送的命令打斷,導(dǎo)致數(shù)據(jù)不一致或者邏輯錯(cuò)誤。

因此,Redis 命令沒有原子性是指多個(gè)命令組合起來執(zhí)行時(shí)沒有原子性。

以下是使用 Lua 腳本實(shí)現(xiàn)分布式鎖的示例:

-- 獲取鎖
function acquire_lock(key, uuid, timeout)
  local value = redis.call("GET", key)
  if value == nil then
    redis.call("SET", key, uuid, "NX", "PX", timeout)
    return 1
  else
    return 0
  end
end

-- 釋放鎖
function release_lock(key, uuid)
  redis.call("DEL", key)
end

上述腳本實(shí)現(xiàn)了簡(jiǎn)單的 SETNX 和 DEL 操作,可以保證同一時(shí)刻只有一個(gè)客戶端可以獲取到鎖。

在實(shí)際使用中,可以根據(jù)具體的業(yè)務(wù)場(chǎng)景來調(diào)整 Lua 腳本的實(shí)現(xiàn)。

責(zé)任編輯:姜華 來源: 今日頭條
相關(guān)推薦

2023-01-12 08:24:45

ZookeeperZK服務(wù)器

2022-07-06 08:01:05

數(shù)據(jù)庫(kù)分布式

2024-09-24 16:30:46

分布式鎖Redis數(shù)據(jù)中間件

2022-08-11 18:27:50

面試Redis分布式鎖

2020-12-15 10:20:24

分布式鎖RedisZookeeper

2021-12-16 18:38:13

面試Synchronize

2021-06-03 08:55:54

分布式事務(wù)ACID

2024-02-22 17:02:09

IDUUID雪花算法

2023-08-03 07:49:39

N1節(jié)點(diǎn)網(wǎng)絡(luò)

2021-06-29 10:21:54

this面試前端

2020-07-09 13:30:03

RedisJava分布式鎖

2019-07-23 09:30:17

HTTP 2.0HTTP協(xié)議傳輸

2015-08-13 10:29:12

面試面試官

2020-09-24 10:30:29

Redis數(shù)據(jù)庫(kù)面試

2021-03-10 08:04:11

this面試題JavaScript

2018-05-10 15:48:47

面試面試官Java

2022-03-21 14:09:19

面試C語(yǔ)言代碼

2021-09-01 07:21:41

面試官開發(fā)讀寫鎖

2024-06-26 11:55:44

2020-09-27 06:52:22

分布式存儲(chǔ)服務(wù)器
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)