本文章为你收集了分布式的基础理论,罗列了常见的分布式应用场景的实现方案:分布式锁,分布式事务,分布式主键ID(美团的Leaf),服务限流算法,一致性hash算法。同时每一部份内容笔者都详细的进行了收集整理并分析了每一种方案的优缺点。笔者但愿该文章可以成为网速比较全面的汇总文章,能为读者带来比较系统的讲解。若是读者发现文章内还有收集不全以及错误的地方还请在评论区留言,笔者会尽快完善文章内容。谢谢!
CAP理论认为:一个分布式系统最多只能同时知足,一致性,可用性,分区容错性的三项中的两项。因为分区容错性是必然存在的,因此大部分分布式软件系统都在CP和AP中作取舍redis
Base理论:即基本可用(Basically Available),软状态(Soft State),最终一致性(Eventually Consistent)。既然没法作到强一致性,那么不一样的应用可用根据本身的业务特色,采用适当的方式来达到最终一致性。Base理论是对CAP理论的实际应用算法
一种简单的副本控制协议,当客户端向一个分布式应用发送写请求的时候,只有当全部的副本节点都更新成功以后,此次写操做才算成功。不然视为失败。这下降了写操做的可用性,提升了读操做的可用性。sql
假设有N个副本,客户端向一个分布式应用发送写请求的时候,若是有W个副本更新成功以后,此次写操做才算成功。则读操做最多须要读N-W个副本就能读取到最新的结果。
Quorm没法保证强一致性,它是分布式系统中经常使用的一种机制,用来保证数据冗余的最终一致性的投票算法。Kafka的ISR机制有点相似该机制。数据库
在Paxos协议中,一共有三类角色节点segmentfault
分布式事务解决方案有 两阶段提交协议,三阶段提交协议,TCC分段提交,基于消息队列的最终一致性缓存
缺点:安全
为解决两阶段提交协议中,公共资源占用堵塞的问题,三阶段提交协议中协调者和参与者都引入了超时机制,而后把两阶段提交协议里的第一个阶段拆分为两步:先询问(CanCommit),再锁资源(PreCommit),再最后提交(DoCommit)。服务器
PreCommit:根据CanCommit响应状况有如下两种执行状况。网络
DoCommit:阶段根据PreCommit的响应也有两种执行状况。并发
缺点:在DoCommit阶段中,假设协调者发出了事务commit的通知仅被一部分参与者所收到并执行,其他的参与者则由于没有收到通知一直处于阻塞状态,这时候就产生了数据的不一致性。
TCC:TCC是支付宝提出的分布式事务解决方案,每一个分布式事务的参与者都须要实现3个接口:try、confirm、cancel。
优势
1. TCC解决了跨服务的业务操做原子性问题,可让应用本身定义数据库操做的粒度,下降锁冲突,提升系统的业务吞吐量。 2. TCC的每一阶段都由业务本身控制,避免了长事务,提升了性能。
缺点
1. 业务侵入性强:业务逻辑必须都要实现Try,Confirm,Cancel三个操做
异常状况

