OpenStack Yoga版安装笔记(22)Swift笔记20250418

发布于:2025-04-19 ⋅ 阅读:(19) ⋅ 点赞:(0)

一、官方文档

https://docs.openstack.org/swift/yoga/admin/objectstorage-components.html#https://docs.openstack.org/swift/yoga/admin/objectstorage-components.html#

二、对象存储简介(Introduction to Object Storage)

OpenStack 对象存储(Swift)用于构建冗余、可扩展的数据存储系统,通过一组标准化服务器集群来存储 PB 级(百万 GB)数据,并保证数据可访问。它是一个面向长期存储的大规模静态数据系统,支持数据的检索和更新。

Swift 使用一种分布式架构,没有集中式控制点,因此拥有更好的扩展性、冗余能力和持久性。对象数据会被写入多个硬件设备中,OpenStack 软件则负责在整个集群中确保数据的副本一致性和完整性

存储集群支持横向扩展(scale out),也就是说,只需添加新的节点,就可以提升存储能力。如果某个节点失效,OpenStack 会自动从其他活跃节点复制内容以进行修复。

由于 Swift 使用的是软件逻辑来保证数据复制和分布,因此可以使用廉价的普通硬盘和服务器来代替昂贵的高端设备。

对象存储非常适合低成本、弹性扩展的存储需求。它提供了一个完全分布式、可通过 API 访问的存储平台,可以直接集成到应用程序中,或用于备份、归档和数据保留等场景。

什么是对象存储(Object Storage)?

对象存储是一种不同于传统文件系统或块存储的方式,它将数据作为**对象(Object)**进行管理:

  • 每个对象包含数据本体、元数据和唯一标识(如 URL)

  • 不依赖于传统的“文件夹/路径”结构

  • 通常用于存储图片、视频、日志、备份、静态网页、容器镜像等静态数据

Swift 是 OpenStack 的对象存储组件,具备以下特点👇


🔁 1. 冗余性(Redundancy)

  • 每个对象都会存储多份副本(通常是 3 份),分布在不同的服务器上

  • 即使一台设备或整个机架损坏,也不会丢失数据


📈 2. 可扩展性(Scalability)

  • Swift 使用无中心节点的分布式结构

  • 不存在性能瓶颈或单点故障

  • 只需添加新服务器即可扩容,无需重新设计架构


💾 3. 廉价硬件支持(Commodity Hardware)

  • 不需要使用昂贵的 SAN、NAS 或高端企业级磁盘

  • 可以使用普通服务器和硬盘,成本更低,灵活性更高


🔧 4. 自动修复机制(Self-healing)

  • 节点宕机时,系统会自动将丢失的数据从其他副本中复制回来

  • 配合 replication 或 erasure code,保障数据完整性


🧩 5. API 驱动

  • 对象存储完全通过 HTTP REST API 访问

  • 可轻松集成到 Web、云原生应用、容器等系统中

  • 与 Amazon S3 使用方式非常类似


📦 6. 应用场景

场景 描述
应用集成 可以作为图床、视频源、文档托管服务嵌入业务系统
数据备份 保存 VM 镜像、数据库备份文件等
日志归档 存储日志、监控记录等长期归档数据
静态网站托管 用作前端网页/图片/CDN 源站

✅ 总结一句话:

Swift 是 OpenStack 提供的一个去中心化、高冗余、可水平扩展的对象存储系统,适用于低成本、大规模的静态数据存储需求。

三、对象存储的关键特性(Object Storage characteristics)

对象存储的关键特性如下:

  • 所有存储在对象存储中的对象都有一个 URL(统一资源定位符)。

  • 可以使用“存储策略(Storage Policies)”来定义对象在集群中的不同持久性等级。这些策略不仅支持完整副本,也支持纠删码(Erasure Coding)分片。

  • 对于每一个对象,其所有副本或分片都会尽可能存储在**不同的可用区域(zone)**中,以提高持久性和可用性。

  • 每个对象都拥有自己的元数据(metadata)

  • 开发者通过RESTful HTTP API与对象存储系统进行交互。

  • 对象数据可以存储在整个集群中的任意位置

  • 通过添加新节点进行横向扩展,不会牺牲性能,因此相比“整套替换(fork-lift upgrade)”,对象存储能够以更低成本实现线性扩展

  • 无需将数据迁移到全新的存储系统。

  • 可在不中断服务的前提下添加新节点。

  • 故障节点和硬盘也可以在不中断服务的情况下替换。

  • 运行在通用的标准硬件上,例如 Dell、HP、Supermicro 等厂商的设备。

逐条解析一下这些特性在实际场景中的意义👇


🔗 1. 每个对象都有一个 URL

  • 对象存储是基于 HTTP 协议的,每个对象上传之后,都会获得一个可通过 REST API 访问的唯一地址。

  • 举个例子,某张图片上传后就可以通过 http://swift.example.com/v1/account/container/image.jpg 直接访问。


