为什么需要有分布式锁

几乎很多大型网站及应用都是分布式部署的,分布式场景中的数据一致性问题一直是一个比较重要的话题。分布式的 CAP 理论告诉我们“任何一个分布式系统都无法同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance),最多只能同时满足两项。”所以,很多系统在设计之初就要对这三者做出取舍。在互联网领域的绝大多数的场景中,都需要牺牲强一致性来换取系统的高可用性,系统往往只需要保证“最终一致性”,只要这个最终时间是在用户可以接受的范围内即可。

在很多场景中,我们为了保证数据的最终一致性,需要很多的技术方案来支持,比如分布式事务、分布式锁等。有的时候,我们需要保证一个方法在同一时间内只能被同一个线程执行。而且现在互联网企业为了抗住庞大的日活,多少都会使用微服务、分库、分表等架构。导致传统用食物来保持数据正确就不太行的通了。

事务成本太高

如果你想让系统支持海量并发,那数据库的并发处理能力就尤为重要,而影响数据库并发能力最重要的因素是数据库的事务隔离机制。

数据库的四种隔离级别从低到高分别是:

  • 读未提交(READ UNCOMMITTED)
  • 读已提交(READ COMMITTED)
  • 可重复读(REPEATABLE READ)
  • 可串行化(SERIALIZABLE)

其中,可串行化操作就是按照事务的先后顺序,排队执行,然而一个事务操作可能要执行很久才能完成,这就没有并发效率可言了,所以数据库隔离级别越高,系统的并发性能就越差。

从阿里云的压测试报告中可以看出同配置下 MongoDBMySQL 的写入性能相差巨大。

与架构互斥

现在互联都已经微服务化,业务逻辑会拆分到许多的服务,使用不同的存储资源。这样单机的事物会被拆分成分布式的事物,基本上都是只使一个全局锁来保证事物都准确性。

consistency_1

例如同时有 2 个用户购买了一个机器,最后是谁购买到了呢?

consistency_2

这个时候可以在最外层加一个全局的 Lock 来判断,必须拿到这个 Lock 才能进行后续操作,可以在局部保证操作是串行。

如何实现分布式锁

考虑点

设计一个分布式锁,就需要明确分布式锁经常出现哪些问题,以及如何解决。

  • 互斥性:即在分布式系统环境下,对于某一共享资源,需要保证在同一时间只能一个线程或进程对该资源进行操作。
  • 高可用:也就是可靠性,锁服务不能有单点风险,要保证分布式锁系统是集群的,并且某一台机器锁不能提供服务了,其他机器仍然可以提供锁服务。
  • 锁释放:具备锁失效机制,防止死锁。即使出现进程在持有锁的期间崩溃或者解锁失败的情况,也能被动解锁,保证后续其他进程可以获得锁。
  • 可重入:一个节点获取了锁之后,还可以再次获取整个锁资源。

详细设计

在单实例下 Reids 使用, string 类型的数据结构,加锁的同时附带上全局唯一的随机值和过期时间。在解锁时利用 lua 脚本的原子性和锁的随机值进行对比,当匹配成功后才进行删除。具体操作如下图所示:

redlock_process

分布式锁作为核心正确性保障必须需要高可用。正常都会使用多个节点来保证可用性。官方也提供了一个基于多个 Redis 实例实现的算法。

假设我们有 N 个 Redis 主站。这些节点是完全独立的,所以我们不使用复制或任何其他隐含的协调系统。我们已经描述了如何在单个实例中安全地获取和释放锁。我们想当然地认为,算法将使用这种方法在单一实例中获取和释放锁。在我们的例子中,我们设置了 N=5,这是一个合理的值,所以我们需要在不同的计算机或虚拟机上运行 5 个 Redis 主站,以确保它们会以一种基本独立的方式失败。

为了获取锁,客户端会执行一下操作:

  • 客户端获取当前时间戳,以毫秒未单位
  • 客户端试图在所有 N 个实例中依次获得锁,在所有实例中使用相同 key 和 随机 val 值,在所有实例中使用相同的键名和随机值
  • 成功获取锁 - 大多数 Redis 节点接受 && 经过的时间<锁的有效时间
  • 实际锁的有效时间=初始有效时间-已过的时间
  • 如果获取锁失败,尝试解锁所有实例

优点

  • 简单易维护:
    • 大多数互联网企业都有使用 Redis 的习惯,已经有了一定的技术积累。
    • 只需要简单部署多个 Redis 实例,不用考虑多个实例等联通
    • 没有 Etcd、 Zookeeper 这些软件服务站的 Raft、Paxos 等非常复杂的一致性协议。
  • 性能优秀:
    • Redis 单机可抗 10W QPS,阿里云 在此基础上做出来的一个缓存系统 QPS 上限高达千万。

缺点

Redlock 算法提出后大家讨论非常积极, Martin Kleppmann 前辈也都提出质疑,点出了在系统时钟、网络等方面的不足。

如何落地

金融里有句话叫 资本没有对错只有利弊,有句话对咱 农民工 也挺适用的 没有最好的,只有最合适的, 互联网技术更新那么快,一定要用最新的,最牛 X 的吗?

我做技术选型的思考:

  • 我们真的需要如此优秀的设计,是在用屠龙技杀鸡吗?
  • 引入的成本有多高
  • 能带来多少收益
  • 维护成本这么样

日复搬砖,遇到的大部分问题都有成熟的解决方案。但是方案如何落地,复制、粘贴技术如何稳定产生价值,还是挺值得思考的。 例如这个分布式锁,无论怎么设计对业务使用方都会有一定的逻辑侵入,是做成一个 Service 还是 Framework 呢,我们要的是一辆汽车还是引擎。

参考资料