MST

星途 面试题库

面试题:Python中使用Redis实现分布式锁时,如何处理锁的过期时间设置?

在Python使用Redis实现分布式锁机制的场景下,假设业务逻辑执行时间不确定,你认为应该如何合理设置锁的过期时间?请说明理由,并给出简单的代码示例。
16.1万 热度难度
编程语言Python

知识考点

AI 面试

面试题答案

一键面试

合理设置锁过期时间的方法及理由

  1. 方法
    • 对于业务逻辑执行时间不确定的情况,一种可行的办法是设置一个相对较长的锁过期时间,同时在业务逻辑执行过程中,通过定期续约的方式来确保锁不会意外过期。这样既可以避免因业务执行时间长导致锁提前过期,其他进程获取到锁而产生数据不一致等问题,又能防止锁长时间不释放造成资源浪费。
  2. 理由
    • 如果设置的过期时间过短,业务还未执行完锁就过期了,那么其他进程可能会获取到锁,从而引发并发问题,比如重复执行相同的业务逻辑,导致数据不一致等。例如在电商下单场景中,可能会出现超卖现象。
    • 若设置的过期时间过长,虽然能保证业务执行期间锁不会过期,但如果业务出现异常长时间阻塞,锁会一直占用,其他需要获取锁的进程会等待很久,降低了系统的并发性能,并且可能导致资源浪费。通过定期续约,可以在业务正常执行时保持锁的持有,而当业务结束后,锁能及时释放。

简单代码示例

import redis
import threading
import time


def acquire_lock(redis_client, lock_key, lock_value, expire_time):
    # 使用setnx命令尝试获取锁
    result = redis_client.setnx(lock_key, lock_value)
    if result:
        # 如果获取锁成功,设置过期时间
        redis_client.expire(lock_key, expire_time)
    return result


def release_lock(redis_client, lock_key, lock_value):
    pipe = redis_client.pipeline()
    while True:
        try:
            pipe.watch(lock_key)
            if pipe.get(lock_key).decode('utf-8') == lock_value:
                pipe.multi()
                pipe.delete(lock_key)
                pipe.execute()
                return True
            pipe.unwatch()
            break
        except redis.WatchError:
            continue
    return False


def renew_lock(redis_client, lock_key, lock_value, expire_time):
    while True:
        time.sleep(expire_time / 3)  # 每隔一段时间续约
        pipe = redis_client.pipeline()
        while True:
            try:
                pipe.watch(lock_key)
                if pipe.get(lock_key).decode('utf-8') == lock_value:
                    pipe.multi()
                    pipe.expire(lock_key, expire_time)
                    pipe.execute()
                    break
                pipe.unwatch()
                break
            except redis.WatchError:
                continue


def business_logic(redis_client, lock_key, lock_value, expire_time):
    if acquire_lock(redis_client, lock_key, lock_value, expire_time):
        try:
            # 模拟不确定执行时间的业务逻辑
            time.sleep(5)
            print('业务逻辑执行中...')
            # 启动续约线程
            renew_thread = threading.Thread(target=renew_lock, args=(redis_client, lock_key, lock_value, expire_time))
            renew_thread.start()
        finally:
            release_lock(redis_client, lock_key, lock_value)
            print('锁已释放')
    else:
        print('获取锁失败')


if __name__ == '__main__':
    r = redis.Redis(host='localhost', port=6379, db=0)
    lock_key = 'distributed_lock'
    lock_value = 'unique_value'
    expire_time = 10  # 初始过期时间设置为10秒
    business_logic(r, lock_key, lock_value, expire_time)


上述代码实现了使用Redis获取分布式锁、释放锁以及在业务执行过程中定期续约锁的功能。acquire_lock函数用于尝试获取锁并设置过期时间,release_lock函数用于释放锁,renew_lock函数用于定期续约锁,business_logic函数模拟了业务逻辑,在获取锁后执行不确定时长的业务,并启动续约线程,最后释放锁。