📐 2. 存储策略(Storage Policies)

  • 通过策略控制数据副本的类型与数量,提供灵活的存储级别。

    • 比如:

      • 默认策略使用 3 副本

      • 冷数据策略使用纠删码(如 10+4)减少存储开销

      • 快速访问策略使用 SSD 节点

  • 不同容器可以使用不同的策略,方便按业务需求定制。


🗂️ 3. 副本/分片尽可能分布在不同的 Zone

  • Zone 是一种“故障域”,比如不同的:

    • 物理机架

    • 机房

    • 数据中心

  • Swift 会尽量让对象的多个副本/碎片分布到不同的 zone 上,提高数据的可用性与容灾能力


📝 4. 每个对象拥有独立元数据

  • 可以为每个对象添加自定义的属性(metadata):

    • 文件作者、内容类型、加密信息等

  • 元数据通过扩展属性或数据库记录实现,不需要额外的元数据服务器。


🌐 5. RESTful API 访问

  • 所有操作(上传、下载、删除、列出对象)都通过 HTTP 请求完成。

  • 这意味着:

    • 可直接从网页、移动应用、Python 脚本、Java 程序等使用

    • 也可以兼容 S3 API 等常见协议(使用适配器)


🌍 6. 对象可以存在于集群的任何位置

  • 对象的实际位置由 ring(环)系统管理

  • 不需要知道具体在哪台机器上,只要通过 Proxy Server 就能访问

  • 实现了完全的位置透明性


🧱 7. 横向扩展能力强,成本低

  • 通过添加新的普通服务器节点就可以扩容,不影响已有服务

  • 不像传统 SAN/NAS 需要统一的大规模升级

  • 适合云时代的大数据、持续增长的存储需求


🔄 8. 无需整体迁移数据

  • 系统支持热扩容,不需要“把旧系统数据迁走”,这避免了停机或数据迁移成本

  • 数据分布自动通过 rebalance 完成


🚫 9. 新增/替换节点无需停机

  • 无论是增加节点还是替换硬盘/坏掉的服务器,都可以在线完成

  • 系统通过复制、重建等机制自动补齐丢失的数据,保障业务连续性


💻 10. 支持通用标准硬件

  • 不依赖昂贵的专用设备(不像某些传统存储系统)

  • 可使用 Dell、HP、Supermicro 等品牌的 x86 服务器,甚至可以自己组装服务器

  • 降低成本,维护更灵活


✅ 总结一句话:

Swift 对象存储通过 REST API、横向扩展、高可用冗余设计和标准硬件支持,打造了一个稳定、灵活、低成本的云原生数据存储平台。

 四、对象存储组件(Components)

4.1 组件概述

OpenStack 对象存储使用以下组件来实现高可用性、高持久性和高并发性:

  • 代理服务器(Proxy servers) - 处理所有传入的 API 请求。

  • 环(Rings) - 将数据的逻辑名称映射到特定磁盘上的物理位置。

  • 可用区(Zones) - 实现数据隔离。一个 Zone 故障不会影响整个集群,因为数据会跨 Zone 复制。

  • 账户与容器(Accounts and Containers) - 每个账户和容器都是分布在整个集群中的独立数据库。账户数据库包含该账户下的所有容器清单;容器数据库包含该容器中的所有对象清单。

  • 对象(Objects) - 实际存储的数据内容。

  • 分区(Partitions) - 存储对象、账户数据库和容器数据库的逻辑单元,帮助管理数据在集群中的存放位置。

详细讲解:


1️⃣ Proxy Servers(代理服务器)

  • 这是 Swift 的入口点

  • 所有客户端(如 Web 前端、APP、API 调用)发送的请求都会先到 Proxy Server。

  • 它的职责是:

    • 根据 ring 查找数据位置

    • 路由请求到对应的存储节点

    • 对 Erasure Code 数据进行编码/解码(如启用 EC 策略)

  • 本身不存储数据,只负责请求调度与转发


2️⃣ Rings(环)

  • ring 是一个逻辑映射表,用于查找某个对象/容器/账户在集群中的存储位置。

  • 它会基于对象名的 hash 值找到“分区号”,再根据分区号找到在哪些设备上存储。

  • Swift 中有三个主要 ring:

    • account ring

    • container ring

    • object ring(每个 Storage Policy 有一个)


3️⃣ Zones(可用区)

  • 可用区类似于故障域,代表物理隔离的单位,例如:

    • 不同的机架

    • 不同的服务器组

    • 不同的数据中心

  • 数据会被跨 Zone 存储,从而实现:

    • 一个 Zone 挂了,数据不会丢

    • 剩下的 Zone 还能提供服务

  • 它是实现高可用性和容灾的核心设计。


4️⃣ Accounts and Containers(账户和容器)

  • 账户:类似于用户空间,一个账户下可以有多个容器。

  • 容器:类似于文件夹,一个容器中可以存多个对象。

  • 它们的元数据是存储在 SQLite 数据库中的,且这些数据库也会被复制。

    • 账户数据库:记录这个账户下有哪些容器

    • 容器数据库:记录这个容器下有哪些对象

  • 这些数据库也是分布式的,并有多副本,保障查询元数据的稳定性。


