自制文件系统系列的开端,主要是因为绝大部分人用到了文件这个概念,但是总是不以为然或者讳莫如深。其实关键还是对其原理缺乏了解。
总有朋友对 inode,文件的管理,权限等等有疑问,而这些只是最基础的概念。
自制文件系统的诞生理由就来源于此,目的是让大家认清楚,文件系统不过如此,朴实无华。
自制文件系统系列是一个完整的系列,从最开始教你看文件系统,介绍 FUSE 框架,最后做一个只读的文件系统,再做一个可写的分布式文件系统,是层层递进的。
03,04 篇介绍了原理并且附有完整的 Go 实现代码,可以让 0 基础的朋友,也能完整的制作出来,至于领悟多少,就看个人发挥了。
自制文件系统系列是典型的实战系列,着眼于动手,而不是纯粹的讲解。并且笔者也尽可能的把复杂的,非主干的知识剔除,目的是让你感受纯粹的文件系统,文件的概念。
- 亲自参与了文件的组织管理,权限的配置,内容的处理,加密存储,解密读取;
- 亲自参与了数据的跨节点传输;
- 亲手捕捉了来自内核的 IO 请求;
你以为文件系统就这样了吗?不不不,还远远不够。
我们在 04 篇 里提到了分布式文件系统的架构,分为 Client,元数据中心,Server 这三大部分。
Client 一般会有哪些功能在里面?如下图:
客户端主要是承接用户请求,然后把请求分发到后端服务器,这里就涉及到怎么分发的策略了。
一般有 3 类分发策略:
- 轮询:请求依次分发,这样能保证每一个后段节点都能得到请求;
- 普通哈希:普通的哈希策略,这个考量一定的随机性,一般可以选定一个随机因子。比如时间戳,请求数等等;
- 一致性哈希:这个方式在扩缩容的场景有比较好的表现,能够保证在扩缩容的保证迁移量控制在一个常数级别;
轮询下发示意图:
思考问题:哈希策略是为了后端节点的请求均衡,那如果由于各种原因已经导致了不均衡,怎么办?
这时候就要提到重要话题了:均衡迁移,一般也叫做 balance ,目的就是为了每个节点的数量平衡。
由于 balance 迁移一般会跟正常业务抢占 IO 资源,所以这里会涉及到 Qos 的策略,并且 balance 的触发时机和力度都是必须要考虑的。
哈希均衡是一个分布式系统里重要的模块功能,关系到怎么才能合理利用分布式机器节点。
副本就是数据存储多个副本的意思,这样就避免单点问题,数据就算丢了一份,还有其他份,这样可以提高可靠性。
多副本说起来简单,但其实有很有讲究,其中最关键就是一致性的问题。
怎么能保证写下去的数据是一致的?这里就关乎策略了,都写成功了才算成功,还是写部分成功了就报告成功?
上面分别对应两大类策略:WARO(Write ALl Read One),Quroum。这两大策略被统一在 CAP 理论里面,你要可用性高,那么必然要容忍一段时间的不一致。要数据完全一致,同一个副本组那么可用性就会拉跨。
WRAO
Quroum
现在讲了副本的策略,那么组成副本的形式又有哪几种呢?
这个其实可以按照粒度来区分,一般有几种形式组成副本:
- 文件级别
- 磁盘级别
- 主机级别
主机级别
- 优点:这个简单了,把多台主机组成副本镜像,主机的数据完全一致,互成镜像;
- 缺点:修复的粒度太大,扩容、缩容的粒度必须是副本数倍数的主机数,而且要是同构的机器,这个给资源准备提出了高要求;
磁盘级别
- 优点:磁盘镜像(这个跟 raid 1 很像,区别在分布式上)实现比主机级别的复杂点,这个是把多个磁盘组成副本镜像组。这个扩容、缩容的粒度就可以做到磁盘级别;
- 缺点:实现自然是比主机镜像要复杂,修复粒度还是较大,副本粒度还可以再做小一点;
文件级别
- 优点:把多个文件组成副本镜像组,副本粒度更小,调度更灵活。并且可以实现全局打散,让一块磁盘和所有的主机产生副本关系。这样在修复盘的时候,效率最高;
- 缺点:但同样的,粒度越小实现越复杂,元数据膨胀,越难管理;
纠删码也是数据冗余的一种策略,和副本不同,纠删码能够做到更高的可靠性和更低的冗余度。什么意思?
举个例子:
- 在 3 副本系统中,1 TiB 的用户数据,需要 3 TiB 的物理空间。只能容许一个副本节点损坏;
- 在 6+3 的纠删码系统中,1T 的用户数据,只需要 1.5 TiB 的空间,能够容许 3 个节点损坏;
纠删码的优势够大,对吧?特别是在这个数据为王的时代。公有云厂商如果用更低的价格存储了更多的数据,那就有极大的竞争力。
俗话说的好,天下没有免费的早餐。纠删码也是如此,虽然有更高的可靠性,还有更低的冗余度。但是却带来更复杂的管理,还有更多的计算能力要求。
划重点:校验块是要用算法计算出来的,吃 CPU ,所以纠删码的节点,CPU 不能太差。因为写的时候要计算,修复读的时候要计算。
修复策略,这个跟冗余策略是关联的。
- 副本镜像策略的话,修复就是数据拷贝成镜像即可,把数据源拷贝到目的文件;
- 纠删码的修复就是通过算法计算出缺失的数据块,然后写到目的地(这里是要吃计算能力的);
元数据中心一般有哪些功能?
顾名思义,就是元数据集中放置的地方,一般会放置一些用户的元数据,当然还有集群本身的元数据。这是一个非常重要的角色,但是由于过于重要,所以问题也常常会出现在这里,比如性能瓶颈,单点故障,那遇到这种情况,你会怎么做呢?
怎么解决单点故障?
划重点:以前说过的,使用冗余的节点是解决单点故障的唯一途径。 多节点随之而来的是一致性,所以一般用 Paxos,raft 或者其他一致性算法来管理管理多节点,形成一个对外有机的整体。
怎么解决性能问题?
性能这个就尴尬了,除非是支持线性扩容,否则这元数据中心必定是有性能极限的。所以一般通过固化集群规模,搭建多个集群来解决。
更极端一点?
是的,还有更极端的方式:取消元数据中心,设计一个无元数据的集群。
有项目实践的例子吗?
有,比如 Glusterfs ,就是一个典型的实现,最大的特点就是无元数据中心。
一般元数据中心还可以加上一个服务注册发现的功能,这样就能在集群组件扩容,缩容的时候从容面对。
集群拓扑的管理是元数据中心必须承担的责任。比如副本镜像的绑定关系,纠删码的绑定关系,存储节点的状态维护等等。
举个例子,A,B,C 这三台主机组成一个主机镜像。
192.168.56.1
192.168.56.2
192.168.56.3
这样写数据的时候如果选到了这组主机镜像,那么就只需要把请求发往这三台主机。
Server 一般有哪些功能?
存储服务端最核心的就是存储用户数据,管理磁盘空间,这是最核心的需求。
在这个模块什么是最重要的考虑?
稳定和安全。怎么把数据快速的存入,安全的持久化,快速的读出 是这个模块最核心的诉求。
所谓存储引擎就是最底层存储数据的模块,比如和文件,或者块设备打交道的模块。目的是掏空硬盘硬件的性能,跑出最高 iops,最高吞吐,最低时延 是这里的毕生追求。
这里的主角是 IO ,引擎模块就是要极限的压榨出 IO 的极限性能。
举个例子?
有,很多,比如 rocksdb 其实就可以作为一个很好的存储引擎模块,这个是一个 kv 存储引擎库,以前常用于 mongodb 的底层。当然了,现在 mongodb 已经改用 WiredTiger 这个存储引擎。
业务上增、删、改是常有的事。数据新增了要扩容,删除了自然要回收,这样才能达到重复利用空间的目的。怎么回收?很简单的,就是把删除的空间腾出来即可。如下,Compact 就是一个非常简单的方式。
磁盘坏盘是经常的,所以换盘的流程是 必须要设计的。这个是做到服务端(当然要客户端配合)。这个原则是什么呢?当然是数据安全。只有确认数据能够修复回来,那么就可以安全换盘。
这里只提到了一些最关键的模块,其实还有很多模块没提到,比如缓存策略,Qos 模块,任务调度策略等等。
- 做存储的,对象存储,块存储,文件存储等从业人员;
- Linux 后端开发人员;
- 大学生,正在理解“文件”的概念;
- Go 开发人员,对存储感兴趣的开发人员;
- 自制文件系统系列通过介绍文件系统,框架原理介绍,动手实践形成一个完整的系列文章,让人从 0 开始感受了一个文件系统的诞生;
- 文件系统内部也是分布式的,客户端和核心功能是协议解析,请求均衡转发,冗余策略(副本策略、纠删码策略),修复策略,等等。服务端的核心模块是存储引擎(单机存储),磁盘管理;
- 希望能让大家理解到一个观点:“文件”就是这样朴实无华。
文件系统远不止如此,但是后续的功能就不在这个模块讲了,这个系列就只狙击“文件”的概念。自制系列完结撒花,后续更新其他系列文章。坚持思考,就会很酷。