{"action":"created","comment":{"url":"https://api.github.com/repos/xitu/gold-miner/pulls/comments/192316531","pull_request_review_id":125069559,"id":192316531,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDE5MjMxNjUzMQ==","diff_hunk":"@@ -1,226 +1,226 @@\n > * 原文地址：[Algorithms Behind Modern Storage Systems](https://queue.acm.org/detail.cfm?id=3220266)\n-> * 原文作者：[acmqueue](https://queue.acm.org/)\n+> * 原文作者：[Alex Petrov](http://coffeenco.de/)\n > * 译文出自：[掘金翻译计划](https://github.com/xitu/gold-miner)\n > * 本文永久链接：[https://github.com/xitu/gold-miner/blob/master/TODO1/algorithms-behind-modern-storage-systems.md](https://github.com/xitu/gold-miner/blob/master/TODO1/algorithms-behind-modern-storage-systems.md)\n-> * 译者：\n+> * 译者：[LeopPro](https://github.com/LeopPro)\n > * 校对者：\n \n-# Algorithms Behind Modern Storage Systems\n+# 支撑现代存储系统的算法\n \n-## Different uses for read-optimized B-trees and write-optimized LSM-trees\n+## 读优化 B-Tree 和写优化 LSM-Tree 的不同用途\n \n-### Alex Petrov\n+### 作者：Alex Petrov\n \n-The amounts of data processed by applications are constantly growing. With this growth, scaling storage becomes more challenging. Every database system has its own tradeoffs. Understanding them is crucial, as it helps in selecting the right one from so many available choices.\n+应用程序处理的数据量不断增长。因此，扩展存储变得愈发具有挑战性。每个数据库系统都有自己的方案。为了做出正确的选择，了解它们是至关重要的。\n \n-Every application is different in terms of read/write workload balance, consistency requirements, latencies, and access patterns. Familiarizing yourself with database and storage internals facilitates architectural decisions, helps explain why a system behaves a certain way, helps troubleshoot problems when they arise, and fine-tunes the database for your workload.\n+每个应用程序在读写负载平衡、一致性、延迟和访问模式方面各不相同。熟悉数据库和底层存储有助于架构决策；有助于解释系统行为；有助于解决问题；有助于根据具体情况调优。\n \n-It's impossible to optimize a system in all directions. In an ideal world there would be data structures guaranteeing the best read and write performance with no storage overhead but, of course, in practice that's not possible.\n+十全十美的优化系统是不可能的。我们当然希望有一个数据结构既能保证最佳的读写性能，又不需要任何存储开销，但显然，这是不存在的。\n \n-This article takes a closer look at two storage system design approaches used in a majority of modern databases—read-optimized [B-trees](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.96.6637&rep=rep1&type=pdf)<sup>1</sup> and write-optimized [LSM (log-structured merge)-trees](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.44.2782&rep=rep1&type=pdf)<sup>5</sup>—and describes their use cases and tradeoffs.\n+本文深入讨论了大多数现代数据库中使用的两种存储系统设计 —— 读优化 [B-Tree](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.96.6637&rep=rep1&type=pdf) <sup><a href=\"#note1\">[1]</a></sup> 和写优化 [LSM（结构化日志合并）-Tree](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.44.2782&rep=rep1&type=pdf) <sup><a href=\"#note5\">[5]</a></sup> —— 并描述了它们的用例和优缺权衡。\n \n-### B-Trees\n+### B-Tree\n \n-B-trees are a popular read-optimized indexing data structure and generalization of binary trees. They come in many variations and are used in many databases (including [MySQL InnoDB](https://dev.mysql.com/doc/refman/5.7/en/innodb-physical-structure.html)<sup>4</sup> and [PostgreSQL](http://www.interdb.jp/pg/pgsql01.html)<sup>7</sup>) and even [file systems](https://en.wikipedia.org/wiki/HTree) (HFS+<sup>8</sup>, HTrees in ext4<sup>9</sup>). The _B_ in _B_-tree stands for _Bayer_, the author of the original data structure, or _Boeing_, where he worked at that time.\n+B-Tree 是一种流行的读优化索引数据结构，是二叉树的泛化。它有许多变种，并且被用于多种数据库（包括 [MySQL InnoDB](https://dev.mysql.com/doc/refman/5.7/en/innodb-physical-structure.html) <sup><a href=\"#note4\">[4]</a></sup>、[PostgreSQL](http://www.interdb.jp/pg/pgsql01.html) <sup><a href=\"#note7\">[7]</a></sup>）甚至[文件系统](https://en.wikipedia.org/wiki/HTree)（HFS+ <sup><a href=\"#note8\">[8]</a></sup>、HTrees ext4 <sup><a href=\"#note9\">[9]</a></sup>）。B-Tree 中的 _B_ 代表原始数据结构的作者 _Bayer_，或是他当时就职的公司 _Boeing_。\n \n-In a [binary tree](https://en.wikipedia.org/wiki/Binary_tree) every node has two children (referred as a left and a right child). Left and right subtrees hold the keys that are less than and greater than the current node key, respectively. To keep the tree depth to a minimum, a binary tree has to be balanced: when randomly ordered keys are being added to the tree, it is natural that one side of the tree will eventually get deeper than the other.\n+在[搜索二叉树](https://en.wikipedia.org/wiki/Binary_tree)中，每个节点都有两个孩子（称为左右孩子）。左子树的节点值小于当前节点值，右子树反之。为了保持树的深度最小，搜索二叉树必须是平衡的：当随机顺序的值被添加到树中时，如果不加调整，终会导致树的倾斜。\n \n-One way to rebalance a binary tree is to use so-called rotation: rearrange nodes, pushing the parent node of the longer subtree down below its child and pulling this child up, effectively placing it in its parent's position. Figure 1 is an example of rotation used for balancing in a binary tree. On the left, a binary tree is unbalanced after adding node 2 to it. In order to balance the tree, node 3 is used as a pivot (the tree is rotated around it). Then node 5, previously a root node and a parent node for 3, becomes its child node. After the rotation step is done, the height of the left subtree decreases by one and the height of the right subtree increases by one. The maximum depth of the tree has decreased.\n+一种平衡二叉树的方法是所谓的旋转：重新排列节点，将较深子树的父节点向下推到其子节点下方，并将该子节点拉上来，将其放在原父节点的位置。图 1 是平衡二叉树中的旋转示例。在左侧添加节点 2 后，二叉树失去平衡。为了使该树平衡，将其以节点 3 为轴旋转（树围绕它旋转）。然后节点 5（旋转前是根节点和节点 3 的父节点）成为其子节点。旋转完成后，左侧子树的深度减少 1，右侧子树的深度增加 1。树的最大深度已经减小。\n \n-![Algorithms Behind Modern Storage Systems](http://deliveryimages.acm.org/10.1145/3230000/3220266/petrov1.png)\n+![支撑现代存储系统的算法](https://s1.ax1x.com/2018/05/18/C6yZiF.png)\n \n-Binary trees are most useful as in-memory data structures. Because of balancing (the need to keep the depth of all subtrees to a minimum) and low fanout (a maximum of two pointers per node), they don't work well on disk. B-trees allow for storing more than two pointers per node and work well with block devices by matching the node size to the page size (e.g., 4 KB). Some implementations today use larger node sizes, spanning across multiple pages in size.\n+二叉树是最有用的内存数据结构。然而由于平衡（保持所有子树的深度最小）和低出度（每个节点最多两个子节点），它们在磁盘上水土不服。B-Tree 允许每个节点存储两个以上的指针，并通过将节点大小与页面大小（例如，4 KB）进行匹配来与块设备协同工作。今天的一些实现将使用更大的节点大小，跨越多个页面。\n \n-B-trees have the following properties:\n+B-Tree 有以下几个性质：\n \n-• Sorted. This allows sequential scans and simplifies lookups.\n+• 有序。这允许顺序扫描并简化查找。\n \n-• Self-balancing. There's no need to balance the tree during insertion and deletion: when a B-tree node is full, it is split in two, and when the occupancy of the neighboring nodes falls below a certain threshold, the nodes are merged. This also means that leaves are equally distant from the root and can be located using the same amount of steps during lookup.\n+• 自平衡。在插入和删除时不需要平衡树：当 B-Tree 节点已满时，它被分成两部分，并且当相邻节点的利用率低于某个阈值时，合并这两个节点。这也意味着各叶子节点与根节点的距离相等，并且在查找过程中定位的步数是相同的。\n \n-• Guarantee of logarithmic lookup time. This makes B-trees a good choice for database indexes, where lookup times are important.\n+• 对数级查找时间复杂度。查找时间是非常重要的，这使得 B-Tree 成为数据库索引的理想选择。\n \n-• Mutable. Inserts, updates, and deletes (also, subsequent splits and merges) are performed on disk in place. To make in-place updates possible, a certain amount of space overhead is required. A B-tree can be organized as a clustered index, where actual data is stored on the leaf nodes or as a heap file with an unclustered B-tree index.\n+• 易变。插入、更新、删除（包括因此导致的拆分和合并）过程在磁盘上进行。为了使就地更新成为可能，需要一定的空间开销。B-Tree 可以作为聚集索引，实际数据存储在叶子节点上，也可以作为非聚集索引，称为一个堆文件。\n \n-This article discusses the B+tree,<sup>3</sup> a modern variant of the B-tree often used for database storage. The B+tree is different from the original B-tree<sup>1</sup> in that (a) it has an additional level of linked leaf nodes holding the values, and (b) these values cannot be stored on internal nodes.\n+本文讨论的 B+Tree <sup><a href=\"#note3\">[3]</a></sup> 是一种经常用于数据库存储的 B-Tree 现代变种。B+Tree 与原始 B-Tree <sup><a href=\"#note1\">[1]</a></sup> 的不同之处在于：(1)它采用额外链接的叶节点存储值；(2)值不能存储在内部节点上。\n \n-#### Anatomy of the B-tree\n+#### 剖析 B-Tree\n \n-Let's first take a closer look at the B-tree building blocks, illustrated in figure 2. B-trees have several node types: root, internal, and leaf. Root (top) is the node that has no parents (i.e., it is not a child of any other node). Internal nodes (middle) have both a parent and children; they connect a root node with leaf nodes. Leaf nodes (bottom) carry the data and have no children. Figure 2 depicts a B-tree with a branching factor of four (four pointers, three keys in internal nodes, and four key/value pairs on leaves).\n+我们先来仔细看看 B-Tree 的结构，如图 2 所示。B-Tree 的节点有几种类型：根节点，内部节点和叶子节点。根节点（顶部）是没有双亲的节点（即，它不是任何节点的子节点）。内部节点（中间）有双亲和孩子节点；他们将根节点和叶子节点连接起来。叶子节点（底部）持有数据并且没有孩子节点。图 2 描绘了分支因子为 4（4 个指针，内部节点中有 3 个键，叶上有 4 个键/值对）的 B-Tree。\n \n-![Algorithms Behind Modern Storage Systems](http://deliveryimages.acm.org/10.1145/3230000/3220266/petrov2.png)\n+![支撑现代存储系统的算法](https://s1.ax1x.com/2018/05/18/C6qgs0.png)\n \n-B-trees are characterized by the following:\n+B-Tree 的特性如下：\n \n-• Branching factor-the number (_N_) of pointers to the child nodes. Along with the pointers, root and internal nodes hold up to _N-1_ keys.\n+• 分支因子 —— 指向子节点的指针数(_N_)。除指针外，根节点和内部节点还持有 N-1 个键。\n \n-• Occupancy-how many pointers to child items the node is currently holding, out of the maximum available. For example, if the tree-branching factor is _N_, and the node is currently holding _N/2_ pointers, occupancy is 50 percent.\n+• 利用率 —— 节点当前持有的指向子节点的指针数量与可用最大值之比。例如，若某树分支因子是 _N_，且其中某节点当前持有 _N/2_ 个指针，则该节点利用率为 50%。\n \n-• Height-the number of B-tree levels, signifying how many pointers have to be followed during lookup.\n+• 高度 —— B-Tree 的数量级，表示在查找过程中必须经过多少指针。\n \n-Every nonleaf node in the tree holds up to _N_ keys (index entries), separating the tree into _N+1_ subtrees that can be located by following a corresponding pointer. Pointer _i_ from an entry _K<sub>i</sub>_ points to a subtree in which all the index entries are such that _K<sub>i-1</sub> <= K<sub>searched</sub> < K<sub>i</sub>_ (where _K_ is a set of keys). The first and last pointers are special cases, pointing to subtrees in which all the entries are less than or equal to _K<sub>0</sub>_ in the case of the leftmost child, or greater than _K<sub>N-1</sub>_ in the case of the rightmost child. A leaf node may also hold a pointer to the previous and next nodes on the same level, forming a doubly linked list of sibling nodes. Keys in all the nodes are always sorted.\n+树中的每个非叶节点最多可持有 _N_ 个键（索引条目），这些键将树分为 _N+1_ 个子树，这些子树可以通过相应的指针定位。项 _K<sub>i</sub>_ 中的指针 _i_ 指向某子树，该子树中包含所有 _K<sub>i-1</sub> <= K<sub>目标</sub> < K<sub>i</sub>_（其中 _K_ 是一组键）的索引项。首尾指针是特殊的，它们指向的子树中所有的项都小于等于最左子节点的 _K<sub>0</sub>_ 或大于最右子节点的 _K<sub>N-1</sub>_。叶子节点同时持有其同级前后节点的指针，形成兄弟节点间的双向链表。所有节点中的键总是有序的。\n \n-#### Lookups\n+#### 查找\n \n-When performing lookups, the search starts at the root node and follows internal nodes recursively down to the leaf level. On each level, the search space is reduced to the child subtree (the range of this subtree includes the searched value) by following the child pointer. Figure 3 shows a lookup in a B-tree making a single root-to-leaf pass, following the pointers “between” the two keys, one of which is greater than (or equal to) the searched term, and the other of which is less than the searched term. When a point query is performed, the search is complete after locating the leaf node. During the range scan, the keys and values of the found leaf, and then the sibling leaf's nodes, are traversed, until the end of the range is reached.\n+进行查找时，将从根节点开始搜索，并经过内部节点递归向下到叶子节点层。在每层中，通过指向子节点的指针将搜索范围缩小到某子树（包含搜索目标值的子树）。图 3 展示了 B-Tree 的一次从根到叶的搜索过程，指针在两个键之间，其中一个大于（或等于）搜索目标，另一个小于搜索目标。进行点查询时，搜索将在定位到叶子节点后完成。进行范围扫描时，遍历所找到的叶子节点的键和值，然后遍历范围内的兄弟叶子节点。\n \n-![Algorithms Behind Modern Storage Systems](http://deliveryimages.acm.org/10.1145/3230000/3220266/petrov3.png)\n+![支撑现代存储系统的算法](https://s1.ax1x.com/2018/05/19/Cc6dRe.png)\n \n-In terms of complexity, B-trees guarantee _log(n)_ lookup, because finding a key within the node is performed using binary search, shown in figure 4. Binary search is easily explained in terms of searching for words beginning with a certain letter in the dictionary, where all words are sorted alphabetically. First you open the dictionary exactly in the middle. If the searched letter is alphabetically “less than” (appears earlier than) the one opened, you continue your search in the left half of the dictionary; otherwise, you continue in the right half. You keep reducing the remaining page range by half and picking the side to follow until you find the desired letter. Every step halves the search space, making the lookup time logarithmic. Searches in B-trees have logarithmic complexity, since on the node level keys are sorted, and the binary search is performed in order to find a match. This is also why it's important to keep the occupancy high and uniform across the tree.\n+在复杂度方面，B-Tree 保证查询的时间复杂度为 _log(n)_，因为查找一个节点中的键使用二分查找，如图 4 所示。二进制搜索可以通俗的解释为在字典中查找以某字母开头的单词，字典中所有单词都按字母顺序排序。首先你翻开正好在字典中间的一页。如果要查找的单词字母顺序小于（在前面）当前页，你继续在字典的左半边查找；否则就继续在右半边查找。你继续像这样将剩余的页码范围分为一半，选择一边，直到找到期望的字母。每一步都将搜索范围减半，因此查找的时间复杂度为对数级。 B-Tree 节点上的键是有序的，且使用二分查找算法进行匹配，因此 B-Tree 的搜索复杂度是对数级的。这也说明了保持树的高利用率和统一访问的重要性。\n \n-![Algorithms Behind Modern Storage Systems](http://deliveryimages.acm.org/10.1145/3230000/3220266/petrov4.png)\n+![支撑现代存储系统的算法](https://s1.ax1x.com/2018/05/19/CcRZy4.png)\n \n-#### Insertions, updates, and deletions\n+#### 插入、更新、删除\n \n-When performing insertions, the first step is to locate the target leaf. For that, the aforementioned search algorithm is used. After the target leaf is located, key and value are appended to it. If the leaf does not have enough free space, this situation is called overflow, and the leaf has to be split in two. This is done by allocating a new leaf, moving half the elements to it and appending a pointer to this newly allocated leaf to the parent. If the parent doesn't have free space either, a split is performed on the parent level as well. The operation continues until the root is reached. When the root overflows, its contents are split between the newly allocated nodes, and the root node itself is overwritten in order to avoid relocation. This also implies that the tree (and its height) always grows by splitting the root node.\n+进行插入时，第一步是定位目标叶子节点。此过程使用前序搜索算法。在定位目标页面后，键和值将被添加至该节点。如果该节点没有足够的可用空间，这种情况称为溢出，则将叶子节点分割成两部分。这是通过分配一个新的叶子节点，将一半元素移动到新节点并将一个指向这个新节点的指针添加到父节点来完成的。如果父节点没有足够的空间，则也会在父节点上进行分割。操作一直持续到根节点为止。当根节点溢出时，其内容在新分配的节点之间被分割，根节点本身被覆盖以避免重定位。这也意味着树（及其高度）总是通过分裂根节点而增长。\n \n-### LSM-Trees\n+### LSM-Tree\n \n-The log-structured merge-tree is an immutable disk-resident write-optimized data structure. It is most useful in systems where writes are more frequent than lookups that retrieve the records. LSM-trees have been getting more attention because they can eliminate random insertions, updates, and deletions.\n+结构化日志合并树是一个不可变的基于磁盘的写优化数据结构。它适用于写入比查询操作更频繁的场景。LSM-Tree 已经获得了更多的关注，因为它可以避免随机插入，更新和删除。\n \n-#### Anatomy of the LSM-tree\n+#### 剖析 LSM-Tree\n \n-To allow sequential writes, LSM-trees batch writes and updates in a memory-resident table (often implemented using a data structure allowing logarithmic time lookups, such as a [binary search tree](https://en.wikipedia.org/wiki/Self-balancing_binary_search_tree) or [skip list](https://en.wikipedia.org/wiki/Skip_list)) until its size reaches a threshold, at which point it is written on disk (this operation is called a _flush_). Retrieving the data requires searching all disk-resident parts of the tree, checking the in-memory table, and merging their contents before returning the result. Figure 5 shows the structure of an LSM-tree: a memory-resident table used for writes. Whenever the memory table is large enough, its sorted contents are written on disk. Reads are served, hitting both disk- and memory-resident tables, requiring a merge process to reconcile the data.\n+为了允许连续写入，LSM-Tree 在内存中的表（通常使用支持查找的时间复杂度为对数的数据结构，例如二叉搜索树或跳跃表）中批量写入和更新，当其大小达到阈值时将它写在磁盘上（这个操作称为刷新）。检索数据时需要搜索树所有磁盘中的部分，检查内存中的表，合并它们的内容，然后再返回结果。图 5 展示了 LSM-Tree 的结构：用于写入的基于内存的表。只要内存表体积达到一定程度，内存表就会被写入磁盘。进行读取时，同时读取磁盘和内存表，通过一个合并操作来整合数据。\n \n-![Algorithms Behind Modern Storage Systems](http://deliveryimages.acm.org/10.1145/3230000/3220266/petrov5.png)\n+![支撑现代存储系统的算法](https://s1.ax1x.com/2018/05/20/CgQ3cQ.png)\n \n-#### Sorted String Tables\n+#### 有序串行表\n \n-Many modern LSM-tree implementations (such as [RocksDB](https://en.wikipedia.org/wiki/RocksDB) and [Apache Cassandra](https://en.wikipedia.org/wiki/Apache_Cassandra)) implement disk-resident tables as SSTables (Sorted String Tables), because of their simplicity (easy to write, search, and read) and merge properties (during the merge, source SSTable scans and merged result writes are sequential).\n+因为 SSTable（有序串行表）的简单性（易于写入，搜索和读取）与合并性能（合并期间，扫描源 SSTable，合并结果的写入是顺序的），多数现代的 LSM-Tree 实现（例如 [RocksDB](https://en.wikipedia.org/wiki/RocksDB) 和 [Apache Cassandra](https://en.wikipedia.org/wiki/Apache_Cassandra)）都选用 SSTable 作为硬盘表。\n \n-An SSTable is a disk-resident ordered immutable data structure. Structurally, an SSTable is split into two parts: data and index blocks, as shown in figure 6. A data blocks consists of sequentially written unique key/value pairs, ordered by key. An index block contains keys mapped to data-block pointers, pointing to where the actual record is located. An index is often implemented using a format optimized for quick searches, such as a B-tree, or using a hash table for a point-query. Every value item in an SSTable has a timestamp associated with it. This specifies the write time for inserts and updates (which are often indistinguishable) and removal time for deletes.\n+SSTable 是一种基于硬盘的有序不可变的数据结构。从结构上来看，SSTable 可以分为两部分：数据块和索引块，如图 6 所示。数据块包含以键为顺序写入的唯一键值对。索引块包含映射到数据块指针的键，指针指向实际记录的位置。为了快速搜索，索引一般使用优化的结构实现，例如 B-Tree 或用于点查询的哈希表。SSTable 中的每一个值都有一个时间戳与之对应。时间戳记录了插入、更新（这两者一般不做区分）和删除时间。\n \n-![Algorithms Behind Modern Storage Systems](http://deliveryimages.acm.org/10.1145/3230000/3220266/petrov6.png)\n+![支撑现代存储系统的算法](https://s1.ax1x.com/2018/05/22/C2vwlD.png)\n \n-SSTables have some nice properties:\n+SSTable 具有以下优点：\n \n-• Point-queries (i.e., finding a value by key) can be done quickly by looking up the primary index.\n+• 通过查询主键索引可以实现快速的点查询（例如，通过键寻找值）。\n \n-• Scans (i.e., iterating over all key/value pairs in a specified key range) can be done efficiently simply by reading key/value pairs sequentially from the data block.\n+• 只需要顺序读取数据块上的键值对就可以实现扫描（例如，遍历制定范围内的键值对）。\n \n-An SSTable represents a snapshot of all database operations over a period of time, as the SSTable is created by the _flush_ process from the memory-resident table that served as a buffer for operations against the database state for this period.\n+SSTable 代表一段时间内所有数据库操作的快照，因为 SSTable 是通过对内存表的**刷新**操作创建的，该表充当此时段内对数据库状态的缓冲区。\n \n-#### Lookups\n+#### 查询\n \n-Retrieving data requires searching all SSTables on disk, checking the memory-resident tables, and merging their contents before returning the result. The merge step during the read is required since the searched data can reside in multiple SSTables.\n+检索数据需要搜索硬盘上的所有 SSTable，检查内存表，并且合并它们的内容后返回结果。要搜索的数据可以存储在多个 SSTable 中，因此合并步骤是必须的。\n \n-The merge step is also necessary to ensure that the deletes and updates work. Deletes in an LSM-tree insert placeholders (often called _tombstones_), specifying which key was marked for deletion. Similarly, an update is just a record with a later timestamp. During the read, the records that get shadowed by deletes are skipped and not returned to the client. A similar thing happens with the updates: out of two records with the same key, only the one with the later timestamp is returned. Figure 7 shows a merge step reconciling the data stored in separate tables for the same key: as shown here, the record for Alex was written with timestamp 100 and updated with a new phone and timestamp 200; the record for John was deleted. The other two entries are taken as is, as they're not shadowed.\n+合并步骤也是确保删除和更新正常工作所必需的。在 LSM-Tree 中，通过插入占位符（通常称为墓碑）来指定哪个键被标记为删除。同样的，更新操作只是一个记录一个有较晚的时间戳。在读取期间，被标记删除的记录被跳过，不会返回给客户端。更新操作与之类似：在具有相同键的两个记录中，只返回具有较晚时间戳的记录。图 7 展示了一次合并操作，用于对在不同表中存储的同一个键的数据进行整合：如图，Alex 记录中时间戳是 100，随后更新了新的电话，时间戳为 200；John 记录被删除。另外两项没有改变，因为它们没有被覆盖。","path":"TODO1/algorithms-behind-modern-storage-systems.md","position":158,"original_position":158,"commit_id":"30727dae967751ade7018fbdaec8eeb3d85aa08a","original_commit_id":"27da73df0a06eb71b28e748755ff6d64c6c44ec6","user":{"login":"LeopPro","id":33834665,"node_id":"MDQ6VXNlcjMzODM0NjY1","avatar_url":"https://avatars1.githubusercontent.com/u/33834665?v=4","gravatar_id":"","url":"https://api.github.com/users/LeopPro","html_url":"https://github.com/LeopPro","followers_url":"https://api.github.com/users/LeopPro/followers","following_url":"https://api.github.com/users/LeopPro/following{/other_user}","gists_url":"https://api.github.com/users/LeopPro/gists{/gist_id}","starred_url":"https://api.github.com/users/LeopPro/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/LeopPro/subscriptions","organizations_url":"https://api.github.com/users/LeopPro/orgs","repos_url":"https://api.github.com/users/LeopPro/repos","events_url":"https://api.github.com/users/LeopPro/events{/privacy}","received_events_url":"https://api.github.com/users/LeopPro/received_events","type":"User","site_admin":false},"body":"此处根据您的意见小修改了一下 修改为“更新操作只是提交一个带较晚时间戳的记录”，原句“更新操作是记录”有一些歧义。","created_at":"2018-06-01T07:27:43Z","updated_at":"2018-06-01T07:27:44Z","html_url":"https://github.com/xitu/gold-miner/pull/3907#discussion_r192316531","pull_request_url":"https://api.github.com/repos/xitu/gold-miner/pulls/3907","author_association":"CONTRIBUTOR","_links":{"self":{"href":"https://api.github.com/repos/xitu/gold-miner/pulls/comments/192316531"},"html":{"href":"https://github.com/xitu/gold-miner/pull/3907#discussion_r192316531"},"pull_request":{"href":"https://api.github.com/repos/xitu/gold-miner/pulls/3907"}},"in_reply_to_id":192096185},"pull_request":{"url":"https://api.github.com/repos/xitu/gold-miner/pulls/3907","id":191071731,"node_id":"MDExOlB1bGxSZXF1ZXN0MTkxMDcxNzMx","html_url":"https://github.com/xitu/gold-miner/pull/3907","diff_url":"https://github.com/xitu/gold-miner/pull/3907.diff","patch_url":"https://github.com/xitu/gold-miner/pull/3907.patch","issue_url":"https://api.github.com/repos/xitu/gold-miner/issues/3907","number":3907,"state":"open","locked":false,"title":"支撑现代存储系统的算法","user":{"login":"LeopPro","id":33834665,"node_id":"MDQ6VXNlcjMzODM0NjY1","avatar_url":"https://avatars1.githubusercontent.com/u/33834665?v=4","gravatar_id":"","url":"https://api.github.com/users/LeopPro","html_url":"https://github.com/LeopPro","followers_url":"https://api.github.com/users/LeopPro/followers","following_url":"https://api.github.com/users/LeopPro/following{/other_user}","gists_url":"https://api.github.com/users/LeopPro/gists{/gist_id}","starred_url":"https://api.github.com/users/LeopPro/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/LeopPro/subscriptions","organizations_url":"https://api.github.com/users/LeopPro/orgs","repos_url":"https://api.github.com/users/LeopPro/repos","events_url":"https://api.github.com/users/LeopPro/events{/privacy}","received_events_url":"https://api.github.com/users/LeopPro/received_events","type":"User","site_admin":false},"body":"译文翻译完成，resolve #3842","created_at":"2018-05-29T08:42:55Z","updated_at":"2018-06-01T07:27:44Z","closed_at":null,"merged_at":null,"merge_commit_sha":"999b0af6150363eecefd40685b2b2261a7a0df56","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[{"id":514335651,"node_id":"MDU6TGFiZWw1MTQzMzU2NTE=","url":"https://api.github.com/repos/xitu/gold-miner/labels/%E5%90%8E%E7%AB%AF","name":"后端","color":"1d76db","default":false},{"id":322084033,"node_id":"MDU6TGFiZWwzMjIwODQwMzM=","url":"https://api.github.com/repos/xitu/gold-miner/labels/%E6%AD%A3%E5%9C%A8%E6%A0%A1%E5%AF%B9","name":"正在校对","color":"c2e0c6","default":false}],"milestone":null,"commits_url":"https://api.github.com/repos/xitu/gold-miner/pulls/3907/commits","review_comments_url":"https://api.github.com/repos/xitu/gold-miner/pulls/3907/comments","review_comment_url":"https://api.github.com/repos/xitu/gold-miner/pulls/comments{/number}","comments_url":"https://api.github.com/repos/xitu/gold-miner/issues/3907/comments","statuses_url":"https://api.github.com/repos/xitu/gold-miner/statuses/30727dae967751ade7018fbdaec8eeb3d85aa08a","head":{"label":"LeopPro:translation/algorithms-behind-modern-storage-systems.md","ref":"translation/algorithms-behind-modern-storage-systems.md","sha":"30727dae967751ade7018fbdaec8eeb3d85aa08a","user":{"login":"LeopPro","id":33834665,"node_id":"MDQ6VXNlcjMzODM0NjY1","avatar_url":"https://avatars1.githubusercontent.com/u/33834665?v=4","gravatar_id":"","url":"https://api.github.com/users/LeopPro","html_url":"https://github.com/LeopPro","followers_url":"https://api.github.com/users/LeopPro/followers","following_url":"https://api.github.com/users/LeopPro/following{/other_user}","gists_url":"https://api.github.com/users/LeopPro/gists{/gist_id}","starred_url":"https://api.github.com/users/LeopPro/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/LeopPro/subscriptions","organizations_url":"https://api.github.com/users/LeopPro/orgs","repos_url":"https://api.github.com/users/LeopPro/repos","events_url":"https://api.github.com/users/LeopPro/events{/privacy}","received_events_url":"https://api.github.com/users/LeopPro/received_events","type":"User","site_admin":false},"repo":{"id":117784119,"node_id":"MDEwOlJlcG9zaXRvcnkxMTc3ODQxMTk=","name":"gold-miner","full_name":"LeopPro/gold-miner","owner":{"login":"LeopPro","id":33834665,"node_id":"MDQ6VXNlcjMzODM0NjY1","avatar_url":"https://avatars1.githubusercontent.com/u/33834665?v=4","gravatar_id":"","url":"https://api.github.com/users/LeopPro","html_url":"https://github.com/LeopPro","followers_url":"https://api.github.com/users/LeopPro/followers","following_url":"https://api.github.com/users/LeopPro/following{/other_user}","gists_url":"https://api.github.com/users/LeopPro/gists{/gist_id}","starred_url":"https://api.github.com/users/LeopPro/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/LeopPro/subscriptions","organizations_url":"https://api.github.com/users/LeopPro/orgs","repos_url":"https://api.github.com/users/LeopPro/repos","events_url":"https://api.github.com/users/LeopPro/events{/privacy}","received_events_url":"https://api.github.com/users/LeopPro/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/LeopPro/gold-miner","description":"🥇掘金翻译计划，可能是世界最大最好的英译中技术社区，最懂读者和译者的翻译平台：","fork":true,"url":"https://api.github.com/repos/LeopPro/gold-miner","forks_url":"https://api.github.com/repos/LeopPro/gold-miner/forks","keys_url":"https://api.github.com/repos/LeopPro/gold-miner/keys{/key_id}","collaborators_url":"https://api.github.com/repos/LeopPro/gold-miner/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/LeopPro/gold-miner/teams","hooks_url":"https://api.github.com/repos/LeopPro/gold-miner/hooks","issue_events_url":"https://api.github.com/repos/LeopPro/gold-miner/issues/events{/number}","events_url":"https://api.github.com/repos/LeopPro/gold-miner/events","assignees_url":"https://api.github.com/repos/LeopPro/gold-miner/assignees{/user}","branches_url":"https://api.github.com/repos/LeopPro/gold-miner/branches{/branch}","tags_url":"https://api.github.com/repos/LeopPro/gold-miner/tags","blobs_url":"https://api.github.com/repos/LeopPro/gold-miner/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/LeopPro/gold-miner/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/LeopPro/gold-miner/git/refs{/sha}","trees_url":"https://api.github.com/repos/LeopPro/gold-miner/git/trees{/sha}","statuses_url":"https://api.github.com/repos/LeopPro/gold-miner/statuses/{sha}","languages_url":"https://api.github.com/repos/LeopPro/gold-miner/languages","stargazers_url":"https://api.github.com/repos/LeopPro/gold-miner/stargazers","contributors_url":"https://api.github.com/repos/LeopPro/gold-miner/contributors","subscribers_url":"https://api.github.com/repos/LeopPro/gold-miner/subscribers","subscription_url":"https://api.github.com/repos/LeopPro/gold-miner/subscription","commits_url":"https://api.github.com/repos/LeopPro/gold-miner/commits{/sha}","git_commits_url":"https://api.github.com/repos/LeopPro/gold-miner/git/commits{/sha}","comments_url":"https://api.github.com/repos/LeopPro/gold-miner/comments{/number}","issue_comment_url":"https://api.github.com/repos/LeopPro/gold-miner/issues/comments{/number}","contents_url":"https://api.github.com/repos/LeopPro/gold-miner/contents/{+path}","compare_url":"https://api.github.com/repos/LeopPro/gold-miner/compare/{base}...{head}","merges_url":"https://api.github.com/repos/LeopPro/gold-miner/merges","archive_url":"https://api.github.com/repos/LeopPro/gold-miner/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/LeopPro/gold-miner/downloads","issues_url":"https://api.github.com/repos/LeopPro/gold-miner/issues{/number}","pulls_url":"https://api.github.com/repos/LeopPro/gold-miner/pulls{/number}","milestones_url":"https://api.github.com/repos/LeopPro/gold-miner/milestones{/number}","notifications_url":"https://api.github.com/repos/LeopPro/gold-miner/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/LeopPro/gold-miner/labels{/name}","releases_url":"https://api.github.com/repos/LeopPro/gold-miner/releases{/id}","deployments_url":"https://api.github.com/repos/LeopPro/gold-miner/deployments","created_at":"2018-01-17T04:31:23Z","updated_at":"2018-05-16T12:31:37Z","pushed_at":"2018-05-31T09:29:54Z","git_url":"git://github.com/LeopPro/gold-miner.git","ssh_url":"git@github.com:LeopPro/gold-miner.git","clone_url":"https://github.com/LeopPro/gold-miner.git","svn_url":"https://github.com/LeopPro/gold-miner","homepage":"https://juejin.im/tag/掘金翻译计划","size":32714,"stargazers_count":0,"watchers_count":0,"language":null,"has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":0,"license":null,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"xitu:master","ref":"master","sha":"a75d3a0a5df5c3db26c2d11734305afc593aef6c","user":{"login":"xitu","id":10482599,"node_id":"MDEyOk9yZ2FuaXphdGlvbjEwNDgyNTk5","avatar_url":"https://avatars2.githubusercontent.com/u/10482599?v=4","gravatar_id":"","url":"https://api.github.com/users/xitu","html_url":"https://github.com/xitu","followers_url":"https://api.github.com/users/xitu/followers","following_url":"https://api.github.com/users/xitu/following{/other_user}","gists_url":"https://api.github.com/users/xitu/gists{/gist_id}","starred_url":"https://api.github.com/users/xitu/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/xitu/subscriptions","organizations_url":"https://api.github.com/users/xitu/orgs","repos_url":"https://api.github.com/users/xitu/repos","events_url":"https://api.github.com/users/xitu/events{/privacy}","received_events_url":"https://api.github.com/users/xitu/received_events","type":"Organization","site_admin":false},"repo":{"id":45512989,"node_id":"MDEwOlJlcG9zaXRvcnk0NTUxMjk4OQ==","name":"gold-miner","full_name":"xitu/gold-miner","owner":{"login":"xitu","id":10482599,"node_id":"MDEyOk9yZ2FuaXphdGlvbjEwNDgyNTk5","avatar_url":"https://avatars2.githubusercontent.com/u/10482599?v=4","gravatar_id":"","url":"https://api.github.com/users/xitu","html_url":"https://github.com/xitu","followers_url":"https://api.github.com/users/xitu/followers","following_url":"https://api.github.com/users/xitu/following{/other_user}","gists_url":"https://api.github.com/users/xitu/gists{/gist_id}","starred_url":"https://api.github.com/users/xitu/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/xitu/subscriptions","organizations_url":"https://api.github.com/users/xitu/orgs","repos_url":"https://api.github.com/users/xitu/repos","events_url":"https://api.github.com/users/xitu/events{/privacy}","received_events_url":"https://api.github.com/users/xitu/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/xitu/gold-miner","description":"🥇掘金翻译计划，可能是世界最大最好的英译中技术社区，最懂读者和译者的翻译平台：","fork":false,"url":"https://api.github.com/repos/xitu/gold-miner","forks_url":"https://api.github.com/repos/xitu/gold-miner/forks","keys_url":"https://api.github.com/repos/xitu/gold-miner/keys{/key_id}","collaborators_url":"https://api.github.com/repos/xitu/gold-miner/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/xitu/gold-miner/teams","hooks_url":"https://api.github.com/repos/xitu/gold-miner/hooks","issue_events_url":"https://api.github.com/repos/xitu/gold-miner/issues/events{/number}","events_url":"https://api.github.com/repos/xitu/gold-miner/events","assignees_url":"https://api.github.com/repos/xitu/gold-miner/assignees{/user}","branches_url":"https://api.github.com/repos/xitu/gold-miner/branches{/branch}","tags_url":"https://api.github.com/repos/xitu/gold-miner/tags","blobs_url":"https://api.github.com/repos/xitu/gold-miner/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/xitu/gold-miner/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/xitu/gold-miner/git/refs{/sha}","trees_url":"https://api.github.com/repos/xitu/gold-miner/git/trees{/sha}","statuses_url":"https://api.github.com/repos/xitu/gold-miner/statuses/{sha}","languages_url":"https://api.github.com/repos/xitu/gold-miner/languages","stargazers_url":"https://api.github.com/repos/xitu/gold-miner/stargazers","contributors_url":"https://api.github.com/repos/xitu/gold-miner/contributors","subscribers_url":"https://api.github.com/repos/xitu/gold-miner/subscribers","subscription_url":"https://api.github.com/repos/xitu/gold-miner/subscription","commits_url":"https://api.github.com/repos/xitu/gold-miner/commits{/sha}","git_commits_url":"https://api.github.com/repos/xitu/gold-miner/git/commits{/sha}","comments_url":"https://api.github.com/repos/xitu/gold-miner/comments{/number}","issue_comment_url":"https://api.github.com/repos/xitu/gold-miner/issues/comments{/number}","contents_url":"https://api.github.com/repos/xitu/gold-miner/contents/{+path}","compare_url":"https://api.github.com/repos/xitu/gold-miner/compare/{base}...{head}","merges_url":"https://api.github.com/repos/xitu/gold-miner/merges","archive_url":"https://api.github.com/repos/xitu/gold-miner/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/xitu/gold-miner/downloads","issues_url":"https://api.github.com/repos/xitu/gold-miner/issues{/number}","pulls_url":"https://api.github.com/repos/xitu/gold-miner/pulls{/number}","milestones_url":"https://api.github.com/repos/xitu/gold-miner/milestones{/number}","notifications_url":"https://api.github.com/repos/xitu/gold-miner/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/xitu/gold-miner/labels{/name}","releases_url":"https://api.github.com/repos/xitu/gold-miner/releases{/id}","deployments_url":"https://api.github.com/repos/xitu/gold-miner/deployments","created_at":"2015-11-04T03:29:13Z","updated_at":"2018-06-01T07:27:06Z","pushed_at":"2018-06-01T06:00:14Z","git_url":"git://github.com/xitu/gold-miner.git","ssh_url":"git@github.com:xitu/gold-miner.git","clone_url":"https://github.com/xitu/gold-miner.git","svn_url":"https://github.com/xitu/gold-miner","homepage":"https://juejin.im/tag/掘金翻译计划","size":33251,"stargazers_count":14248,"watchers_count":14248,"language":null,"has_issues":true,"has_projects":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":2499,"mirror_url":null,"archived":false,"open_issues_count":40,"license":null,"forks":2499,"open_issues":40,"watchers":14248,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/xitu/gold-miner/pulls/3907"},"html":{"href":"https://github.com/xitu/gold-miner/pull/3907"},"issue":{"href":"https://api.github.com/repos/xitu/gold-miner/issues/3907"},"comments":{"href":"https://api.github.com/repos/xitu/gold-miner/issues/3907/comments"},"review_comments":{"href":"https://api.github.com/repos/xitu/gold-miner/pulls/3907/comments"},"review_comment":{"href":"https://api.github.com/repos/xitu/gold-miner/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/xitu/gold-miner/pulls/3907/commits"},"statuses":{"href":"https://api.github.com/repos/xitu/gold-miner/statuses/30727dae967751ade7018fbdaec8eeb3d85aa08a"}},"author_association":"CONTRIBUTOR"}}
{"actor":{"display_login":"LeopPro"}}