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

Redis 有什么给 key 加锁的方法吗?

  •  
  •   deweixu ·
    deweixu · 2019-05-22 19:45:47 +08:00 · 3586 次点击
    这是一个创建于 921 天前的主题,其中的信息可能已经有所发展或是发生改变。

    当一个线程获取某个 key 的数据后,别的线程获取这个 key 的数据要等待前面的线程释放锁。

    16 条回复    2019-05-24 12:13:25 +08:00
    swulling
        1
    swulling   2019-05-22 19:49:43 +08:00 via iPhone
    用一个专门的 read lock key 来解决就行,

    实际使用的时候把给 lock key 加锁或者解锁以及读 key 写到事务里就可以的
    mooncakejs
        2
    mooncakejs   2019-05-22 19:52:24 +08:00
    这不就是分布式锁的意义吗
    menc
        3
    menc   2019-05-22 19:55:46 +08:00
    在 redis 加一个 key 用来 lock 就好。
    所有线程取数据之前读 lock key,如果是 idle 值,就 write 1 次本线程 id,再 read 一次,如果 read 到的 id 是本线程,则抢到锁,去读数据,读完后将 lock key 的 value 置回 idle 即可。如果非 idle 值,就等待直到 value 变成 idel 值

    先 write 1 次再 read 是考虑多个线程抢占 lock 的情况,只允许一个线程拿到锁,所以每个线程尝试一次,谁抢到算谁的。
    usingnamespace
        4
    usingnamespace   2019-05-22 20:52:36 +08:00 via iPhone
    @menc 但是第一次 write 后的不就是希望把锁给第一次 write 的这个(调用 /进程 /进程 /上游)吗?真的有必要写了就让大家读一次?
    raychar
        5
    raychar   2019-05-22 20:57:38 +08:00
    etcd
    Chanston
        6
    Chanston   2019-05-22 20:59:22 +08:00
    另外增加一个 key 作为 lock,获取 lock 时用 redis 的 setNx 方法(意思是 SET if Not eXist, 如果 lock 已经存在,那么就无法 set 了;不同语言的 redis client 可能方法名不一定叫这个,但应该都有对应的方法的)。
    如果 setNx 成功表示加锁成功,然后就可以执行你的读取数据的操作了,执行完之后需要将 lock key 删除;
    如果 setNx 失败表示已有前面的线程在使用,需要等待一定时间然后重试 setNx

    搜索 redis 分布式锁,可以看到更多详细的使用介绍
    menc
        7
    menc   2019-05-22 21:12:15 +08:00
    @usingnamespace 多个 write 可能覆盖
    vindurriel
        8
    vindurriel   2019-05-22 21:22:19 +08:00 via iPhone   ❤️ 2
    @menc 你的方法不是原子的
    @Chanston 的方法基本正确 但没有超时 正确的实现应该是 SET key value timeout NX
    usingnamespace
        9
    usingnamespace   2019-05-22 23:29:46 +08:00 via iPhone
    @menc 然而 redis 并不是多适合分布式锁
    kimown
        10
    kimown   2019-05-23 08:12:41 +08:00 via Android
    @vindurriel

    加 timeout 的原因是如果 key 一直处于锁状态, client 会一直等待吗
    ihipop
        11
    ihipop   2019-05-23 09:10:27 +08:00 via Android
    @kimown 不加 timeout 你的加锁线程挂了以后就一直锁着了
    ryanking8215
        12
    ryanking8215   2019-05-23 09:44:39 +08:00
    jifengg
        13
    jifengg   2019-05-23 11:37:49 +08:00
    楼上说的是用 redis “实现”锁的方式。
    redis 本身并不支持“锁定”不让读或写。
    需要通过你的业务代码去实现。也就是说,即使用了楼上说的“锁”,所有的代码也都得遵循这个机制来写才行,如果有的代码就不管你实现的锁,直接去做读写操作,那也是可以的。
    buhi
        15
    buhi   2019-05-23 13:50:33 +08:00
    @menc 的方法会有问题, 就像上面某位说的不是原子操作, 例如
    [idle]线程 A 读
    [idle]线程 B 读
    [idle]线程 A 写
    [A]线程 A 读 (A 认为自己拿到了锁)
    [A]线程 B 写
    [B]线程 B 读 (B 认为自己拿到了锁)
    vindurriel
        16
    vindurriel   2019-05-24 12:13:25 +08:00 via iPhone
    @kimown 是的 比如程序刚拿到锁就挂了
    关于   ·   帮助文档   ·   API   ·   FAQ   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   3134 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 58ms · UTC 05:05 · PVG 13:05 · LAX 21:05 · JFK 00:05
    ♥ Do have faith in what you're doing.