分布式存储Swift原理分析

在“分布式系列之分开源分布式存储技术分析”中曾提到swift是完全无中心架构,采用一致性哈希算法获得数据的位置。本文简要总结swift存储的整体架构和关键技术,进一步了解不同分布式存储技术实现原因上的差异。


1、Swift整体架构

Swift是对象存储,用于永久类型的静态数据的长期存储,这些数据可以检索、调整,必要时进行更新。Swift通过在软件层面引入一致性哈希技术和数据冗余性,牺牲一定程度的数据一致性来达到高可用性和可伸缩性,支持多租户模式、容器和对象读写操作,适合解决非结构化数据存储问题。最适合存储的数据类型的例子是虚拟机镜像、图片存储、邮件存储和存档备份。

1.1 Swift数据模型

Swift是对象存储,采用层次数据模型,包括三层逻辑结构:Account、Container和Object。每层的节点数均没有限制,可以任意扩展。

  • Account:层次结构的顶层,Swift天生支持多租户,在OpenStack中account中可以理解为租户,用来做顶层的隔离机制。Swift租户的隔离性体现在metadata上,而不是object data上,数据包括元数据和container列表,保存在SQLite数据库中。
  • Container:容器,类似于文件系统中的目录,由用户自定义,它包含自身的元数据和容器内的对象列表,数据保存在 SQLite 数据库中。一个account中可以创建任意数量的container,两个不同container中的同名object表示不同的object。
  • Object:对象,包含数据和数据的元数据,以文件的形式保存在文件系统中。

在这里插入图片描述

1.2 Swift系统架构

Swift采用完全对称、面向资源的分布式系统架构设计,所有组件都可扩展,避免因单点失效而扩散并影响整个系统运转;通信方式采用非阻塞式I/O模式,提高了系统吞吐和响应能力。Swift组件如下图所示,包括:

在这里插入图片描述

  • 代理服务(Proxy Server):对外提供对象服务 API,会根据环的信息来查找服务地址并转发用户请求至相应的账户、容器或者对象服务;由于采用无状态的 REST 请求协议,可以进行横向扩展来均衡负载。
  • 认证服务(Authentication Server):验证访问用户的身份信息,并获得一个对象访问令牌(Token),在一定的时间内会一直有效;验证访问令牌的有效性并缓存下来直至过期时间。
  • 缓存服务(Cache Server):缓存的内容包括对象服务令牌,账户和容器的存在信息,但不会缓存对象本身的数据;缓存服务可采用 Memcached 集群,Swift 会使用一致性散列算法来分配缓存地址。
  • 账户服务(Account Server):提供账户元数据和统计信息,并维护所含容器列表的服务,每个账户的信息被存储在一个 SQLite 数据库中。
  • 容器服务(Container Server):提供容器元数据和统计信息,并维护所含对象列表的服务,每个容器的信息也存储在一个 SQLite 数据库中。
  • 对象服务(Object Server):提供对象元数据和内容服务,每个对象的内容会以文件的形式存储在文件系统中,元数据会作为文件属性来存储,建议采用支持扩展属性的 XFS 文件系统。
  • 复制服务(Replicator):会检测本地分区副本和远程副本是否一致,具体是通过对比散列文件和高级水印来完成,发现不一致时会采用推式(Push)更新远程副本,例如对象复制服务会使用远程文件拷贝工具 rsync 来同步;另外一个任务是确保被标记删除的对象从文件系统中移除。
  • 更新服务(Updater):当对象由于高负载的原因而无法立即更新时,任务将会被序列化到在本地文件系统中进行排队,以便服务恢复后进行异步更新;例如成功创建对象后容器服务器没有及时更新对象列表,这个时候容器的更新操作就会进入排队中,更新服务会在系统恢复正常后扫描队列并进行相应的更新处理。
  • 审计服务(Auditor):检查对象,容器和账户的完整性,如果发现比特级的错误,文件将被隔离,并复制其他的副本以覆盖本地损坏的副本;其他类型的错误会被记录到日志中。
  • 账户清理服务(Account Reaper):移除被标记为删除的账户,删除其所包含的所有容器和对象。
1.2.1 Proxy Server

