MST
星途 面试题库

面试题:在微服务架构中使用Redis分布式锁如何解决锁的可重入问题

假设你正在一个微服务项目中使用Redis分布式锁,当一个服务需要多次获取同一把锁时,如何设计实现可重入的Redis分布式锁,详细说明设计思路和实现代码(可使用伪代码或你熟悉的编程语言)。
42.6万 热度难度
数据库Redis

知识考点

AI 面试

面试题答案

一键面试

设计思路

  1. 加锁逻辑:使用 SETNX 命令尝试获取锁,如果获取成功则记录锁的持有次数并设置锁的持有者标识(如当前服务实例的唯一ID)。如果锁已被持有且持有者是当前服务实例,则增加持有次数,实现可重入。
  2. 解锁逻辑:当解锁时,判断持有次数,只有持有次数减为0时,才真正释放锁。

实现代码(Python示例)

import redis
import uuid


class ReentrantRedisLock:
    def __init__(self, redis_client, lock_key, expire_time=10):
        self.redis_client = redis_client
        self.lock_key = lock_key
        self.expire_time = expire_time
        self.lock_holder = str(uuid.uuid4())
        self.hold_count = 0

    def acquire(self):
        if self.hold_count > 0:
            self.hold_count += 1
            return True
        result = self.redis_client.set(self.lock_key, self.lock_holder, ex=self.expire_time, nx=True)
        if result:
            self.hold_count = 1
            return True
        current_holder = self.redis_client.get(self.lock_key)
        if current_holder and current_holder.decode('utf-8') == self.lock_holder:
            self.hold_count += 1
            return True
        return False

    def release(self):
        if self.hold_count <= 0:
            return
        self.hold_count -= 1
        if self.hold_count == 0:
            self.redis_client.delete(self.lock_key)


# 使用示例
if __name__ == "__main__":
    r = redis.Redis(host='localhost', port=6379, db=0)
    lock = ReentrantRedisLock(r, 'test_lock')
    if lock.acquire():
        try:
            # 业务逻辑
            print('Lock acquired, doing business logic...')
            # 再次获取锁
            if lock.acquire():
                try:
                    print('Re - acquired lock, doing more business logic...')
                finally:
                    lock.release()
        finally:
            lock.release()

伪代码实现

// 初始化锁
function ReentrantRedisLock(redisClient, lockKey, expireTime) {
    this.redisClient = redisClient
    this.lockKey = lockKey
    this.expireTime = expireTime
    this.lockHolder = generateUniqueId()
    this.holdCount = 0
}

// 获取锁
function acquire() {
    if (this.holdCount > 0) {
        this.holdCount = this.holdCount + 1
        return true
    }
    result = this.redisClient.set(this.lockKey, this.lockHolder, ex = this.expireTime, nx = true)
    if (result) {
        this.holdCount = 1
        return true
    }
    currentHolder = this.redisClient.get(this.lockKey)
    if (currentHolder && currentHolder == this.lockHolder) {
        this.holdCount = this.holdCount + 1
        return true
    }
    return false
}

// 释放锁
function release() {
    if (this.holdCount <= 0) {
        return
    }
    this.holdCount = this.holdCount - 1
    if (this.holdCount == 0) {
        this.redisClient.delete(this.lockKey)
    }
}