分布式锁的一点看法

分布式锁的主流方案

  • 基于数据库

    • select for update。这个锁理论上没有超时能力,只能等客户端断开连接才行。所以实际上不能用于高并发&执行高耗时的场景。
    • 另外一种方案是仿照redis,在数据库里insert行。并设置唯一索引。这其实和redis锁的原理是一致的。
  • 基于zookeeper
    https://blog.csdn.net/define_us/article/details/79698940
    zookeepr的锁其实和数据库是一样的问题。如果有临时节点的机器没有正确处理监听器逻辑。那么,整个锁就会被卡死。解决的办法也和redis锁一样。(本质上zk的znode也是一个key-value格式嘛)。机器会循环遍历,一旦发现自己的前序临时节点的前序临时节点已经删除。就在本地计时。在超时后,直接将该节点删除。唤醒本节点。但是zookeeper作为分布式锁的最大问题,是它仅保证最终一致性。你不能假设每台服务器都能从zookeeper看到相同的状态。
    所以这种方案其实是不可行的。

  • 基于redis
    https://blog.csdn.net/define_us/article/details/79698408
    可以说,redis的锁是唯一在大规模并发场景中可以使用的锁。

数据库和redis可以实现在不考虑超时的情况下实现严格的分布式锁。因为他们本身就是单点。但是,如果考虑超时,这个问题就无解了。没有一种考虑超时的分布式锁能够保证全局唯一执行。

所以,一个简单的办法是,redis作为第一层,数据库行锁作为第二层。为了克服数据库行锁没有超时时间的问题,
Spring事务提供了超时时间的配置

@Transactional(timeout=1)

但可惜,这个超时能力是spring计时器提供的。不是数据库本身提供的。可能会存在比如说获取行锁后展开大规模的GC,整个JVM被卡住。(但是数据库session不会超时,因为为了配合数据库连接池,一般数据库session超时配置到很大)。让然会造成整个系统卡住。

https://qingmiaogu.blog.csdn.net/article/details/80108576

其实,这样的问题出现的概率很低才对。第一层的redis锁已经把压力降下来了,第二层数据库操作却意外超时了。这简直是不可接受的。所以,我们需要努力在代码和系统配置容量上,避免上述出现的问题。这也其实是所有使用select for update的系统必须要考虑的问题。

猜你喜欢

转载自blog.csdn.net/define_us/article/details/111770071