分布式系统基础知识

CAP定理

布鲁尔定理,它指出对于一个分布式计算系统来说,不可能同时满足一下三点:

  • 一致性(Consistency): 所有节点访问同一份最新的数据副本
  • 可用性(Availability):每次请求都能获取到非错的响应-但是不保证获取的数据为最新数据
  • 分区容错性(Partition tolerance):系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,必须就当前操作在C和A之间做出选择

分布式数据调度

  • CA:关注一致性和可用性,它需要非常严格的全体一致的协议
  • CP:关注一致性和分区容忍性,关注系统里大多数人的一致性
  • AP:关注可用性和分区容忍性,无法达成一致性

2PC - 二阶段提交

  1. 表决阶段:协调者向所有参与者发送一个vote request,参与者确认后返回VOTE_COMMIT/VOTE_ABORT
  2. 提交阶段:协调者收到所有参与者的表决信息,如果参与者一致认为可以提交事务,那么协调者发送GLOBAL_COMMIT/GLOBAL_ABORT

3PC

Gossip算法

  • Push: 发起信息交换的节点A随机选择联系节点B,并向其发送自己的信息,节点B在收到信息后更新比自己新的数据,一般拥有新信息的节点才会作为发起节点
  • Pull:发起信息交换的节点A随机选择联系节点B,并从对方获取信息。一般无新信息的节点才会作为发起节点

Paxos算法

Neat Algorithms - Paxos

Raft算法

分成以下部分:

  • Leader Selection
  • Log Replication
  • Safety
  • Membership Changes

Raft - The Secret Lives of Data

异步通讯

请求响应式

发送方(sender)会直接请求接收方(receiver),被请求方接收到请求后,直接返回 - 收到请求,正在处理

  • 轮询模式
  • 回调模式

订阅方式

receiver订阅sender的消息,sender会把相关的消息或者数据放到receiver所订阅的队列中,而接收方会从队列中获取数据。

通过Broker的方式

Broker中间人

  • 必须是高可用的
  • 必须是高性能而且可以水平扩展的
  • 必须是可以持久化不丢数据的

分布式事务

ACID

  • 原子性(Atomicity): 整个事务中的所有操作,要么全部完成、要么全部失败,不可能在中间某个环节
  • 一致性(Consistency):在事务开始之前和事务结束之后,数据库的完整性约束没有被破坏
  • 隔离性(Isolation):两个事务的执行是互不干扰的,一个事务不可能看到其他事务运行中间某一时刻的数据
  • 持久性(Durability):在事务完成后,该事务对数据库的所做的更改便持久地保存在数据库之中,并不会被回滚。

BASE

  • Base Availability: 基本可用。系统可以出现暂时不可用的状态,而后面会快速恢复
  • Soft-state: 软状态。介于”有状态”Stateful和“无状态”Stateless的服务的一种中间状态。为了提高性能,可以让服务暂时保存一些状态或数据,这些状态和数据不是强一致性的。
  • Eventual Consistency: 最终一致性,系统在一个短暂的时间段内是不一致的,但最终整个系统看到的数据是一致的。

幂等性

一次和多次请求资源某一个资源应该具备同样的副作用:f(x) = f(f(x))

调用创建订单接口,第一次调用网络超时了,调用方应该怎么办?

  • 下游系统提供查询接口,超时之后查询一次,然后再做判断
  • 下游系统保证接口幂等,重新发起创建订单接口的调用,由下游系统保证一次和多次的请求结果一致

关键在于:全局唯一ID

弹性设计

熔断

熔断器的几种状态:

  • 闭合状态(Closed)
  • 断开状态(Open)
  • 半开状态(Half-Open):允许应用程序一定数量的请求去调用服务

限流

限流的策略:

  • 拒绝服务
  • 服务降级
  • 特权请求
  • 延时处理
  • 弹性收缩

限流的实现:

  • 计数器
  • 队列算法
  • 漏斗算法
  • 令牌桶算法
  • 基于响应时间的动态限流

降级

  • 降低一致性:从强一致性变成最终一致性
  • 停止次要功能:停止访问不重要的功能,从而释放出更多的资源
  • 简化功能:把一些功能简化掉

性能设计

缓存

Cache Aside

  • 失效:应用程序先从Cache取数据,如果没有得到,则从数据库中取数据,成功后,放到缓存后
  • 命中:应用程序从Cache中取数据,取到后返回
  • 更新:先把数据存到数据库中,成功后,再让缓存失效

Read/Write Through

  • Read Through: 查询操作中更新缓存,当缓存失效时,Read Through缓存服务自己加载新数据
  • Write Through: 当数据更新得时候,如果没有命中缓存,直接更新数据库;如果命中缓存,则更新缓存,然后由Cache自己更新数据库

Write Behind Caching

在更新数据的时候,只更新缓存,不更新数据库,缓存会异步地批量地更新数据库,而且可以合并同一个数据的多次操作

异步处理

数据库扩展

读写分离

优势:

  • 容易实现
  • 业务隔离性好
  • 分担数据库的读操作

劣势:

  • 写库有单点故障问题
  • 同步不实时

分库分表 Sharding

分片策略必须依赖业务知识

秒杀

活动:苹果手机5折抢购,限量100台,预计一百万人抢购,系统理论处理能力1000TPS

挑战:

  • 网络
  • 数据库:单一记录热点数据

分布式锁

type Counter struct {
	mu  sync.Mutex
	val int32
}

func (c *Counter) increase() {
	c.mu.Lock()
	c.val++
	c.mu.Unlock()
}


func BenchmarkAtomicSingleGoroutine(b *testing.B) {
	var val int32
	for i := 0; i < b.N; i++ {
		atomic.AddInt32(&val, 1)
	}
	fmt.Println(val)
}

func BenchmarkMutexSingleGoroutine(b *testing.B) {
	c := new(Counter)

	for i := 0; i < b.N; i++ {
		c.increase()
	}
	fmt.Println(c.val)
}
Testcase Result
BenchmarkAtomicSingleGoroutine 5.36 ns/op
BenchmarkAtomicSingleGoroutine 15.4 ns/op
BenchmarkAtomicMultipleGoroutines 584 ns/op
BenchmarkMutexMultipleGoroutines 624 ns/op

CAS: Compare-and-Swap

原子操作:不可被中断的一个或一系列操作

  • MySQL
  • Redis
  • ZooKeeper

问题点:

  • 如果获得锁的进程挂掉:锁服务加上过期时间
  • 如果锁服务自动解锁,新的进程拿到锁,原进程以为自己还有锁
  • 锁服务高可用

学习金字塔