2. 整体架构
Ambry包含负责保存和检索数据的数据节点(data node),前端节点(Frontend node)将请求经过预处理发送到后端数据节点,并且集群管理者(Cluster manager)管理并协调数据节点上的数据。数据节点之间互相复制数据,并且可以跨机房复制,并需要保证写之后读的一致性。前端提供HTTP API,包括POST,GET和DELETE对象。同样的,这个路由库可以直接被客户端调用以提升性能。在LinkedIn,这些前端节点扮演着CDN的角色。 Ambry是一个偏向于处理的存储。这意味着当一个对象放入Ambry时,这个对象的ID被返回。这简化了系统设计并促使系统去中心化。客户端可以通过这个ID访问对应对象,这也意味着Ambry内的对象是不可变的。基于Ambry建立一个键值对访问并且支持对象可变的系统非常繁琐。
3. 集群图
集群图控制拓扑,保持资源状态并协调集群操作。集群图主要包括两部分:
- 硬件布局(Hardware layout):包括机器列表,每台机器上的磁盘以及这些磁盘的空间。在运行时,它维持并检查资源信息(机器和磁盘的状态),并且指定机器名还有端口用来连接并访问(plain方式和SSL方式)数据节点。
- 分片布局(Partition layout):包括分片列表,分片分部信息和他们的状态。Ambry的一个分片是集群的一个逻辑切片。每个分片都有一个数字ID,并且拥有一个可以跨机房的复制片列表,和一个状态标明他是否可以写。分片是固定大小的资源,任何集群间数据重新负载均衡都发生在分片级别。
数据节点还有前端节点都能访问集群图,并且根据他们自己对于集群图的缓存视图来工作。例如选择一个可用机器,向下发放复制分片,识别一个对象的位置。
4.存储
存储节点存放管理不同分片的备份。每个存储节点包含N个磁盘,备份分布在这些磁盘上。这些备份的结构还有管理都是一样的。
4.1 持久化
每个备份都是预分配日志的形式。所有的新信息都被顺序的添加到日志的尾部,一条信息包含实际的对象块和附带的元信息(系统和用户)。这使得写的吞吐量提高并且避免磁盘碎片。这些日志有两个用处:用来存储真实对象和作为用来记录所有写操作的事务日志。用一个索引来记录所有对象ID还有日志中的消息。这个索引它本身是一个按照由最新到最旧排好序的条目,每个条目包含每条信息在log中的偏移量,消息属性和一些其他域。每一块索引还会维护一个布隆过滤器来优化磁盘IO访问时间。持久化层提供一个可调的配置来控制何时数据刷到磁盘中。默认的,系统配置为牺牲一定的持久性来提高性能。
4.2 零拷贝
从WIKI的定义中,我们看到“零拷贝”是指计算机操作的过程中,CPU不需要为数据在内存之间的拷贝消耗资源。而它通常是指计算机在网络上发送文件时,不需要将文件内容拷贝到用户空间(User Space)而直接在内核空间(Kernel Space)中传输到网络的方式。 传统方式
零拷贝方式
支持大对象处理的一个重要设计挑战就是确保所有对象端到端传输,而且控制内存使用边界。所有的读操作利用linux系统中的sendFile API实现零拷贝方式从日志文件中传输到网络IO。这使得系统调用减少,数据不再读入用户内存使得性能得到提高并且缓冲池管理减少很多。
4.3 故障恢复
在一个有状态系统故障恢复是最困难的。故障恢复是必须的,因为系统和机器可能会故障,而且磁盘数据会损坏。在启动时,存储层读取日志最后已知的检查点并恢复索引。恢复过程也会产生内存中的记录。日志是最终可信源(source of truth)而且是持久化的。这确保索引没必要保存对象,并且提供了从零恢复索引的能力。
4.4 备份
存储节点也负责在同步时保证分片的备份。每个节点都有一个复制服务,用来保证本地备份和远程节点的任意一个备份都一致。所用的协议用来发现两份复制之间的差异并修补。为了确保备份高效而且可靠,我们做了很多优化。一些核心优化是: - 批处理两个节点之间的所有分片操作,来提高吞吐量。 - 利用不同的线程池来负责内部和外部备份,来提高隔离性。 - 内存中记录保存最近的对象 - 优先处理落后的备份来提高磁盘修复速度 内存中记录正好切合最常用的场景,就是备份在跟进读取最新的对象。备份也会在每个对象被复制时生成变化快照,用于离线监控