Proxy Server可以说是Swift的核心,运行着swift-proxy-server进程。它提供Swift API的服务,负责Swift其余组件间的相互通信。对于每个客户端的请求,它在Ring中查询相应Account、Container以及Object的位置,并且转发这些请求。

  • 提供了Rest-full API,开发者可以通过这个接口快捷构建定制的客户端与Swift交互。
  • Proxy Server也会处理存储错误,当一个服务器无法对一个对象的PUT操作进行响应,它将从Ring中查询一个可以接手的服务器并将请求传递给它。
  • Ring是数据到物理设备映射的集合,通过Zone(区域)、Device(设备)、Partition(分区)和Replica(副本)来维护映射信息
1.2.2 存储节点

存储节点需要运行account和container、objectserver等服务

  • Object Server:对象服务是一个简单的二进制大对象存储服务,可以用来存储、检索和删除本地设备上的对象。在文件系统上,对象以二进制文件的形式存储,它的元数据存储在文件系统的扩展属性(xattrs)中。这要求用于对象服务的文件系统需要支持文件有扩展属性。
  • Container Server:容器服务的首要工作是处理对象的列表。容器服务并不知道对象存在哪,只知道指定容器里存的哪些对象。 这些对象信息以sqlite数据库文件的形式存储,和对象一样在集群上做类似的备份。容器服务也做一些跟踪统计,比如对象的总数,容器的使用情况。
  • Account Server:账户服务与容器服务非常相似,不同的是负责处理容器的列表而不是对象。
1.3 关键技术
1.3.1 一致性哈希

Swift是基于一致性哈希技术,通过计算将对象均匀分布到虚拟空间的虚拟节点上,在增加或删除节点时可大大减少需移动的数据量;虚拟空间大小通常采用2的n次幂,便于进行高效的移位操作;然后通过独特的数据结构 Ring(环)再将虚拟节点映射到实际的物理存储设备上,完成寻址过程。衡量一致性哈希的4个指标:

  • 平衡性(Balance):平衡性是指Hash的结果能够尽可能分布均匀,充分利用所有缓存空间。
  • 单调性(Monotonicity):单调性是指如果已经有一些内容通过哈希分派到了相应的缓冲中,又有新的缓冲加入到系统中。哈希的结果应能够保证原有已分配的内容可以被映射到新的缓冲中去,而不会被映射到旧的缓冲集合中的其他缓冲区。
  • 分散性(Spread):分散性定义了分布式环境中,不同终端通过Hash过程将内容映射至缓存上时,因可见缓存不同,Hash结果不一致,相同的内容被映射至不同的缓冲区。
  • 负载(Load):负载是对分散性要求的另一个纬度。既然不同的终端可以将相同的内容映射到不同的缓冲区中,那么对于一个特定的缓冲区而言,也可能被不同的用户映射为不同的内容。

在这里插入图片描述

Swift使用一致性HASH算法的主要目的是在改变集群的node数量时(增加/删除服务器),能够尽可能少地改变已存在key和node的映射关系,以满足单调性。在一致性HASH中引入了虚拟节点的概念,一个实际节点对应了若干个“虚拟节点”,“虚拟节点”在哈希空间中以哈希值排列。因此,在Swift中存在两种映射关系,对于一个文件,通过哈希算法(MD5)找到对应的虚节点(一对一的映射关系),虚节点再通过映射关系(ring文件中二维数组)找到对应的设备(多对多的映射关系),这样就完成了一个文件存储在设备上的映射。

1.3.2 数据一致性模型

根据CAP理论,无法同时满足一致性、可用性和分区容忍性,Swift放弃严格一致性(满足ACID事务级别),而采用最终一致性模型(Eventual Consistency),来达到高可用性和无限水平扩展能力。Swift采用Quorum仲裁协议,算法如下:

  • 定义:N表示数据的副本总数;W表示写操作被确认接受的副本数量;R表示读操作的副本数量。
  • 强一致性:R+W>N,以保证对副本的读写操作会产生交集,从而保证可以读取到最新版本;如果W=N,R=1,则需要全部更新, 适合大量读少量写操作场景下的强一致性;如果R=N,W=1,则只更新一个副本,通过读取全部副本来得到最新版本,适合大量写少量读场景下的强一致性。
  • 弱一致性:R+W<=N,如果读写操作的副本集合不产生交集,就可能会读到脏数据;适合对一致性要求比较低的场景。

