Bfs串讲时被问到的问题以及我整理的答案

by 杨策

  1. BlockMapping内的东西不落地么?重启需重新加载么?耗时多少?

    • BlockMapping内的东西确实是不落地的。可以通过Namespace中的内容以及ChunkServer的block report重建出来。每次NameServer重启时都会重建Blockmapping中的内容。耗时主要是顺序扫描leveldb的过程。
  2. BlockMapping所占内存空间会成为nameserver的瓶颈么?

    • BlockMapping中每个block都是int64编号的,所以数值上空间不成问题,但由于BlockMapping中存放了所有block的信息,所以实际上,物理机器上的内存大小可能会成为制约因素。但是,后续可能会考虑将BlockMapping拆分成分布式存储的。目前来看,每个entry占用的内存不到100个字节,几十G的内存粗略估算下应该来可存放亿级别的block数。
  3. 为什么namespace的存储设计成这样?为什么不存原路径?

    • 主要是为了高效的list以及mv
  4. mv操作是低频操作,有必要牺牲create的性能去提高mv的效率么?

    • Create其实也不会太慢。时间复杂度为O(n),其中n为目录深度。
  5. 为什么不缓存namespace?

    • Namespace中的数据其实是存放在leveldb中的,leveldb本身是有缓存的。至于sdk端的缓存,可能会产生一些不一致性问题。
  6. sdk拿到文件所在的cs之后,发生了rename,会不会读到错误的文件?

    • Sdk在向cs发起读请求时,给cs提供的参数是需要进行读取的block id。比如说现在要读A文件,然后其它人将A rename为了B,但是A中block的id是不变的,所以可以正常读。如果要读A时, B被rename成了A,那么A之前的block会被gc策略删除,但是即使物理文件被删除,在fd尚未被close之前,仍然可以读到A中的数据。这也与linux中的语义是一样的(linux中也可以删除正在读写的文件,只有当该文件的最后一个fd被关闭时,文件才真正的被删除)
  7. 文件的block是一次性拿到的还是多次拿到的?

    • 当前实现中还只有一个block,所以是一次性拿到的。如果有多个block时,一次需要全部拿来。否则发生rename时,可能会新旧两个文件各读一半。
  8. 允许用户自定义block大小么?

    • 目前没考虑允许。目前的设计是支持比较大的文件,如果允许的话,用户配置的block过小,可能导致Nameserver内存爆掉。
  9. AddBlock在什么时候会被调用?

    • AddBlock在SDK首次向 一个新创建的文件调用Write时被调用,然后sdk会与NameServer通信,Namserver为其分配block id,以及挑选出足够的ChunkServer供sdk写入使用。
  10. 为什么不在创建文件的时候就直接AddBlock?

    • 因为有可能为一些空文件,这里使用lazy的模式,到最后一步再去创建。
  11. 什么时候chunkserver上会有一个大小为零的block?

    • Sdk创建了一个文件后,根本没进行过Write,这时,只有在NameServer中有该文件的元信息,但是ChunkServer上是没有的。
    • Sdk向一个新建的文件发起了写请求,但是该写请求在反映到ChunkServer中时,ChunkServer刚创建完文件就挂了,此里该block的大小为0。
    • 实际实现过程中,Sdk在AddBlock时,会首先发送一个长度为0的写请求,促使ChunkServer提前创建好写block所需的文件,但是这时该block的大小为0.(@世光和@丽媛昨天说的应该是这种情况?)
  12. 链式写有什么问题么?

    • 链式写有可能存在慢节点,一旦链中某个结点速度较慢,则会拖慢整个链的写入速度,现在也支持扇出写,同时写四个副本,只要有三个成功就返回成功,可以去规避慢节点。
  13. sdk写一定会选本地的cs么?

    • 如果sdk本地有cs,而且cs上的磁盘空间以及读写压力在可承受范围内,会在选择cs时增加该cs的权重,即增加其被选择的概率,因为如果一定选择本地的cs的话,本地的cs将很快被打满。
  14. 写流程中的流控有哪些?

    • 流控分两个方面:1.sdk端 2. ChunkServer端。每次sdk向ChunkServer发起写请求后,若sdk收到ChunkServer返回的写成功,则会移动自己维护的滑动窗口,当滑动窗口编号的上限小于sdk当前将要发送的写请求编号时,会将当前的写请求Dealy(Delay的是线程池中的task)
  15. sdk端内存有控制么?

    • 当前并没有。如果sdk写的过快,确实可能内在暴增。
  16. sync操作落盘么?如果不落盘会不会丢数据?

    • Sync操作返回成功时可以保证sdk把自己所缓存的数据全部推到了ChunkServer端,然而此时ChunkServer并不见得把所有数据都落盘了。但是,ChunkServer在正常停机时,会将自己所有内存中的数据落盘,如果三个副本所在的cs都同时突然了异常退出的情况,这些数据是可能丢失的,这一点后面会改进。
  17. 混合模式写主挂了,是不是就写不了了,还能open文件继续写么?

    • 是的,如果primary replica挂掉,写操作将会失败,NameServer会将该block的所有副本都关闭。文件不可以open继续写,因为当前Bfs的设计是所有文件一旦被close掉就不可以再以写的方式打开。
  18. 如果sync失败,怎么知道文件到底有多大?

    • Sync失败后,NameServer会将该block的所有副本都close掉,然后进行副本修复,修复的原则是:尽量以当前仍在线的,具有数据最多的副本为标准进行修复。修复完成后,Stat中以得到文件的大小。
  19. 不sync得数据会丢,上层用户会需要做额外工作,怎么解决?

    • 其实使用单机文件系统也有这个问题。用户可以记录一个上次sync时的offset。
  20. 多种些模式,需要暴露给用户么?

    • 当前是做成了sdk端可配置的选项,在后续的过程中会通过测试来选取一个最合适的策略,尽量不暴露给用户。
  21. 正在读写的文件被删掉会发生什么?还能继续读写么?

    • 先说正在读的,被删除时,ChunkServer删除物理文件,释放引用计数,删除内存中对应的条目,如果文件只是被读,并未被写,则这时引用计数应该会变为0,从而引起内存结构的析构,文件fd被关闭,后续不可再被读。
    • 如果是正在写的,由于删除后,内存中维护的block信息已经被删除,所以后面写请求会失败。然而由于ChunkServer端的写buffer是异步的,每次写Buffer时会增加引用计数,所以,当Buffer被全部写出去之后,引用计数会变为0,引起内存结构的析构,文件fd被关闭。
    • 但是,由于文件删除使用的是gc的模式,所以无论是读还是写,都只有在ChunkServer向NameServer进行block report后,NameServer告知ChunkServer可以对某个block进行gc后,ChunkServer才真正的进入删除流程,在此之前,文件读写都是可以正常进行的。
  22. 为什么读文件不加引用计数?

    • ChunkServer中维护的内存结构中本身就有一个对Block的引用,无论是读还是写。
  23. 如果持续open文件但是不写,会不会对cs产生攻击效果,怎么解决?

    • 嗯,当前没有限制。可以在SDK中限制一下,类似linux中每个进程的最大打开文件数。
  24. 挂一个副本,副本恢复的时候会对其他两个副本做校验么?

    • 副本恢复时,为了尽量的给用户呈现出最完整的数据,所以会以以拥有数据量最大的副本作为标准进行恢复。当该副本数恢复到两个及两个以上时,再删除其它版本的副本。至于副本的checksum,目前还没有做。
  25. 有什么策略保证三副本间是一致的?

    • 同上,目前没有支持类似checksum的功能,不过个人感觉,保证数据不出错应该是多层去保证,类似TCP/IP协议?链路层有校验,网络层也会有校验
  26. 正在写的过程中,挂了一个cs,副本恢复的时候以最高版本为准还是以最低版本为准?

    • 前面已经提到,以最高版本为标准。
    • 有没有必要增加一个强制sync的接口?

    • 嗯,这个可以有~

  27. sdk可以开放异步接口?

    • 这个也可以有,实现起来比较容易
  28. bfs的性能指标和监控系统?

    • 由于目前ChunkServer端的写是异步的,所以可能导致用户看到的数据虚高。。 有一个简单的Web页面可以看到集群的一些基本负载状态。