一、关于分布式锁
关于分布式锁,可能绝大部分人都会或多或少涉及到。
我举二个例子:
场景一:从前端界面发起一笔支付请求,如果前端没有做防重处理,那么可能在某一个时刻会有二笔一样的单子同时到达系统后台。
场景二:在App中下订单的时候,点击确认之后,没反应,就又点击了几次。在这种情况下,如果无法保证该接口的幂等性,那么将会出现重复下单问题。
在接收消息的时候,消息推送重复。如果处理消息的接口无法保证幂等,那么重复消费消息产生的影响可能会非常大。
类似这种场景,我们有很多种方法,可以使用幂等操作,也可以使用锁的操作。
我们先来解释一下什么是幂等操作:
所谓幂等,简单地说,就是对接口的多次调用所产生的结果和调用一次是一致的。扩展一下,这里的接口,可以理解为对外发布的HTTP接口或者Thrift接口,也可以是接收消息的内部接口,甚至是一个内部方法或操作。
在分布式环境中,网络环境更加复杂,
因前端操作抖动、网络故障、消息重复、响应速度慢等原因,对接口的重复调用概率会比集中式环境下更大,尤其是重复消息在分布式环境中很难避免。Tyler Treat也在《You Cannot Have Exactly-Once Delivery》一文中提到:
Within the context of a distributed system, you cannot have exactly-once message delivery.
分布式环境中,有些接口是天然保证幂等性的,如查询操作。有些对数据的修改是一个常量,并且无其他记录和操作,那也可以说是具有幂等性的。其他情况下,所有涉及对数据的修改、状态的变更就都有必要防止重复性操作的发生。通过间接的实现接口的幂等性来防止重复操作所带来的影响,成为了一种有效的解决方案。
于是我们根据以上内容就可以讲一下使用分布式锁的方法有哪些。
1、使用数据库乐观锁,包括主键防重,版本号控制。但是这两种方法各有利弊。
-
使用主键冲突的策略进行防重,在并发量非常高的情况下对数据库性能会有影响,尤其是应用数据表和主键冲突表在一个库的时候,表现更加明显。其实针对是否会对数据库性能产生影响这个话题,我也和一些专业的DBA同学讨论过,普遍认可的是在MySQL数据库中采用主键冲突防重,在大并发情况下有可能会造成锁表现象,比较好的办法是在程序中生产主键进行防重。
-
使用版本号策略
这个策略源于mysql的mvcc机制,使用这个策略其实本身没有什么问题,唯一的问题就是对数据表侵入较大,我们要为每个表设计一个版本号字段,然后写一条判断sql每次进行判断。
2、Zookeeper防重策略
利用ZK确实是一个不错的方案,流程如下:
以前的版本中普遍传言说它的性能不好,但是后续的版本性能得到了较大提高,经过系统压测还是能够支撑较大并发量的,经过压测三台Zookeeper能搞住20000tps。
用zookeeper的优点大概有:高可用、公平锁、心跳保持锁。
3、Redis防重策略
关于主从Redis方案最简单的实现流程如下:
表面来看,这个方案似乎很管用,但是这里存在一个问题:在我们的系统架构里存在一个单点故障,如果Redis的master节点宕机了怎么办呢?有人可能会说:加一个slave节点!在master宕机时用slave就行了!但是其实这个方案明显是不可行的,因为这种方案无法保证第1个安全互斥属性,因为Redis的复制是异步的。 总的来说,这个方案里有一个明显的竞争条件(race condition),举例来说:
- 客户端A在master节点拿到了锁。
- master节点在把A创建的key写入slave之前宕机了。
- slave变成了master节点
- B也得到了和A还持有的相同的锁(因为原来的slave里还没有A持有锁的信息)
于是我就在想,我该如何做才能让Redis在分布式锁这一块能够达到高可用呢?
于是基于Tedis的思想(http://www.oschina.net/p/tedis) 我自己写了一套针对分布式锁的双写Redis框架。
二、双写Redis的架构图
说明:
组件名叫YeeRedisGroup,基本服务主要有四个,当数据到来的时候,会分别插入二个Redis服务,这二个Redis服务采用的是异地双活的方案,当其中一个Redis服务挂了以后,会将这个Redis服务从可用队列中摘除,放入重试队列中,另一个Redis则会继续使用。同样读取Redis的时候只会从可用队列中读取第一个Redis服务继续读取。
三、双写Redis的类图结构
说明:这个图其实没什么可说的,大家自己看就可以了。
四、双写Redis的时序图
说明:这个图主要就是说明了整体系统交互流程是怎样的。
五、故障容错流程图
六、故障重试流程图
七、主动通知与主动查询流程图
八、Redis可用队列与重试队列结构图
相关推荐
本文将深入探讨如何基于Redis实现分布式方法锁,并分析其工作原理、优势以及应用场景。 **1. Redis分布式锁的概念** Redis分布式锁是一种基于键值存储系统的锁机制,它通过在Redis中设置一个带有超时时间的key来...
本资料包主要探讨了如何利用 Redis 实现分布式锁和信号量,以确保系统的一致性和数据完整性。 首先,我们需要理解什么是分布式锁。在单机环境下,我们可以很容易地使用互斥锁(Mutex)来控制对共享资源的访问,但在...
本文将深入探讨如何使用Redis实现分布式锁,进而完成简单的秒杀功能。 #### 二、业务场景与挑战 ##### 1. 业务场景解析 - **业务视角**:秒杀是指在短时间内大量用户尝试购买限量商品的现象。这不仅考验着系统的...
在Redis中实现分布式锁,有以下几个关键点: 1. **唯一标识符**:每个客户端在请求锁时应携带一个唯一的ID,这样可以避免死锁和锁的释放问题。 2. **超时机制**:为了避免客户端异常导致锁无法释放,需要设置锁的...
Redis提供了多种实现分布式锁的方法,如使用`SETNX`命令设置带有过期时间的键,或者使用`RedLock`算法,通过在多个Redis实例上创建锁来提高健壮性。`SETNX`简单易用,但不支持可重入性和自动释放;而`RedLock`通过...
#### 四、基于Redis实现分布式锁的注意事项 - **锁的有效期**: 为了避免死锁的情况发生,可以为锁设置一个过期时间。即使持有锁的进程异常退出,也不会导致锁永远无法释放。 - **锁的可重入性**: 允许同一个进程多次...
1. 基于数据库实现:例如MySQL,通过行级锁或者表级锁,利用数据库的事务特性来实现分布式锁。但是这种方式存在性能瓶颈,不适用于高并发场景。 2. 基于Redis实现:Redis提供了丰富的数据结构和操作命令,如`SETNX`...
使用数据库实现分布式锁,通常通过创建一个包含锁标识的表,通过插入或删除记录来实现锁的获取和释放。这种方法简单易懂,但存在以下问题: - **单点故障**:数据库是整个系统的单点,一旦数据库出现问题,可能导致...
- Zookeeper分布式锁:利用Zookeeper的临时节点实现分布式锁。 - Redis分布式锁:基于Redis的命令特性实现锁服务。 7. **微服务架构**: - 微服务的概念与优势:小规模、独立部署、松耦合的服务单元。 - Docker...
1. **基于数据库**:利用数据库的事务和行级锁机制来实现分布式锁。例如,通过执行SQL的SELECT FOR UPDATE语句锁定特定记录,直到事务结束时释放锁。这种方法简单易懂,但可能会引入数据库的额外负载。 2. **基于...
总结,lock4j高性能分布式锁为开发人员提供了强大而可靠的并发控制工具,通过理解其工作原理和实现,开发者可以更好地应对分布式系统中的挑战,提高系统的稳定性和性能。lock4j-v2.2.6版本的源码分析和实践,无论是...
Redisson 通过使用 SETNX 命令来实现分布式锁的机制。 Redisson 分布式锁的使用方法 使用 Redisson 分布式锁需要在 Spring Boot 项目中引入 Redisson 依赖,创建 Redisson 客户端实例,并使用该实例来获取锁对象。...
在这个“分布式锁简单实现”项目中,开发者提供了一个基本的分布式锁实现,尽管可能在效率上存在一些问题,但对于我们理解分布式锁的工作原理和实现方式具有一定的参考价值。 分布式锁的核心目标是解决在多服务器、...
2. **基于缓存(如Redis)实现分布式锁** - **Redis命令实现**:使用`SETNX`命令尝试设置键值,只有键不存在时才会成功;`EXPIRE`设置超时时间防止死锁;`DEL`命令删除键。在实际应用中,还需要考虑锁的公平性和...
2. **基于Redis的分布式锁**:Redis提供了原子操作如`SETNX`(设置如果不存在)和`EXPIRE`(设置过期时间),可以利用这些特性来实现分布式锁。客户端尝试用`SETNX`设置一个键,并设置一个合理的过期时间,防止锁...
5. 数据分布与一致性:包括Gossip协议、Chubby锁服务等实现分布式一致性的方式。 另一方面,《分布式服务框架原理与实践》聚焦于服务间的通信与协调,这在微服务架构中尤为重要。书中的内容可能涵盖: 1. 服务发现...
2. 分布式锁的实现:学习如何在.NET环境中实现分布式锁,比如使用Redis、Zookeeper或者Etcd等分布式协调服务,以确保并发访问的正确性。 3. 高并发处理:研究秒杀场景下如何处理大量用户请求,包括限流、队列处理和...
考虑到项目已有Redis作为缓存和数据存储组件,因此选择基于Redis实现分布式锁更为合适。 第一版本的实现使用`set`和`del`命令来获取和释放锁。然而,这种方法存在线程安全问题,因为`set`操作在高并发环境下可能...
#### 二、基于Redis实现分布式锁 1. **基本原理**: - 使用Redis的`setnx`命令尝试设置锁,该命令在键不存在时设置键值,返回1表示设置成功,否则返回0。 - 设置一个超时时间,避免死锁的发生。 - 在释放锁时,...