为什么需要有分布式锁
几乎很多大型网站及应用都是分布式部署的,分布式场景中的数据一致性问题一直是一个比较重要的话题。分布式的 CAP 理论告诉我们“任何一个分布式系统都无法同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance),最多只能同时满足两项。”所以,很多系统在设计之初就要对这三者做出取舍。在互联网领域的绝大多数的场景中,都需要牺牲强一致性来换取系统的高可用性,系统往往只需要保证“最终一致性”,只要这个最终时间是在用户可以接受的范围内即可。
在很多场景中,我们为了保证数据的最终一致性,需要很多的技术方案来支持,比如分布式事务、分布式锁等。有的时候,我们需要保证一个方法在同一时间内只能被同一个线程执行。而且现在互联网企业为了抗住庞大的日活,多少都会使用微服务、分库、分表等架构。导致传统用食物来保持数据正确就不太行的通了。
事务成本太高
如果你想让系统支持海量并发,那数据库的并发处理能力就尤为重要,而影响数据库并发能力最重要的因素是数据库的事务隔离机制。
数据库的四种隔离级别从低到高分别是:
- 读未提交(READ UNCOMMITTED)
- 读已提交(READ COMMITTED)
- 可重复读(REPEATABLE READ)
- 可串行化(SERIALIZABLE)
其中,可串行化操作就是按照事务的先后顺序,排队执行,然而一个事务操作可能要执行很久才能完成,这就没有并发效率可言了,所以数据库隔离级别越高,系统的并发性能就越差。
从阿里云的压测试报告中可以看出同配置下 MongoDB 比MySQL 的写入性能相差巨大。
与架构互斥
现在互联都已经微服务化,业务逻辑会拆分到许多的服务,使用不同的存储资源。这样单机的事物会被拆分成分布式的事物,基本上都是只使一个全局锁来保证事物都准确性。
例如同时有 2 个用户购买了一个机器,最后是谁购买到了呢?
这个时候可以在最外层加一个全局的 Lock 来判断,必须拿到这个 Lock 才能进行后续操作,可以在局部保证操作是串行。
如何实现分布式锁
考虑点
设计一个分布式锁,就需要明确分布式锁经常出现哪些问题,以及如何解决。
- 互斥性:即在分布式系统环境下,对于某一共享资源,需要保证在同一时间只能一个线程或进程对该资源进行操作。
- 高可用:也就是可靠性,锁服务不能有单点风险,要保证分布式锁系统是集群的,并且某一台机器锁不能提供服务了,其他机器仍然可以提供锁服务。
- 锁释放:具备锁失效机制,防止死锁。即使出现进程在持有锁的期间崩溃或者解锁失败的情况,也能被动解锁,保证后续其他进程可以获得锁。
- 可重入:一个节点获取了锁之后,还可以再次获取整个锁资源。
详细设计
在单实例下 Reids 使用, string 类型的数据结构,加锁的同时附带上全局唯一的随机值和过期时间。在解锁时利用 lua 脚本的原子性和锁的随机值进行对比,当匹配成功后才进行删除。具体操作如下图所示:
分布式锁作为核心正确性保障必须需要高可用。正常都会使用多个节点来保证可用性。官方也提供了一个基于多个 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 呢,我们要的是一辆汽车还是引擎。