- MSA: MicroService Architecture
- REST:
- Thrift(Facebook)编码格式,自带RPC
- ProtoBuf(Google)编码格式,gRPC
- Avro(Hadoop子项目)编码格式,自带RPC
- RPC 的问题:
- 本地函数的调用是可预测的; 而网络请求是不可预测的。
- 本地函数要么返回结果/抛出异常/永远不返回; 网络请求可能会超时,无法知道请求是否成功。
- 重试网络请求,可能会发生:请求已经完成,只是响应丢失的情况。
- 网络请求依赖于网络延迟。
- 本地函数的调用可以传递引用,网络请求则需要传递整个序列化的对象。
- SOA或REST 的客户端/服务端可以用不用语言实现,RPC框架必须将数据从一种语言转换成另一种。
- 与RPC相比的优点:
- 如果接收方不可用,可以充当缓冲区,提高可靠性。
- 可以自动将消息重新发送到crash的进程,防止消息丢失。
- 避免了发送方需要知道接收方的IP,端口的情况。
- 支持一发多。
- 在逻辑上将发送方与接收方分离。
- RabbitMQ
- ActiveMQ
- Kafka(Linkedin)
- Akka
- Orleans
- Erlang OTP
- 场景:
- 扩展:单机负载承受有限
- 容错:单机故障时系统可以继续工作
- 延迟:客户遍布,全球布署
- 复制(Replication) vs 分区(Patitioning)
- 复制:在多个节点上保存相同数据的复本。
- 分区:将大块数据扩分成多个小块子集分区。不同的分区分配给不同的节点。
- 复制类型:数据同步到其他节点的方式
- 主从复制: 主写从读
- 多主复制: 多主节点,一个主节点同时扮演其他主节点的从。使用场景:
- 多数据中心: 提高可用性和性能。
- 离线客户端: 比如手机断网后,添加的数据仍然希望在下次联网后同步。
- 协作编辑: 多个用户同时编辑数据。
- 无主复制: 任何节点都接受写请求,写请求同时发给所有节点。
- 同步复制:主节点写完后等从节点确认后才完成。(可以配置为一个从节点是同步,其他从节点是异步)
- 优点:如果主节点发生故障,从节点可以继续被访问,且服务一致。
- 缺点:如果从节点无法完成确认(比如当机),则主节点会被阻塞。
- 异步复制:无需等待从节点确认后就立即返回。
- 优点:保证主节点的高可用性(可以一直响应写请求)
- 缺点:如果主节点当机,则尚未复制到从节点的数据会丢失。
- 添加新的从节点(先复制快照,再逐步追赶)
- 节点失效(比如升级内核,或者当机)
- 从节点失效:追赶式恢复
- 主节点失效:切换节点
- 确认节点失效:比如心跳超时(需合理设置超时时间,太长则恢复时花费越久,太短则可能是假失效)
- 选举新主节点:共识选举
- 新节点生效:新请求发向新节点,原节点上线后降为从节点(需丢弃当机前未完成复制的写请求)
- 同步日志
基于每条语句: 某些环境相关的函数,比如Now()会得到不同结果。 故不采用。基于预写日志: 直接复制存储结构的底层数据,比如B-Tree日志。依赖于底层实现,故不采用。基于触发器的复制: 数据改变时执行触发器,开销大,一般不采用。- 逻辑日志复制(基于行): 逻辑日志与存储逻辑分离。
- 同步滞后问题
- 读取自己写入的数据: 用户提交个人数据后,想立即查看自己的提交,如果从节点尚未同步,则看不到数据。这时可以直接从主节点读取。
- 单调读: 用户从不同从节点读取数据,刷新页面后得到的数据可能不同。对于同一个用户应该只使用同一个从节点。
- 坚持读取顺序: 对于有顺序要求的数据写入,读取时仍然要按照这个顺序,否则会发生错误。
- 多主复制时的写冲突
- 当两个用户同时编辑了wiki的标题,user1改成A->B,user2改成A->C,无法确定使用哪一个,可能有以下解决方式:
- 基于一个值: 比如写入ID,一个随机数,一个UUID,或者时间戳,数值大的获胜,但是这样容易丢失数据。
- 合并二值: 比如写入 B/C。
- 保留冲突: 比如git merge时的字符串
- 当两个用户同时编辑了wiki的标题,user1改成A->B,user2改成A->C,无法确定使用哪一个,可能有以下解决方式:
- 无主复制的节点失效情况
- 当一个节点失效时,只要一半以上成功写入,可以忽略失败的节点。
- 当读取时,并行请求多个节点,以版本号确定哪个节点的值更新而使用。
- 失效节点上线后的数据修复: 读取时检测差异后再修复/后台进程不断反熵修复。
- 不同数据库对分区的称呼:
- MongoDB | Elasticsearch: Shard
- HBase: Region
- Bigtable: tablet
- Cassandra | Riak: vnode
- Couchbase: vBucket
- 分区的目的: 将查询的负载均衡的分布在所有节点上
- 基于主键的范围分区: 比如A-C, D-F, 时间范围等。Bigtable采用这种分区,缺点是有些时候查询会导致热点。
- 基于主键的Hash分区: 通过Hash函数来划分主键的范围,以避免顺序区域过热。
- 但是这样仍然无法避免某些热点,比如某些社交媒体上的大v的热点事件,大v的用户id是不变的。
- 可以在id后加一个随机数,来分散请求,但是这样的后果是需要在应用层合并查询的结果。
- 分区与二级索引
- 分区再平衡: 当节点迁移时,需要重新分区使负载均衡。
- 动态平衡
- 手动平衡
- 请求路由: 客户端请求哪个节点?
- 客户端可以自由选择请求某个节点所在分区,如果不能处理,则转发给下一分区。(Cassandra使用gossip协议同步状态,不依赖ZK)
- 有一个路由层负责转发。(Linkedin的Espresso进行集群管理,实现路由层)
- 客户端感知分区与节点的分配关系(订阅消息感知变化,如ZooKeeper)
- ACID:
- Atomicity(原子性): 在出错时中止事务,并回滚已写入的数据。
- Consistency(前后一致性): 由应用层维护,保持数据有特定的预期,比如账户的贷款余额与贷款余额相等。
- Isolation(独立隔离性): 并发时访问同一条记录时相互隔离。
- Durability(持久性): 保证提交的数据写入了磁盘。
- 单对象写入: 当写入20kb的json数据到数据库中时
- 如果发送了10kb后断网,数据库是否存储10kb?
- 如果数据库写入数据时断电,数据库是否存储已写入数据?
- 如果另一个客户端读取正在写入的的文档,是否看到部分更新的内容?
- 多对象事务: 多数分布式存储系统不支持多对象事务,因为跨分区时难以实现,甚至带来负面影响。
- 弱隔离级别: 两个事务并发访问同一条记录时,强隔离会严重影响性能
- Read committed: 只读取或只覆盖写入已成功提交的数据(给未完成的事务加锁)
- No dirty reads: 防止读取部分写入但还未提交的数据(加锁会延迟读取,所以可以提供两个版本的数据)
- No dirty write: 推迟第二个写请求,防止第一个请求被覆盖
- Read committed: 只读取或只覆盖写入已成功提交的数据(给未完成的事务加锁)
- 快照隔离: 从某个时间点的快照中读取(不用加锁),而用写锁防止脏写。
- 防止更新丢失: 两个事务并发写时的冲突(原子写,加锁)
- 可串行性: 严格按照顺序执行
- 2PL: 二阶段锁,多个事务可以同时读取一个对象,但是一旦有写操作,必须独占访问
- 不可靠的网络(网络,乱序,硬件,超时,阻塞)
- 不可靠的时钟
- 节点暂停(垃圾回收,当机)
- 可线性化(Linearizability): 让分布式系统看起来只有一个副本,比如后查询的结果一定是基于先查询的结果。
- 如何做到线性化?
- 顺序保证
- 全序广播
- CAP 理论:: 不可能同时做到以下3者
- 一致性(Consistency): 多个副本间数据的一致
- 可用性(Availablity): 服务保持可用状态
- 分区容错性(PartitionTolerance): 在某个分区出现故障时,仍然可以提供服务
- 2PC: 2 Phase Commit,阻塞式,需要有一个协调者来协调所有参与者
- Requst for Commit(投票阶段)
- Execute Commit(完成阶段)
- 3PC: 3 Phase Commit,非阻塞式,依赖于一个故障检测器,判断节点是否当机
- CanCommit(询问)
- PreCommit(预提交)
- DoCommit(执行提交)
- 实践中的分布式事务: 由于难以保证,且有性能问题,所以云服务商基本不支持分布式事务
- 使用场景:
- 主节点选举
- 原子事务提交: 事务在某些节点成功,在某些节点失败,为了让所有节点的结果一致,要么全部成功,要么全部失败。
- ZooKeeper: 分布式应用的协调服务,功能包括:
- 分配任务给节点
- 发现服务: 找到执行某个服务需要连接的IP地址
- 成员服务: 检测哪个节点处于活跃状态
- Paxos 理论
- 算法过程
- 实现:Chubby(分布式锁服务 by Google),用于松耦合的分布式系统,Google-Chubby-PDF
- ZAB协议: ZooKeeper原子消息广播协议, 与Paxos的比较
- 实现:ZooKeeper(分布式协调服务 by Yahoo)
- Raft 一致性算法
- Gossip 协议
- 分布式系统的谬论
- 拜占廷容错
- 拜占廷将军问题
- 分布式一致性的可能性
- Paxos 问题
- PostgreSQL(9.0+)
- MySQL
- Oracle Data Guard
- SQLServer AlwaysOn Availability Groups
- Dynamo(Amazon): 分布式键值数据库,无主复制,非开源。
- Cassandra: 分布式键值数据库,无主复制,去中心化,非关系型数据库
- GFS(Google): 分布式文件系统
- HDFS(GFS的开源实现): 分布式文件系统
- Hadoop(Google):分布式存储系统(包括多个组件,比如HDFS,MapReduce,HBASE)
- BigTable(Google),分布式表格系统,GFS作为其文件存储系统,使用MapReduce处理海量数据,使用Chubby作为协同服务
- HBase(BigTable的开源实现),Hadoop HDFS作为其文件存储系统,使用MapReduce处理海量数据,使用Zookeeper作为协同服务
- MapReduce(Google): 分布式计算模型
- CRUSH: Controlled, Scalable, Decentralized Placement of Replicated Data
- Dapper(Google)
- 分布式编程模型 https://web.cs.ucdavis.edu/~pandey/Research/Papers/icdcs01.pdf
- PSync, https://www.di.ens.fr/~cezarad/popl16.pdf
- Programming Models for Distributed Computing, https://heather.miller.am/teaching/cs7680/
- Logic and Lattices for Distributed Programming, https://db.cs.berkeley.edu/papers/UCB-lattice-tr.pdf
- How we implemented consistent hashing efficiently
- Notes on Distributed Systems for Young Bloods
- High Scalability
- All Things Distributed
- Distributed Systems: Take Responsibility for Failover https://ivolo.me/distributed-systems-take-responsibility-for-failover/
- Files are hard
- On Designing and Deploying Internet-Scale Services
- 反向代理
- Forward Proxy: Acting on behalf of a requestor (or service consumer)
- Reverse Proxy: Acting on behalf of service/content producer.
- 负载均衡
- Nginx:高性能、高并发的web服务器;功能包括负载均衡、反向代理、静态内容缓存、访问控制;工作在应用层
- LVS: Linux virtual server,基于集群技术和Linux操作系统实现一个高性能、高可用的服务器;工作在网络层
- WebServer
- Apache
- Tomcat
- 容器
- Docker
- Kubernetes (k8s)
- 缓存
- Redis
- MemCache
- 协调中心
- ZooKeeper
- 消息队列
- Kafka
- RabbitMQ
- 实时数据平台
- akka
- Storm
- 离线数据平台
- Hadoop
- Spark
- DB Proxy
- cobar
- 搜索
- ElasticSearch
- solr
- 日志
- rsyslog
- elk