文章插图
不过这种方式对于单主却无法自动切换主从的 MySQL 来说 , 基本就无法实现 P 分区容错性(MySQL 自动主从切换在目前并没有十分完美的解决方案) 。
可以说这种方式强依赖于数据库的可用性 , 数据库写操作是一个单点 , 一旦数据库挂掉 , 就导致锁的不可用 。这种方式基本不在 CAP 的一个讨论范围 。
基于 Redis 实现分布式锁
Redis 单线程串行处理天然就是解决串行化问题 , 用来解决分布式锁是再适合不过 。
实现方式:
setnx key value Expire_time 获取到锁 返回 1 , 获取失败 返回 0 为了解决数据库锁的无主从切换的问题 , 可以选择 Redis 集群 , 或者是 Sentinel 哨兵模式 , 实现主从故障转移 , 当 Master 节点出现故障 , 哨兵会从 Slave 中选取节点 , 重新变成新的 Master 节点 。
文章插图
哨兵模式故障转移是由 Sentinel 集群进行监控判断 , 当 Maser 出现异常即复制中止 , 重新推选新 Slave 成为 Master , Sentinel 在重新进行选举并不在意主从数据是否复制完毕具备一致性 。
所以 Redis 的复制模式是属于 AP 的模式 。保证可用性 , 在主从复制中“主”有数据 , 但是可能“从”还没有数据 。
这个时候 , 一旦主挂掉或者网络抖动等各种原因 , 可能会切换到“从”节点 , 这个时候可能会导致两个业务线程同时获取得两把锁 。
文章插图
这个过程如下:
- 业务线程 -1 向主节点请求锁
- 业务线程 -1 获取锁
- 业务线程 -1 获取到锁并开始执行业务
- 这个时候 Redis 刚生成的锁在主从之间还未进行同步
- Redis 这时候主节点挂掉了
- Redis 的从节点升级为主节点
- 业务线程 -2 想新的主节点请求锁
- 业务线程 -2 获取到新的主节点返回的锁
- 业务线程 -2 获取到锁开始执行业务
- 这个时候业务线程 -1 和业务线程 -2 同时在执行任务
Redis 官方推荐 Redlock 算法来保证 , 问题是 Redlock 至少需要三个 Redis 主从实例来实现 , 维护成本比较高 。
相当于 Redlock 使用三个 Redis 集群实现了自己的另一套一致性算法 , 比较繁琐 , 在业界也使用得比较少 。
能不能使用 Redis 作为分布式锁?这个本身就不是 Redis 的问题 , 还是取决于业务场景 。
我们先要自己确认我们的场景是适合 AP 还是 CP , 如果在社交发帖等场景下 , 我们并没有非常强的事务一致性问题 , Redis 提供给我们高性能的 AP 模型是非常适合的 。
但如果是交易类型 , 对数据一致性非常敏感的场景 , 我们可能要寻找一种更加适合的 CP 模型 。
基于 Zookeeper 实现分布式锁
刚刚也分析过 , Redis 其实无法确保数据的一致性 , 先来看 Zookeeper 是否适合作为我们需要的分布式锁 。
首先 ZK 的模式是 CP 模型 , 也就是说 , 当 ZK 锁提供给我们进行访问的时候 , 在 ZK 集群中能确保这把锁在 ZK 的每一个节点都存在 。
文章插图
这个实际上是 ZK 的 Leader 通过二阶段提交写请求来保证的 , 这个也是 ZK 的集群规模大了的一个瓶颈点 。
①ZK 锁实现的原理
说 ZK 的锁问题之前先看看 Zookeeper 中几个特性 , 这几个特性构建了 ZK 的一把分布式锁 。
ZK 的特性如下:
- 有序节点:当在一个父目录下如 /lock 下创建 有序节点 , 节点会按照严格的先后顺序创建出自节点 lock000001 , lock000002 , lock0000003 , 以此类推 , 有序节点能严格保证各个自节点按照排序命名生成 。
- 临时节点:客户端建立了一个临时节点 , 在客户端的会话结束或会话超时 , Zookepper 会自动删除该节点 ID 。
推荐阅读
- 泡茶不好喝 你的茶汤出尽了吗
- 爱喝茶的人常有的四条怪癖
- 每种茶都有自己的特性茶文化博大精深
- 茶叶香气的九大类型
- 这篇java的NIO编程,保证你能看懂
- 到底什么才是好茶
- 一篇全面的 MySQL 高性能优化实战总结
- 普洱散茶的冲泡技巧
- 武夷水仙和凤凰水仙有啥区别
- Python数据类型详解——元组