在这里插入图片描述

Swift中默认配置是N=3,W=2>N/2,R=1或2,即每个对象会存在3个副本,这些副本会尽量被存储在不同区域的节点上;W=2表示至少需要更新2个副本才算写成功;当R=1时意味着某一个读操作成功便立刻返回,此种情况下可能会读取到旧版本(弱一致性模型);当R=2时,需要通过在读操作请求头中增加 x-newest=true 参数来同时读取2个副本的元数据信息,然后比较时间戳来确定哪个是最新版本(强一致性模型)。

注:如果数据出现了不一致,后台服务进程会在一定时间窗口内通过检测和复制协议来完成数据同步,从而保证达到最终一致性

1.3.3 Ring

Ring将虚拟节点(partition)均衡的映射到一组物理设备上,其数据结构由以下信息组成:

  1. 存储设备列表(List of Devices),表示集群中设备的列表,在Ring类内部被称为devs,设备信息包括唯一标识号(id)、区域号(zone)、权重(weight)、IP 地址(ip)、端口(port)、设备名称(device)、元数据(meta);
  2. Partition Assignment List,用于存放每个replica与device间映射关系,在Ring类内部被称为分区到设备映射关系(replica2part2dev_id数组);
  3. Partition Shift Value,表示计算数据hash的移位量,计算分区号的位移(part_shift整数)

Swift为account、container和object分别定义了ring,其查找过程如下:

在这里插入图片描述

  • 使用对象的层次结构account/container/object作为键,使用MD5散列算法得到一个散列值
  • 对该散列值的前4个字节进行右移操作得到分区索引号,移动位数由上面的part_shift设置指定(一致性哈希映射到虚拟节点);
  • 按照分区索引号在分区到设备映射表(replica2part2dev_id)里查找该对象所在分区的对应的所有设备编号,这些设备会被尽量选择部署在不同区域(Zone)内

Swift中Ring使用zone来保证数据的物理隔离,每个partition的replica都确保放在不同的zone中,Ring中默认有3个replica。Zone只是一个抽象概念,它可以是一个磁盘(disk drive),一台服务器(server),一个机架(cabinet),一个交换机(switch),甚至是一个数据中心(datacenter),建议至少部署5个zone。

总的来说,Ring引入一致性哈希的原因是为了减少由于增加结点导致数据项移动的数量来提高单调性;引入partition的原因是为了减少由于节点数过少导致移动过多的数据项(数据负载不均衡);引入replica的原因是防止数据单点、提高冗余性;引入zone的原因是为了保证分区容忍性;引入weight的原因是为了保证partition分配的均衡。

1.4 Swift存储结构

Swift存储内容一般放在/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要长得多。

2、Swift all in one安装部署

单节点部署swift环境,感兴趣的可以参考“https://docs.openstack.org/swift/latest/development_saio.html


参考资料:

  1. https://blog.csdn.net/sinat_27186785/article/details/51921458
  2. https://docs.openstack.org/swift/latest/development_saio.html
  3. https://www.cnblogs.com/Skybiubiu/p/14614288.html
  4. https://blog.51cto.com/u_15352876/3773632
  5. https://blog.csdn.net/sinat_27186785/article/details/51930074
  6. 分布式系列之开源分布式存储技术分析

转载请注明原文地址:https://blog.csdn.net/solihawk/article/details/123837321
文章会同步在公众号“牧羊人的方向”更新,感兴趣的可以关注公众号,谢谢!

在这里插入图片描述

相关文章

软件简介:蓝湖辅助工具,减少移动端开发中控件属性的复制和粘...
现实生活中,我们听到的声音都是时间连续的,我们称为这种信...
前言最近在B站上看到一个漂亮的仙女姐姐跳舞视频,循环看了亿...
【Android App】实战项目之仿抖音的短视频分享App(附源码和...
前言这一篇博客应该是我花时间最多的一次了,从2022年1月底至...
因为我既对接过session、cookie,也对接过JWT,今年因为工作...