V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
zou8944
V2EX  ›  程序员

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

  •  
  •   zou8944 · 305 天前 · 1485 次点击
    这是一个创建于 305 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    单纯做技术讨论,使用 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 来做

    第 1 条附言  ·  305 天前
    修正锁续期的说法:只有锁存在且在有效期内才能续期,否则续期无效
    15 条回复    2023-06-27 23:29:44 +08:00
    opengps
        1
    opengps  
       305 天前
    我没看明白,这个 distribute_locks 表存在哪,因为我始终都想知道怎么实现的分布式锁。
    因为我关注点是:这到底是多个数据库的锁,还是分布式应用的共享一个库里的行数据作为锁
    zuisong
        2
    zuisong  
       305 天前
    设置一个最大续期次数?
    liprais
        3
    liprais  
       305 天前
    先想想隔离级别的事
    zou8944
        4
    zou8944  
    OP
       305 天前
    @opengps 后者
    zou8944
        5
    zou8944  
    OP
       305 天前
    @zuisong 不可行,这个和设置一个超长的锁有效期没有本质区别
    zou8944
        6
    zou8944  
    OP
       305 天前
    @liprais 为什么要想隔离级别的事情?
    leonshaw
        7
    leonshaw  
       305 天前
    续期的时候不看所有权?
    zou8944
        8
    zou8944  
    OP
       305 天前
    @leonshaw 所有权也要看,这里漏掉了
    lolizeppelin
        9
    lolizeppelin  
       305 天前
    直接 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  
       305 天前
    没看懂,为什么发现冲突时是去续期呢?不用判断当前线程是否持有锁的吗?
    shinyruo2020
        11
    shinyruo2020  
       305 天前
    噢噢,看到你上面的回复了,sry
    voidmnwzp
        12
    voidmnwzp  
       305 天前 via iPhone
    分布式锁不都是 zk 或者 redis 不会用硬盘数据库吧
    rrfeng
        13
    rrfeng  
       305 天前
    只要逻辑没错,你用啥都行。

    只要是可靠的强一致性的支持事务的存储,都可以用来做分布式锁。很多时候没必要引入 redis/zk 。
    xiangyuecn
        14
    xiangyuecn  
       305 天前
    删除也一样要校验所有权。简单加一个随机值,谁持有这个随机值就代表谁持有这个锁,可删除和更新(不管过期不过期都能操作,逻辑上简单粗暴)。
    wqtacc
        15
    wqtacc  
       305 天前
    感觉现在的实现,如果是多个客户端,加锁时的 id 怎么分配的,冲突就展期,防止不了冲突,释放锁也是一样的
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1410 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 17:24 · PVG 01:24 · LAX 10:24 · JFK 13:24
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.