CAP定理
布鲁尔定理,它指出对于一个分布式计算系统来说,不可能同时满足一下三点:
- 一致性(Consistency): 所有节点访问同一份最新的数据副本
- 可用性(Availability):每次请求都能获取到非错的响应-但是不保证获取的数据为最新数据
- 分区容错性(Partition tolerance):系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,必须就当前操作在C和A之间做出选择
分布式数据调度
- CA:关注一致性和可用性,它需要非常严格的全体一致的协议
- CP:关注一致性和分区容忍性,关注系统里大多数人的一致性
- AP:关注可用性和分区容忍性,无法达成一致性
2PC - 二阶段提交
- 表决阶段:协调者向所有参与者发送一个
vote request
,参与者确认后返回VOTE_COMMIT
/VOTE_ABORT
- 提交阶段:协调者收到所有参与者的表决信息,如果参与者一致认为可以提交事务,那么协调者发送
GLOBAL_COMMIT
/GLOBAL_ABORT
3PC
Gossip算法
- Push: 发起信息交换的节点A随机选择联系节点B,并向其发送自己的信息,节点B在收到信息后更新比自己新的数据,一般拥有新信息的节点才会作为发起节点
- Pull:发起信息交换的节点A随机选择联系节点B,并从对方获取信息。一般无新信息的节点才会作为发起节点
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
问题点:
- 如果获得锁的进程挂掉:锁服务加上过期时间
- 如果锁服务自动解锁,新的进程拿到锁,原进程以为自己还有锁
- 锁服务高可用