本帖最后由 howtodown 于 2016-10-3 16:01 编辑
问题导读
1.为什么会产生分布式锁?
2.使用分布式锁的方法有哪些?
3.本文创造的分布式锁的双写Redis框架都包含哪些内容?

一、关于分布式锁

关于分布式锁,可能绝大部分人都会或多或少涉及到。 
我举二个例子: 
场景一:从前端界面发起一笔支付请求,如果前端没有做防重处理,那么可能在某一个时刻会有二笔一样的单子同时到达系统后台。
场景二:在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确实是一个不错的方案,流程如下: 
<ignore_js_op> 
以前的版本中普遍传言说它的性能不好,但是后续的版本性能得到了较大提高,经过系统压测还是能够支撑较大并发量的,经过压测三台Zookeeper能搞住20000tps。 
用zookeeper的优点大概有:高可用、公平锁、心跳保持锁。
3、Redis防重策略 
关于主从Redis方案最简单的实现流程如下: 
<ignore_js_op> 
表面来看,这个方案似乎很管用,但是这里存在一个问题:在我们的系统架构里存在一个单点故障,如果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的架构图
<ignore_js_op>
说明: 
组件名叫YeeRedisGroup,基本服务主要有四个,当数据到来的时候,会分别插入二个Redis服务,这二个Redis服务采用的是异地双活的方案,当其中一个Redis服务挂了以后,会将这个Redis服务从可用队列中摘除,放入重试队列中,另一个Redis则会继续使用。同样读取Redis的时候只会从可用队列中读取第一个Redis服务继续读取。
三、双写Redis的类图结构
<ignore_js_op> 
说明:这个图其实没什么可说的,大家自己看就可以了。
四、双写Redis的时序图
<ignore_js_op> 
说明:这个图主要就是说明了整体系统交互流程是怎样的。
五、故障容错流程图
<ignore_js_op>
六、故障重试流程图
<ignore_js_op>
七、主动通知与主动查询流程图
<ignore_js_op>
八、Redis可用队列与重试队列结构图
<ignore_js_op>

借读:分布式锁和双写Redis的更多相关文章

  1. 分布式锁的实现之 redis 篇

    为什么需要分布式锁 引入经典的秒杀情景,100件商品供客户抢.如果是单机版的话,我们使用synchronized 或者 lock 都可以实现线程安全.但是如果多个服务器的话,synchronized ...

  2. springboot实现分布式锁(spring integration,redis)

    Springboot实现分布式锁(Spring Integration+Redis) 一.在项目的pom.xml中添加相关依赖 1)Spring Integration依赖 <dependenc ...

  3. 分布式锁-基于ZK和Redis实现

    一.基于zookeeper实现分布式锁 1.1 Zookeeper的常用接口 package register; import java.util.List; import java.util.con ...

  4. 分布式锁(2) ----- 基于redis的分布式锁

    分布式锁系列文章 分布式锁(1) ----- 介绍和基于数据库的分布式锁 分布式锁(2) ----- 基于redis的分布式锁 分布式锁(3) ----- 基于zookeeper的分布式锁 代码:ht ...

  5. 什么是分布式锁及正确使用redis实现分布式锁

    分布式锁 分布式锁其实可以理解为:控制分布式系统有序的去对共享资源进行操作,通过互斥来保持一致性. 举个不太恰当的例子:假设共享的资源就是一个房子,里面有各种书,分布式系统就是要进屋看书的人,分布式锁 ...

  6. 分布式锁实现(一):Redis

    前言 单机环境下我们可以通过JAVA的Synchronized和Lock来实现进程内部的锁,但是随着分布式应用和集群环境的出现,系统资源的竞争从单进程多线程的竞争变成了多进程的竞争,这时候就需要分布式 ...

  7. Redis 分布式锁,C#通过Redis实现分布式锁(转)

    目录(?)[+] 分布式锁一般有三种实现方式: 可靠性   分布式锁一般有三种实现方式: 1. 数据库乐观锁; 2. 基于Redis的分布式锁; 3. 基于ZooKeeper的分布式锁.本篇博客将介绍 ...

  8. 分布式锁中的基于redis的setnx的原理以及set和setnx的区别是什么

    基于Redis实现分布式锁.虽然网上介绍的Redis分布式锁博客比较多,却有着各种各样的问题,本篇博客将详细介绍如何正确地使用setnx实现Redis分布式锁 这里就不介绍错误的示范了 大家直接看正确 ...

  9. C# Redis分布式锁(基于ServiceStack.Redis)

    相关的文章其实不少,我也从中受益不少,但是还是想自己梳理一下,毕竟自己写的更走心! 首先给出一个拓展类,通过拓展方法实现加锁和解锁. 注:之所以增加拓展方法,是因为合理使用拓展类(方法),可以让程序更 ...

随机推荐

  1. 如何完全卸载VS2010(亲自体验过) (转)

    1.首先用360卸载,当卸载完成后,提示有残余的话,就强力清除 2,接着,下载IobitUninstaller工具 3.按照下面进行卸载 1.Microsoft .NET Framework 4 框架 ...

  2. spring使用 RestTemplate 来进行http访问

    https://www.jianshu.com/p/2b03a812d588 https://my.oschina.net/sdlvzg/blog/1800395 异常:org.springframe ...

  3. R实战 第六篇:数据变换(aggregate+dplyr)

    数据分析的工作,80%的时间耗费在处理数据上,而数据处理的主要过程可以分为:分离-操作-结合(Split-Apply-Combine),也就是说,首先,把数据根据特定的字段分组,每个分组都是独立的:然 ...

  4. Asp.Net_上传文件(ftp、webClient、webService)

    第一种:通过FTP来上传文件 首先,在另外一台服务器上设置好FTP服务,并创建好允许上传的用户和密码,然后,在ASP.NET里就可以直接将文件上传到这台 FTP 服务器上了.代码如下: <%@ ...

  5. 使用顽灯浏览器执行H5游戏辅助挂机

    前一篇<使用Fidder从安卓模拟器获取APP内H5游戏网址>我们获取到了APP内H5游戏的网址,那么接下来我们使用辅助工具做一些日常任务,如:每天晚上20点做副本,定时喊话,自动清理包裹 ...

  6. 在Windows上安装配置Git

    用安装 https://git-scm.com/ 官网下载安装包 (官网有安装步骤 https://git-scm.com/book/zh/v1/%E8%B5%B7%E6%AD%A5-%E5%AE%8 ...

  7. Unity攻击敌人时产生泛白效果

    Shader的代码如下,主要是将透明度为1的像素点输出为白色,其中_BeAttack表示角色被攻击的泛白状态 // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_M ...

  8. 华为笔试——C++字符串四则运算的实现

    题目:字符串四则运算的实现 有字符串表示的一个四则运算表达式,要求计算出该表达式的正确数值.四则运算即:加减乘除"+-*/",另外该表达式中的数字只能是1位(数值范围0~9),运算 ...

  9. IDA Pro 在CSAPP lab2中的使用

    在做lab2的时候,偶然的情况下,发现了IDA pro这样一个反汇编工具,总的来说对于lab2这样的小实验读懂代码的大体功能是有作用的,但对于想要具体明白某一条指令的执行过程,又显得不足,到最后还是需 ...

  10. 《Linux内核分析》 第四周

    [李行之 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000] <Linux内 ...