来讨论一下用数据库实现简单分布式锁的问题 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
zou8944
V2EX    程序员

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

  •  
  •   zou8944 2023-06-27 16:24:48 +08:00 2183 次点击
    这是一个创建于 840 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

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

    只要是可靠的强一致性的支持事务的存储,都可以用来做分布式锁。很多时候没必要引入 redis/zk 。
    xiangyuecn
        14
    xiangyuecn  
       2023-06-27 19:22:16 +08:00
    删除也一样要校验所有权。简单加一个随机值,谁持有这个随机值就代表谁持有这个锁,可删除和更新(不管过期不过期都能操作,逻辑上简单粗暴)。
    wqtacc
        15
    wqtacc  
       2023-06-27 23:29:44 +08:00
    感觉现在的实现,如果是多个客户端,加锁时的 id 怎么分配的,冲突就展期,防止不了冲突,释放锁也是一样的
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5595 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 34ms UTC 01:25 PVG 09:25 LAX 18:25 JFK 21:25
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86