解决方法:记录事务执行状态,若是执行过了,就再也不执行。
接口幂等性:指的是在调用方屡次调用的状况下,接口最终获得的数据是一致的。查询接口具备自然的幂等性。
XA事务是基于两阶段提交协议的,XA规范主要定义了事务协调者和资源管理器之间的接口。
XA事务执行流程与两阶段提交协议差很少。
MySQL中的XA事务有两种状况,内部XA和外部XA。若是事务发生在MySQL服务器单机上使用内部XA,若是事务发生在多个外部节点上,使用外部XA。
内部XA: Mysql会同时维护binlog日志与InnoDB的redolog,为了保证两个日志一致性,MySql使用了XA事务。当有事务提交时:
1. 第一步:InnoDB进入Prepare阶段,将事务的XID写入redo日志。binlog不作任何操做。 2. 第二步:进行写binlog日志,也将XID写入binlog。 3. 第三部:调用InnoDB引擎的Commit完成事务的提交。而后将Commit信息写入redo日志。
得到锁:当要锁住某一个资源时,就在表中插入对应的一条记录。
释放锁:删除对应的记录。
基于数据库实现分布式锁的方案实现简单,但有不少的问题存在。
利用Zookeeper支持的临时顺序节点的特性,能够实现分布式锁。
得到锁: 当要对某个资源加锁时,Zookeeper在该资源对应的指定的节点目录下,生成一个惟一的临时节点。其余客户端对该节点设置一个Watcher通知。
释放锁:Zookeeper删除对应的临时节点,其余客户端能够监听到节点被删除的通知,并从新竞争锁。
得到读锁:
得到写锁
释放锁
删除对应的临时节点。
原理:在获取锁以前,先查询一下以该锁为key对应的value是否存在,若存在,说明该锁被其余客户端获取了。
改进1:为了防止获取锁的客户端忽然宕机,须要在设置key的时候,指定一个过时时间,以确保即便宕机了,锁也能最后释放。经过SETNX命令设置key的值,经过EXPIRE命令设置过时时间。
改进2:因为SETNX和EXPIRE命令的执行不是原子性的,多个客户端在检验锁是否存在时会致使多个客户端都认为本身能获取到锁。Redis提供了
Set原子性命令,在设置值的同时指定过时时间。
改进3:客户端获取锁之后任务未执行完,但锁已通过期,被别的客户端获取了,这时客户端扔会释放锁,致使锁失效。能够在设置key的时候,设置value为一个随机值r,删除的时候先比较一下redis里的value是否为r再决定删除。
改进4:客户端先比较一下redis的value是否为r再决定删除,但因为比较后再删除锁不是原子的。在比较过程当中,key有可能因过时而被清除了致使一个客户端就能够获取到key。Redis并无提供相关的比较后删除的原子操做。此时释放锁的过程可使用lua脚本,redis将lua脚本的命令视为一个原子操做。
UID使用以太网卡地址、纳秒级时间、芯片ID码和许多可能的数字来生成一串惟一随机32位长度数据。
优势:性能好,本地生成,全局惟一。
缺点:
对于多台数据库,经过每台数据库的起始值增值和自增值的跨度,能够实现全局的自增ID。以4台数据库为例,以下表
数据库编号 | 起始值增值 | 自增值的跨度 | 生成的主键序列 |
---|---|---|---|
1 | 1 | 4 | [1,5,9,13,17,21,25,29.....] |
2 | 2 | 4 | [2,6,10,14,18,22,26,30....] |
3 | 3 | 4 | [3,7,11,15,19,23,27,31....] |
4 | 4 | 4 | [4,8,12,16,20,24,28,32....] |
优势:容易存储,能够直接用数据库存储。
缺点:
snowflake生成id的结果是一个64bit大小的整数。由一位标识位,41个比特位的时间戳,10位的机器位,能够标识1024台机器,还有就是10比特位的自增序列化组成。结构以下:
优势:趋势递增,不依赖第三方组件(数据库等),性能更高,能够根据自身业务特色动态分配bit位。
缺点:强依赖机器时钟,若是出现时钟回拨,那么整个系统生成的ID将会不可用。
leaf提供了的两种模式。
Segment模式在以前数据库方案基础之上进行了优化。该模式不是每次都获取ID都操做一次数据库,而是异步的一次性的从数据库中取N个ID组成一个号段,而后放入本地缓存。同时采用双buffer 方法,在第一个号段下发了必定的百分比时,就会有另外一个线程启动来获取并更新下一个号段的缓存数据。
优势:
缺点:
美团Leaf的Snowflake像较于普通的Snowflake,有两点改进。
从第一个请求进来开始计时,在接下去的1s内,每来一个请求,就把计数加1,若是累加的数字达到了100,那么后续的请求就会被所有拒绝。等到1s结束后,把计数恢复成0,从新开始计数.可以使用redis的incr原子自增性和线程安全便可轻松实现。
若是我在单位时间1ms内的前10ms,已经经过了100个请求,那后面的990ms,请求所有会被拒绝,即:突刺现象。
滑动窗口算法是将时间周期分为N个小周期,分别记录每一个小周期内访问次数,而且根据时间滑动删除过时的小周期,滑动窗口的格子划分的越多,那么滑动窗口的滚动就越平滑,限流的统计就会越精确
算法内部有一个容器,无论上面流量多大,下面流出的速度始终保持不变。能够准备一个队列,用来保存请求,另外经过一个线程池按期从队列中获取请求并执行,能够一次性获取多个并发执行。
算法中存在一种机制,以必定的速率往桶中放令牌。每次请求调用须要先获取令牌,只有拿到令牌,才有机会继续执行,不然选择选择等待可用的令牌、或者直接拒绝。能够准备一个队列,用来保存令牌,另外经过一个线程池按期生成令牌放到队列中,每来一个请求,就从队列中获取一个令牌,并继续执行。guava的RateLimiter能够简单生成一个令牌限流器。
集群限流:每次有相关操做的时候,就向redis服务器发送一个incr命令,好比须要限制某个用户访问/index接口的次数,只须要拼接用户id和接口名生成redis的key,每次该用户访问此接口时,只须要对这个key执行incr命令,在这个key带上过时时间,就能够实现指定时间的访问频率。
使用Hash算法让固定的一部分请求落到同一台服务器上,这样每台服务器固定处理一部分请求起到负载均衡的做用。可是普通的hash算法伸缩性不好,当新增或者下线服务器机器时候,用户id与服务器的映射关系会大量失效。一致性hash则利用hash环对其进行了改进。
一致性hash:将全部的服务器散列值当作一个从0开始的顺时针环,而后看请求的hash值落到了hash环的那个地方,在hash环上的位置顺时针找距离最近的ip做为处理服务器