OpenStack Swift 对象存储方案知识地图:
1 Swift概述
1.1 OpenStack Swift项目简述
OpenStack Swift 开源项目提供了弹性可伸缩、高可用的分布式对象存储服务,适合存储大规模非结构化数据。本文将深入介绍 Swift 的基本设计原理、对称式的系统架构和 RESTful API。
OpenStack Object Storage ( Swift ) 是 OpenStack 开源云计算项目的子项目之一。 Swift的目的是使用普通硬件来构建冗余的、可扩展的分布式对象存储集群,存储容量可达PB级。
Swift并不是文件系统或者实时的数据存储系统,它是对象存储,用于永久类型的静态数据的长期存储,这些数据可以检索、调整,必要时进行更新。最适合存储的数据类型的例子是虚拟机镜像、图片存储、邮件存储和存档备份。
Swift无需采用RAID(磁盘冗余阵列),也没有中心单元或主控结点。Swift通过在软件层面引入一致性哈希技术和数据冗余性,牺牲一定程度的数据一致性来达到高可用性(High Availability,简称HA)和可伸缩性,支持多租户模式、容器和对象读写操作,适合解决互联网的应用场景下非结构化数据存储问题。
1.2 Swift技术特性
1.2.1 Swift的主要特征
Swift的主要特性如下:
- 极高的数据持久性(Durability)。
- 完全对称的系统架构:“对称”意味着Swift中各节点可以完全对等,能极大地降低系统维护成本。
- 无限的可扩展性:一是数据存储容量无限可扩展;二是Swift性能(如QPS、吞吐量等)可线性提升。
- 无单点故障:Swift的元数据存储是完全均匀随机分布的,并且与对象文件存储一样,元数据也会存储多份。整个Swift集群中,也没有一个角色是单点的,并且在架构和设计上保证无单点业务是有效的。
- 简单、可依赖。
1.2.2 Swift和HDFS的技术差异
Swift和Hadoop分布式文件系统(HDFS)都有着相似的目的:实现冗余、快速、联网的存储,它们的技术差异如下:
- 在Swift中,元数据呈分布式,跨集群复制。而在HDFS使用了中央系统来维护文件元数据(Namenode,名称节点),这对HDFS来说无异于单一故障点,因而扩展到规模非常大的环境显得更困难。
- Swift在设计时考虑到了多租户架构,而HDFS没有多租户架构这个概念。
- 在Swift中,文件可以写入多次;在并发操作环境下,以最近一次操作为准。而在HDFS中,文件写入一次,而且每次只能有一个文件写入。
- Swift用Python来编写,而HDFS用Java来编写。
- Swift被设计成了一种比较通用的存储解决方案,能够可靠地存储数量非常多的大小不一的文件;而HDFS被设计成可以存储数量中等的大文件(HDFS针对更庞大的文件作了优化),以支持数据处理。
2 Swift系统架构
2.1 产品分层架构
Swift 的总体分层架构如下:
逻辑架构图
2.2 组件逻辑
Swift主要组件图
Swift 组件包括:
- 代理服务(Proxy Server):对外提供对象服务 API,转发请求至相应的账户、容器或对象服务
- 认证服务(Authentication Server):验证用户的身份信息,并获得一个访问令牌(Token)
- 缓存服务(Cache Server):缓存令牌,账户和容器信息,但不会缓存对象本身的数据
- 账户服务(Account Server):提供账户元数据和统计信息,并维护所含容器列表的服务
- 容器服务(Container Server):提供容器元数据和统计信息,并维护所含对象列表的服务
- 对象服务(Object Server):提供对象元数据和内容服务,每个对象会以文件存储在文件系统中
- 复制服务(Replicator):检测本地副本和远程副本是否一致,采用推式(Push)更新远程副本
- 更新服务(Updater):对象内容的更新
- 审计服务(Auditor):检查对象、容器和账户的完整性,如果发现错误,文件将被隔离
- 账户清理服务(Account Reaper):移除被标记为删除的账户,删除其所包含的所有容器和对象
2.3 物理部署架构
物理架构图
2.4 网络架构
以一个对外提供对象存储服务的集群为例,其网络架构可以为:
- 外部流量被放在一个单独的(上图中紫色)VLAN 中,终点为 LB
- 控制(管理)网络连接所有节点
- Swift 前端(front end / public)网络连接 LB 和 所有 Proxy server 节点
- Swift 后端 (backend / private) 网络连接所有 Proxy server 节点和 存储节点
- 需要的话,还可以从后端网络中分离出复制(replication)网络
在网络带宽选择上,
- 考虑到复制数据的容量较大(往往是几个TB起步),后端网络往往是用 10GbE 网络
- 根据前端负载,前端网络可以使用 1GbE 网络,或者有条件时使用 10GbE 网络
- 管理/IPMI网络往往是用 1GbE 网络
这是 SwiftStack 的一个例子:
3 关键技术
3.1 一致性哈希(ConsistentHashing)
在分布式对象存储中,一个关键问题是数据该如何存放。 Swift是基于一致性哈希技术,通过计算可将对象均匀分布到虚拟空间的虚拟节点上,在增加或删除节点时可大大减少需移动的数据量;虚拟空间大小通常采用2的n次幂,便于进行高效的移位操作;然后通过独特的数据结构 Ring(环)再将虚拟节点映射到实际的物理存储设备上,完成寻址过程。
图1. 一致性哈希环结构
衡量一致性哈希的4个指标:
- 平衡性(Balance):平衡性是指Hash的结果能够尽可能分布均匀,充分利用所有缓存空间。
- 单调性(Monotonicity):单调性是指如果已经有一些内容通过哈希分派到了相应的缓冲中,又有新的缓冲加入到系统中。哈希的结果应能够保证原有已分配的内容可以被映射到新的缓冲中去,而不会被映射到旧的缓冲集合中的其他缓冲区。
- 分散性(Spread):分散性定义了分布式环境中,不同终端通过Hash过程将内容映射至缓存上时,因可见缓存不同,Hash结果不一致,相同的内容被映射至不同的缓冲区。
- 负载(Load):负载是对分散性要求的另一个纬度。既然不同的终端可以将相同的内容映射到不同的缓冲区中,那么对于一个特定的缓冲区而言,也可能被不同的用户映射为不同的内容。
Swift使用该算法的主要目的是在改变集群的node数量时(增加/删除服务器),能够尽可能少地改变已存在key和node的映射关系,以满足单调性。
考虑到哈希算法在node较少的情况下,改变node数会带来巨大的数据迁移。为了解决这种情况,一致性哈希引入了“虚拟节点”(vnode,也称为partition)的概念: “虚拟节点”是实际节点在环形空间的复制品,一个实际节点对应了若干个“虚拟节点”,“虚拟节点”在哈希空间中以哈希值排列。
总的来说,Swift中存在两种映射关系,对于一个文件,通过哈希算法(MD5)以及移位运算找到对应的虚节点(一对一的映射关系);虚节点再通过映射关系(ring文件中二维数组)找到对应的设备(多对多的映射关系),这样就完成了一个文件存储在设备上的映射。
图2. 对象、虚结点、节点间的映射关系
(这一段未讲清楚)在设置虚结点数的时候,需要对系统预期的规模做充分考虑,假如集群的规模不会超过6000个结点,那么可以将虚结点数设置为结点数的100倍。这样,变动任意一个结点的负载仅影响1%的数据项。此时有6百万个vnode数,使用2bytes来存储结点数(0~65535)。基本的内存占用是6*(10^6)*2bytes=12Mb,对于服务器来说完全可以承受。
假设有65536(2^16)个node,有128(2^7)倍的partition数(2^23,则PARTITION_POWER=23)。由于MD5码是32位的,使用PARTITION_SHIFT(等于32- PARTITION_POWER)将数据项的MD5哈希值映射到partition的2^23的空间中。
3.2 数据一致性模型(ConsistencyModel)
按照Eric Brewer的CAP(Consistency,Availability,PartitionTolerance)理论,无法同时满足3个方面,Swift放弃严格一致性(满足ACID事务级别),而采用最终一致性模型(Eventual Consistency),来达到高可用性和无限水平扩展能力。
为了实现这一目标,Swift采用Quorum仲裁协议(Quorum有法定投票人数的含义):
- 定义:N:数据的副本总数;W:写操作被确认接受的副本数量;R:读操作的副本数量。
- 强一致性:R+W>N,以保证对副本的读写操作会产生交集,从而保证可以读取到最新版本;如果 W=N,R=1,则需要全部更新,适合大量读少量写操作场景下的强一致性;如果 R=N,W=1,则只更新一个副本,通过读取全部副本来得到最新版本,适合大量写少量读场景下的强一致性。
- 弱一致性:R+W<=N,如果读写操作的副本集合不产生交集,就可能会读到脏数据;适合对一致性要求比较低的场景。
Swift针对的是读写都比较频繁的场景,所以采用了比较折中的策略,即写操作需要满足至少一半以上成功W>N/2,再保证读操作与写操作的副本集合至少产生一个交集,即R+W>N。在分布式系统中,数据的单点是不允许存在的。线上正常存在的replica数量是1的话将非常危险的,因为一旦这个replica再次错误,就可能发生数据的永久性错误。假如我们把N设置成为2,那么,只要有一个存储节点发生损坏,就会有单点的存在。所以N必须大于2。但N越高,系统的维护和整体成本就越高。所以,工业界通常把N设置为3。
Swift默认配置是N=3,W=2>N/2,R=1或2,即每个对象会存在3个副本,这些副本会被尽量存储在不同区域的节点上;W=2表示至少需要更新2个副本才算写成功。
当R=1时,意味着某一个读操作成功便立刻返回,此种情况下可能会读取到旧版本(弱一致性模型)。
当R=2时,需要通过在读操作请求头中增加x-newest=true参数来同时读取2个副本的元数据信息,然后比较时间戳来确定哪个是最新版本(强一致性模型)。
如果数据出现了不一致,后台服务进程会在一定时间窗口内通过检测和复制协议来完成数据同步,从而保证达到最终一致性。
图3. Quorum协议示例
但其实根据源码分析来看,Quorum仲裁机制必须要等到3个Response都返回之后才会选举出最佳的Response返回给客户端(即使是Timeout,也会等待Storage Server返回),所以一个集群一旦遇到有一个慢节点,将非常影响Swift的性能,导致写入速度上不去(这是一个可以改进的地方!)
3.3 环(Ring)
3.3.1 Ring组件简述
Ring是swfit中最重要的组件,用于记录存储对象与物理位置之间的映射关系,当用户需要对Account、Container、Object操作时,就需要查询对应的Ring文件(Account、Container、Object都有自己对应的Ring),Ring 使用Region(最近几个版本中新加入的)、Zone、Device、Partition和Replica来维护这些信息,对于每一个对象,根据你在部署swift设置的Replica数量,集群中会存有Replica个对象。部署完成后,相应的Ring文件也创建,在/etc/swift中会存有Ring文件。
环是为了将虚拟节点(partition,分区)均衡地映射到一组物理存储设备上,并提供一定的冗余度而设计的,其数据结构由以下信息组成:
- 存储设备列表,设备信息包括唯一标识号(id)、区域号(zone)、权重(weight)、IP 地址(ip)、端口(port)、设备名称(device)、元数据(meta);
- 分区到设备映射关系(replica2part2dev_id 数组);
- 计算分区号的位移(part_shift 整数)
3.3.2 Ring的数据结构实现
Ring 的数据结构由三个顶层域构成,其中:
- List of Devices,表示集群中设备的列表,在Ring类内部被称为devs;
- Partition Assignment List,用于存放每个replica与device间映射关系,在Ring类内部被称为_replica2part2dev_id;
- Partition Shift Value,表示计算数据hash的移位量,在Ring类内部称为_part_shift。
使用Python 读取/etc/swift/object.ring.gz存放的数据,可以获得以devs、 part_shift、 replica2part2dev_id 为key的dict类数据。
3.3.3 Ring的运作机制流程
以查找一个对象的计算过程为例:
图4. 环的运作机制
使用对象的层次结构 account/container/object 作为键,使用 MD5 散列算法得到一个散列值,对该散列值的前 4 个字节进行右移操作得到分区索引号,移动位数由上面的 part_shift 设置指定(一致性哈希映射到虚拟节点);按照分区索引号在分区到设备映射表(replica2part2dev_id)里查找该对象所在分区的对应的所有设备编号,这些设备会被尽量选择部署在不同区域(Zone)内,区域只是个抽象概念,它可以是某台机器,某个机架,甚至某个建筑内的机群,以提供最高级别的冗余性,建议至少部署 5 个区域;权重参数是个相对值,可以来根据磁盘的大小来调节,权重越大表示可分配的空间越多,可部署更多的分区。
Swift为账户、容器和对象分别定义了的Ring,其查找过程是相同的。Ring中每个partition在集群中都默认有3个replica。每个partition的位置由ring来维护,并存储在映射中。
Ring使用zone来保证数据的物理隔离。每个partition的replica都确保放在了不同的zone中。Zone只是个抽象概念,它可以是一个磁盘(disk drive),一个服务器(server),一个机架(cabinet),一个交换机(switch),甚至是一个数据中心(datacenter),以提供最高级别的冗余性,建议至少部署5个zone。
权重参数是个相对值,可以来根据磁盘的大小来调节,权重越大表示可分配的空间越多,可部署更多的分区。
当集群中发生存储节点宕机、新增(删)存储节点、新增(删)zone等必须改变partition和node间的映射关系时,还可以对Ring文件通过重新平衡(rebalance)来进行更新。当虚节点需要移动时,环会确保一次移动最少数量的虚节点数,并且一次只移动一个虚节点的一个副本。
总的来说,Ring引入一致性哈希的原因是为了减少由于增加结点导致数据项移动的数量来提高单调性;引入partition的原因是为了减少由于节点数过少导致移动过多的数据项(数据负载不均衡);引入replica的原因是防止数据单点、提高冗余性;引入zone的原因是为了保证分区容忍性;引入weight的原因是为了保证partition分配的均衡。
4 架构设计
4.1 Swift整体架构设计
Swift采用完全对称、面向资源的分布式系统架构设计,所有组件都可扩展,避免因单点失效而扩散并影响整个系统运转;通信方式采用非阻塞式 I/O 模式,提高了系统吞吐和响应能力。
Swift系统架构图
Swift组件包括:
- 代理服务(ProxyServer):Swift通过Proxy Server向外提供基于HTTP的REST服务接口,会根据环的信息来查找服务地址并转发用户请求至相应的账户、容器或者对象,进行CRUD(增删改查)等操作。由于采用无状态的REST请求协议,可以进行横向扩展来均衡负载。在访问Swift服务之前,需要先通过认证服务获取访问令牌,然后在发送的请求中加入头部信息 X-Auth-Token。代理服务器负责Swift架构的其余组件间的相互通信。代理服务器也处理大量的失败请求。例如,如果对于某个对象PUT请求时,某个存储节点不可用,它将会查询环可传送的服务器并转发请求。对象以流的形式到达(来自) 对象服务器,它们直接从代理服务器传送到(来自)用户—代理服务器并不缓冲它们。
- 认证服务(AuthenticationServer):验证访问用户的身份信息,并获得一个对象访问令牌(Token),在一定的时间内会一直有效;验证访问令牌的有效性并缓存下来直至过期时间。
- 缓存服务(CacheServer):缓存的内容包括对象服务令牌,账户和容器的存在信息,但不会缓存对象本身的数据;缓存服务可采用Memcached集群,Swift会使用一致性哈希算法来分配缓存地址。
- 账户服务(AccountServer):提供账户元数据和统计信息,并维护所含容器列表的服务,每个账户的信息被存储在一个SQLite数据库中。
- 容器服务(ContainerServer):提供容器元数据和统计信息(比如对象的总数,容器的使用情况等),并维护所含对象列表的服务。容器服务并不知道对象存在哪,只知道指定容器里存的哪些对象。 这些对象信息以SQLite数据库文件的形式存储,和对象一样在集群上做类似的备份。
- 对象服务(ObjectServer):提供对象元数据和内容服务,可以用来存储、检索和删除本地设备上的对象。在文件系统中,对象以二进制文件的形式存储,它的元数据存储在文件系统的扩展属性(xattr)中,建议采用默认支持扩展属性(xattr)的XFS文件系统。每个对象使用对象名称的哈希值和操作的时间戳组成的路径来存储。最后一次写操作总可以成功,并确保最新一次的对象版本将会被处理。删除也被视为文件的一个版本(一个以".ts"结尾的0字节文件,ts表示墓碑)。
- 复制服务(Replicator):会检测本地分区副本和远程副本是否一致,具体是通过对比哈希文件和高级水印来完成,发现不一致时会采用推式(Push)更新远程副本:对于对象的复制,更新只是使用rsync同步文件到对等节点。帐号和容器的复制通过HTTP或rsync来推送整个数据库文件上丢失的记录;另外一个任务是确保被标记删除的对象从文件系统中移除:当有一项(对象、容器、或者帐号)被删除,则一个墓碑文件被设置作为该项的最新版本。复制器将会检测到该墓碑文件并确保将它从整个系统中移除。
- 更新服务(Updater):当对象由于高负载或者系统故障等原因而无法立即更新时,任务将会被序列化到在本地文件系统中进行排队,以便服务恢复后进行异步更新;例如成功创建对象后容器服务器没有及时更新对象列表,这个时候容器的更新操作就会进入排队中,更新服务会在系统恢复正常后扫描队列并进行相应的更新处理。
- 审计服务(Auditor):在本地服务器上会反复地爬取来检查对象,容器和账户的完整性,如果发现比特级的错误,文件将被隔离,并复制其他的副本以覆盖本地损坏的副本;其他类型的错误(比如在任何一个容器服务器中都找不到所需的对象列表)会被记录到日志中。
- 账户清理服务(AccountReaper):移除被标记为删除的账户,删除其所包含的所有容器和对象。删除账号的过程是相当直接的。对于每个账号中的容器,每个对象先被删除然后容器被删除。任何失败的删除请求将不会阻止整个过程,但是将会导致整个过程最终失败(例如,如果一个对象的删除超时,容器将不能被删除,因此账号也不能被删除)。整个处理过程即使遭遇失败也继续执行,这样它不会因为一个麻烦的问题而中止恢复集群空间。账号收割器将会继续不断地尝试删除账号直到它最终变为空,此时数据库在db_replicator中回收处理,最终移除这个数据库文件。
Swift支持的所有操作可以总结为下表:
表1 SwiftRESTful API总结
4.2 Swift数据存储逻辑
4.2.1 Swift数据模型
Swift采用层次数据模型,共设三层逻辑结构:Account/Container/Object(账户/容器/对象),每层节点数均没有限制,可以任意扩展:
- Account: 账户/租户,Swift 是天生支持多租户的。如果使用 OpenStack Keystone 做用户校验的话,account 与 OpenStack project/tenant 的概念相同。Swift 租户的隔离性体现在metadata上,而不是体现在 object data 上。数据包括自身元数据 和 container 列表,被保存在 SQLite 数据库中。
- Container: 容器,类似于文件系统中的目录,由用户自定义,它包含自身的元数据和容器内的对象列表。数据保存在 SQLite 数据库中。在新版中,Swift 支持在容器内添加文件夹。
- Object: 对象,包括数据和数据的元数据,以文件形式保存在文件系统上。
Swift 数据模型
三类数据模型的特性如下:
- Containers 是用户创建的,用来 hold objects。
- objects 可以是 0 bytes 长度,或者包含数据。
- container 中的 object 最大大小为 5GB;超过的话,会做特殊处理。
- 每个 object 使用它的 name 来被 referenced;Swift 没有目录概念。
- 在 object name 中可以使用任意的可以被 ‘URL-encoded’ 的 字符,最大长度为 URL - coded 之后 1034 个字符。
- object name 中可以带 '/' 字符,它会带来目录结构的幻觉(illusion),比如 dir/dir2/name。即使看起来象个目录,但是它仍然只是一个 object name。此时,不需要 dir 或者 dir/dir2 container 的存在。
- 如果一个 container 所有 objects 的大小为0,那么它将看起来象一个目录。
- 客户端使用 HTTP 或者 HTTPS 访问 Swift,包括读、写、删除 objects。也支持 COPY 操作,它会创建一个新的 object,使用一个新的 object name,包含老 object 的 data。没有 rename 操作,它会首先 copy 出一个新的,然后再将老的删除。
4.2.2 选择数据存放位置
Swift 保存每个对象为多分拷贝,它按照物理位置的特点,尽量将这些拷贝放在不同的物理位置上,来保证数据的地理位置上的可靠性。它主要考虑以下几种位置属性:
- Region:地理位置上的区域,比如不同城市甚至不同国家的机房,这主要是从灾备方面考虑的。
- Zone:一个数据中心根据物理网络、供电、空调等基础设施分开的独立的域,往往将一个机架(Rack)内的服务器分在一个 Zone 内。
- Node (节点):物理服务器。
- Disk (磁盘):物理服务器上的磁盘。
Swift 在确定对象的放置位置时,会尽量将对象及其拷贝放在不会同时损失的物理位置上,示例见下图。
4.2.3 数据一致性保障
对象及其拷贝放置在某个磁盘上后,Swift 会使用Replicators、Updaters 和 Auditors 等后台服务来保证其数据的最终一致性。
- Replicator – 拷贝对象,确保系统的最终一致性(Replicate objects and make a system in a consistent state);恢复磁盘和网络错误(Recover disk failure, network outages situation)
- Updater – 更新元数据(Update metadata),从容器和账户元数据高负载导致的问题上恢复(Recover failure caused by container, account metadata high load)
- Auditor – 删除问题账户,容器和对象,然后从别的服务器上拷贝过来(Delete problematic account, container or objects and replicate from other server);恢复数据库和文件数据错误(Recover dbs or files which have bit rot problem.
其中,Replicator 服务以可配置的间隔来周期性启动,默认是30s,它以replication为最小单位,以 node 为范围,周期性地执行数据拷贝。详细过程请参考文末的参考文档。考虑到Swift 实现的是最终一致性而非强一致性,它不合适于需要数据强一致性的应用,比如银行存款和订票系统等。需要做 replication 的情形包括但不限于:
- Proxy server 在写入第三份时失败,它依然会向客户端返回成功,后台服务会写第三份拷贝。
- 后台进程发现某个replication 数据出现损坏,它会在新的位置重新写入。
- 在跨 Region 的情况下,Proxy server 只会向它所在 Region 的存储上写入,远处 region 上的数据由后台进程复杂写入。
- 在更换磁盘或者添加磁盘的情况下,数据需要重新平衡时。
4.3 Swift存储结构设计
4.3.1 Swift存储结构简述
在Storage Node上运行着Linux系统并使用了XFS文件系统,逻辑上使用一致性哈希算法将固定总数的partition映射到每个Storage Node上,每个data也使用同样的哈希算法映射到partition上。
存储内容一般放在/srv/node/sdb1之类的路径下,其目录结构如下所示:accounts、async_pending、containers、objects、quarantined和tmp。其中accounts、containers、objects分别是账号、容器、对象的存储目录,async_pending是异步待更新目录,quarantined是隔离目录,tmp是临时目录。
- objects:在objects目录下存放的是各个partition目录,其中每个partition目录是由若干个suffix_path名的目录和一个hashes.pkl文件组成,suffix_path目录下是由object的hash_path名构成的目录,在hash_path目录下存放了关于object的数据和元数据;object的数据存放在后缀为.data的文件中,它的metadata存放在以后缀为.meta的文件中,将被删除的Object以一个0字节后缀为.ts的文件存放。
- accounts:在accounts目录下存放的是各个partition,而每个partition目录是由若干个suffix_path目录组成,suffix_path目录下是由account的hsh名构成的目录,在hsh目录下存放了关于account的sqlite db;在account的db文件中,包含了account_stat、container、incoming_sync 、outgoing_sync 4张表;其中,表account_stat是记录关于account的信息,如名称、创建时间、container数统计等等;表container记录关于container的信息;表incoming_sync记录到来的同步数据项;表outgoing_sync表示推送出的同步数据项。
- containers:containers目录结构和生成过程与accounts类似,containers的db中共有5张表,其中incoming_sync和outgoing_sync的schema与accounts中的相同。其他3张表分别为container_stat、object、sqlite_sequence;表container_stat与表account_stat相似,其区别是container_stat存放的是关于container信息。
- tmp:tmp目录作为account/container/object server向partition目录内写入数据前的临时目录。例如,client向server上传某一文件,object server调用DiskFile类的mkstemp方法创建在路径为path/device/tmp的目录。在数据上传完成之后,再调用put()方法,将数据移动到相应路径。
- async_pending:async_pending存放未能及时更新而被加入更新队列的数据。本地server在与remote server建立HTTP连接或者发送数据时超时导致更新失败时,将把文件放入async_pending目录。这种情况经常发生在系统故障或者是高负荷的情况下。如果更新失败,本次更新被加入队列,然后由Updater继续处理这些失败的更新工作;account与container的db和object两者的pending文件处理方式有所不同:db的pending文件在更新完其中的一项数据之后,删除pending文件中的相应的数据项,而object的数据在更新完成之后,移动pending文件到目标目录。
- quarantined:quarantined路径用于隔离发生损坏的数据。Auditor进程会在本地服务器上每隔一段时间就扫描一次磁盘来检测account、container、object的完整性。一旦发现不完整的数据,该文件就会被隔离,该目录就称为quarantined目录。为了限制Auditor消耗过多的系统资源,其默认扫描间隔是30秒,每秒最大的扫描文件数为20,最高速率为10Mb/s。account和container的Auditor的扫描间隔比object要长得多。
隔离对象的处理流程
4.3.2 跨Region存储冗余
通过将对象存放在不同物理位置上的 Region 内,可以进一步增强数据的可用性。其基本原则是:对于 N 份 replica 和 M 个 region,每个 region 中的 replica 数目为 N/M 的整数,剩余的 replica 在 M 个region 中随机选择。以 N = 3, M = 2 为例,一个 region 中有 1 个 replica,另一个 region 中有两个 replica,如下图所示:
对于一个 PUT 操作来说,Proxy server 只会将 replica 写入它所在的 region 中的 node,远端 region 中的 replica 由 replicator 写入。因此,Swift 的算法应该尽量保证 proxy server 所在的 region 中的 replica 份数相对多一些,这也称为 replica 的 proxy server 亲和性。
显然,跨 region 的数据复制加重了对网络带宽的要求。
两种形式的 Region:
(1)远端 region 实时写入 replica
(2)远端 region 的 replica 异步写入
4.3.3 Storage Polices (存储策略)
上面的描述中,一个Swift 集群只支持一套 Ring 配置,这意味着整个机器的配置是唯一的。类似 Ceph 中 pool 的定义,Swift 在 2.0 版本(包含在 OpenStack Juno 版本中)中,添加了一个非常大的功能:Storage policy。在新的实现中,一个 Swift 可以由多套 Ring 配置,每套 Ring 的配置可以不相同。比如,Ring 1 保存 3 份对象拷贝,Ring 2 保存 2 份对象拷贝。几个特点:
- Policy 被实现在 container 级别
- 创建 container 时可以指定 policy。一旦被指定,不可以修改。
- 一个 policy 可以被多个 container 共享使用
通过应用该新的功能,Swift 用户可以制定不同的存储策略,来适应不同应用的存储需求。比如对关键应用的数据,制定一个存储策略使得数据被保存到 SSD 上;对于一般关键性的数据,指定存储策略使得数据只保存2份来节约磁盘空间。比如说:
详细信息,请参考 OpenStack 官方文档 和 SwiftStack 官方文档。
5 小结
Swift牺牲一定程度的数据一致性,来达到高可用性和可伸缩性,支持多租户模式、容器和对象读写操作,适合解决互联网的应用场景下非结构化数据存储问题。 有理由相信,因为其完全的开放性、广泛的用户群和社区贡献者,Swift可能会成为云存储的开放标准,从而打破Amazon S3在市场上的垄断地位,推动云计算在朝着更加开放和可互操作的方向前进。
6 参考链接
Openstack Swift 原理、架构与API介绍_HeyManLeader的博客-CSDN博客_swift架构
OpenStack Swift学习笔记_i_chips的博客-CSDN博客_openstack swift
OpenStack对象存储:Swift架构详解_西门仙忍的博客-CSDN博客_swift对象存储架构
OpenStack_Swift源码分析——Ring基本原理及一致性Hash算法_猪-哥-靓的博客-CSDN博客_ring算法
理解 OpenStack Swift (2):架构、原理及功能 [Architecture, Implementation and Features] - SammyLiu - 博客园