5️⃣ Objects(对象)

  • 对象就是我们真正上传的内容,比如:

    • 图片、视频、备份文件、日志等

  • Swift 将对象以二进制文件形式存储在文件系统中,并附带有 xattr 元数据

  • 对象的数据是通过 hash 值和分区规则分散地存放在多个磁盘上的。


6️⃣ Partitions(分区)

  • 分区是 Swift 中的最小数据管理单元

  • 它不代表磁盘分区,而是 Swift 内部对数据的逻辑划分。

  • 每个对象、容器或账户会被分配到某个“分区号”中。

  • 分区会被复制(通常 3 副本),分布在不同设备和 Zone 上。

  • ring 会告诉 Proxy 某个分区对应在哪些设备上,这样 Proxy 就知道数据的位置。


✅ 总结一句话:

通过 Proxy Server 协调访问,Ring 映射数据位置,Zone 实现容灾隔离,Account/Container/Partition 管理元数据与数据分布,Swift 构建了一个高度可扩展、高可用的对象存储系统。

4.2 总体架构

这张图展示了 OpenStack Object Storage(Swift) 的核心架构组件和数据流转过程,是理解 Swift 的高可用性、分布式和可扩展性的关键图示。下面是对这张图的详细讲解:


总体结构

图中展示了 Swift 的各个组成部分之间的逻辑关系,从上到下依次是:

🔼 API Calls

  • 外部客户端通过 RESTful API 发起请求,比如上传对象、列出容器内容等。

  • 这些 API 请求会首先到达 Swift 的 Proxy Server(代理服务器)


🟪 Proxy Servers(代理服务器)

  • 这是 Swift 的网关组件,负责:

    • 接收来自用户的 API 请求

    • 通过 Ring 找到对象/容器/账户应该被路由到的物理节点

    • 将请求转发到对应的存储节点

    • 返回操作结果给客户端

  • Proxy 本身不存储对象,只做请求分发与协调


🔄 The Ring(环)

  • Ring 是 Swift 中最核心的数据结构之一,用于映射数据到存储设备

  • 包含三种 ring:

    • account.ring.gz

    • container.ring.gz

    • object.ring.gz(每个 storage policy 一个)

  • ring 负责以下映射逻辑:

    • 客户端发来的 API 请求(如 PUT object)会通过 proxy 查询 ring

    • 根据对象的 hash 值找到其所属的分区(partition)

    • 查出这个分区被分配给哪些 zone 和 device(device就是指磁盘)

  • ring 是静态文件,可以部署在 proxy 和 storage 节点上,无需实时通讯。


🏗 Nodes 和 Zones(节点和可用区)

底部展示了多个存储节点(Node),并标明它们所在的 Zone。Zone 是一个逻辑上的“容灾单元”,每个 Zone 内可以包含多个 Node 和磁盘。下面是对它们的说明:

区域颜色 Zone 编号 描述
🟧 橙色 zone 1 一组节点,例如一个机架或数据中心
🟩 浅绿 zone 2 另一组节点,物理隔离于 zone 1
⬛ 深灰 zone 3 又一个隔离区域
🟨 米色 zone 4 继续物理隔离
🟦 蓝色 zone 5 第五个 zone,增强高可用性
  • 这 5 个 Zone 分布在不同的节点中,数据会跨 Zone 存储(如副本、EC 分片),确保某个 Zone 故障时数据不会丢失。

  • 每个 Node 下的“Physical Storage”表示服务器上的硬盘或文件系统。

    • (补充)选择数据存放位置

      • Swift 保存每个对象为多分拷贝,它按照物理位置的特点,尽量将这些拷贝放在不同的物理位置上,来保证数据的地理位置上的可靠性。它主要考虑以下几种位置属性:

        • Region:地理位置上的区域,比如不同城市甚至不同国家的机房,这主要是从灾备方面考虑的。

        • Zone:一个数据中心根据物理网络、供电、空调等基础设施分开的独立的域,往往将一个机架(Rack)内的服务器分在一个 Zone 内。

        • Node (节点):物理服务器

        • Disk (磁盘):物理服务器上的磁盘

      • Swift 在确定对象的放置位置时,会尽量将对象及其拷贝放在不会同时损失的物理位置上。


💡 数据存储与容灾逻辑:

举个例子,如果你上传了一个对象:

  1. 请求到达 Proxy Server。

  2. Proxy 查询 ring,确定这个对象属于哪一个分区。

  3. ring 会告诉 proxy,这个分区要分布在哪几个 Zone 的哪些 Node 上(通常是 3 个副本或 EC 分片)。

  4. Proxy 将这个对象分别写入这些节点。

  5. 如果其中一个 Zone 挂掉了,其他 Zone 的副本仍可正常服务。


✅ 总结

这张图完整展现了 Swift 的工作流程和架构设计理念:

  • Proxy Server 接收并调度请求;

  • Ring 负责对象定位;

  • Zones 和 Nodes 实现了数据分布和容灾;

  • 支持横向扩展,只需添加节点即可增加容量和吞吐;

  • 典型的 分布式对象存储系统设计,稳定且高效。

