> For the complete documentation index, see [llms.txt](https://mit-public-courses-cn-translatio.gitbook.io/mit6-824/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://mit-public-courses-cn-translatio.gitbook.io/mit6-824/lecture-03-gfs/3.1.md).

# 3.1分布式存储系统的难点（Why Hard）

今天我们讨论一下GFS，也就是The Google File System这篇论文。这门课程的主要内容是“大型存储”，而GFS这是这门课里有关如何构建大型存储系统的众多案例学习的第一篇。

![](https://2933519158-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MAkokVMtbC7djI1pgSw%2F-MDRswwJEUg8vzbx9ijK%2F-MDRuobAqWnJso3mgDLw%2Fimage.png?alt=media\&token=2fd99c2e-d91d-40d6-b179-47c4e44ec830)

之所以要说存储，原因是，存储是一种关键的抽象。你可以想象，在分布式系统中，可能有各种各样重要的抽象可以应用在分布式系统中，但是实际上，简单的存储接口往往非常有用且极其通用。所以，构建分布式系统大多都是关于如何设计存储系统，或是设计其它基于大型分布式存储的系统。所以我们会更加关注如何为大型分布式存储系统设计一个优秀的接口，以及如何设计存储系统的内部结构，这样系统才能良好运行。通过阅读GFS论文，我们可以开始了解到这是怎么做到的。

同时，GFS论文也涉及到很多本课程常出现的话题，例如并行性能、容错、复制和一致性。论文的内容也较为直观，容易理解。论文本身也是一篇优秀的系统论文，它从硬件到使用了GFS的软件都有讨论，并且它也是一个成功的现实世界的设计。尽管这是在学术会议上发表的学术论文，但是文章里介绍的东西（GFS）也相当成功，并且在现实世界中使用了相当长的时间。所以，我们知道，我们今天讨论的是一个非常好的且有用的设计。

在讨论GFS之前，我想聊一聊关于分布式存储系统，做一些背景介绍。

首先 为什么分布式存储系统会如此之难，以至于你需要做大量的工作才能让它正确工作？我们从一个特殊的角度来理解，在这门课程的后面，这种理解角度也会出现。

![](https://2933519158-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MAkokVMtbC7djI1pgSw%2F-MDRswwJEUg8vzbx9ijK%2F-MDRzGnwRXTb27gtT0re%2Fimage.png?alt=media\&token=316c8032-c46b-49c7-bf09-621b5b7bf142)

人们设计大型分布式系统或大型存储系统出发点通常是，他们想获取巨大的性能加成，进而利用数百台计算机的资源来同时完成大量工作。因此，性能问题就成为了最初的诉求。 之后，很自然的想法就是将数据分割放到大量的服务器上，这样就可以并行的从多台服务器读取数据。我们将这种方式称之为分片（Sharding）。

![](https://2933519158-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MAkokVMtbC7djI1pgSw%2F-MDS1vSBN_hMHTLrvBv8%2F-MDSC7GVqnDWUDJM7DUA%2Fimage.png?alt=media\&token=768b9cb3-51f5-43a3-b6a1-21a1b1473c1a)

如果你在成百上千台服务器进行分片，你将会看见常态的故障。如果你有数千台服务器，那么总是会有一台服务器宕机，每天甚至每个小时都可能会发生错误。所以，我们需要自动化的方法而不是人工介入来修复错误。我们需要一个自动的容错系统，这就引出了容错这个话题（fault tolerance）。

![](https://2933519158-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MAkokVMtbC7djI1pgSw%2F-MDS1vSBN_hMHTLrvBv8%2F-MDSDRU_e_-eUd5eIM45%2Fimage.png?alt=media\&token=76a663c7-867d-4376-9dec-86d8d1a359f3)

实现容错最有用的一种方法是使用复制，只需要维护2-3个数据的副本，当其中一个故障了，你就可以使用另一个。所以，如果想要容错能力，就得有复制（replication）。

![](https://2933519158-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MAkokVMtbC7djI1pgSw%2F-MDS1vSBN_hMHTLrvBv8%2F-MDSDr9aNnP0fIaXk2wB%2Fimage.png?alt=media\&token=f8564fac-392c-4c2e-8b29-f8358df0de81)

如果有复制，那就有了两份数据的副本。可以确定的是，如果你不小心，它们就会不一致。所以，你本来设想的是，有了两个数据副本，你可以任意使用其中一个副本来容错。但是如果你不够小心，两个数据的副本就不是完全一致，严格来说，它们就不再互为副本了。而你获取到的数据内容也将取决于你向哪个副本请求数据。这对于应用程序来说就有些麻烦了。所以，如果我们有了复制，我们就有不一致的问题（inconsistency）。

![](https://2933519158-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MAkokVMtbC7djI1pgSw%2F-MDS1vSBN_hMHTLrvBv8%2F-MDSEs5jWJmm9BBKR5cd%2Fimage.png?alt=media\&token=40bee031-7a9f-4065-b931-e0960731d2a3)

通过聪明的设计，你可以避免不一致的问题，并且让数据看起来也表现的符合预期。但是为了达到这样的效果，你总是需要额外的工作，需要不同服务器之间通过网络额外的交互，而这样的交互会降低性能。所以如果你想要一致性，你的代价就是低性能。但这明显不是我们最开始所希望的。

![](https://2933519158-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MAkokVMtbC7djI1pgSw%2F-MDS1vSBN_hMHTLrvBv8%2F-MDSGe1jmv1bbMQqTMDI%2Fimage.png?alt=media\&token=d056d961-3f34-4c72-9a82-83aa59fb2899)

当然，这里并不是绝对的。你可以构建性能很高的系统，但是不可避免的，都会陷入到这里的循环来。现实中，如果你想要好的一致性，你就要付出相应的代价。如果你不想付出代价，那就要忍受一些不确定的行为。我们之后会在很多系统中看到这里介绍的循环。通常，人们很少会乐意为好的一致性付出相应的性能代价。
