mirror of
https://github.com/Vonng/ddia.git
synced 2024-12-06 15:20:12 +08:00
update contribution list and some formatting in ch3.md
This commit is contained in:
parent
406b558a0d
commit
438cd62228
@ -143,13 +143,15 @@
|
||||
5. [词汇表](glossary.md)、[后记](colophon.md)关于野猪的部分 by [@Chowss](https://github.com/Vonng/ddia/commits?author=Chowss)
|
||||
6. [繁體中文](https://github.com/Vonng/ddia/pulls)版本与转换脚本 by [@afunTW](https://github.com/afunTW)
|
||||
7. 多处翻译修正 by [@songzhibin97](https://github.com/Vonng/ddia/commits?author=songzhibin97)
|
||||
8. 感谢所有作出贡献,提出意见的朋友们:
|
||||
8. 多处翻译修正 by [@MamaShip](https://github.com/Vonng/ddia/commits?author=MamaShip)
|
||||
9. 感谢所有作出贡献,提出意见的朋友们:
|
||||
|
||||
<details>
|
||||
<summary><a href="https://github.com/Vonng/ddia/pulls">Pull Requests</a> & <a href="https://github.com/Vonng/ddia/issues">Issues</a></summary>
|
||||
|
||||
| ISSUE & Pull Requests | USER | Title |
|
||||
| ----------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
|
||||
| [263](https://github.com/Vonng/ddia/pull/263) | [@zydmayday](https://github.com/zydmayday) | ch5: 修正译文中的重复单词 |
|
||||
| [260](https://github.com/Vonng/ddia/pull/260) | [@haifeiWu](https://github.com/haifeiWu) | ch4: 修正部分不准确的翻译 |
|
||||
| [258](https://github.com/Vonng/ddia/pull/258) | [@bestgrc](https://github.com/bestgrc) | ch3: 修正一处翻译错误 |
|
||||
| [257](https://github.com/Vonng/ddia/pull/257) | [@UnderSam](https://github.com/UnderSam) | ch8: 修正一处拼写错误 |
|
||||
|
12
ch3.md
12
ch3.md
@ -92,7 +92,7 @@ $ cat database
|
||||
|
||||
像 Bitcask 这样的存储引擎非常适合每个键的值经常更新的情况。例如,键可能是某个猫咪视频的网址(URL),而值可能是该视频被播放的次数(每次有人点击播放按钮时递增)。在这种类型的工作负载中,有很多写操作,但是没有太多不同的键 —— 每个键有很多的写操作,但是将所有键保存在内存中是可行的。
|
||||
|
||||
到目前为止,我们只是在追加写入一个文件 —— 所以如何避免最终用完硬盘空间?一种好的解决方案是,将日志分为特定大小的**段**(**segment**),当日志增长到特定尺寸时关闭当前段文件,并开始写入一个新的段文件。然后,我们就可以对这些段进行 **压缩(compaction)**,如 [图 3-2](img/fig3-2.png) 所示。这里的压缩意味着在日志中丢弃重复的键,只保留每个键的最近更新。
|
||||
到目前为止,我们只是在追加写入一个文件 —— 所以如何避免最终用完硬盘空间?一种好的解决方案是,将日志分为特定大小的 **段(segment)**,当日志增长到特定尺寸时关闭当前段文件,并开始写入一个新的段文件。然后,我们就可以对这些段进行 **压缩(compaction)**,如 [图 3-2](img/fig3-2.png) 所示。这里的压缩意味着在日志中丢弃重复的键,只保留每个键的最近更新。
|
||||
|
||||
![](img/fig3-2.png)
|
||||
|
||||
@ -193,7 +193,7 @@ $ cat database
|
||||
|
||||
这种索引结构最早由 Patrick O'Neil 等人发明,且被命名为日志结构合并树(或 LSM 树)【10】,它是基于更早之前的日志结构文件系统【11】来构建的。基于这种合并和压缩排序文件原理的存储引擎通常被称为 LSM 存储引擎。
|
||||
|
||||
Lucene,是一种全文搜索的索引引擎,在 Elasticsearch 和 Solr 被使用,它使用类似的方法来存储它的关键词词典【12,13】。全文索引比键值索引复杂得多,但是基于类似的想法:在搜索查询中,由一个给定的单词,找到提及单词的所有文档(网页,产品描述等)。这也是通过键值结构实现的:其中键是单词(**term**),值是所有包含该单词的文档的 ID 列表(**postings list**)。在 Lucene 中,从词语到记录列表的这种映射保存在类似于 SSTable 的有序文件中,并根据需要在后台执行合并【14】。
|
||||
Lucene,是一种全文搜索的索引引擎,在 Elasticsearch 和 Solr 被使用,它使用类似的方法来存储它的关键词词典【12,13】。全文索引比键值索引复杂得多,但是基于类似的想法:在搜索查询中,由一个给定的单词,找到提及单词的所有文档(网页,产品描述等)。这也是通过键值结构实现的:其中键是 **单词(term)**,值是所有包含该单词的文档的 ID 列表(**postings list**)。在 Lucene 中,从词语到记录列表的这种映射保存在类似于 SSTable 的有序文件中,并根据需要在后台执行合并【14】。
|
||||
|
||||
#### 性能优化
|
||||
|
||||
@ -212,7 +212,7 @@ Lucene,是一种全文搜索的索引引擎,在 Elasticsearch 和 Solr 被
|
||||
|
||||
像 SSTables 一样,B 树保持按键排序的键值对,这允许高效的键值查找和范围查询。但这也就是仅有的相似之处了:B 树有着非常不同的设计理念。
|
||||
|
||||
我们前面看到的日志结构索引将数据库分解为可变大小的段,通常是几兆字节或更大的大小,并且总是按顺序写入段。相比之下,B 树将数据库分解成固定大小的**块**(**block**)或**分页**(**page**),传统上大小为 4KB(有时会更大),并且一次只能读取或写入一个页面。这种设计更接近于底层硬件,因为硬盘空间也是按固定大小的块来组织的。
|
||||
我们前面看到的日志结构索引将数据库分解为可变大小的段,通常是几兆字节或更大的大小,并且总是按顺序写入段。相比之下,B 树将数据库分解成固定大小的 **块(block)** 或 **分页(page)**,传统上大小为 4KB(有时会更大),并且一次只能读取或写入一个页面。这种设计更接近于底层硬件,因为硬盘空间也是按固定大小的块来组织的。
|
||||
|
||||
每个页面都可以使用地址或位置来标识,这允许一个页面引用另一个页面 —— 类似于指针,但在硬盘而不是在内存中。我们可以使用这些页面引用来构建一个页面树,如 [图 3-6](img/fig3-6.png) 所示。
|
||||
|
||||
@ -226,7 +226,7 @@ Lucene,是一种全文搜索的索引引擎,在 Elasticsearch 和 Solr 被
|
||||
|
||||
最终,我们将到达某个包含单个键的页面(叶子页面,leaf page),该页面或者直接包含每个键的值,或者包含了对可以找到值的页面的引用。
|
||||
|
||||
在 B 树的一个页面中对子页面的引用的数量称为**分支因子**(**branching factor**)。例如,在 [图 3-6](img/fig3-6.png) 中,分支因子是 6。在实践中,分支因子的大小取决于存储页面引用和范围边界所需的空间,但这个值通常是几百。
|
||||
在 B 树的一个页面中对子页面的引用的数量称为 **分支因子(branching factor)**。例如,在 [图 3-6](img/fig3-6.png) 中,分支因子是 6。在实践中,分支因子的大小取决于存储页面引用和范围边界所需的空间,但这个值通常是几百。
|
||||
|
||||
如果要更新 B 树中现有键的值,需要搜索包含该键的叶子页面,更改该页面中的值,并将该页面写回到硬盘(对该页面的任何引用都将保持有效)。如果你想添加一个新的键,你需要找到其范围能包含新键的页面,并将其添加到该页面。如果页面中没有足够的可用空间容纳新键,则将其分成两个半满页面,并更新父页面以反映新的键范围分区,如 [图 3-7](img/fig3-7.png) 所示 [^ii]。
|
||||
|
||||
@ -258,7 +258,7 @@ B 树的基本底层写操作是用新数据覆写硬盘上的页面,并假定
|
||||
* 我们可以通过不存储整个键,而是缩短其大小,来节省页面空间。特别是在树内部的页面上,键只需要提供足够的信息来充当键范围之间的边界。在页面中包含更多的键允许树具有更高的分支因子,因此也就允许更少的层级 [^iii]。
|
||||
* 通常,页面可以放置在硬盘上的任何位置;没有什么要求相邻键范围的页面也放在硬盘上相邻的区域。如果某个查询需要按照排序顺序扫描大部分的键范围,那么这种按页面存储的布局可能会效率低下,因为每个页面的读取都需要执行一次硬盘查找。因此,许多 B 树的实现在布局树时会尽量使叶子页面按顺序出现在硬盘上。但是,随着树的增长,要维持这个顺序是很困难的。相比之下,由于 LSM 树在合并过程中一次性重写一大段存储,所以它们更容易使顺序键在硬盘上连续存储。
|
||||
* 额外的指针被添加到树中。例如,每个叶子页面可以引用其左边和右边的兄弟页面,使得不用跳回父页面就能按顺序对键进行扫描。
|
||||
* B 树的变体如**分形树**(**fractal trees**)【22】借用了一些日志结构的思想来减少硬盘查找(而且它们与分形无关)。
|
||||
* B 树的变体如 **分形树(fractal trees)**【22】借用了一些日志结构的思想来减少硬盘查找(而且它们与分形无关)。
|
||||
|
||||
[^iii]: 这个变种有时被称为 B+ 树,但因为这个优化已被广泛使用,所以经常无法区分于其它的 B 树变种。
|
||||
|
||||
@ -329,7 +329,7 @@ SELECT * FROM restaurants WHERE latitude > 51.4946 AND latitude < 51.5079
|
||||
|
||||
一个标准的 B 树或者 LSM 树索引不能够高效地处理这种查询:它可以返回一个纬度范围内的所有餐馆(但经度可能是任意值),或者返回在同一个经度范围内的所有餐馆(但纬度可能是北极和南极之间的任意地方),但不能同时满足两个条件。
|
||||
|
||||
一种选择是使用**空间填充曲线**(space-filling curve)将二维位置转换为单个数字,然后使用常规 B 树索引【34】。更普遍的是,使用特殊化的空间索引,例如 R 树。例如,PostGIS 使用 PostgreSQL 的通用 GiST 工具【35】将地理空间索引实现为 R 树。这里我们没有足够的地方来描述 R 树,但是有大量的文献可供参考。
|
||||
一种选择是使用 **空间填充曲线(space-filling curve)** 将二维位置转换为单个数字,然后使用常规 B 树索引【34】。更普遍的是,使用特殊化的空间索引,例如 R 树。例如,PostGIS 使用 PostgreSQL 的通用 GiST 工具【35】将地理空间索引实现为 R 树。这里我们没有足够的地方来描述 R 树,但是有大量的文献可供参考。
|
||||
|
||||
有趣的是,多维索引不仅可以用于地理位置。例如,在电子商务网站上可以使用建立在(红,绿,蓝)维度上的三维索引来搜索特定颜色范围内的产品,也可以在天气观测数据库中建立(日期,温度)的二维索引,以便有效地搜索 2013 年内的温度在 25 至 30°C 之间的所有观测资料。如果使用一维索引,你将不得不扫描 2013 年的所有记录(不管温度如何),然后通过温度进行过滤,或者反之亦然。二维索引可以同时通过时间戳和温度来收窄数据集。这个技术被 HyperDex 所使用【36】。
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user