块存储、对象存储和文件存储
块存储、对象存储、文件存储是云计算云存储的概念,我想基于云存储,但也不限于云存储,谈谈自己的想法。
概念
首先给出chatgpt对块、对象、文件存储的定义
块存储(Block Storage),是一种高性能存储技术,主要用于对结构化或非结构化数据进行低延迟、高效存储。它将数据划分为固定大小的“块”,每个块都有唯一的地址,独立存储于存储介质上。这种存储方式广泛用于云计算、数据库和虚拟化等场景。
文件存储(File Storage),是一种常见的数据存储方式,使用文件和文件夹的层次化结构来组织和管理数据。它类似于操作系统中的文件系统,提供直观的路径访问和共享功能,适用于多个客户端访问共享数据的场景。
对象存储是一种现代化的存储架构,专为存储大量非结构化数据而设计。它将数据存储为对象,并通过唯一的标识符(Object ID)进行访问,而不是使用传统的文件系统层次结构。对象存储以高可扩展性、弹性和低成本著称,是云计算和大规模数据存储的核心技术之一。
下面从我的角度给出以上名词定义的解释版本
块存储
块存储功能上等价于一个块设备,linux 上可以使用lsblk 查看设备。在没有虚拟化情况下,一个块设备就是一块物理意义的硬盘。linux用sda,sdb, sdc表示块设备,块设备可以分区,例如sda1, sda2, sda3就是sda设备上的三个分区。linux 常用的分区工具是fdisk,每个块设备分区可以单独格式化文件系统(类似windows C, D等区)。
块存储的访问是在文件系统层下的,linux用户io会先走内核文件系统层,文件系统会维护(文件id,offset, length)到块设备的(设备号,块号)的映射,这种映射也可以理解成一种虚拟地址到物理地址的映射。一旦得到设备号和块号,对于本地存储,操作系统会调用驱动程序读写磁盘,并利用中断实现数据读写完毕后通知cpu处理。对于云存储,操作系统会携带(设备号,块号)发送网络包,同样注册中断实现网络包返回时通知cpu处理。一般而言,TCP协议网络包可以满足要求,如果追求高性能,就要涉及到用户态tcp协议,dpdk工具链,virtio协议规范,rdma高性能网络了。
根据块存储定义“它将数据划分为固定大小的“块”,每个块都有唯一的地址,独立存储于存储介质上”,可以得到块对齐的特点
- 块存储的单位是“块”,块是固定大小的(通常为4KB)。相较之下,文件存储的单位“文件”(File),对象存储的单位“对象”(object),是任意大小的。
- 由于”块”是固定大小,例如4K,那读写iosize可以设计成4K对齐。这可以大大减小存储涉及的复杂性。如果读写大小是任意的,用户可以写
[1023, 1025]
,[1022, 1026]
等任意区间,新区间数据还应该覆盖旧区间数据。可以想到,随着不断写入,区间数量线性增长,不仅占用大量内存,读操作还由于遍历所有区间导致性能下降。块存储的读写iosize设计成4K对齐的,用户只能4K对齐写,区间管理和读性能大大降低。
写区间管理是存储的重要模块,称为layout管理,可以分析得到,
写区间越多->内存占用越多->读性能下降,写性能不受影响
除了固定大小和iosize外,块存储的另一个重要特点是,一个块每时每刻只会被一个ip主机访问,一个云硬盘每时每刻也只会被一个ip主机访问,这很关键,这意味着,如果我们把云硬盘划分成固定大小的segment(segment可以很大,例如16GB、32GB,因为云硬盘大小很大)。我们可以把相同云硬盘的多个segment调度到多机上,而不用考虑对多机加分布式锁,甚至不用加锁! 我们可以想象块存储架构的一般实现形式,只关心segment的调度和segment中的(devid, blockid)到实际物理存储的映射,每个server记录了某个segment (devid, blockid)到实际物理存储映射(索引表),根据索引表处理读写请求,而master记录了segment和server的调度计划,如果底层采用的是类似hdfs的分布式文件系统,只需要把索引表全量复制到其他server,就可以实现segment的调度。
如果允许多个ip同时访问一块云硬盘,可以想到,为了保证数据一致性,需要保证某ip请求写数据时,其他ip无法写数据,也就是加写锁。但如果一个segment永远只有一个ip读写数据,我们只需要保证io处理顺序和客户端发出的请求顺序一致,不需要加写锁!单ip访问的特点也让块存储优化读写路径变得容易,因为不需要考虑多客户端缓存/数据一致性。
单ip访问导致块存储的缺点是,块存储不能多机共享。这是块存储的应用上的最大劣势。
总结块存储的三大特点
- 功能等价硬盘
- 块和读写iosize 4K对齐,layout管理容易
- 每个块设备每时每刻只有一个ip访问
文件存储
块存储相当于硬盘块设备,文件系统相当于ext4等文件系统。块存储访问的路径是,用户程序->虚拟文件系统->ext4等单机文件系统->网络层(TCP->IP->网卡)->后端;文件系统则是,用户程序->虚拟文件系统->网络层(TCP->IP->网卡)->后端。文件系统的网络层同样可以利用块存储介绍的用户态tcp协议,dpdk工具链,virtio协议规范,rdma高性能网络了来优化。
块存储遵循的网络协议常见的是iSCSI和NVMe-oF,前者更通用后者更追求性能。文件存储的网络协议Linux是NFS协议,Windows是SMB协议。其中,Linux 内核除了提供NFS模块之外,还提供fuse接口支持用户态自定义文件系统。
相比块存储协议主要是块读写,文件存储的协议更复杂。除读写外,还有rename(移动),readdir(读目录文件),link(软硬链接),文件锁等。文件协议的全部特性实现复杂,很多选择基于fuse自定义文件存储协议。
文件系统用N叉树来组织结构,或者说二元组(父目录handle,子文件name)。子文件和父目录常常相互影响,例如创建子目录后父目录link数量需要+1,子文件删除需要父目录加锁等
除了复杂的文件存储协议,文件存储另一个挑战是多客户端共享。和块存储不同,文件存储支持多客户端同时访问一个文件系统。这给文件存储实现带来很大困难
- 文件系统元数据难以拆分。如果一个文件系统拆分成两部分,每部分位于一个节点。那么从机器A rename文件到机器B就要加分布式锁,保证只有一个请求执行rename,否则会导致死锁。分布式锁性能代价很大。事实上rename 的死锁问题在单机文件系统也存在,linux通过保证rename操作全局唯一来避免死锁,可以参考这个文章, Linux文件系统中元数据的加锁机制与组织方式。元数据不拆分会导致元数据iops受限于单机iops,一个文件系统只能位于某个节点,那该文件系统的元数据iops受制于单节点的iops(数据可以拆分到多个节点,因此读写iops和吞吐可以水平扩展)
- readdir和remove 等语义实现困难,某客户端正在readdir,另一个客户端把目录或目录下的文件remove掉。可以对readdir的目录加读锁,删目录需要申请写锁;此外由于readdir 会分批返回,因此应该对readdir 的cookie文件进行软删除,使readdir 下次请求还可以在cookie文件处继续readdir,不受cookie文件删除影响。
- 复杂的元数据缓存。元数据缓存至少有inode缓存,缓存inode 的size, mode, link数量, atime/ctime/mtime等信息,加快getattr速度;dentry缓存,缓存目录项下的文件名,提高readdir速度。带来的问题就是, lookup,create,remove,rename会写dentry缓存(缓存穿透,会读后端,结果更新缓存),写期间需要对dentry加写锁,导致缓存无法被读写(相当于缓存失效),即dentry的所有请求都需要阻塞直到缓存更新好,会拖累元数据性能。方案是考虑多shot并发写后端元数据,读操作使用多版本,在写成功之前,只能读到写成功的数据。
- 客户端缓存一致性困难。客户端天然pagecache缓存是write-back缓存策略,多客户端同时写文件会不可避免的导致某客户端写入的数据无法被其他客户端感知的。处理方案,1.close-and-open一致性,即close文件会把数据刷到后端,从而被其他客户端看到 2. 定期刷到后端,nfsv3使用该策略,会牺牲一部分一致性 3. 使用文件锁,保证某文件同时刻只有一个客户端执行写操作,即保证1写多读。但文件锁的实现难点是服务端failover。nfsv3协议是无状态的,好处是服务端/客户端崩溃后重新连接不用恢复状态;客户端文件锁实现要求协议具有状态,问题就是崩溃后需要状态恢复。
块存储的块大小和iosize 4K对齐,对块存储的io区间管理(称为layout管理)降低了难度。不幸的是,文件存储对这俩都没要求,文件大小不固定,iosize也不固定。为了降低layout管理的复杂度,可以通过fuse自定义客户端让客户端发往后端的iosize尽量对齐。
文件存储的实现充满了折衷,例如元数据拆分能提高getattr,readdir等元数据操作的性能,但牺牲了rename的延迟和iops。客户端文件锁的实现提高了一致性和状态管理,但牺牲了客户端的多写能力。相对容易的文件存储实现架构是后端分为协议层、元数据层和数据层。协议层负责解析协议,转发,鉴权; 元数据层管理inode, dentry和hardlink表和元数据操作,数据层管理文件layout和读写;数据拆分,元数据不拆分;fuse实现自定义的客户端取代nfs。
总结文件存储的特点
- 功能等价文件系统
- 块和读写iosize 不要求对齐,layout管理困难
- 支持多ip共享访问
- 协议复杂,实现多样。主要的调用有lookup, getattr, setaddr, access, read, write, link, symlink, readlink, readdir, readdirplus, remove,rename, fsstat等
对象存储
对象存储是文件存储简化而来的,由于文件存储实现复杂,处处需要折衷,难以元数据拆分(水平扩展),无法提供稳定的高性能。
- 文件存储元数据难以拆分的原因是其利用(目录,文件)构建的目录树结构,对象存储取消目录概念,文件全部通过对象路径hash查找。因此对象存储的元数据就是一个简单的hash表,hash表是容易多机拆分的。只需要1. 将对象路径hash到节点ip 2. 将对象路径hash到key。因此,对象存储bucket的元数据能力远超文件存储。前者元数据iops能达到几十万上百万,后者只有几万。
- 对象存储没有了目录概念,自然也没有了rename,link,目录锁概念。而rename,link是文件存储最难实现的调用之一。
- 对象存储只有object元数据缓存,没有dentry缓存,缺点就是对象存储的listobject 性能一般
- 对象存储的块大小定长且很大(高达1M),对象存储原生不支持对象的随机写和追加写,没有文件存储客户端锁保证一写多读和layout管理困难的问题。
对象存储在读io上类似文件存储,支持对象随机读,只需要把路径/ 解析为目录;对象存储不支持写操作。如果要对象存储和文件存储结合使用,可以 1. 写io 写文件存储,文件存储后台tiering到对象存储 2. 读io 先读文件存储,文件存储不存在再读对象存储。
对象存储协议s3协议是一种http协议,也就是说对象存储支持浏览器互联网的访问,对象存储的服务环境更复杂,这让对象存储在鉴权、安全等方面建设比文件存储/块存储更完善,后两者一般在高可信、高速网络中使用rpc调用。
总结对象存储的特点
- 文件系统的简化版,无目录概念,支持海量客户端访问
- s3协议是一种http协议,服务更广泛(块/对象的主要服务对象是ecs/gpu等计算单元)
- 支持随机读但不支持写,几乎不需要layout管理。元数据能力容易扩展。
layout管理
- 写操作
块存储的layout,(设备号、块)->后台(文件id, offset),offset和length是4K对齐的
文件存储的layout,用户(文件inode, offset)-> 后台(文件id, offset),offset和length可以不对齐
对象存储的layout,用户(objectid)->后台(文件id, offset)
- 读操作
块存储的layout,(设备号、块)->后台(文件id, offset, length),offset和length是4K对齐的,后台文件offset是最新的写入
文件存储的layout,用户(文件inode, offset, length)-> 后台(文件id, offset, length),offset和length可以不对齐,offset是最新的写入
对象存储的layout,用户(objectid)->后台(文件id, offset)。直接记录object在后台文件存放的起始位置,即可随机读。对象存储没有覆盖写
块存储和文件存储由于支持随机覆盖写,读操作需要保证始终读到最新写入的offset,对象存储不支持随机写,读操作只需要找到object的起始位置即可。
索引表的分裂和调度
块存储/文件存储的layout表,文件存储的元数据inode/dentry表都要记录到KV存储中。KV存储的基本能力是,
- 如果KV体积庞大,能分裂成两个KV;
- KV可以调度到任何机器中
如果KV的key是有序的,KV的分裂可以按照key来拆分,小于某key的为KV1,大于的为KV2。KV分裂依赖于复制,
- 全量复制。对旧KV复制出两个新KV,复制过程没有发生数据的复制,只是数据的引用。KV内存态的信息记录在redolog,磁盘态的信息记录在SST文件。新的KV1,2 replay redolog生成内存态KV,根据区间引用SST获得磁盘态信息。全量复制应当是复制到某个checkpoint。
- 增量复制。增量复制的进度单位应该是inode,设备等物理概念,当一个inode全量复制完毕,后续该inode写操作即可在新KV上进行。逐渐过渡到全部inode,设备复制完毕,把旧KV清理掉。
KV的调度也依赖于复制
- 将旧KV在新节点上复制一份。同样是先全量复制,后增量复制。
本文标题:块存储、对象存储和文件存储
文章作者:Infinity
发布时间:2024-11-15
最后更新:2025-01-11
版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-SA 3.0 CN 许可协议。转载请注明出处!