4.3 Proxy servers

Proxy servers 是对象存储系统对外公开的接口,负责处理所有进入的 API 请求。
一旦代理服务器接收到请求,它会根据对象的 URL(例如:
https://swift.example.com/v1/account/container/object)来确定应访问哪个存储节点。
代理服务器还负责协调响应、处理故障以及协调时间戳。

代理服务器采用的是“无共享架构”(shared-nothing architecture),并且可以根据预计的工作负载随需扩展。
建议至少部署两个代理服务器,并将它们置于一个独立管理的负载均衡器之后。
如果其中一个代理服务器发生故障,其它的代理服务器可以接管任务,确保服务不中断。

详细讲解:


💡 什么是 Proxy Server?

在 OpenStack Swift 中,Proxy Server 是客户端和后端存储节点之间的“中介者”或“门面”,所有对象存储的操作请求(如上传、下载、删除对象等)都必须先经过它。

🌐 如何处理请求?

举个例子:
当用户通过浏览器或 API 访问如下 URL:

https://swift.example.com/v1/myaccount/mycontainer/myphoto.jpg

这个请求会首先到达 Proxy Server,然后由它去解析:

  • 是哪个账号(account)

  • 哪个容器(container)

  • 哪个对象(object) 再根据 Ring 文件(存储映射表)查出这些数据位于哪个后端节点(服务器、磁盘),并将请求转发过去。

🔄 Proxy Server 的主要职责:

职责 说明
路由请求 根据 URL 识别资源,并查找对应的数据位置
调用 Ring 查询 account、container 或 object 所在的物理节点
处理错误 若某个节点不可达,会自动重试其他副本或记录失败
时间戳管理 Swift 使用“最后写入优先”策略,依赖统一的时间戳协调
响应聚合 将后端多个节点的响应结果聚合成一个最终响应

🧱 Shared-nothing 架构:

  • 每个 Proxy Server 都是独立的,没有共享内存或硬盘;

  • 无需集中协调,扩展简单,增加新的 Proxy 节点不会影响现有系统;

  • 没有单点故障,避免一个 Proxy 崩溃导致整个系统瘫痪。

📊 高可用部署建议:

  • 至少部署两个 Proxy Server

  • 放在**负载均衡器(如 HAProxy、NGINX、F5)**后

  • 这样当一个 Proxy 节点故障时,请求会被路由到其他健康的 Proxy 节点


✅ 总结:

Proxy Server 是 Swift 架构中处理客户端请求的“前台”,负责将 REST API 请求分发到正确的后端存储节点,并聚合响应。
它是无状态的、可扩展的、冗余的,是系统高可用性和性能扩展的关键组件。

4.4 Ring(环)

Ring(环) 表示集群中实体名称与其物理磁盘位置之间的映射关系。Swift 中有三个独立的 ring,分别用于:

  • 账户(accounts)

  • 容器(containers)

  • 对象(objects)

当系统中的某个组件需要对某个对象、容器或账户执行操作时,它会查询相应的 ring,来确定该数据在集群中的实际存储位置。

Ring 使用 zone(区域)device(设备)partition(分区)replica(副本) 来维护这个映射关系。
默认情况下,ring 中的每个 partition 会在集群中有三个副本,并且这些副本的位置也由 ring 中的映射关系来管理。
ring 同时也负责在发生故障时决定使用哪些设备作为“临时接管设备(handoff)”。

数据可以被隔离到 ring 中的不同 zone 中。每个 partition 的副本会尽可能地分布在不同的 zone 中。
一个 zone 可以是一个磁盘、一个服务器、一组机架、一个交换机,甚至一个数据中心。

ring 中的 partitions 会分布在对象存储系统的所有设备上。
当需要移动 partition(例如新增设备时),ring 会确保每次移动的数据量最小,并且每次只会移动一个副本。

你可以使用权重(weight)来平衡不同设备上的 partition 分布。
这在集群中包含不同容量磁盘的情况下特别有用。

ring 被代理服务器(proxy server)和多个后台进程(如 replication)使用。

详细讲解:


🌀 什么是 Ring?

在 Swift 中,ring 是一个非常核心的概念。它是一个数据结构,用于告诉系统 “某个数据(account/container/object)存在哪台服务器的哪个硬盘上”。

📦 Ring 管理哪些内容?

每一个 ring 都包含如下信息:

  • Zones(区域):用于故障隔离,比如机柜、服务器或数据中心

  • Devices(设备):每台存储服务器上的硬盘

  • Partitions(分区):逻辑上的存储单位,用于将数据平均分散到整个集群中

  • Replicas(副本):每个 partition 通常有多个副本,默认是 3 个

这些信息被 ring 工具封装为 .ring.gz 文件(压缩后的 ring 映射文件),供 Swift 各个组件加载使用。


🗺️ Ring 如何工作?

当你访问一个对象,例如:

https://swift.example.com/v1/account1/photos/cat.jpg

Proxy Server 会:

  1. 调用 Object Ring

  2. 使用 account/container/object 的名字进行哈希

  3. 查出这个对象属于哪个 partition

  4. 查出这个 partition 分布在哪几个设备上(主副本和备用副本)

  5. 发起请求到对应的节点执行操作


⚙️ Ring 的特性:

特性 说明
多副本容灾 每个 partition 默认有 3 个副本,分布在不同 zone 提高可靠性
动态扩展 添加设备时只会迁移少量 partition,避免大规模数据搬迁
权重调整 大硬盘可以分配更多 partition,确保磁盘利用率均衡
隔离设计 zone 可代表交换机/机柜/IDC,实现物理隔离,提高系统鲁棒性
handoff 支持 如果副本丢失,ring 知道哪些设备可以临时接管存储任务

谁使用 ring?

使用者 用途
Proxy Server 解析对象位置并转发请求
Replicator 确定副本在哪台机器上进行同步
Updater 更新容器或账户元数据时查找目标节点
Auditor 检查 partition 内容正确性时查找设备位置

✅ 总结:

  • Ring 是 Swift 的“大脑”,提供数据位置的查询服务;

  • 你可以理解它是一张分布式的“导航地图”;

  • 它设计得非常高效:自动副本管理、故障转移、负载均衡;

  • 有 3 个独立的 Ring:account.ring.gz、container.ring.gz、object.ring.gz;

  • 修改 Ring(如添加节点、调整权重)后要重新生成并同步到各节点。

* The ring

这些 Ring 文件是由外部工具进行管理的。服务器进程(Server processes)自身不会修改 ring,而是通过其他工具生成或修改后,将新的 ring 文件提供给服务使用。

Ring 使用的是对象路径MD5 哈希值中的一部分比特位,将其作为一个 分区(partition)索引,这个索引用于标识该对象应存储在哪个设备上。使用的比特位数量是一个可以配置的值,称为 partition power(分区幂)。2 的这个幂次方就代表了总共的分区数量。例如 partition power 是 10,就有 2¹⁰ = 1024 个分区。

将整个MD5 哈希空间进行分区,让集群的其他部分可以批量处理数据项,这比一个个地处理所有对象,或一次性处理整个集群,要更高效或更简单。

另一个可配置的值是 replica count(副本数),表示每个分区要分配到多少个设备上,也就是说,一个Ring 中有多少份副本。对于同一个分区,每个副本所对应的设备会被安排在 不同的 zone(区域)。zone 可以根据物理位置、供电分区、网络隔离,或其他能提高副本可用性的属性进行划分。

详细讲解:

🧠 1. 为什么叫 "Ring"?

“Ring” 是 OpenStack Swift 用来映射对象路径到存储设备的一个机制,名字来源于它使用了 一致性哈希环(Consistent Hash Ring) 的设计思想。


🧩 2. 分区与 Partition Power

👉 哈希路径 → 分区:

每一个对象路径(例如 /account/container/object)都会被转换成一个 MD5 哈希值,而系统从中取若干比特位来决定该对象属于哪个分区。

  • partition_power = 10 → 表示系统有 2^10 = 1024 个分区;

  • 越多的分区,系统的分布精度越高,但 ring 文件越大;

  • 分区的作用是让系统可以按块管理,而不是每个对象单独管理,提升效率。


🧾 3. 副本数(replica count)

这表示每个对象的分区会复制到多个设备上,默认是 3 个副本好处:

  • 容错能力强:某个设备宕机,其他副本还在;

  • 性能更好:可以从最近的副本读取,提高吞吐量。


🏘️ 4. Zones 的意义

副本之间尽量分散在 不同的 Zone。Zone 可以是:

  • 一个硬盘;

  • 一台服务器;

  • 一个机柜;

  • 一个数据中心。

这样做的目的就是为了 防止单点故障,比如:

  • 如果 Zone 1 宕机了,其他 Zone 还能提供副本;

  • 比如一个容器里有多个副本,它们分别在上海、北京、广州机房,即使北京断电,服务仍可用。


⚠️ 小结要点

概念 意义
ring 映射路径到物理设备的核心结构
partition power 控制分区数量:2^n 个分区
replica count 每个分区存几份(不是每个object存几份)
zone 控制副本之间的物理隔离,提高高可用性

4.5 Zone

对象存储允许配置“区域(Zones)”,以便隔离故障边界。如果条件允许,每一个数据副本都会被存放在不同的Zone中。从最小层面来看,一个 zone 可以是一个硬盘,或者一组硬盘。如果系统中有五个对象存储服务器,那么每台服务器可以分别作为一个 zone。在更大型的部署中,一个完整的机架(甚至多个机架)的存储服务器,可以被视为一个 zone。zones 的目标是让集群在面对严重存储服务器故障时,不会导致数据的所有副本丢失

详细讲解:


✅ 什么是 Zone?

在 OpenStack Swift 中,Zone 是一个逻辑上的故障隔离单元(failure domain)

  • 它可以是:一个硬盘、一个服务器、一个机架、一个机房、甚至是一个数据中心。

  • Zone 是用来隔离故障的:如果一个 zone 挂了,其他 zone 的数据还在,就能继续服务。


🔄 为什么副本要分布在不同的 Zone?

Swift 默认把每个对象保存 3 份(副本数为 3),为了保障高可用性,这 3 个副本必须尽量放在不同的 zone,比如:

副本编号 放置位置(Zone)
副本1 Zone A(比如服务器A)
副本2 Zone B(比如服务器B)
副本3 Zone C(比如服务器C)

这样即使 Zone A 整台服务器故障,系统还能从 B 或 C 中读出数据,数据不会丢失


🧱 Zone 的粒度可以多大?

Zone 的定义是灵活的,取决于你部署 Swift 的规模:

部署规模 Zone 通常对应的对象
小型部署 一个硬盘、一个服务器
中型部署 一台服务器(常见)
大型部署 一整排机架、一个数据中心

📌 总结一句话:

Zone 就像是存储集群的“防火墙隔间”,副本放在不同的 zone 能让系统即使部分故障也能保证数据不丢,高可用、高可靠。

4.6 Accounts and containers 

每个账户(account)和容器(container)都是一个独立的 SQLite 数据库,并分布在整个集群中。一个账户数据库中保存着该账户下所有容器的列表。一个容器数据库中保存着该容器中所有**对象(object)**的列表。为了追踪对象数据(Object data)的位置,系统中的每个账户都有一个数据库来记录其所有容器,而每个容器的数据库又会记录其所包含的每个对象。

详细讲解:


🔹 什么是 account 和 container?

在 Swift 中:

  • Account:相当于用户的存储空间。

  • Container:相当于文件夹,用于组织对象。

  • Object:是最终存储的数据单元(比如文件、图片等)。

🔹 为什么用 SQLite?

Swift 采用 SQLite 来管理账户和容器的元数据,是因为:

  • SQLite 是轻量级的嵌入式数据库,适合频繁读写。

  • 单个 .db 文件易于复制、备份与同步。

  • 它不需要专门的数据库服务进程,便于部署。

🔹 数据结构示意:

  • 每个 Account 拥有一个 .db 文件,列出了该账户下有哪些 Containers

  • 每个 Container 也有一个 .db 文件,列出了该容器里有哪些 Objects

这是一种分布式、去中心化的元数据管理方式,不需要一个中央数据库,系统能更好地横向扩展。

🔹 数据一致性与复制:

  • 这些 SQLite 数据库文件也和对象数据一样是多副本的(默认 3 副本),通过 replication 或 EC 策略保持一致性。

  • 这样即使某个节点或数据库文件损坏,也可以通过复制机制从其他副本恢复。

4.7 Partitions

**分区(Partition)**是存储数据的集合,包含账户数据库(account databases)、容器数据库(container databases)以及对象(objects)。分区是复制系统(replication system)的核心。

可以把分区想象成一个在配送中心仓库里流动的“箱子”。每个用户的订单会被放进这个箱子里。系统会把这个箱子当作一个整体来处理,而不是处理一个个零散的订单。这样更容易管理,也能减少系统中活动组件的数量。

系统中的复制器(replicators)以及对象的上传/下载操作,都是以“分区”为单位进行的。随着系统规模扩大,其行为依旧是可预测的,因为分区的数量是固定的。

实现一个分区从概念上来说是非常简单的:它就是一个磁盘上的目录,里面有一个对应的哈希表,标明它包含了哪些内容。

详细讲解:


🔹 什么是 Partition(分区)?

在 Swift 中,一个分区就是一个最小的数据管理单元,里面可能包含:

  • 一组对象(object files)

  • 某个容器(container)的数据库文件

  • 某个账户(account)的数据库文件

这个“分区”在物理上就是磁盘上的一个目录,里面保存了真实数据和索引信息(通过哈希值标识)。


🔹 为什么要用 Partition?

使用分区有两个核心好处:

  1. 方便批量管理和复制:
    Swift 中的复制系统不是对单个文件进行复制,而是以“分区”为单位批量复制,这样效率更高。

  2. 可控性强、易扩展:
    分区总数是固定的(比如 2182^{18}218 = 262,144 个),所以随着设备或节点的增加,分区只是被重新分配,不增加复杂度。


🔹 分区数量是怎么确定的?

由 ring 配置中的 partition_power 控制:

partition_power = 18 

意味着总共有 2182^{18}218 个分区,这些分区将均匀分布在集群内的存储设备上。


🔹 举个简单的比喻:

假设你在管理一个快递仓库(就是 Swift 集群):

  • 你不会对每个快递单独搬运。

  • 你会用大箱子(分区)统一装快递,然后一起移动、分发。

Swift 也是这个思路,通过将数据“装进分区”来降低系统复杂度,提高性能。

* 每个对象文件对应一个特定的 partition(分区)

✅ 一个对象对应一个分区

在 OpenStack Swift 中:

  • 每个对象(object)都有一个唯一的 URL,比如:

    /v1/account/container/object

  • Swift 会对这个对象路径进行 哈希(通常是 MD5),并根据 ring 配置中的 partition_power,从哈希值中截取一部分 bits 作为分区编号。

举例说明:

  • 假设 partition_power = 18,那就意味着 Swift 会使用对象路径哈希值的前 18 位来决定该对象属于哪个分区(最多有 2^18 = 262144 个分区)。

  • 然后,这个分区号再通过 ring 文件(如 object.ring.gz)来映射到具体的设备(某块硬盘上的某个目录)。

📦 所以总结就是:

内容 说明
一个对象 哈希后定位到一个分区
一个分区 包含多个对象(或容器/账户数据库)
对象上传/下载/复制 都是以分区为单位来操作的
分区目录 就是磁盘上的一个目录,比如 /srv/node/sda1/objects/12345/,里面放着对象的 .data 文件和元数据

📌 小提示

  • 一个分区可以包含多个对象(但每个对象只属于一个分区)。

  • 多个对象可以落在同一个分区中(如果它们的哈希值前几位相同)。

4.8 Replicators

为了确保每份数据在系统中都有三份副本,replicators(副本同步器) 会不断地检查每个分区(partition)。

对于本地的每一个分区,replicator 会将其与其它 zone 中对应的副本进行比较,以检测它们之间是否存在差异。

Replicator 是通过检查 哈希值(hashes) 来判断是否需要复制。每个分区都有一个 哈希文件(hash file),其中包含该分区每个目录的哈希值。对于同一个分区的多个副本,系统会比较它们的哈希文件。如果发现哈希值不同,就说明这些副本不一致,需要进行数据同步。

这也是“分区”设计非常有用的地方。系统中实体数量较少(即分区数量固定),因此可以以较大的数据块进行传输(而不是无数个小 TCP 请求),效率更高,而且需要比较的哈希数量是可控的。

整个集群具有“最终一致性”行为:有时候系统可能从一个未更新的副本中读取到旧数据,但副本同步最终会让所有分区的数据趋于最新,保持一致。

详细讲解:


🔁 什么是 Replicator?

Swift 的存储机制依赖于“冗余副本”。默认情况下,每份对象数据会存储三份,分别放在不同的 zone 上以防止故障导致数据丢失。

而这些副本的同步工作,不是实时发生的,而是由一个名叫 replicator 的后台进程定期完成。


🧩 工作流程概览:

  1. 检查本地每个分区(partition)

  2. 读取哈希文件(hashes.pkl) → 每个分区内目录的哈希信息

  3. 对比其它副本的哈希值

  4. 发现不一致 → 复制本地数据覆盖对方,或从对方拉取数据

  5. 实现最终一致性


🔍 具体机制详解:

✅ 为什么需要哈希?

  • Swift 不会逐个文件对比内容,那样太慢。

  • 它会为每个分区中的每个目录(例如对象的文件夹)计算哈希。

  • 如果副本 A 和 B 的分区 1024 的哈希值不一样,就说明数据有出入。

✅ 哈希值存哪?

  • 每个 partition 有一个对应的哈希文件,记录了当前所有目录的 hash 值。

  • 这些文件默认保存在 /var/lib/swift 的数据目录中。

✅ 数据怎么复制?

  • 如果对比出差异,replicator 会把本地的那个目录复制到另一个节点(或者拉对方的)

  • 不是一个个复制对象文件,而是“目录为单位”,提高了效率。

✅ 为什么说 partition 有用?

Swift 故意将数据“划分成分区”后处理:

  • 拿一个“桶”去处理多个对象,而不是一个个对象来回传。

  • 减少连接数和元数据操作,效率高。

  • 而且分区数量是固定的,所以系统的同步成本是可控的。


⏳ 最终一致性是怎么回事?

  • 假如三个副本中有一个还没同步到最新数据;

  • 客户端可能刚好访问的是旧副本,读取的是旧数据;

  • 不过不要担心!replicator 迟早会同步这个旧副本,它就会和最新副本一致;

  • 这种行为就叫做“最终一致性(Eventually Consistent)”。


举个例子:

假设你有一个重要文件被存了三份在:

  • 服务器 A(zone 1)

  • 服务器 B(zone 2)

  • 服务器 C(zone 3)

你修改了文件,结果只有 A 和 B 收到更新,C 当时宕机了。

🛠️ 一段时间后,C 重启了,此时 replicator 开始对比分区哈希值,发现 C 的分区内容落后了,于是:

  • 从 A 或 B 拉数据,覆盖 C;

  • 三份副本再次一致!


🧾 总结表格:

角色 作用
分区(Partition) 数据分组单位,用于组织副本和复制操作
哈希文件 每个分区目录的哈希摘要,用于比对副本一致性
Replicator 后台进程,负责定期比对并同步分区副本
最终一致性 系统允许短时间不一致,最终通过同步保持一致性

五、使用场景举例

5.1 Upload

客户端通过 REST API 向一个已存在的容器发送一个 HTTP PUT 请求来上传一个对象。集群接收到该请求。

首先,系统需要确定数据将被存储到哪里。为此,它会使用 账户名(account name)容器名(container name)对象名(object name) 来计算出该对象所属的分区(partition)

接着,通过查找 ring,系统确定哪些存储节点包含该分区(partition)。

然后,数据被发送到每一个目标存储节点,并存储在相应的分区中。必须有三个副本中的至少两个写入成功,客户端才会收到“上传成功”的响应。

最后,系统会异步地更新容器数据库,反映出该容器中新增了一个对象的信息。

详细讲解:


我们一步步来剖析整个上传流程在 Swift 中是如何运作的:


🧾 步骤 1:客户端发起 PUT 请求

  • 客户端使用 REST API(通常是 PUT /v1/account/container/object)上传一个对象。

  • 这个请求中携带了对象的数据和元信息。


🧾 步骤 2:系统决定对象去哪存(找分区)

  • Swift 使用账户名、容器名、对象名,拼接起来,计算出一个 MD5 哈希

  • 从这个哈希中提取一部分比特,用于确定该对象应该属于哪个分区(partition index)。

  • 这个过程由 ring 控制,确保对象写入的位置是可预测的。


🧾 步骤 3:通过 ring 查找存储节点

  • 有了分区编号,接下来 Swift 去 ring 中查找哪些 storage node(存储节点)包含这个分区的副本。

  • 默认会找出 3 个副本所在的位置(分布在不同的 zone)。


🧾 步骤 4:发送数据到存储节点

  • 客户端数据由 Proxy Server 转发到这 3 个存储节点(比如 node A、B、C)。

  • 每个节点会把数据写入它本地磁盘对应分区的目录下。

📌 写入确认机制:

  • 只要 3 个副本中有至少 2 个写入成功,客户端就会收到一个 2xx 响应(成功)。

  • 这是一种 高可用但低延迟 的折中方案。


🧾 步骤 5:异步更新容器数据库

  • 对象已经写完,但此时 容器数据库中还没有这条记录

  • Swift 会使用后台异步任务(如 container-updater)更新对应的 container.db,加入这个新对象的信息。

🔁 这种“先写数据,再补登记”的机制提高了写入性能,但也导致数据查询时有可能出现短暂的不可见状态(最终一致性模型)。


📌 举个例子:

比如你上传一个头像 PUT /v1/acc1/photos/avatar.jpg,流程大致是这样:

  1. 系统用 acc1/photos/avatar.jpg 生成一个哈希,比如落到 partition 125。

  2. ring 查出 partition 125 在 node A, B, C 上。

  3. 数据被写入 A、B、C 的对应路径,如 /srv/node/sda/objects/125/xyz...

  4. A、B 写入成功,C 刚好繁忙,但 A+B 足够满足“2-of-3”写成功的条件。

  5. Swift 返回 201 Created 给客户端。

  6. 后台异步将 avatar.jpg 加入 container photos 的数据库记录中。


📌 总结图示(逻辑流程):

客户端 → Proxy Server → 计算 partition → 查 ring → 得到 node A/B/C → 数据写入 node A/B/C → 至少2成功 → 响应客户端 → 异步更新 container.db

5.2 Download

当一个请求到达(例如:account/container/object),系统会使用同样的一致性哈希算法来确定该对象所在的分区索引(partition index)

接着,在 ring 中查找这个分区所对应的存储节点。

系统首先会向其中一个存储节点发送请求以获取该对象。如果该请求失败,就会尝试从其他存储节点获取对象。

详细讲解:


下载流程与上传有些相似,但不需要写入,而是从副本中读取:


🧾 步骤 1:客户端请求下载对象

客户端通过 REST API 发出 GET /v1/account/container/object 请求。比如:

http
GET /v1/myaccount/photos/avatar.jpg
​​​🧾 步骤 2:一致性哈希 → 定位分区

Swift 使用 account/container/object 组合字符串生成一个哈希值。

  • 提取哈希中的高位 bits(由 partition power 决定)来得到对象所在的 partition index(分区编号)

这个过程和上传时是一致的,保证上传和下载对同一个对象得到的是相同的分区编号


🧾 步骤 3:查询 ring → 获取存储位置

系统通过查询 ring 文件(比如 object.ring.gz)来查找该分区副本分别位于哪些存储节点上(通常是三个副本,分布在不同 zone)。

例如:

  • partition 125 可能在节点 A、B、C 上。


🧾 步骤 4:从一个节点请求对象数据

Proxy Server 向节点 A 发出请求获取 /srv/node/sda/objects/125/xxx_data 中的对象文件。

📌 如果:

  • 节点 A 正常 → 返回数据。

  • 节点 A 故障或超时 → 系统自动尝试节点 B 或节点 C。

这也是 Swift 高可用性的体现 —— 只要任意副本能成功读取,就能满足用户请求


📌 总结流程图:

客户端请求 object → Proxy Server 计算 partition index → 查 ring,定位节点 A/B/C → 优先从 A 获取对象 → A 不通则尝试 B,再尝试 C → 读取成功 → 返回给客户端 

📘 说明几点:

  • Swift 的下载是从副本中任意读取一个,不是三副本都读。

  • 数据一致性是通过后台的 replicator 进程 来维护的(最终一致性)。

  • 这种架构保证了高并发高可用性水平扩展能力


网站公告

今日签到

点亮在社区的每一天
去签到