V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
• 请不要在回答技术问题时复制粘贴 AI 生成的内容
zou8944
V2EX  ›  程序员

来讨论一下用数据库实现简单分布式锁的问题

  •  
  •   zou8944 · Jun 27, 2023 · 2514 views
    This topic created in 1042 days ago, the information mentioned may be changed or developed.

    来讨论一下用数据库实现简单分布式锁的问题

    单纯做技术讨论,使用 PG 数据库实现一个分布式锁。仅考虑锁的正确性,不考虑可重入等功能。我的想法如下。

    create table distribute_locks
    (
        id         varchar     not null primary key,
        expire_at  timestamptz not null,
        created_at timestamptz not null default current_timestamp,
        updated_at timestamptz not null default current_timestamp
    );
    
    • id 作为加锁的 key
    • expire_at 作为锁的过期时间。锁不存在或过期都算锁已被释放,此时其它方可以获取到该锁

    用法

    • 加锁
    insert into distribute_locks (id, expire_at)
    values (:id, now() + interval '1 minute')
    on conflict (id) 
    do update set expire_at = now() + interval '1 minute'
    where distribute_locks.expire_at < current_timestamp
    returning id
    

    有内容返回时获取锁成功,否则获取锁失败

    • 锁续期
    update distribute_locks
    set expire_at = now() + interval '1 minute'
    where id = :id and expire_at > current_timestamp
    

    只有锁存在且过期才能续期,否则续期无效

    • 释放锁
    delete from distribute_locks
    where id = :id
    

    疑问点

    这样设计的锁能满足基本需求了,但还有一个问题没有解决,即如何稳定续期。

    问题点在于,如果我在获取到锁时启动一个线程去续期,那如果当前线程结束,没有主动释放锁。该续期线程要如何结束呢?

    我用的是 python 来做

    Supplement 1  ·  Jun 27, 2023
    修正锁续期的说法:只有锁存在且在有效期内才能续期,否则续期无效
    15 replies    2023-06-27 23:29:44 +08:00
    opengps
        1
    opengps  
       Jun 27, 2023
    我没看明白,这个 distribute_locks 表存在哪,因为我始终都想知道怎么实现的分布式锁。
    因为我关注点是:这到底是多个数据库的锁,还是分布式应用的共享一个库里的行数据作为锁
    zuisong
        2
    zuisong  
       Jun 27, 2023
    设置一个最大续期次数?
    F281M6Dh8DXpD1g2
        3
    F281M6Dh8DXpD1g2  
       Jun 27, 2023
    先想想隔离级别的事
    zou8944
        4
    zou8944  
    OP
       Jun 27, 2023
    @opengps 后者
    zou8944
        5
    zou8944  
    OP
       Jun 27, 2023
    @zuisong 不可行,这个和设置一个超长的锁有效期没有本质区别
    zou8944
        6
    zou8944  
    OP
       Jun 27, 2023
    @liprais 为什么要想隔离级别的事情?
    leonshaw
        7
    leonshaw  
       Jun 27, 2023
    续期的时候不看所有权?
    zou8944
        8
    zou8944  
    OP
       Jun 27, 2023
    @leonshaw 所有权也要看,这里漏掉了
    lolizeppelin
        9
    lolizeppelin  
       Jun 27, 2023
    直接 zk 或者 etcd 做不就行了....为什么折腾 pg
    数据库做锁没法支持连接断开后清理锁,用 expire_at 很别扭的

    字段里加个 lokcer 存放 uuid, 这个 uuid 由于获得上锁的客户端生成, 由于这个 uuid 只有上锁的客户端才知道,这样就可以做到过期前只有指定的 locker 才能释放

    上锁
    update lock set locker = 'fffffffffffffffffffffffffffffffffffff' where id = 'locker-id' and locker is null

    放锁
    update lock set locker = null where id = 'locker-id' and locker = 'fffffffffffffffffffffffffffffffff'
    shinyruo2020
        10
    shinyruo2020  
       Jun 27, 2023
    没看懂,为什么发现冲突时是去续期呢?不用判断当前线程是否持有锁的吗?
    shinyruo2020
        11
    shinyruo2020  
       Jun 27, 2023
    噢噢,看到你上面的回复了,sry
    voidmnwzp
        12
    voidmnwzp  
       Jun 27, 2023 via iPhone
    分布式锁不都是 zk 或者 redis 不会用硬盘数据库吧
    rrfeng
        13
    rrfeng  
       Jun 27, 2023
    只要逻辑没错,你用啥都行。

    只要是可靠的强一致性的支持事务的存储,都可以用来做分布式锁。很多时候没必要引入 redis/zk 。
    xiangyuecn
        14
    xiangyuecn  
       Jun 27, 2023
    删除也一样要校验所有权。简单加一个随机值,谁持有这个随机值就代表谁持有这个锁,可删除和更新(不管过期不过期都能操作,逻辑上简单粗暴)。
    wqtacc
        15
    wqtacc  
       Jun 27, 2023
    感觉现在的实现,如果是多个客户端,加锁时的 id 怎么分配的,冲突就展期,防止不了冲突,释放锁也是一样的
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   2691 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 350ms · UTC 03:30 · PVG 11:30 · LAX 20:30 · JFK 23:30
    ♥ Do have faith in what you're doing.