start to re-check the translation of chapter 3

This commit is contained in:
Gang Yin 2021-11-29 22:25:43 +08:00
parent d1c031bef8
commit ef8178bd3c
2 changed files with 150 additions and 306 deletions

230
ch3.md
View File

@ -15,136 +15,131 @@
在[第二章](ch2.md)中,我们讨论了数据模型和查询语言,即程序员将数据录入数据库的格式,以及再次要回数据的机制。在本章中我们会从数据库的视角来讨论同样的问题:数据库如何存储我们提供的数据,以及如何在我们需要时重新找到数据。
作为程序员,为什么要关心数据库内部存储与检索的机理?你可能不会去从头开始实现自己的存储引擎,但是你**确实**需要从许多可用的存储引擎中选择一个合适的。而且为了协调存储引擎以适配应用工作负载,你也需要大致了解存储引擎在底层究竟做什么。
作为程序员,为什么要关心数据库内部存储与检索的机理?你可能不会去从头开始实现自己的存储引擎,但是你**确实**需要从许多可用的存储引擎中选择一个合适的。而且为了让存储引擎能在你的工作负载类型上运行良好,你也需要大致了解存储引擎在底层究竟做什么。
特别需要注意,针对**事务**负载和**分析性**负载优化的存储引擎之间存在巨大差异。稍后我们将在 “[事务处理还是分析?](#事务处理还是分析?)” 一节中探讨这一区别,并在 “[列存储](#列存储)”中讨论一系列针对分析优化存储引擎。
特别需要注意,针对**事务**负载优化的针对**分析性**负载优化的存储引擎之间存在巨大差异。稍后我们将在 “[事务处理还是分析?](#事务处理还是分析?)” 一节中探讨这一区别,并在 “[列存储](#列存储)”中讨论一系列针对分析性负载而优化存储引擎。
我们将从您最可能熟悉的两大类数据库传统关系型数据库与很多所谓的“NoSQL”数据库开始通过介绍它们的**存储引擎**来开始本章的内容。我们会研究两大类存储引擎:**日志结构log-structured** 的存储引擎,以及**面向页面page-oriented** 的存储引擎例如B树
首先我们将从你可能已经很熟悉的两大类数据库传统的关系型数据库和很多所谓的“NoSQL”数据库中使用的**存储引擎**来开始本章的内容。我们将研究两大类存储引擎:**日志结构log-structured** 的存储引擎,以及**面向页面page-oriented** 的存储引擎例如B树
## 驱动数据库的数据结构
世界上最简单的数据库可以用两个Bash函数实现
```bash
#!/bin/bash
db_set () {
echo "$1,$2" >> database
}
#!/bin/bash
db_set () {
echo "$1,$2" >> database
}
db_get () {
grep "^$1," database | sed -e "s/^$1,//" | tail -n 1
}
db_get () {
grep "^$1," database | sed -e "s/^$1,//" | tail -n 1
}
```
这两个函数实现了键值存储的功能。执行 `db_set key value` 会将 **键key** 和**值value** 存储在数据库中。键和值几乎可以是你喜欢的任何东西例如值可以是JSON文档。然后调用 `db_get key` 查找与该键关联的最新值并将其返回。
这两个函数实现了键值存储的功能。执行 `db_set key value` 会将 **键key** 和**值value** 存储在数据库中。键和值几乎可以是你喜欢的任何东西例如值可以是JSON文档。然后调用 `db_get key` 查找与该键关联的最新值并将其返回。
麻雀虽小,五脏俱全:
```bash
$ db_set 123456 '{"name":"London","attractions":["Big Ben","London Eye"]}'
$ db_set 123456 '{"name":"London","attractions":["Big Ben","London Eye"]}'
$ db_set 42 '{"name":"San Francisco","attractions":["Golden Gate Bridge"]}'
$ db_set 42 '{"name":"San Francisco","attractions":["Golden Gate Bridge"]}'
$ db_get 42
{"name":"San Francisco","attractions":["Golden Gate Bridge"]}
$ db_get 42
{"name":"San Francisco","attractions":["Golden Gate Bridge"]}
```
底层的存储格式非常简单一个文本文件每行包含一条逗号分隔的键值对忽略转义问题的话大致与CSV文件类似。每次对 `db_set` 的调用都会向文件末尾追加记录,所以更新键的时候旧版本的值不会被覆盖 —— 因而查找最新值的时候,需要找到文件中键最后一次出现的位置(因此 `db_get` 中使用了 `tail -n 1 ` 。)
```bash
$ db_set 42 '{"name":"San Francisco","attractions":["Exploratorium"]}'
$ db_set 42 '{"name":"San Francisco","attractions":["Exploratorium"]}'
$ db_get 42
{"name":"San Francisco","attractions":["Exploratorium"]}
$ db_get 42
{"name":"San Francisco","attractions":["Exploratorium"]}
$ cat database
123456,{"name":"London","attractions":["Big Ben","London Eye"]}
42,{"name":"San Francisco","attractions":["Golden Gate Bridge"]}
42,{"name":"San Francisco","attractions":["Exploratorium"]}
$ cat database
123456,{"name":"London","attractions":["Big Ben","London Eye"]}
42,{"name":"San Francisco","attractions":["Golden Gate Bridge"]}
42,{"name":"San Francisco","attractions":["Exploratorium"]}
```
`db_set` 函数对于极其简单的场景其实有非常好的性能,因为在文件尾部追加写入通常是非常高效的。与`db_set`做的事情类似,许多数据库在内部使用了**日志log**,也就是一个 **仅追加append-only** 的数据文件。真正的数据库有更多的问题需要处理(如并发控制,回收磁盘空间以避免日志无限增长,处理错误与部分写入的记录),但基本原理是一样的。日志极其有用,我们还将在本书的其它部分重复见到它好几次。
> **日志log** 这个词通常指应用日志:即应用程序输出的描述发生事情的文本。本书在更普遍的意义下使用**日志**这一词:一个仅追加的记录序列。它可能压根就不是给人类看的,使用二进制格式,并仅能由其他程序读取。
> **日志log** 这个词通常指应用日志:即应用程序输出的描述正在发生事情的文本。本书在更普遍的意义下使用**日志**这一词:一个仅追加的记录序列。它可能压根就不是给人类看的,它可以使用二进制格式,并仅能由其他程序读取。
另一方面,如果这个数据库中有着大量记录,则这个`db_get` 函数的性能会非常糟糕。每次你想查找一个键时,`db_get` 必须从头到尾扫描整个数据库文件来查找键的出现。用算法的语言来说,查找的开销是 `O(n)` :如果数据库记录数量 n 翻了一倍,查找时间也要翻一倍。这就不好了。
为了高效查找数据库中特定键的值,我们需要一个数据结构:**索引index**。本章将介绍一系列的索引结构,并它们进行对比。索引背后的大致思想是,保存一些额外的元数据作为路标,帮助你找到想要的数据。如果您想在同一份数据中以几种不同的方式进行搜索,那么你也许需要不同的索引,建在数据的不同部分上
为了高效查找数据库中特定键的值,我们需要一个数据结构:**索引index**。本章将介绍一系列的索引结构,并在它们之间进行比较。索引背后的大致思想是通过保存一些额外的元数据作为路标来帮助你找到想要的数据。如果你想以几种不同的方式搜索同一份数据,那么你也许需要在数据的不同部分上建立多个索引
索引是从主数据衍生的**附加additional** 结构。许多数据库允许添加与删除索引,这不会影响数据的内容,它只影响查询的性能。维护额外的结构会产生开销,特别是在写入时。写入性能很难超过简单地追加写入文件,因为追加写入是最简单的写入操作。任何类型的索引通常都会减慢写入速度,因为每次写入数据时都需要更新索引。
索引是从主数据衍生的**额外的additional** 结构。许多数据库允许添加与删除索引,这不会影响数据的内容,而只会影响查询的性能。维护额外的结构会产生开销,特别是在写入时。写入性能很难超过简单地追加写入文件,因为追加写入是最简单的写入操作。任何类型的索引通常都会减慢写入速度,因为每次写入数据时都需要更新索引。
这是存储系统中一个重要的权衡:精心选择的索引加快了读查询的速度,但是每个索引都会拖慢写入速度。因为这个原因,数据库默认并不会索引所有的内容,而需要你程序员或DBA通过对应用查询模式的了解来手动选择索引。你可以选择能为应用带来最大收益同时又不会引入超出必要开销的索引。
这是存储系统中一个重要的权衡:精心选择的索引加快了读查询的速度,但是每个索引都会拖慢写入速度。因为这个原因,数据库默认并不会索引所有的内容,而需要你也就是程序员或数据库管理员DBA基于对应用的典型查询模式的了解来手动选择索引。你可以选择那些能为应用带来最大收益而且又不会引入超出必要开销的索引。
### 散列索引
### 哈希索引
让我们从**键值数据key-value Data** 的索引开始。这不是你可以索引的唯一数据类型,但键值数据是很常见的。对于更复杂的索引来说,这也是一个有用的构建模块。
让我们从 **键值数据key-value Data** 的索引开始。这不是您可以索引的唯一数据类型,但键值数据是很常见的。对于更复杂的索引来说,这是一个有用的构建模块。
键值存储与在大多数编程语言中可以找到的**字典dictionary** 类型非常相似,通常字典都是用**散列映射hash map**(或**散列表hash table**实现的。散列映射在许多算法教科书中都有描述【1,2】所以这里我们不会讨论它的工作细节。既然我们已经可以用散列映射来表示**内存中**的数据结构,为什么不使用它来索引**磁盘上**的数据呢?
键值存储与在大多数编程语言中可以找到的**字典dictionary** 类型非常相似,通常字典都是用**散列映射hash map**(或**哈希表hash table**实现的。哈希映射在许多算法教科书中都有描述【1,2】所以这里我们不会讨论它的工作细节。既然我们已经有**内存中**数据结构 —— 哈希映射,为什么不使用它来索引在**磁盘上**的数据呢?
假设我们的数据存储只是一个追加写入的文件,就像前面的例子一样。那么最简单的索引策略就是:保留一个内存中的哈希映射,其中每个键都映射到一个数据文件中的字节偏移量,指明了可以找到对应值的位置,如[图3-1](img/fig3-1.png)所示。当你将新的键值对追加写入文件中时,还要更新散列映射,以反映刚刚写入的数据的偏移量(这同时适用于插入新键与更新现有键)。当你想查找一个值时,使用哈希映射来查找数据文件中的偏移量,**寻找seek** 该位置并读取该值。
假设我们的数据存储只是一个追加写入的文件,就像前面的例子一样,那么最简单的索引策略就是:保留一个内存中的散列映射,其中每个键都映射到数据文件中的一个字节偏移量,指明了可以找到对应值的位置,如[图3-1](img/fig3-1.png)所示。当你将新的键值对追加写入文件中时,还要更新散列映射,以反映刚刚写入的数据的偏移量(这同时适用于插入新键与更新现有键)。当你想查找一个值时,使用散列映射来查找数据文件中的偏移量,**寻找seek** 该位置并读取该值即可。
![](img/fig3-1.png)
**图3-1 以类CSV格式存储键值对的日志并使用内存哈希映射进行索引。**
**图3-1 以类CSV格式存储键值对的日志并使用内存散列映射进行索引。**
听上去简单但这是一个可行的方法。现实中Bitcask实际上就是这么做的Riak中默认的存储引擎【3】。 Bitcask提供高性能的读取和写入操作所有键必须能放入可用内存中,因为哈希映射完全保留在内存中。这些值可以使用比可用内存更多的空间,因为可以从磁盘上通过一次`seek`加载所需部分如果数据文件的那部分已经在文件系统缓存中则读取根本不需要任何磁盘I/O。
听上去简单但这是一个可行的方法。现实中Bitcask实际上就是这么做的Riak中默认的存储引擎【3】。 Bitcask提供高性能的读取和写入操作要求所有的键必须能放入可用内存中,因为散列映射完全保留在内存中。而数据值可以使用比可用内存更多的空间,因为可以在磁盘上通过一次`seek`操作来加载所需部分如果数据文件的那部分已经在文件系统缓存中则读取根本不需要任何磁盘I/O。
像Bitcask这样的存储引擎非常适合每个键的值经常更新的情况。例如键可能是视频的URL值可能是它播放的次数(每次有人点击播放按钮时递增)。在这种类型的工作负载中,有很多写操作,但是没有太多不同的键——每个键有很多的写操作,但是将所有键保存在内存中是可行的。
像Bitcask这样的存储引擎非常适合每个键的值经常更新的情况。例如键可能是某个猫咪视频的网址URL而值可能是该视频被播放的次数(每次有人点击播放按钮时递增)。在这种类型的工作负载中,有很多写操作,但是没有太多不同的键 —— 每个键有很多的写操作,但是将所有键保存在内存中是可行的。
直到现在,我们只是追加写入一个文件 —— 所以如何避免最终用完磁盘空间?一种好的解决方案是,将日志分为特定大小的段,当日志增长到特定尺寸时关闭当前段文件,并开始写入一个新的段文件。然后,我们就可以对这些段进行**压缩compaction**,如[图3-2](img/fig3-2.png)所示。压缩意味着在日志中丢弃重复的键,只保留每个键的最近更新。
直到现在,我们只是追加写入一个文件 —— 所以如何避免最终用完磁盘空间?一种好的解决方案是,将日志分为特定大小的段segment,当日志增长到特定尺寸时关闭当前段文件,并开始写入一个新的段文件。然后,我们就可以对这些段进行**压缩compaction**,如[图3-2](img/fig3-2.png)所示。这里的压缩意味着在日志中丢弃重复的键,只保留每个键的最近更新。
![](img/fig3-2.png)
**图3-2 压缩键值更新日志(统计猫视频的播放次数),只保留每个键的最近值**
**图3-2 键值更新日志(统计猫视频的播放次数)的压缩,只保留每个键的最近值**
而且,由于压缩经常会使得段变得很小(假设在一个段内键被平均重写了好几次),我们也可以在执行压缩的同时将多个段合并在一起,如[图3-3](img/fig3-3.png)所示。段被写入后永远不会被修改,所以合并的段被写入一个新的文件。冻结段的合并和压缩可以在后台线程中完成,在进行时,我们仍然可以继续使用旧的段文件来正常提供读写请求。合并过程完成后,我们将读取请求转换为使用新的合并段而不是旧段 —— 然后可以简单地删除旧的段文件
而且,由于压缩经常会使得段变得很小(假设在一个段内键被平均重写了好几次),我们也可以在执行压缩的同时将多个段合并在一起,如[图3-3](img/fig3-3.png)所示。段被写入后永远不会被修改,所以合并的段被写入一个新的文件。冻结段的合并和压缩可以在后台线程中完成,这个过程进行的同时,我们仍然可以继续使用旧的段文件来正常提供读写请求。合并过程完成后,我们将读取请求转换为使用新合并的段而不是旧的段 —— 然后旧的段文件就可以简单地删除掉了
![](img/fig3-3.png)
**图3-3 同时执行压缩和分段合并**
每个段现在都有自己的内存散列表,将键映射到文件偏移量。为了找到一个键的值,我们首先检查最近段的哈希映射;如果键不存在,我们就检查第二个最近的段,依此类推。合并过程将保持段的数量足够小,所以查找过程不需要检查太多的哈希映射。
每个段现在都有自己的内存散列表,将键映射到文件偏移量。为了找到一个键的值,我们首先检查最近的段的散列映射;如果键不存在,我们就检查第二个最近的段,依此类推。合并过程将保持段的数量足够小,所以查找过程不需要检查太多的散列映射。
要让这个简单的想法在实际中能工作会涉及到大量的细节。简单来说,下面几点都是实现过程中需要认真考虑的问题:
***文件格式***
CSV不是日志的最佳格式。使用二进制格式更快更简单,首先以字节为单位对字符串的长度进行编码,然后使用原始字符串(不需要转义)。
CSV不是日志的最佳格式。使用二进制格式更快更简单:首先以字节为单位对字符串的长度进行编码,然后是原始的字符串(不需要转义)。
***删除记录***
如果要删除一个键及其关联的值,则必须在数据文件(有时称为逻辑删除)中附加一个特殊的删除记录。当日志段被合并时,逻辑删除告诉合并过程丢弃被删除键的任何以前的值。
如果要删除一个键及其关联的值,则必须在数据文件中追加一个特殊的删除记录有时称为逻辑删除tombstone。当日志段被合并时,逻辑删除告诉合并过程丢弃被删除键的任何以前的值。
***崩溃恢复***
如果数据库重新启动,则内存散列映射将丢失。原则上,您可以通过从头到尾读取整个段文件并在每次按键时注意每个键的最近值的偏移量来恢复每个段的哈希映射。但是,如果段文件很大,这可能需要很长时间,这将使服务器重新启动痛苦。 Bitcask 通过存储每个段的哈希映射的快照在磁盘上来加速恢复,可以使哈希映射更快地加载到内存中。
如果数据库重新启动,则内存散列映射将丢失。原则上,你可以通过从头到尾读取整个段文件并记录下来每个键的最近值来恢复每个段的散列映射。但是,如果段文件很大,可能需要很长时间,这会使服务的重启比较痛苦。 Bitcask 通过将每个段的散列映射的快照存储在磁盘上来加速恢复,可以使散列映射更快地加载到内存中。
***部分写入记录***
数据库可能随时崩溃,包括将记录附加到日志中途。 Bitcask文件包含校验和允许检测和忽略日志的这些损坏部分。
数据库随时可能崩溃,包括在将记录追加到日志的过程中。 Bitcask文件包含校验和允许检测和忽略日志的这些损坏部分。
***并发控制***
由于写操作是以严格顺序的顺序附加到日志中的,所以常见的实现选择是只有一个写入器线程。数据文件段是附加的,或者是不可变的,所以它们可以被多个线程同时读取。
由于写操作是以严格的顺序追加到日志中的,所以常见的实现是只有一个写入线程。也因为数据文件段是仅追加的或者说是不可变的,所以它们可以被多个线程同时读取。
乍一看,只有追加日志看起来很浪费:为什么不更新文件,用新值覆盖旧值?但是只能追加设计的原因有几个
乍一看,仅追加日志似乎很浪费:为什么不直接在文件里更新,用新值覆盖旧值?仅追加的设计之所以是个好的设计,有如下几个原因
* 追加和分段合并是顺序写入操作,通常比随机写入快得多,尤其是在磁盘旋转硬盘上。在某种程度上,顺序写入在基于闪存的 **固态硬盘SSD** 上也是优选的【4】。我们将在“[比较B树和LSM树](#比较B树和LSM树)”中进一步讨论这个问题。
* 如果段文件是附加的或不可变的,并发和崩溃恢复就简单多了。例如,您不必担心在覆盖值时发生崩溃的情况,而将包含旧值和新值的一部分的文件保留在一起
* 合并旧段可以避免数据文件随着时间的推移而分散的问题。
* 追加和分段合并是顺序写入操作,通常比随机写入快得多,尤其是在磁性机械硬盘上。在某种程度上,顺序写入在基于闪存的**固态硬盘SSD** 上也是好的选择【4】。我们将在“[比较B树和LSM树](#比较B树和LSM树)”中进一步讨论这个问题。
* 如果段文件是仅追加的或不可变的,并发和崩溃恢复就简单多了。例如,当一个数据值被更新的时候发生崩溃,你不用担心文件里将会同时包含旧值和新值各自的一部分
* 合并旧段的处理也可以避免数据文件随着时间的推移而碎片化的问题。
但是,哈希表索引也有局限性:
但是,散列表索引也有其局限性:
* 散列表必须能放进内存
如果你有非常多的键那真是倒霉。原则上可以在磁盘上保留一个哈希映射不幸的是磁盘哈希映射很难表现优秀。它需要大量的随机访问I/O当它变满时增长是很昂贵的并且散列冲突需要很多的逻辑【5】。
* 范围查询效率不高。例如您无法轻松扫描kitty00000和kitty99999之间的所有键——您必须在散列映射中单独查找每个键。
在下一节中,我们将看看一个没有这些限制的索引结构。
* 散列表必须能放进内存。如果你有非常多的键那真是倒霉。原则上可以在磁盘上维护一个散列映射不幸的是磁盘散列映射很难表现优秀。它需要大量的随机访问I/O当它用满时想要再增长是很昂贵的并且散列冲突的处理也需要很烦琐的逻辑【5】。
* 范围查询效率不高。例如你无法轻松扫描kitty00000和kitty99999之间的所有键——你必须在散列映射中单独查找每个键。
在下一节中,我们将看到一个没有这些限制的索引结构。
### SSTables和LSM树
@ -155,7 +150,7 @@ CSV不是日志的最佳格式。使用二进制格式更快更简单
我们把这个格式称为**排序字符串表Sorted String Table**简称SSTable。我们还要求每个键只在每个合并的段文件中出现一次压缩过程已经保证。与使用散列索引的日志段相比SSTable有几个很大的优势
1. 合并段是简单而高效的,即使文件大于可用内存。这种方法就像归并排序算法中使用的方法一样,如[图3-4](img/fig3-4.png)所示:开始并排读取输入文件,查看每个文件中的第一个键,复制最低键(根据排序顺序)到输出文件,并重复。这产生一个新的合并段文件,也按键排序。
1. 合并段是简单而高效的,即使文件大于可用内存。这种方法就像归并排序算法中使用的方法一样,如[图3-4](img/fig3-4.png)所示:开始并排读取输入文件,查看每个文件中的第一个键,复制最低键(根据排序顺序)到输出文件,并重复。这产生一个新的合并段文件,也按键排序。
![](img/fig3-4.png)
@ -163,13 +158,13 @@ CSV不是日志的最佳格式。使用二进制格式更快更简单
如果在几个输入段中出现相同的键,该怎么办?请记住,每个段都包含在一段时间内写入数据库的所有值。这意味着一个输入段中的所有值必须比另一个段中的所有值更新(假设我们总是合并相邻的段)。当多个段包含相同的键时,我们可以保留最近段的值,并丢弃旧段中的值。
2. 为了在文件中找到一个特定的键,你不再需要保存内存中所有键的索引。以[图3-5](img/fig3-5.png)为例:假设你正在内存中寻找键 `handiwork`,但是你不知道段文件中该关键字的确切偏移量。然而,你知道 `handbag``handsome` 的偏移,而且由于排序特性,你知道 `handiwork` 必须出现在这两者之间。这意味着您可以跳到 `handbag` 的偏移位置并从那里扫描,直到您找到 `handiwork`(或没找到,如果该文件中没有该键)。
2. 为了在文件中找到一个特定的键,你不再需要保存内存中所有键的索引。以[图3-5](img/fig3-5.png)为例:假设你正在内存中寻找键 `handiwork`,但是你不知道段文件中该关键字的确切偏移量。然而,你知道 `handbag``handsome` 的偏移,而且由于排序特性,你知道 `handiwork` 必须出现在这两者之间。这意味着你可以跳到 `handbag` 的偏移位置并从那里扫描,直到你找到 `handiwork`(或没找到,如果该文件中没有该键)。
![](img/fig3-5.png)
**图3-5 具有内存索引的SSTable**
您仍然需要一个内存中索引来告诉您一些键的偏移量,但它可能很稀疏:每几千字节的段文件就有一个键就足够了,因为几千字节可以很快被扫描[^i]。
你仍然需要一个内存中索引来告诉你一些键的偏移量,但它可能很稀疏:每几千字节的段文件就有一个键就足够了,因为几千字节可以很快被扫描[^i]。
3. 由于读取请求无论如何都需要扫描所请求范围内的多个键值对,因此可以将这些记录分组到块中,并在将其写入磁盘之前对其进行压缩(如[图3-5](img/fig3-5.png)中的阴影区域所示) 。稀疏内存中索引的每个条目都指向压缩块的开始处。除了节省磁盘空间之外压缩还可以减少IO带宽的使用。
@ -181,7 +176,7 @@ CSV不是日志的最佳格式。使用二进制格式更快更简单
到目前为止,但是如何让你的数据首先被按键排序呢?我们的传入写入可以以任何顺序发生。
在磁盘上维护有序结构是可能的(请参阅“[B树](#B树)”但在内存保存则要容易得多。有许多可以使用的众所周知的树形数据结构例如红黑树或AVL树【2】。使用这些数据结构可以按任何顺序插入键,并按排序顺序读取它们。
在磁盘上维护有序结构是可能的(请参阅“[B树](#B树)”但在内存保存则要容易得多。有许多可以使用的众所周知的树形数据结构例如红黑树或AVL树【2】。使用这些数据结构可以按任何顺序插入键,并按排序顺序读取它们。
现在我们可以使我们的存储引擎工作如下:
@ -202,7 +197,7 @@ Lucene是Elasticsearch和Solr使用的一种全文搜索的索引引擎它使
#### 性能优化
与往常一样大量的细节使得存储引擎在实践中表现良好。例如当查找数据库中不存在的键时LSM树算法可能会很慢必须检查内存表然后将这些段一直回到最老的可能必须从磁盘读取每一个然后才能确定键不存在。为了优化这种访问存储引擎通常使用额外的Bloom过滤器【15】。 (布隆过滤器是用于近似集合内容的内存高效数据结构,它可以告诉数据库中是否出现键,从而为不存在的键节省许多不必要的磁盘读取操作。)
与往常一样大量的细节使得存储引擎在实践中表现良好。例如当查找数据库中不存在的键时LSM树算法可能会很慢必须检查内存表然后将这些段一直回到最老的可能必须从磁盘读取每一个然后才能确定键不存在。为了优化这种访问存储引擎通常使用额外的Bloom过滤器【15】。 (布隆过滤器是用于近似集合内容的内存高效数据结构,它可以告诉数据库中是否出现键,从而为不存在的键节省许多不必要的磁盘读取操作。)
还有不同的策略来确定SSTables如何被压缩和合并的顺序和时间。最常见的选择是size-tiered和leveled compaction。LevelDB和RocksDB使用leveled compactionLevelDB因此得名HBase使用size-tieredCassandra同时支持【16】。对于sized-tiered较新和较小的SSTables相继被合并到较旧的和较大的SSTable中。对于leveled compactionkey范围被拆分到较小的SSTables而较旧的数据被移动到单独的层级level这使得压缩compaction能够更加增量地进行并且使用较少的磁盘空间。
@ -246,7 +241,7 @@ Lucene是Elasticsearch和Solr使用的一种全文搜索的索引引擎它使
B树的基本底层写操作是用新数据覆盖磁盘上的页面。假定覆盖不改变页面的位置当页面被覆盖时对该页面的所有引用保持完整。这与日志结构索引如LSM树形成鲜明对比后者只附加到文件并最终删除过时的文件但从不修改文件。
可以考虑将硬盘上的页面覆盖为实际的硬件操作。在磁性硬盘驱动器上这意味着将磁头移动到正确的位置等待旋转盘上的正确位置出现然后用新的数据覆盖适当的扇区。在固态硬盘上由于SSD必须一次擦除和重写相当大的存储芯片块所以会发生更复杂的事情【19】。
可以考虑将硬盘上的页面覆盖为实际的硬件操作。在磁性硬盘驱动器上这意味着将磁头移动到正确的位置等待旋转盘上的正确位置出现然后用新的数据覆盖适当的扇区。在固态硬盘上由于SSD必须一次擦除和重写相当大的存储芯片块所以会发生更复杂的事情【19】。
而且,一些操作需要覆盖几个不同的页面。例如,如果因为插入导致页面过满而拆分页面,则需要编写已拆分的两个页面,并覆盖其父页面以更新对两个子页面的引用。这是一个危险的操作,因为如果数据库在仅有一些页面被写入后崩溃,那么最终将导致一个损坏的索引(例如,可能有一个孤儿页面不是任何父项的子项) 。
@ -268,7 +263,7 @@ B树的基本底层写操作是用新数据覆盖磁盘上的页面。假定覆
尽管B树实现通常比LSM树实现更成熟但LSM树由于其性能特点也非常有趣。根据经验通常LSM树的写入速度更快而B树的读取速度更快【23】。 LSM树上的读取通常比较慢因为它们必须检查几种不同的数据结构和不同压缩Compaction层级的SSTables。
然而,基准测试通常对工作负载的细节不确定且敏感。 需要测试具有特定工作负载的系统,以便进行有效的比较。在本节中,我们将简要讨论一些在衡量存储引擎性能时值得考虑的事情。
然而,基准测试通常对工作负载的细节不确定且敏感。 需要测试具有特定工作负载的系统,以便进行有效的比较。在本节中,我们将简要讨论一些在衡量存储引擎性能时值得考虑的事情。
#### LSM树的优点
@ -290,7 +285,7 @@ LSM树可以被压缩得更好因此经常比B树在磁盘上产生更小的
压缩的另一个问题出现在高写入吞吐量:磁盘的有限写入带宽需要在初始写入(记录和刷新内存表到磁盘)和在后台运行的压缩线程之间共享。写入空数据库时,可以使用全磁盘带宽进行初始写入,但数据库越大,压缩所需的磁盘带宽就越多。
如果写入吞吐量很高并且压缩没有仔细配置压缩跟不上写入速率。在这种情况下磁盘上未合并段的数量不断增加直到磁盘空间用完读取速度也会减慢因为它们需要检查更多段文件。通常情况下即使压缩无法跟上基于SSTable的存储引擎也不会限制传入写入的速率所以需要进行明确的监控来检测这种情况【29,30】。
如果写入吞吐量很高并且压缩没有仔细配置压缩跟不上写入速率。在这种情况下磁盘上未合并段的数量不断增加直到磁盘空间用完读取速度也会减慢因为它们需要检查更多段文件。通常情况下即使压缩无法跟上基于SSTable的存储引擎也不会限制传入写入的速率所以需要进行明确的监控来检测这种情况【29,30】。
B树的一个优点是每个键只存在于索引中的一个位置而日志结构化的存储引擎可能在不同的段中有相同键的多个副本。这个方面使得B树在想要提供强大的事务语义的数据库中很有吸引力在许多关系数据库中事务隔离是通过在键范围上使用锁来实现的在B树索引中这些锁可以直接连接到树【5】。在[第七章](ch7.md)中,我们将更详细地讨论这一点。
@ -300,7 +295,7 @@ B树在数据库体系结构中是非常根深蒂固的为许多工作负载
到目前为止,我们只讨论了键值索引,它们就像关系模型中的**主键primary key** 索引。主键唯一标识关系表中的一行或文档数据库中的一个文档或图形数据库中的一个顶点。数据库中的其他记录可以通过其主键或ID引用该行/文档/顶点,并且索引用于解析这样的引用。
有次级索引也很常见。在关系数据库中,可以使用 `CREATE INDEX` 命令在同一个表上创建多个次级索引,而且这些索引通常对于有效地执行联接而言至关重要。例如,在[第二章](ch2.md)中的[图2-1](img/fig2-1.png)中,很可能在 `user_id` 列上有一个次级索引,以便可以在每个表中找到属于同一用户的所有行。
有次级索引也很常见。在关系数据库中,可以使用 `CREATE INDEX` 命令在同一个表上创建多个次级索引,而且这些索引通常对于有效地执行联接而言至关重要。例如,在[第二章](ch2.md)中的[图2-1](img/fig2-1.png)中,很可能在 `user_id` 列上有一个次级索引,以便可以在每个表中找到属于同一用户的所有行。
一个次级索引可以很容易地从一个键值索引构建。主要的不同是键不是唯一的。即可能有许多行文档顶点具有相同的键。这可以通过两种方式来解决或者通过使索引中的每个值成为匹配行标识符的列表如全文索引中的发布列表或者通过向每个索引添加行标识符来使每个关键字唯一。无论哪种方式B树和日志结构索引都可以用作次级索引。
@ -337,7 +332,7 @@ SELECT * FROM restaurants WHERE latitude > 51.4946 AND latitude < 51.5079
#### 全文搜索和模糊索引
到目前为止所讨论的所有索引都假定您有确切的数据,并允许您查询键的确切值或具有排序顺序的键的值范围。他们不允许你做的是搜索类似的键,如拼写错误的单词。这种模糊的查询需要不同的技术。
到目前为止所讨论的所有索引都假定你有确切的数据,并允许你查询键的确切值或具有排序顺序的键的值范围。他们不允许你做的是搜索类似的键,如拼写错误的单词。这种模糊的查询需要不同的技术。
例如全文搜索引擎通常允许搜索一个单词以扩展为包括该单词的同义词忽略单词的语法变体并且搜索在相同文档中彼此靠近的单词的出现并且支持各种其他功能取决于文本的语言分析。为了处理文档或查询中的拼写错误Lucene能够在一定的编辑距离内搜索文本编辑距离1意味着添加删除或替换了一个字母【37】。
@ -375,7 +370,7 @@ SELECT * FROM restaurants WHERE latitude > 51.4946 AND latitude < 51.5079
即使数据库开始被用于许多不同类型的数据,比如博客文章的评论,游戏中的动作,地址簿中的联系人等等,基本访问模式仍然类似于处理商业交易。应用程序通常使用索引通过某个键查找少量记录。根据用户的输入插入或更新记录。由于这些应用程序是交互式的,这种访问模式被称为 **在线事务处理OLTP, OnLine Transaction Processing**
但是,数据库也开始越来越多地用于数据分析,这些数据分析具有非常不同的访问模式。通常,分析查询需要扫描大量记录,每个记录只读取几列,并计算汇总统计信息(如计数,总和或平均值),而不是将原始数据返回给用户。例如,如果的数据是一个销售交易表,那么分析查询可能是:
但是,数据库也开始越来越多地用于数据分析,这些数据分析具有非常不同的访问模式。通常,分析查询需要扫描大量记录,每个记录只读取几列,并计算汇总统计信息(如计数,总和或平均值),而不是将原始数据返回给用户。例如,如果的数据是一个销售交易表,那么分析查询可能是:
* 一月份每个商店的总收入是多少?
* 在最近的推广活动中多卖了多少香蕉?
@ -474,7 +469,7 @@ GROUP BY
在大多数OLTP数据库中存储都是以面向行的方式进行布局的表格的一行中的所有值都相邻存储。文档数据库是相似的整个文档通常存储为一个连续的字节序列。你可以在[图3-1](img/fig3-1.png)的CSV例子中看到这个。
为了处理像[例3-1]()这样的查询,可能在 `fact_sales.date_key` `fact_sales.product_sk`上有索引它们告诉存储引擎在哪里查找特定日期或特定产品的所有销售情况。但是面向行的存储引擎仍然需要将所有这些行每个包含超过100个属性从磁盘加载到内存中解析它们并过滤掉那些不符合要求的属性。这可能需要很长时间。
为了处理像[例3-1]()这样的查询,可能在 `fact_sales.date_key` `fact_sales.product_sk`上有索引它们告诉存储引擎在哪里查找特定日期或特定产品的所有销售情况。但是面向行的存储引擎仍然需要将所有这些行每个包含超过100个属性从磁盘加载到内存中解析它们并过滤掉那些不符合要求的属性。这可能需要很长时间。
面向列的存储背后的想法很简单:不要将所有来自一行的值存储在一起,而是将来自每一列的所有值存储在一起。如果每个列存储在一个单独的文件中,查询只需要读取和解析查询中使用的那些列,这可以节省大量的工作。这个原理如[图3-10](img/fig3-10.png)所示。
@ -484,7 +479,7 @@ GROUP BY
列存储在关系数据模型中是最容易理解的但它同样适用于非关系数据。例如Parquet 【57】是一种列式存储格式支持基于Google的Dremel 【54】的文档数据模型。
面向列的存储布局依赖于每个列文件包含相同顺序的行。 因此,如果您需要重新组装整行,您可以从每个单独的列文件中获取第23项并将它们放在一起形成表的第23行。
面向列的存储布局依赖于每个列文件包含相同顺序的行。 因此,如果你需要重新组装整行,你可以从每个单独的列文件中获取第23项并将它们放在一起形成表的第23行。
@ -547,7 +542,7 @@ WHERE product_sk = 31 AND store_sk = 3
#### 几个不同的排序顺序
这个想法的巧妙扩展在C-Store中引入并在商业数据仓库Vertica【61,62】中被采用。不同的查询受益于不同的排序顺序为什么不以几种不同的方式来存储相同的数据呢无论如何数据需要复制到多台机器这样如果一台机器发生故障您不会丢失数据。您可能还需要存储以不同方式排序的冗余数据,以便在处理查询时,可以使用最适合查询模式的版本。
这个想法的巧妙扩展在C-Store中引入并在商业数据仓库Vertica【61,62】中被采用。不同的查询受益于不同的排序顺序为什么不以几种不同的方式来存储相同的数据呢无论如何数据需要复制到多台机器这样如果一台机器发生故障你不会丢失数据。你可能还需要存储以不同方式排序的冗余数据,以便在处理查询时,可以使用最适合查询模式的版本。
在一个面向列的存储中有多个排序顺序有点类似于在一个面向行的存储中有多个次级索引。但最大的区别在于面向行的存储将每一行保存在一个地方(在堆文件或聚簇索引中),次级索引只包含指向匹配行的指针。在列存储中,通常在其他地方没有任何指向数据的指针,只有包含值的列。
@ -577,11 +572,11 @@ WHERE product_sk = 31 AND store_sk = 3
**图3-12 数据立方的两个维度,通过求和聚合**
想象一下,现在每个事实都只有两个维度表的外键——在[图3-12](img/fig-3-12.png)中,这些是日期和产品。现在可以绘制一个二维表格,一个轴线上是日期,另一个轴线上是产品。每个单元包含具有该日期 - 产品组合的所有事实的属性(例如,`net_price`)的聚集(例如,`SUM`)。然后,可以沿着每行或每列应用相同的汇总,并获得一个维度减少的汇总(按产品的销售额,无论日期,还是按日期销售,无论产品如何)。
想象一下,现在每个事实都只有两个维度表的外键——在[图3-12](img/fig-3-12.png)中,这些是日期和产品。现在可以绘制一个二维表格,一个轴线上是日期,另一个轴线上是产品。每个单元包含具有该日期 - 产品组合的所有事实的属性(例如,`net_price`)的聚集(例如,`SUM`)。然后,可以沿着每行或每列应用相同的汇总,并获得一个维度减少的汇总(按产品的销售额,无论日期,还是按日期销售,无论产品如何)。
一般来说事实往往有两个以上的维度。在图3-9中有五个维度日期产品商店促销和客户。要想象一个五维超立方体是什么样子是很困难的但是原理是一样的每个单元格都包含特定日期-产品-商店-促销-客户组合的销售。这些值可以在每个维度上重复概括。
物化数据立方体的优点是某些查询变得非常快,因为它们已经被有效地预先计算了。例如,如果想知道每个商店的总销售额,则只需查看合适维度的总计,无需扫描数百万行。
物化数据立方体的优点是某些查询变得非常快,因为它们已经被有效地预先计算了。例如,如果想知道每个商店的总销售额,则只需查看合适维度的总计,无需扫描数百万行。
缺点是数据立方体不具有查询原始数据的灵活性。例如没有办法计算哪个销售比例来自成本超过100美元的项目因为价格不是其中的一个维度。因此大多数数据仓库试图保留尽可能多的原始数据并将聚合数据如数据立方体仅用作某些查询的性能提升。
@ -608,9 +603,9 @@ WHERE product_sk = 31 AND store_sk = 3
日志结构的存储引擎是相对较新的发展。他们的主要想法是他们系统地将随机访问写入转换为磁盘上的顺序写入由于硬盘驱动器和固态硬盘的性能特点可以实现更高的写入吞吐量。在完成OLTP方面我们通过一些更复杂的索引结构和为保留所有数据而优化的数据库做了一个简短的介绍。
然后我们从存储引擎的内部绕开看看典型数据仓库的高级架构。这一背景说明了为什么分析工作负载与OLTP差别很大的查询需要在大量行中顺序扫描时,索引的相关性就会降低很多。相反,非常紧凑地编码数据变得非常重要,以最大限度地减少查询需要从磁盘读取的数据量。我们讨论了列式存储如何帮助实现这一目标。
然后我们从存储引擎的内部绕开看看典型数据仓库的高级架构。这一背景说明了为什么分析工作负载与OLTP差别很大的查询需要在大量行中顺序扫描时,索引的相关性就会降低很多。相反,非常紧凑地编码数据变得非常重要,以最大限度地减少查询需要从磁盘读取的数据量。我们讨论了列式存储如何帮助实现这一目标。
作为一名应用程序开发人员,如果您掌握了有关存储引擎内部的知识,那么您就能更好地了解哪种工具最适合您的特定应用程序。如果您需要调整数据库的调整参数,这种理解可以让您设想一个更高或更低的值可能会产生什么效果。
作为一名应用程序开发人员,如果你掌握了有关存储引擎内部的知识,那么你就能更好地了解哪种工具最适合你的特定应用程序。如果你需要调整数据库的调整参数,这种理解可以让你设想一个更高或更低的值可能会产生什么效果。
尽管本章不能让你成为一个特定存储引擎的调参专家,但它至少有大概率使你有了足够的概念与词汇储备去读懂数据库的文档,从而选择合适的数据库。
@ -620,142 +615,69 @@ WHERE product_sk = 31 AND store_sk = 3
## 参考文献
1. Alfred V. Aho, John E. Hopcroft, and Jeffrey D. Ullman: *Data Structures and Algorithms*. Addison-Wesley, 1983. ISBN: 978-0-201-00023-8
1. Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, and Clifford Stein: *Introduction to Algorithms*, 3rd edition. MIT Press, 2009. ISBN: 978-0-262-53305-8
1. Justin Sheehy and David Smith: “[Bitcask: A Log-Structured Hash Table for Fast Key/Value Data](http://basho.com/wp-content/uploads/2015/05/bitcask-intro.pdf),” Basho Technologies, April 2010.
1. Yinan Li, Bingsheng He, Robin Jun Yang, et al.: “[Tree Indexing on Solid State Drives](http://www.vldb.org/pvldb/vldb2010/papers/R106.pdf),” *Proceedings of the VLDB Endowment*, volume 3, number 1, pages 11951206, September 2010.
1. Goetz Graefe: “[Modern B-Tree Techniques](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.219.7269&rep=rep1&type=pdf),” *Foundations and Trends in Databases*, volume 3, number 4, pages 203402, August 2011. [doi:10.1561/1900000028](http://dx.doi.org/10.1561/1900000028)
1. Jeffrey Dean and Sanjay Ghemawat: “[LevelDB Implementation Notes](https://github.com/google/leveldb/blob/master/doc/impl.html),” *leveldb.googlecode.com*.
1. Dhruba Borthakur: “[The History of RocksDB](http://rocksdb.blogspot.com/),” *rocksdb.blogspot.com*, November 24, 2013.
1. Matteo Bertozzi: “[Apache HBase I/O HFile](http://blog.cloudera.com/blog/2012/06/hbase-io-hfile-input-output/),” *blog.cloudera.com*, June, 29 2012.
1. Fay Chang, Jeffrey Dean, Sanjay Ghemawat, et al.: “[Bigtable: A Distributed Storage System for Structured Data](http://research.google.com/archive/bigtable.html),” at *7th USENIX Symposium on Operating System Design and Implementation* (OSDI), November 2006.
1. Patrick O'Neil, Edward Cheng, Dieter Gawlick, and Elizabeth O'Neil: “[The Log-Structured Merge-Tree (LSM-Tree)](http://www.cs.umb.edu/~poneil/lsmtree.pdf),” *Acta Informatica*, volume 33, number 4, pages 351385, June 1996. [doi:10.1007/s002360050048](http://dx.doi.org/10.1007/s002360050048)
1. Mendel Rosenblum and John K. Ousterhout: “[The Design and Implementation of a Log-Structured File System](http://research.cs.wisc.edu/areas/os/Qual/papers/lfs.pdf),” *ACM Transactions on Computer Systems*, volume 10, number 1, pages 2652, February 1992.
[doi:10.1145/146941.146943](http://dx.doi.org/10.1145/146941.146943)
1. Mendel Rosenblum and John K. Ousterhout: “[The Design and Implementation of a Log-Structured File System](http://research.cs.wisc.edu/areas/os/Qual/papers/lfs.pdf),” *ACM Transactions on Computer Systems*, volume 10, number 1, pages 2652, February 1992. [doi:10.1145/146941.146943](http://dx.doi.org/10.1145/146941.146943)
1. Adrien Grand: “[What Is in a Lucene Index?](http://www.slideshare.net/lucenerevolution/what-is-inaluceneagrandfinal),” at *Lucene/Solr Revolution*, November 14, 2013.
1. Deepak Kandepet: “[Hacking Lucene—The Index Format]( http://hackerlabs.github.io/blog/2011/10/01/hacking-lucene-the-index-format/index.html),” *hackerlabs.org*, October 1, 2011.
1. Michael McCandless: “[Visualizing Lucene's Segment Merges](http://blog.mikemccandless.com/2011/02/visualizing-lucenes-segment-merges.html),” *blog.mikemccandless.com*, February 11, 2011.
1. Burton H. Bloom: “[Space/Time Trade-offs in Hash Coding with Allowable Errors](http://www.cs.upc.edu/~diaz/p422-bloom.pdf),” *Communications of the ACM*, volume 13, number 7, pages 422426, July 1970. [doi:10.1145/362686.362692](http://dx.doi.org/10.1145/362686.362692)
1. “[Operating Cassandra: Compaction](https://cassandra.apache.org/doc/latest/operating/compaction.html),” Apache Cassandra Documentation v4.0, 2016.
1. Rudolf Bayer and Edward M. McCreight: “[Organization and Maintenance of Large Ordered Indices](http://www.dtic.mil/cgi-bin/GetTRDoc?AD=AD0712079),” Boeing Scientific Research Laboratories, Mathematical and Information Sciences Laboratory, report no. 20, July 1970.
1. Douglas Comer: “[The Ubiquitous B-Tree](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.96.6637&rep=rep1&type=pdf),” *ACM Computing Surveys*, volume 11, number 2, pages 121137, June 1979. [doi:10.1145/356770.356776](http://dx.doi.org/10.1145/356770.356776)
1. Emmanuel Goossaert: “[Coding for SSDs](http://codecapsule.com/2014/02/12/coding-for-ssds-part-1-introduction-and-table-of-contents/),” *codecapsule.com*, February 12, 2014.
1. C. Mohan and Frank Levine: “[ARIES/IM: An Efficient and High Concurrency Index Management Method Using Write-Ahead Logging](http://www.ics.uci.edu/~cs223/papers/p371-mohan.pdf),” at *ACM International Conference on Management of Data* (SIGMOD), June 1992. [doi:10.1145/130283.130338](http://dx.doi.org/10.1145/130283.130338)
1. Howard Chu: “[LDAP at Lightning Speed]( https://buildstuff14.sched.com/event/08a1a368e272eb599a52e08b4c3c779d),” at *Build Stuff '14*, November 2014.
1. Bradley C. Kuszmaul: “[A Comparison of Fractal Trees to Log-Structured Merge (LSM) Trees](http://insideanalysis.com/wp-content/uploads/2014/08/Tokutek_lsm-vs-fractal.pdf),” *tokutek.com*, April 22, 2014.
1. Manos Athanassoulis, Michael S. Kester, Lukas M. Maas, et al.: “[Designing Access Methods: The RUM Conjecture](http://openproceedings.org/2016/conf/edbt/paper-12.pdf),” at *19th International Conference on Extending Database Technology* (EDBT), March 2016.
[doi:10.5441/002/edbt.2016.42](http://dx.doi.org/10.5441/002/edbt.2016.42)
1. Manos Athanassoulis, Michael S. Kester, Lukas M. Maas, et al.: “[Designing Access Methods: The RUM Conjecture](http://openproceedings.org/2016/conf/edbt/paper-12.pdf),” at *19th International Conference on Extending Database Technology* (EDBT), March 2016. [doi:10.5441/002/edbt.2016.42](http://dx.doi.org/10.5441/002/edbt.2016.42)
1. Peter Zaitsev: “[Innodb Double Write](https://www.percona.com/blog/2006/08/04/innodb-double-write/),” *percona.com*, August 4, 2006.
1. Tomas Vondra: “[On the Impact of Full-Page Writes](http://blog.2ndquadrant.com/on-the-impact-of-full-page-writes/),” *blog.2ndquadrant.com*, November 23, 2016.
1. Mark Callaghan: “[The Advantages of an LSM vs a B-Tree](http://smalldatum.blogspot.co.uk/2016/01/summary-of-advantages-of-lsm-vs-b-tree.html),” *smalldatum.blogspot.co.uk*, January 19, 2016.
1. Mark Callaghan: “[Choosing Between Efficiency and Performance with RocksDB](http://www.codemesh.io/codemesh/mark-callaghan),” at *Code Mesh*, November 4, 2016.
1. Michi Mutsuzaki: “[MySQL vs. LevelDB](https://github.com/m1ch1/mapkeeper/wiki/MySQL-vs.-LevelDB),” *github.com*, August 2011.
1. Benjamin Coverston, Jonathan Ellis, et al.: “[CASSANDRA-1608: Redesigned Compaction](https://issues.apache.org/jira/browse/CASSANDRA-1608), *issues.apache.org*, July 2011.
1. Igor Canadi, Siying Dong, and Mark Callaghan: “[RocksDB Tuning Guide](https://github.com/facebook/rocksdb/wiki/RocksDB-Tuning-Guide),”
*github.com*, 2016.
1. Igor Canadi, Siying Dong, and Mark Callaghan: “[RocksDB Tuning Guide](https://github.com/facebook/rocksdb/wiki/RocksDB-Tuning-Guide),” *github.com*, 2016.
1. [*MySQL 5.7 Reference Manual*](http://dev.mysql.com/doc/refman/5.7/en/index.html). Oracle, 2014.
1. [*Books Online for SQL Server 2012*](http://msdn.microsoft.com/en-us/library/ms130214.aspx). Microsoft, 2012.
1. Joe Webb: “[Using Covering Indexes to Improve Query Performance](https://www.simple-talk.com/sql/learn-sql-server/using-covering-indexes-to-improve-query-performance/),” *simple-talk.com*, 29 September 2008.
1. Frank Ramsak, Volker Markl, Robert Fenk, et al.: “[Integrating the UB-Tree into a Database System Kernel](http://www.vldb.org/conf/2000/P263.pdf),” at *26th International Conference on Very Large Data Bases* (VLDB), September 2000.
1. The PostGIS Development Group: “[PostGIS 2.1.2dev Manual](http://postgis.net/docs/manual-2.1/),” *postgis.net*, 2014.
1. Robert Escriva, Bernard Wong, and Emin Gün Sirer: “[HyperDex: A Distributed, Searchable Key-Value Store](http://www.cs.princeton.edu/courses/archive/fall13/cos518/papers/hyperdex.pdf),” at *ACM SIGCOMM Conference*, August 2012. [doi:10.1145/2377677.2377681](http://dx.doi.org/10.1145/2377677.2377681)
1. Michael McCandless: “[Lucene's FuzzyQuery Is 100 Times Faster in 4.0](http://blog.mikemccandless.com/2011/03/lucenes-fuzzyquery-is-100-times-faster.html),” *blog.mikemccandless.com*, March 24, 2011.
1. Steffen Heinz, Justin Zobel, and Hugh E. Williams: “[Burst Tries: A Fast, Efficient Data Structure for String Keys](http://citeseer.ist.psu.edu/viewdoc/summary?doi=10.1.1.18.3499),” *ACM Transactions on Information Systems*, volume 20, number 2, pages 192223, April 2002. [doi:10.1145/506309.506312](http://dx.doi.org/10.1145/506309.506312)
1. Klaus U. Schulz and Stoyan Mihov: “[Fast String Correction with Levenshtein Automata](http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.16.652),” *International Journal on Document Analysis and Recognition*, volume 5, number 1, pages 6785, November 2002. [doi:10.1007/s10032-002-0082-8](http://dx.doi.org/10.1007/s10032-002-0082-8)
1. Christopher D. Manning, Prabhakar Raghavan, and Hinrich Schütze: [*Introduction to Information Retrieval*](http://nlp.stanford.edu/IR-book/). Cambridge University Press, 2008. ISBN: 978-0-521-86571-5, available online at *nlp.stanford.edu/IR-book*
1. Michael Stonebraker, Samuel Madden, Daniel J. Abadi, et al.: “[The End of an Architectural Era (Its Time for a Complete Rewrite)](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.137.3697&rep=rep1&type=pdf),” at *33rd International Conference on Very Large Data Bases* (VLDB), September 2007.
1. “[VoltDB Technical Overview White Paper](https://www.voltdb.com/wptechnicaloverview),” VoltDB, 2014.
1. Stephen M. Rumble, Ankita Kejriwal, and John K. Ousterhout: “[Log-Structured Memory for DRAM-Based Storage](https://www.usenix.org/system/files/conference/fast14/fast14-paper_rumble.pdf),” at *12th USENIX Conference on File and Storage Technologies* (FAST), February 2014.
1. Stavros Harizopoulos, Daniel J. Abadi, Samuel Madden, and Michael Stonebraker: “[OLTP Through the Looking Glass, and What We Found There](http://hstore.cs.brown.edu/papers/hstore-lookingglass.pdf),” at *ACM International Conference on Management of Data*
(SIGMOD), June 2008. [doi:10.1145/1376616.1376713](http://dx.doi.org/10.1145/1376616.1376713)
1. Stavros Harizopoulos, Daniel J. Abadi, Samuel Madden, and Michael Stonebraker: “[OLTP Through the Looking Glass, and What We Found There](http://hstore.cs.brown.edu/papers/hstore-lookingglass.pdf),” at *ACM International Conference on Management of Data* (SIGMOD), June 2008. [doi:10.1145/1376616.1376713](http://dx.doi.org/10.1145/1376616.1376713)
1. Justin DeBrabant, Andrew Pavlo, Stephen Tu, et al.: “[Anti-Caching: A New Approach to Database Management System Architecture](http://www.vldb.org/pvldb/vol6/p1942-debrabant.pdf),” *Proceedings of the VLDB Endowment*, volume 6, number 14, pages 19421953, September 2013.
1. Joy Arulraj, Andrew Pavlo, and Subramanya R. Dulloor: “[Let's Talk About Storage & Recovery Methods for Non-Volatile Memory Database Systems](http://www.pdl.cmu.edu/PDL-FTP/NVM/storage.pdf),” at *ACM International Conference on Management of Data* (SIGMOD), June 2015. [doi:10.1145/2723372.2749441](http://dx.doi.org/10.1145/2723372.2749441)
1. Edgar F. Codd, S. B. Codd, and C. T. Salley: “[Providing OLAP to User-Analysts: An IT Mandate](http://www.minet.uni-jena.de/dbis/lehre/ss2005/sem_dwh/lit/Cod93.pdf),” E. F. Codd Associates, 1993.
1. Surajit Chaudhuri and Umeshwar Dayal: “[An Overview of Data Warehousing and OLAP Technology](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/sigrecord.pdf),” *ACM SIGMOD Record*, volume 26, number 1, pages 6574, March 1997. [doi:10.1145/248603.248616](http://dx.doi.org/10.1145/248603.248616)
1. Per-Åke Larson, Cipri Clinciu, Campbell Fraser, et al.: “[Enhancements to SQL Server Column Stores](http://research.microsoft.com/pubs/193599/Apollo3%20-%20Sigmod%202013%20-%20final.pdf),” at *ACM International Conference on Management of Data* (SIGMOD), June 2013.
1. Franz Färber, Norman May, Wolfgang Lehner, et al.: “[The SAP HANA Database An Architecture Overview](http://sites.computer.org/debull/A12mar/hana.pdf),” *IEEE Data Engineering Bulletin*, volume 35, number 1, pages 2833, March 2012.
1. Michael Stonebraker: “[The Traditional RDBMS Wisdom Is (Almost Certainly) All Wrong](http://slideshot.epfl.ch/talks/166),” presentation at *EPFL*, May 2013.
1. Daniel J. Abadi: “[Classifying the SQL-on-Hadoop Solutions](https://web.archive.org/web/20150622074951/http://hadapt.com/blog/2013/10/02/classifying-the-sql-on-hadoop-solutions/),” *hadapt.com*, October 2, 2013.
1. Marcel Kornacker, Alexander Behm, Victor Bittorf, et al.: “[Impala: A Modern, Open-Source SQL Engine for Hadoop](http://pandis.net/resources/cidr15impala.pdf),” at *7th Biennial Conference on Innovative Data Systems Research* (CIDR), January 2015.
1. Sergey Melnik, Andrey Gubarev, Jing Jing Long, et al.: “[Dremel: Interactive Analysis of Web-Scale Datasets](http://research.google.com/pubs/pub36632.html),” at *36th International Conference on Very Large Data Bases* (VLDB), pages
330339, September 2010.
1. Sergey Melnik, Andrey Gubarev, Jing Jing Long, et al.: “[Dremel: Interactive Analysis of Web-Scale Datasets](http://research.google.com/pubs/pub36632.html),” at *36th International Conference on Very Large Data Bases* (VLDB), pages 330339, September 2010.
1. Ralph Kimball and Margy Ross: *The Data Warehouse Toolkit: The Definitive Guide to Dimensional Modeling*, 3rd edition. John Wiley & Sons, July 2013. ISBN: 978-1-118-53080-1
1. Derrick Harris: “[Why Apple, eBay, and Walmart Have Some of the Biggest Data Warehouses Youve Ever Seen](http://gigaom.com/2013/03/27/why-apple-ebay-and-walmart-have-some-of-the-biggest-data-warehouses-youve-ever-seen/),” *gigaom.com*, March 27, 2013.
1. Julien Le Dem: “[Dremel Made Simple with Parquet](https://blog.twitter.com/2013/dremel-made-simple-with-parquet),” *blog.twitter.com*, September 11, 2013.
1. Daniel J. Abadi, Peter Boncz, Stavros Harizopoulos, et al.: “[The Design and Implementation of Modern Column-Oriented Database Systems](http://cs-www.cs.yale.edu/homes/dna/papers/abadi-column-stores.pdf),” *Foundations and Trends in Databases*, volume 5, number 3, pages 197280, December 2013. [doi:10.1561/1900000024](http://dx.doi.org/10.1561/1900000024)
1. Peter Boncz, Marcin Zukowski, and Niels Nes: “[MonetDB/X100: Hyper-Pipelining Query Execution](http://www.cidrdb.org/cidr2005/papers/P19.pdf),”
at *2nd Biennial Conference on Innovative Data Systems Research* (CIDR), January 2005.
1. Jingren Zhou and Kenneth A. Ross: “[Implementing Database Operations Using SIMD Instructions](http://www1.cs.columbia.edu/~kar/pubsk/simd.pdf),”
at *ACM International Conference on Management of Data* (SIGMOD), pages 145156, June 2002.
[doi:10.1145/564691.564709](http://dx.doi.org/10.1145/564691.564709)
1. Michael Stonebraker, Daniel J. Abadi, Adam Batkin, et al.: “[C-Store: A Column-oriented DBMS](http://www.vldb2005.org/program/paper/thu/p553-stonebraker.pdf),”
at *31st International Conference on Very Large Data Bases* (VLDB), pages 553564, September 2005.
1. Peter Boncz, Marcin Zukowski, and Niels Nes: “[MonetDB/X100: Hyper-Pipelining Query Execution](http://www.cidrdb.org/cidr2005/papers/P19.pdf),” at *2nd Biennial Conference on Innovative Data Systems Research* (CIDR), January 2005.
1. Jingren Zhou and Kenneth A. Ross: “[Implementing Database Operations Using SIMD Instructions](http://www1.cs.columbia.edu/~kar/pubsk/simd.pdf),” at *ACM International Conference on Management of Data* (SIGMOD), pages 145156, June 2002. [doi:10.1145/564691.564709](http://dx.doi.org/10.1145/564691.564709)
1. Michael Stonebraker, Daniel J. Abadi, Adam Batkin, et al.: “[C-Store: A Column-oriented DBMS](http://www.vldb2005.org/program/paper/thu/p553-stonebraker.pdf),” at *31st International Conference on Very Large Data Bases* (VLDB), pages 553564, September 2005.
1. Andrew Lamb, Matt Fuller, Ramakrishna Varadarajan, et al.: “[The Vertica Analytic Database: C-Store 7 Years Later](http://vldb.org/pvldb/vol5/p1790_andrewlamb_vldb2012.pdf),” *Proceedings of the VLDB Endowment*, volume 5, number 12, pages 17901801, August 2012.
1. Julien Le Dem and Nong Li: “[Efficient Data Storage for Analytics with Apache Parquet 2.0](http://www.slideshare.net/julienledem/th-210pledem),” at *Hadoop Summit*, San Jose, June 2014.
1. Jim Gray, Surajit Chaudhuri, Adam Bosworth, et al.: “[Data Cube: A Relational Aggregation Operator Generalizing Group-By, Cross-Tab, and Sub-Totals](http://arxiv.org/pdf/cs/0701155.pdf),” *Data Mining and Knowledge Discovery*, volume 1, number 1, pages 2953, March 2007. [doi:10.1023/A:1009726021843](http://dx.doi.org/10.1023/A:1009726021843)

View File

@ -15,136 +15,131 @@
在[第二章](ch2.md)中,我們討論了資料模型和查詢語言,即程式設計師將資料錄入資料庫的格式,以及再次要回資料的機制。在本章中我們會從資料庫的視角來討論同樣的問題:資料庫如何儲存我們提供的資料,以及如何在我們需要時重新找到資料。
作為程式設計師,為什麼要關心資料庫內部儲存與檢索的機理?你可能不會去從頭開始實現自己的儲存引擎,但是你**確實**需要從許多可用的儲存引擎中選擇一個合適的。而且為了協調儲存引擎以適配應用工作負載,你也需要大致瞭解儲存引擎在底層究竟做什麼。
作為程式設計師,為什麼要關心資料庫內部儲存與檢索的機理?你可能不會去從頭開始實現自己的儲存引擎,但是你**確實**需要從許多可用的儲存引擎中選擇一個合適的。而且為了讓儲存引擎能在你的工作負載型別上執行良好,你也需要大致瞭解儲存引擎在底層究竟做什麼。
特別需要注意,針對**事務**負載和**分析性**負載最佳化的儲存引擎之間存在巨大差異。稍後我們將在 “[事務處理還是分析?](#事務處理還是分析?)” 一節中探討這一區別,並在 “[列儲存](#列儲存)”中討論一系列針對分析最佳化儲存引擎。
特別需要注意,針對**事務**負載最佳化的針對**分析性**負載最佳化的儲存引擎之間存在巨大差異。稍後我們將在 “[事務處理還是分析?](#事務處理還是分析?)” 一節中探討這一區別,並在 “[列儲存](#列儲存)”中討論一系列針對分析性負載而最佳化儲存引擎。
我們將從您最可能熟悉的兩大類資料庫傳統關係型資料庫與很多所謂的“NoSQL”資料庫開始透過介紹它們的**儲存引擎**來開始本章的內容。我們會研究兩大類儲存引擎:**日誌結構log-structured** 的儲存引擎,以及**面向頁面page-oriented** 的儲存引擎例如B樹
首先我們將從你可能已經很熟悉的兩大類資料庫傳統的關係型資料庫和很多所謂的“NoSQL”資料庫中使用的**儲存引擎**來開始本章的內容。我們將研究兩大類儲存引擎:**日誌結構log-structured** 的儲存引擎,以及**面向頁面page-oriented** 的儲存引擎例如B樹
## 驅動資料庫的資料結構
世界上最簡單的資料庫可以用兩個Bash函式實現
```bash
#!/bin/bash
db_set () {
echo "$1,$2" >> database
}
#!/bin/bash
db_set () {
echo "$1,$2" >> database
}
db_get () {
grep "^$1," database | sed -e "s/^$1,//" | tail -n 1
}
db_get () {
grep "^$1," database | sed -e "s/^$1,//" | tail -n 1
}
```
這兩個函式實現了鍵值儲存的功能。執行 `db_set key value` 會將 **鍵key** 和**值value** 儲存在資料庫中。鍵和值幾乎可以是你喜歡的任何東西例如值可以是JSON文件。然後呼叫 `db_get key` 查詢與該鍵關聯的最新值並將其返回。
這兩個函式實現了鍵值儲存的功能。執行 `db_set key value` 會將 **鍵key** 和**值value** 儲存在資料庫中。鍵和值幾乎可以是你喜歡的任何東西例如值可以是JSON文件。然後呼叫 `db_get key` 查詢與該鍵關聯的最新值並將其返回。
麻雀雖小,五臟俱全:
```bash
$ db_set 123456 '{"name":"London","attractions":["Big Ben","London Eye"]}'
$ db_set 123456 '{"name":"London","attractions":["Big Ben","London Eye"]}'
$ db_set 42 '{"name":"San Francisco","attractions":["Golden Gate Bridge"]}'
$ db_set 42 '{"name":"San Francisco","attractions":["Golden Gate Bridge"]}'
$ db_get 42
{"name":"San Francisco","attractions":["Golden Gate Bridge"]}
$ db_get 42
{"name":"San Francisco","attractions":["Golden Gate Bridge"]}
```
底層的儲存格式非常簡單一個文字檔案每行包含一條逗號分隔的鍵值對忽略轉義問題的話大致與CSV檔案類似。每次對 `db_set` 的呼叫都會向檔案末尾追加記錄,所以更新鍵的時候舊版本的值不會被覆蓋 —— 因而查詢最新值的時候,需要找到檔案中鍵最後一次出現的位置(因此 `db_get` 中使用了 `tail -n 1 ` 。)
```bash
$ db_set 42 '{"name":"San Francisco","attractions":["Exploratorium"]}'
$ db_set 42 '{"name":"San Francisco","attractions":["Exploratorium"]}'
$ db_get 42
{"name":"San Francisco","attractions":["Exploratorium"]}
$ db_get 42
{"name":"San Francisco","attractions":["Exploratorium"]}
$ cat database
123456,{"name":"London","attractions":["Big Ben","London Eye"]}
42,{"name":"San Francisco","attractions":["Golden Gate Bridge"]}
42,{"name":"San Francisco","attractions":["Exploratorium"]}
$ cat database
123456,{"name":"London","attractions":["Big Ben","London Eye"]}
42,{"name":"San Francisco","attractions":["Golden Gate Bridge"]}
42,{"name":"San Francisco","attractions":["Exploratorium"]}
```
`db_set` 函式對於極其簡單的場景其實有非常好的效能,因為在檔案尾部追加寫入通常是非常高效的。與`db_set`做的事情類似,許多資料庫在內部使用了**日誌log**,也就是一個 **僅追加append-only** 的資料檔案。真正的資料庫有更多的問題需要處理(如併發控制,回收磁碟空間以避免日誌無限增長,處理錯誤與部分寫入的記錄),但基本原理是一樣的。日誌極其有用,我們還將在本書的其它部分重複見到它好幾次。
> **日誌log** 這個詞通常指應用日誌:即應用程式輸出的描述發生事情的文字。本書在更普遍的意義下使用**日誌**這一詞:一個僅追加的記錄序列。它可能壓根就不是給人類看的,使用二進位制格式,並僅能由其他程式讀取。
> **日誌log** 這個詞通常指應用日誌:即應用程式輸出的描述正在發生事情的文字。本書在更普遍的意義下使用**日誌**這一詞:一個僅追加的記錄序列。它可能壓根就不是給人類看的,它可以使用二進位制格式,並僅能由其他程式讀取。
另一方面,如果這個資料庫中有著大量記錄,則這個`db_get` 函式的效能會非常糟糕。每次你想查詢一個鍵時,`db_get` 必須從頭到尾掃描整個資料庫檔案來查詢鍵的出現。用演算法的語言來說,查詢的開銷是 `O(n)` :如果資料庫記錄數量 n 翻了一倍,查詢時間也要翻一倍。這就不好了。
為了高效查詢資料庫中特定鍵的值,我們需要一個數據結構:**索引index**。本章將介紹一系列的索引結構,並它們進行對比。索引背後的大致思想是,儲存一些額外的元資料作為路標,幫助你找到想要的資料。如果您想在同一份資料中以幾種不同的方式進行搜尋,那麼你也許需要不同的索引,建在資料的不同部分上
為了高效查詢資料庫中特定鍵的值,我們需要一個數據結構:**索引index**。本章將介紹一系列的索引結構,並在它們之間進行比較。索引背後的大致思想是透過儲存一些額外的元資料作為路標來幫助你找到想要的資料。如果你想以幾種不同的方式搜尋同一份資料,那麼你也許需要在資料的不同部分上建立多個索引
索引是從主資料衍生的**附加additional** 結構。許多資料庫允許新增與刪除索引,這不會影響資料的內容,它隻影響查詢的效能。維護額外的結構會產生開銷,特別是在寫入時。寫入效能很難超過簡單地追加寫入檔案,因為追加寫入是最簡單的寫入操作。任何型別的索引通常都會減慢寫入速度,因為每次寫入資料時都需要更新索引。
這是儲存系統中一個重要的權衡精心選擇的索引加快了讀查詢的速度但是每個索引都會拖慢寫入速度。因為這個原因資料庫預設並不會索引所有的內容而需要你程式設計師或DBA透過對應用查詢模式的瞭解來手動選擇索引。你可以選擇能為應用帶來最大收益同時又不會引入超出必要開銷的索引。
索引是從主資料衍生的**額外的additional** 結構。許多資料庫允許新增與刪除索引,這不會影響資料的內容,而只會影響查詢的效能。維護額外的結構會產生開銷,特別是在寫入時。寫入效能很難超過簡單地追加寫入檔案,因為追加寫入是最簡單的寫入操作。任何型別的索引通常都會減慢寫入速度,因為每次寫入資料時都需要更新索引。
這是儲存系統中一個重要的權衡精心選擇的索引加快了讀查詢的速度但是每個索引都會拖慢寫入速度。因為這個原因資料庫預設並不會索引所有的內容而需要你也就是程式設計師或資料庫管理員DBA基於對應用的典型查詢模式的瞭解來手動選擇索引。你可以選擇那些能為應用帶來最大收益而且又不會引入超出必要開銷的索引。
### 雜湊索引
讓我們從 **鍵值資料key-value Data** 的索引開始。這不是您可以索引的唯一資料型別,但鍵值資料是很常見的。對於更復雜的索引來說,這是一個有用的構建模組。
讓我們從**鍵值資料key-value Data** 的索引開始。這不是你可以索引的唯一資料型別,但鍵值資料是很常見的。對於更復雜的索引來說,這是一個有用的構建模組。
鍵值儲存與在大多數程式語言中可以找到的**字典dictionary** 型別非常相似,通常字典都是用**雜湊對映hash map**(或**雜湊hash table**實現的。雜湊對映在許多演算法教科書中都有描述【1,2】所以這裡我們不會討論它的工作細節。既然我們已經有**記憶體中**資料結構 —— 雜湊對映,為什麼不使用它來索引在**磁碟上**的資料呢?
鍵值儲存與在大多數程式語言中可以找到的**字典dictionary** 型別非常相似,通常字典都是用**雜湊對映hash map**(或**散列hash table**實現的。雜湊對映在許多演算法教科書中都有描述【1,2】所以這裡我們不會討論它的工作細節。既然我們已經可以用雜湊對映來表示**記憶體中**的資料結構,為什麼不使用它來索引**磁碟上**的資料呢?
假設我們的資料儲存只是一個追加寫入的檔案,就像前面的例子一樣那麼最簡單的索引策略就是:保留一個記憶體中的雜湊對映,其中每個鍵都對映到一個數據檔案中的位元組偏移量,指明瞭可以找到對應值的位置,如[圖3-1](../img/fig3-1.png)所示。當你將新的鍵值對追加寫入檔案中時,還要更新雜湊對映,以反映剛剛寫入的資料的偏移量(這同時適用於插入新鍵與更新現有鍵)。當你想查詢一個值時,使用雜湊對映來查詢資料檔案中的偏移量,**尋找seek** 該位置並讀取該值。
假設我們的資料儲存只是一個追加寫入的檔案,就像前面的例子一樣那麼最簡單的索引策略就是:保留一個記憶體中的雜湊對映,其中每個鍵都對映到資料檔案中的一個位元組偏移量,指明瞭可以找到對應值的位置,如[圖3-1](../img/fig3-1.png)所示。當你將新的鍵值對追加寫入檔案中時,還要更新雜湊對映,以反映剛剛寫入的資料的偏移量(這同時適用於插入新鍵與更新現有鍵)。當你想查詢一個值時,使用雜湊對映來查詢資料檔案中的偏移量,**尋找seek** 該位置並讀取該值即可
![](../img/fig3-1.png)
**圖3-1 以類CSV格式儲存鍵值對的日誌並使用記憶體雜湊對映進行索引。**
聽上去簡單但這是一個可行的方法。現實中Bitcask實際上就是這麼做的Riak中預設的儲存引擎【3】。 Bitcask提供高效能的讀取和寫入操作但所有鍵必須能放入可用記憶體中因為雜湊對映完全保留在記憶體中。這些值可以使用比可用記憶體更多的空間,因為可以從磁碟上透過一次`seek`載入所需部分如果資料檔案的那部分已經在檔案系統快取中則讀取根本不需要任何磁碟I/O。
聽上去簡單但這是一個可行的方法。現實中Bitcask實際上就是這麼做的Riak中預設的儲存引擎【3】。 Bitcask提供高效能的讀取和寫入操作要求所有鍵必須能放入可用記憶體中,因為雜湊對映完全保留在記憶體中。而資料值可以使用比可用記憶體更多的空間,因為可以在磁碟上透過一次`seek`操作來載入所需部分如果資料檔案的那部分已經在檔案系統快取中則讀取根本不需要任何磁碟I/O。
像Bitcask這樣的儲存引擎非常適合每個鍵的值經常更新的情況。例如鍵可能是影片的URL值可能是它播放的次數(每次有人點選播放按鈕時遞增)。在這種型別的工作負載中,有很多寫操作,但是沒有太多不同的鍵——每個鍵有很多的寫操作,但是將所有鍵儲存在記憶體中是可行的。
像Bitcask這樣的儲存引擎非常適合每個鍵的值經常更新的情況。例如鍵可能是某個貓咪影片的網址URL而值可能是該影片被播放的次數(每次有人點選播放按鈕時遞增)。在這種型別的工作負載中,有很多寫操作,但是沒有太多不同的鍵 —— 每個鍵有很多的寫操作,但是將所有鍵儲存在記憶體中是可行的。
直到現在,我們只是追加寫入一個檔案 —— 所以如何避免最終用完磁碟空間?一種好的解決方案是,將日誌分為特定大小的段,當日志增長到特定尺寸時關閉當前段檔案,並開始寫入一個新的段檔案。然後,我們就可以對這些段進行**壓縮compaction**,如[圖3-2](../img/fig3-2.png)所示。壓縮意味著在日誌中丟棄重複的鍵,只保留每個鍵的最近更新。
直到現在,我們只是追加寫入一個檔案 —— 所以如何避免最終用完磁碟空間?一種好的解決方案是,將日誌分為特定大小的段segment,當日志增長到特定尺寸時關閉當前段檔案,並開始寫入一個新的段檔案。然後,我們就可以對這些段進行**壓縮compaction**,如[圖3-2](../img/fig3-2.png)所示。這裡的壓縮意味著在日誌中丟棄重複的鍵,只保留每個鍵的最近更新。
![](../img/fig3-2.png)
**圖3-2 壓縮鍵值更新日誌(統計貓影片的播放次數),只保留每個鍵的最近值**
**圖3-2 鍵值更新日誌(統計貓影片的播放次數)的壓縮,只保留每個鍵的最近值**
而且,由於壓縮經常會使得段變得很小(假設在一個段內鍵被平均重寫了好幾次),我們也可以在執行壓縮的同時將多個段合併在一起,如[圖3-3](../img/fig3-3.png)所示。段被寫入後永遠不會被修改,所以合併的段被寫入一個新的檔案。凍結段的合併和壓縮可以在後臺執行緒中完成,在進行時,我們仍然可以繼續使用舊的段檔案來正常提供讀寫請求。合併過程完成後,我們將讀取請求轉換為使用新的合併段而不是舊段 —— 然後可以簡單地刪除舊的段檔案
而且,由於壓縮經常會使得段變得很小(假設在一個段內鍵被平均重寫了好幾次),我們也可以在執行壓縮的同時將多個段合併在一起,如[圖3-3](../img/fig3-3.png)所示。段被寫入後永遠不會被修改,所以合併的段被寫入一個新的檔案。凍結段的合併和壓縮可以在後臺執行緒中完成,這個過程進行的同時,我們仍然可以繼續使用舊的段檔案來正常提供讀寫請求。合併過程完成後,我們將讀取請求轉換為使用新合併的段而不是舊的段 —— 然後舊的段檔案就可以簡單地刪除掉了
![](../img/fig3-3.png)
**圖3-3 同時執行壓縮和分段合併**
每個段現在都有自己的記憶體散列表,將鍵對映到檔案偏移量。為了找到一個鍵的值,我們首先檢查最近段的雜湊對映;如果鍵不存在,我們就檢查第二個最近的段,依此類推。合併過程將保持段的數量足夠小,所以查詢過程不需要檢查太多的雜湊對映。
每個段現在都有自己的記憶體散列表,將鍵對映到檔案偏移量。為了找到一個鍵的值,我們首先檢查最近段的雜湊對映;如果鍵不存在,我們就檢查第二個最近的段,依此類推。合併過程將保持段的數量足夠小,所以查詢過程不需要檢查太多的雜湊對映。
要讓這個簡單的想法在實際中能工作會涉及到大量的細節。簡單來說,下面幾點都是實現過程中需要認真考慮的問題:
***檔案格式***
CSV不是日誌的最佳格式。使用二進位制格式更快更簡單,首先以位元組為單位對字串的長度進行編碼,然後使用原始字串(不需要轉義)。
CSV不是日誌的最佳格式。使用二進位制格式更快更簡單:首先以位元組為單位對字串的長度進行編碼,然後是原始的字串(不需要轉義)。
***刪除記錄***
如果要刪除一個鍵及其關聯的值,則必須在資料檔案(有時稱為邏輯刪除)中附加一個特殊的刪除記錄。當日志段被合併時,邏輯刪除告訴合併過程丟棄被刪除鍵的任何以前的值。
如果要刪除一個鍵及其關聯的值,則必須在資料檔案中追加一個特殊的刪除記錄有時稱為邏輯刪除tombstone。當日志段被合併時,邏輯刪除告訴合併過程丟棄被刪除鍵的任何以前的值。
***崩潰恢復***
如果資料庫重新啟動,則記憶體雜湊對映將丟失。原則上,您可以透過從頭到尾讀取整個段檔案並在每次按鍵時注意每個鍵的最近值的偏移量來恢復每個段的雜湊對映。但是,如果段檔案很大,這可能需要很長時間,這將使伺服器重新啟動痛苦。 Bitcask 透過儲存每個段的雜湊對映的快照在磁碟上來加速恢復,可以使雜湊對映更快地載入到記憶體中。
如果資料庫重新啟動,則記憶體雜湊對映將丟失。原則上,你可以透過從頭到尾讀取整個段檔案並記錄下來每個鍵的最近值來恢復每個段的雜湊對映。但是,如果段檔案很大,可能需要很長時間,這會使服務的重啟比較痛苦。 Bitcask 透過將每個段的雜湊對映的快照儲存在磁碟上來加速恢復,可以使雜湊對映更快地載入到記憶體中。
***部分寫入記錄***
資料庫可能隨時崩潰,包括將記錄附加到日誌中途。 Bitcask檔案包含校驗和允許檢測和忽略日誌的這些損壞部分。
資料庫隨時可能崩潰,包括在將記錄追加到日誌的過程中。 Bitcask檔案包含校驗和允許檢測和忽略日誌的這些損壞部分。
***併發控制***
由於寫操作是以嚴格順序的順序附加到日誌中的,所以常見的實現選擇是隻有一個寫入器執行緒。資料檔案段是附加的,或者是不可變的,所以它們可以被多個執行緒同時讀取。
由於寫操作是以嚴格的順序追加到日誌中的,所以常見的實現是隻有一個寫入執行緒。也因為資料檔案段是僅追加的或者說是不可變的,所以它們可以被多個執行緒同時讀取。
乍一看,只有追加日誌看起來很浪費:為什麼不更新檔案,用新值覆蓋舊值?但是隻能追加設計的原因有幾個
乍一看,僅追加日誌似乎很浪費:為什麼不直接在檔案裡更新,用新值覆蓋舊值?僅追加的設計之所以是個好的設計,有如下幾個原因
* 追加和分段合併是順序寫入操作,通常比隨機寫入快得多,尤其是在磁碟旋轉硬碟上。在某種程度上,順序寫入在基於快閃記憶體的 **固態硬碟SSD** 上也是優選的【4】。我們將在“[比較B樹和LSM樹](#比較B樹和LSM樹)”中進一步討論這個問題。
* 如果段檔案是附加的或不可變的,併發和崩潰恢復就簡單多了。例如,您不必擔心在覆蓋值時發生崩潰的情況,而將包含舊值和新值的一部分的檔案保留在一起
* 合併舊段可以避免資料檔案隨著時間的推移而分散的問題。
* 追加和分段合併是順序寫入操作,通常比隨機寫入快得多,尤其是在磁性機械硬碟上。在某種程度上,順序寫入在基於快閃記憶體的**固態硬碟SSD** 上也是好的選擇【4】。我們將在“[比較B樹和LSM樹](#比較B樹和LSM樹)”中進一步討論這個問題。
* 如果段檔案是僅追加的或不可變的,併發和崩潰恢復就簡單多了。例如,當一個數據值被更新的時候發生崩潰,你不用擔心檔案裡將會同時包含舊值和新值各自的一部分
* 合併舊段的處理也可以避免資料檔案隨著時間的推移而碎片化的問題。
但是,雜湊表索引也有侷限性:
但是,散列表索引也有其侷限性:
* 散列表必須能放進記憶體
如果你有非常多的鍵那真是倒黴。原則上可以在磁碟上保留一個雜湊對映不幸的是磁碟雜湊對映很難表現優秀。它需要大量的隨機訪問I/O當它變滿時增長是很昂貴的並且雜湊衝突需要很多的邏輯【5】。
* 範圍查詢效率不高。例如您無法輕鬆掃描kitty00000和kitty99999之間的所有鍵——您必須在雜湊對映中單獨查詢每個鍵。
在下一節中,我們將看看一個沒有這些限制的索引結構。
* 散列表必須能放進記憶體。如果你有非常多的鍵那真是倒黴。原則上可以在磁碟上維護一個雜湊對映不幸的是磁碟雜湊對映很難表現優秀。它需要大量的隨機訪問I/O當它用滿時想要再增長是很昂貴的並且雜湊衝突的處理也需要很煩瑣的邏輯【5】。
* 範圍查詢效率不高。例如你無法輕鬆掃描kitty00000和kitty99999之間的所有鍵——你必須在雜湊對映中單獨查詢每個鍵。
在下一節中,我們將看到一個沒有這些限制的索引結構。
### SSTables和LSM樹
@ -155,7 +150,7 @@ CSV不是日誌的最佳格式。使用二進位制格式更快更簡單
我們把這個格式稱為**排序字串表Sorted String Table**簡稱SSTable。我們還要求每個鍵只在每個合併的段檔案中出現一次壓縮過程已經保證。與使用雜湊索引的日誌段相比SSTable有幾個很大的優勢
1. 合併段是簡單而高效的,即使檔案大於可用記憶體。這種方法就像歸併排序演算法中使用的方法一樣,如[圖3-4](../img/fig3-4.png)所示:開始並排讀取輸入檔案,檢視每個檔案中的第一個鍵,複製最低鍵(根據排序順序)到輸出檔案,並重復。這產生一個新的合併段檔案,也按鍵排序。
1. 合併段是簡單而高效的,即使檔案大於可用記憶體。這種方法就像歸併排序演算法中使用的方法一樣,如[圖3-4](../img/fig3-4.png)所示:開始並排讀取輸入檔案,檢視每個檔案中的第一個鍵,複製最低鍵(根據排序順序)到輸出檔案,並重復。這產生一個新的合併段檔案,也按鍵排序。
![](../img/fig3-4.png)
@ -163,13 +158,13 @@ CSV不是日誌的最佳格式。使用二進位制格式更快更簡單
如果在幾個輸入段中出現相同的鍵,該怎麼辦?請記住,每個段都包含在一段時間內寫入資料庫的所有值。這意味著一個輸入段中的所有值必須比另一個段中的所有值更新(假設我們總是合併相鄰的段)。當多個段包含相同的鍵時,我們可以保留最近段的值,並丟棄舊段中的值。
2. 為了在檔案中找到一個特定的鍵,你不再需要儲存記憶體中所有鍵的索引。以[圖3-5](../img/fig3-5.png)為例:假設你正在記憶體中尋找鍵 `handiwork`,但是你不知道段檔案中該關鍵字的確切偏移量。然而,你知道 `handbag``handsome` 的偏移,而且由於排序特性,你知道 `handiwork` 必須出現在這兩者之間。這意味著您可以跳到 `handbag` 的偏移位置並從那裡掃描,直到您找到 `handiwork`(或沒找到,如果該檔案中沒有該鍵)。
2. 為了在檔案中找到一個特定的鍵,你不再需要儲存記憶體中所有鍵的索引。以[圖3-5](../img/fig3-5.png)為例:假設你正在記憶體中尋找鍵 `handiwork`,但是你不知道段檔案中該關鍵字的確切偏移量。然而,你知道 `handbag``handsome` 的偏移,而且由於排序特性,你知道 `handiwork` 必須出現在這兩者之間。這意味著你可以跳到 `handbag` 的偏移位置並從那裡掃描,直到你找到 `handiwork`(或沒找到,如果該檔案中沒有該鍵)。
![](../img/fig3-5.png)
**圖3-5 具有記憶體索引的SSTable**
您仍然需要一個記憶體中索引來告訴您一些鍵的偏移量,但它可能很稀疏:每幾千位元組的段檔案就有一個鍵就足夠了,因為幾千位元組可以很快被掃描[^i]。
你仍然需要一個記憶體中索引來告訴你一些鍵的偏移量,但它可能很稀疏:每幾千位元組的段檔案就有一個鍵就足夠了,因為幾千位元組可以很快被掃描[^i]。
3. 由於讀取請求無論如何都需要掃描所請求範圍內的多個鍵值對,因此可以將這些記錄分組到塊中,並在將其寫入磁碟之前對其進行壓縮(如[圖3-5](../img/fig3-5.png)中的陰影區域所示) 。稀疏記憶體中索引的每個條目都指向壓縮塊的開始處。除了節省磁碟空間之外壓縮還可以減少IO頻寬的使用。
@ -181,7 +176,7 @@ CSV不是日誌的最佳格式。使用二進位制格式更快更簡單
到目前為止,但是如何讓你的資料首先被按鍵排序呢?我們的傳入寫入可以以任何順序發生。
在磁碟上維護有序結構是可能的(請參閱“[B樹](#B樹)”但在記憶體儲存則要容易得多。有許多可以使用的眾所周知的樹形資料結構例如紅黑樹或AVL樹【2】。使用這些資料結構可以按任何順序插入鍵,並按排序順序讀取它們。
在磁碟上維護有序結構是可能的(請參閱“[B樹](#B樹)”但在記憶體儲存則要容易得多。有許多可以使用的眾所周知的樹形資料結構例如紅黑樹或AVL樹【2】。使用這些資料結構可以按任何順序插入鍵,並按排序順序讀取它們。
現在我們可以使我們的儲存引擎工作如下:
@ -202,7 +197,7 @@ Lucene是Elasticsearch和Solr使用的一種全文搜尋的索引引擎它使
#### 效能最佳化
與往常一樣大量的細節使得儲存引擎在實踐中表現良好。例如當查詢資料庫中不存在的鍵時LSM樹演算法可能會很慢必須檢查記憶體表然後將這些段一直回到最老的可能必須從磁碟讀取每一個然後才能確定鍵不存在。為了最佳化這種訪問儲存引擎通常使用額外的Bloom過濾器【15】。 (布隆過濾器是用於近似集合內容的記憶體高效資料結構,它可以告訴資料庫中是否出現鍵,從而為不存在的鍵節省許多不必要的磁碟讀取操作。)
與往常一樣大量的細節使得儲存引擎在實踐中表現良好。例如當查詢資料庫中不存在的鍵時LSM樹演算法可能會很慢必須檢查記憶體表然後將這些段一直回到最老的可能必須從磁碟讀取每一個然後才能確定鍵不存在。為了最佳化這種訪問儲存引擎通常使用額外的Bloom過濾器【15】。 (布隆過濾器是用於近似集合內容的記憶體高效資料結構,它可以告訴資料庫中是否出現鍵,從而為不存在的鍵節省許多不必要的磁碟讀取操作。)
還有不同的策略來確定SSTables如何被壓縮和合並的順序和時間。最常見的選擇是size-tiered和leveled compaction。LevelDB和RocksDB使用leveled compactionLevelDB因此得名HBase使用size-tieredCassandra同時支援【16】。對於sized-tiered較新和較小的SSTables相繼被合併到較舊的和較大的SSTable中。對於leveled compactionkey範圍被拆分到較小的SSTables而較舊的資料被移動到單獨的層級level這使得壓縮compaction能夠更加增量地進行並且使用較少的磁碟空間。
@ -246,7 +241,7 @@ Lucene是Elasticsearch和Solr使用的一種全文搜尋的索引引擎它使
B樹的基本底層寫操作是用新資料覆蓋磁碟上的頁面。假定覆蓋不改變頁面的位置當頁面被覆蓋時對該頁面的所有引用保持完整。這與日誌結構索引如LSM樹形成鮮明對比後者只附加到檔案並最終刪除過時的檔案但從不修改檔案。
可以考慮將硬碟上的頁面覆蓋為實際的硬體操作。在磁性硬碟驅動器上這意味著將磁頭移動到正確的位置等待旋轉盤上的正確位置出現然後用新的資料覆蓋適當的扇區。在固態硬碟上由於SSD必須一次擦除和重寫相當大的儲存晶片塊所以會發生更復雜的事情【19】。
可以考慮將硬碟上的頁面覆蓋為實際的硬體操作。在磁性硬碟驅動器上這意味著將磁頭移動到正確的位置等待旋轉盤上的正確位置出現然後用新的資料覆蓋適當的扇區。在固態硬碟上由於SSD必須一次擦除和重寫相當大的儲存晶片塊所以會發生更復雜的事情【19】。
而且,一些操作需要覆蓋幾個不同的頁面。例如,如果因為插入導致頁面過滿而拆分頁面,則需要編寫已拆分的兩個頁面,並覆蓋其父頁面以更新對兩個子頁面的引用。這是一個危險的操作,因為如果資料庫在僅有一些頁面被寫入後崩潰,那麼最終將導致一個損壞的索引(例如,可能有一個孤兒頁面不是任何父項的子項) 。
@ -268,7 +263,7 @@ B樹的基本底層寫操作是用新資料覆蓋磁碟上的頁面。假定覆
儘管B樹實現通常比LSM樹實現更成熟但LSM樹由於其效能特點也非常有趣。根據經驗通常LSM樹的寫入速度更快而B樹的讀取速度更快【23】。 LSM樹上的讀取通常比較慢因為它們必須檢查幾種不同的資料結構和不同壓縮Compaction層級的SSTables。
然而,基準測試通常對工作負載的細節不確定且敏感。 需要測試具有特定工作負載的系統,以便進行有效的比較。在本節中,我們將簡要討論一些在衡量儲存引擎效能時值得考慮的事情。
然而,基準測試通常對工作負載的細節不確定且敏感。 需要測試具有特定工作負載的系統,以便進行有效的比較。在本節中,我們將簡要討論一些在衡量儲存引擎效能時值得考慮的事情。
#### LSM樹的優點
@ -290,7 +285,7 @@ LSM樹可以被壓縮得更好因此經常比B樹在磁碟上產生更小的
壓縮的另一個問題出現在高寫入吞吐量:磁碟的有限寫入頻寬需要在初始寫入(記錄和重新整理記憶體表到磁碟)和在後臺執行的壓縮執行緒之間共享。寫入空資料庫時,可以使用全磁碟頻寬進行初始寫入,但資料庫越大,壓縮所需的磁碟頻寬就越多。
如果寫入吞吐量很高並且壓縮沒有仔細配置壓縮跟不上寫入速率。在這種情況下磁碟上未合併段的數量不斷增加直到磁碟空間用完讀取速度也會減慢因為它們需要檢查更多段檔案。通常情況下即使壓縮無法跟上基於SSTable的儲存引擎也不會限制傳入寫入的速率所以需要進行明確的監控來檢測這種情況【29,30】。
如果寫入吞吐量很高並且壓縮沒有仔細配置壓縮跟不上寫入速率。在這種情況下磁碟上未合併段的數量不斷增加直到磁碟空間用完讀取速度也會減慢因為它們需要檢查更多段檔案。通常情況下即使壓縮無法跟上基於SSTable的儲存引擎也不會限制傳入寫入的速率所以需要進行明確的監控來檢測這種情況【29,30】。
B樹的一個優點是每個鍵只存在於索引中的一個位置而日誌結構化的儲存引擎可能在不同的段中有相同鍵的多個副本。這個方面使得B樹在想要提供強大的事務語義的資料庫中很有吸引力在許多關係資料庫中事務隔離是透過在鍵範圍上使用鎖來實現的在B樹索引中這些鎖可以直接連線到樹【5】。在[第七章](ch7.md)中,我們將更詳細地討論這一點。
@ -300,7 +295,7 @@ B樹在資料庫體系結構中是非常根深蒂固的為許多工作負載
到目前為止,我們只討論了鍵值索引,它們就像關係模型中的**主鍵primary key** 索引。主鍵唯一標識關係表中的一行或文件資料庫中的一個文件或圖形資料庫中的一個頂點。資料庫中的其他記錄可以透過其主鍵或ID引用該行/文件/頂點,並且索引用於解析這樣的引用。
有次級索引也很常見。在關係資料庫中,可以使用 `CREATE INDEX` 命令在同一個表上建立多個次級索引,而且這些索引通常對於有效地執行聯接而言至關重要。例如,在[第二章](ch2.md)中的[圖2-1](../img/fig2-1.png)中,很可能在 `user_id` 列上有一個次級索引,以便可以在每個表中找到屬於同一使用者的所有行。
有次級索引也很常見。在關係資料庫中,可以使用 `CREATE INDEX` 命令在同一個表上建立多個次級索引,而且這些索引通常對於有效地執行聯接而言至關重要。例如,在[第二章](ch2.md)中的[圖2-1](../img/fig2-1.png)中,很可能在 `user_id` 列上有一個次級索引,以便可以在每個表中找到屬於同一使用者的所有行。
一個次級索引可以很容易地從一個鍵值索引構建。主要的不同是鍵不是唯一的。即可能有許多行文件頂點具有相同的鍵。這可以透過兩種方式來解決或者透過使索引中的每個值成為匹配行識別符號的列表如全文索引中的釋出列表或者透過向每個索引新增行識別符號來使每個關鍵字唯一。無論哪種方式B樹和日誌結構索引都可以用作次級索引。
@ -337,7 +332,7 @@ SELECT * FROM restaurants WHERE latitude > 51.4946 AND latitude < 51.5079
#### 全文搜尋和模糊索引
到目前為止所討論的所有索引都假定您有確切的資料,並允許您查詢鍵的確切值或具有排序順序的鍵的值範圍。他們不允許你做的是搜尋類似的鍵,如拼寫錯誤的單詞。這種模糊的查詢需要不同的技術。
到目前為止所討論的所有索引都假定你有確切的資料,並允許你查詢鍵的確切值或具有排序順序的鍵的值範圍。他們不允許你做的是搜尋類似的鍵,如拼寫錯誤的單詞。這種模糊的查詢需要不同的技術。
例如全文搜尋引擎通常允許搜尋一個單詞以擴充套件為包括該單詞的同義詞忽略單詞的語法變體並且搜尋在相同文件中彼此靠近的單詞的出現並且支援各種其他功能取決於文字的語言分析。為了處理文件或查詢中的拼寫錯誤Lucene能夠在一定的編輯距離內搜尋文字編輯距離1意味著新增刪除或替換了一個字母【37】。
@ -375,7 +370,7 @@ SELECT * FROM restaurants WHERE latitude > 51.4946 AND latitude < 51.5079
即使資料庫開始被用於許多不同型別的資料,比如部落格文章的評論,遊戲中的動作,地址簿中的聯絡人等等,基本訪問模式仍然類似於處理商業交易。應用程式通常使用索引透過某個鍵查詢少量記錄。根據使用者的輸入插入或更新記錄。由於這些應用程式是互動式的,這種訪問模式被稱為 **線上事務處理OLTP, OnLine Transaction Processing**
但是,資料庫也開始越來越多地用於資料分析,這些資料分析具有非常不同的訪問模式。通常,分析查詢需要掃描大量記錄,每個記錄只讀取幾列,並計算彙總統計資訊(如計數,總和或平均值),而不是將原始資料返回給使用者。例如,如果的資料是一個銷售交易表,那麼分析查詢可能是:
但是,資料庫也開始越來越多地用於資料分析,這些資料分析具有非常不同的訪問模式。通常,分析查詢需要掃描大量記錄,每個記錄只讀取幾列,並計算彙總統計資訊(如計數,總和或平均值),而不是將原始資料返回給使用者。例如,如果的資料是一個銷售交易表,那麼分析查詢可能是:
* 一月份每個商店的總收入是多少?
* 在最近的推廣活動中多賣了多少香蕉?
@ -474,7 +469,7 @@ GROUP BY
在大多數OLTP資料庫中儲存都是以面向行的方式進行佈局的表格的一行中的所有值都相鄰儲存。文件資料庫是相似的整個文件通常儲存為一個連續的位元組序列。你可以在[圖3-1](../img/fig3-1.png)的CSV例子中看到這個。
為了處理像[例3-1]()這樣的查詢,可能在 `fact_sales.date_key` `fact_sales.product_sk`上有索引它們告訴儲存引擎在哪裡查詢特定日期或特定產品的所有銷售情況。但是面向行的儲存引擎仍然需要將所有這些行每個包含超過100個屬性從磁碟載入到記憶體中解析它們並過濾掉那些不符合要求的屬性。這可能需要很長時間。
為了處理像[例3-1]()這樣的查詢,可能在 `fact_sales.date_key` `fact_sales.product_sk`上有索引它們告訴儲存引擎在哪裡查詢特定日期或特定產品的所有銷售情況。但是面向行的儲存引擎仍然需要將所有這些行每個包含超過100個屬性從磁碟載入到記憶體中解析它們並過濾掉那些不符合要求的屬性。這可能需要很長時間。
面向列的儲存背後的想法很簡單:不要將所有來自一行的值儲存在一起,而是將來自每一列的所有值儲存在一起。如果每個列儲存在一個單獨的檔案中,查詢只需要讀取和解析查詢中使用的那些列,這可以節省大量的工作。這個原理如[圖3-10](../img/fig3-10.png)所示。
@ -484,7 +479,7 @@ GROUP BY
列儲存在關係資料模型中是最容易理解的但它同樣適用於非關係資料。例如Parquet 【57】是一種列式儲存格式支援基於Google的Dremel 【54】的文件資料模型。
面向列的儲存佈局依賴於每個列檔案包含相同順序的行。 因此,如果您需要重新組裝整行,您可以從每個單獨的列檔案中獲取第23項並將它們放在一起形成表的第23行。
面向列的儲存佈局依賴於每個列檔案包含相同順序的行。 因此,如果你需要重新組裝整行,你可以從每個單獨的列檔案中獲取第23項並將它們放在一起形成表的第23行。
@ -547,7 +542,7 @@ WHERE product_sk = 31 AND store_sk = 3
#### 幾個不同的排序順序
這個想法的巧妙擴充套件在C-Store中引入並在商業資料倉庫Vertica【61,62】中被採用。不同的查詢受益於不同的排序順序為什麼不以幾種不同的方式來儲存相同的資料呢無論如何資料需要複製到多臺機器這樣如果一臺機器發生故障您不會丟失資料。您可能還需要儲存以不同方式排序的冗餘資料,以便在處理查詢時,可以使用最適合查詢模式的版本。
這個想法的巧妙擴充套件在C-Store中引入並在商業資料倉庫Vertica【61,62】中被採用。不同的查詢受益於不同的排序順序為什麼不以幾種不同的方式來儲存相同的資料呢無論如何資料需要複製到多臺機器這樣如果一臺機器發生故障你不會丟失資料。你可能還需要儲存以不同方式排序的冗餘資料,以便在處理查詢時,可以使用最適合查詢模式的版本。
在一個面向列的儲存中有多個排序順序有點類似於在一個面向行的儲存中有多個次級索引。但最大的區別在於面向行的儲存將每一行儲存在一個地方(在堆檔案或聚簇索引中),次級索引只包含指向匹配行的指標。在列儲存中,通常在其他地方沒有任何指向資料的指標,只有包含值的列。
@ -577,11 +572,11 @@ WHERE product_sk = 31 AND store_sk = 3
**圖3-12 資料立方的兩個維度,透過求和聚合**
想象一下,現在每個事實都只有兩個維度表的外來鍵——在[圖3-12](../img/fig-3-12.png)中,這些是日期和產品。現在可以繪製一個二維表格,一個軸線上是日期,另一個軸線上是產品。每個單元包含具有該日期 - 產品組合的所有事實的屬性(例如,`net_price`)的聚集(例如,`SUM`)。然後,可以沿著每行或每列應用相同的彙總,並獲得一個維度減少的彙總(按產品的銷售額,無論日期,還是按日期銷售,無論產品如何)。
想象一下,現在每個事實都只有兩個維度表的外來鍵——在[圖3-12](../img/fig-3-12.png)中,這些是日期和產品。現在可以繪製一個二維表格,一個軸線上是日期,另一個軸線上是產品。每個單元包含具有該日期 - 產品組合的所有事實的屬性(例如,`net_price`)的聚集(例如,`SUM`)。然後,可以沿著每行或每列應用相同的彙總,並獲得一個維度減少的彙總(按產品的銷售額,無論日期,還是按日期銷售,無論產品如何)。
一般來說事實往往有兩個以上的維度。在圖3-9中有五個維度日期產品商店促銷和客戶。要想象一個五維超立方體是什麼樣子是很困難的但是原理是一樣的每個單元格都包含特定日期-產品-商店-促銷-客戶組合的銷售。這些值可以在每個維度上重複概括。
物化資料立方體的優點是某些查詢變得非常快,因為它們已經被有效地預先計算了。例如,如果想知道每個商店的總銷售額,則只需檢視合適維度的總計,無需掃描數百萬行。
物化資料立方體的優點是某些查詢變得非常快,因為它們已經被有效地預先計算了。例如,如果想知道每個商店的總銷售額,則只需檢視合適維度的總計,無需掃描數百萬行。
缺點是資料立方體不具有查詢原始資料的靈活性。例如沒有辦法計算哪個銷售比例來自成本超過100美元的專案因為價格不是其中的一個維度。因此大多數資料倉庫試圖保留儘可能多的原始資料並將聚合資料如資料立方體僅用作某些查詢的效能提升。
@ -608,9 +603,9 @@ WHERE product_sk = 31 AND store_sk = 3
日誌結構的儲存引擎是相對較新的發展。他們的主要想法是他們系統地將隨機訪問寫入轉換為磁碟上的順序寫入由於硬碟驅動器和固態硬碟的效能特點可以實現更高的寫入吞吐量。在完成OLTP方面我們透過一些更復雜的索引結構和為保留所有資料而最佳化的資料庫做了一個簡短的介紹。
然後我們從儲存引擎的內部繞開看看典型資料倉庫的高階架構。這一背景說明了為什麼分析工作負載與OLTP差別很大的查詢需要在大量行中順序掃描時,索引的相關性就會降低很多。相反,非常緊湊地編碼資料變得非常重要,以最大限度地減少查詢需要從磁碟讀取的資料量。我們討論了列式儲存如何幫助實現這一目標。
然後我們從儲存引擎的內部繞開看看典型資料倉庫的高階架構。這一背景說明了為什麼分析工作負載與OLTP差別很大的查詢需要在大量行中順序掃描時,索引的相關性就會降低很多。相反,非常緊湊地編碼資料變得非常重要,以最大限度地減少查詢需要從磁碟讀取的資料量。我們討論了列式儲存如何幫助實現這一目標。
作為一名應用程式開發人員,如果您掌握了有關儲存引擎內部的知識,那麼您就能更好地瞭解哪種工具最適合您的特定應用程式。如果您需要調整資料庫的調整引數,這種理解可以讓您設想一個更高或更低的值可能會產生什麼效果。
作為一名應用程式開發人員,如果你掌握了有關儲存引擎內部的知識,那麼你就能更好地瞭解哪種工具最適合你的特定應用程式。如果你需要調整資料庫的調整引數,這種理解可以讓你設想一個更高或更低的值可能會產生什麼效果。
儘管本章不能讓你成為一個特定儲存引擎的調參專家,但它至少有大概率使你有了足夠的概念與詞彙儲備去讀懂資料庫的文件,從而選擇合適的資料庫。
@ -620,142 +615,69 @@ WHERE product_sk = 31 AND store_sk = 3
## 參考文獻
1. Alfred V. Aho, John E. Hopcroft, and Jeffrey D. Ullman: *Data Structures and Algorithms*. Addison-Wesley, 1983. ISBN: 978-0-201-00023-8
1. Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, and Clifford Stein: *Introduction to Algorithms*, 3rd edition. MIT Press, 2009. ISBN: 978-0-262-53305-8
1. Justin Sheehy and David Smith: “[Bitcask: A Log-Structured Hash Table for Fast Key/Value Data](http://basho.com/wp-content/uploads/2015/05/bitcask-intro.pdf),” Basho Technologies, April 2010.
1. Yinan Li, Bingsheng He, Robin Jun Yang, et al.: “[Tree Indexing on Solid State Drives](http://www.vldb.org/pvldb/vldb2010/papers/R106.pdf),” *Proceedings of the VLDB Endowment*, volume 3, number 1, pages 11951206, September 2010.
1. Goetz Graefe: “[Modern B-Tree Techniques](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.219.7269&rep=rep1&type=pdf),” *Foundations and Trends in Databases*, volume 3, number 4, pages 203402, August 2011. [doi:10.1561/1900000028](http://dx.doi.org/10.1561/1900000028)
1. Jeffrey Dean and Sanjay Ghemawat: “[LevelDB Implementation Notes](https://github.com/google/leveldb/blob/master/doc/impl.html),” *leveldb.googlecode.com*.
1. Dhruba Borthakur: “[The History of RocksDB](http://rocksdb.blogspot.com/),” *rocksdb.blogspot.com*, November 24, 2013.
1. Matteo Bertozzi: “[Apache HBase I/O HFile](http://blog.cloudera.com/blog/2012/06/hbase-io-hfile-input-output/),” *blog.cloudera.com*, June, 29 2012.
1. Fay Chang, Jeffrey Dean, Sanjay Ghemawat, et al.: “[Bigtable: A Distributed Storage System for Structured Data](http://research.google.com/archive/bigtable.html),” at *7th USENIX Symposium on Operating System Design and Implementation* (OSDI), November 2006.
1. Patrick O'Neil, Edward Cheng, Dieter Gawlick, and Elizabeth O'Neil: “[The Log-Structured Merge-Tree (LSM-Tree)](http://www.cs.umb.edu/~poneil/lsmtree.pdf),” *Acta Informatica*, volume 33, number 4, pages 351385, June 1996. [doi:10.1007/s002360050048](http://dx.doi.org/10.1007/s002360050048)
1. Mendel Rosenblum and John K. Ousterhout: “[The Design and Implementation of a Log-Structured File System](http://research.cs.wisc.edu/areas/os/Qual/papers/lfs.pdf),” *ACM Transactions on Computer Systems*, volume 10, number 1, pages 2652, February 1992.
[doi:10.1145/146941.146943](http://dx.doi.org/10.1145/146941.146943)
1. Mendel Rosenblum and John K. Ousterhout: “[The Design and Implementation of a Log-Structured File System](http://research.cs.wisc.edu/areas/os/Qual/papers/lfs.pdf),” *ACM Transactions on Computer Systems*, volume 10, number 1, pages 2652, February 1992. [doi:10.1145/146941.146943](http://dx.doi.org/10.1145/146941.146943)
1. Adrien Grand: “[What Is in a Lucene Index?](http://www.slideshare.net/lucenerevolution/what-is-inaluceneagrandfinal),” at *Lucene/Solr Revolution*, November 14, 2013.
1. Deepak Kandepet: “[Hacking Lucene—The Index Format]( http://hackerlabs.github.io/blog/2011/10/01/hacking-lucene-the-index-format/index.html),” *hackerlabs.org*, October 1, 2011.
1. Michael McCandless: “[Visualizing Lucene's Segment Merges](http://blog.mikemccandless.com/2011/02/visualizing-lucenes-segment-merges.html),” *blog.mikemccandless.com*, February 11, 2011.
1. Burton H. Bloom: “[Space/Time Trade-offs in Hash Coding with Allowable Errors](http://www.cs.upc.edu/~diaz/p422-bloom.pdf),” *Communications of the ACM*, volume 13, number 7, pages 422426, July 1970. [doi:10.1145/362686.362692](http://dx.doi.org/10.1145/362686.362692)
1. “[Operating Cassandra: Compaction](https://cassandra.apache.org/doc/latest/operating/compaction.html),” Apache Cassandra Documentation v4.0, 2016.
1. Rudolf Bayer and Edward M. McCreight: “[Organization and Maintenance of Large Ordered Indices](http://www.dtic.mil/cgi-bin/GetTRDoc?AD=AD0712079),” Boeing Scientific Research Laboratories, Mathematical and Information Sciences Laboratory, report no. 20, July 1970.
1. Douglas Comer: “[The Ubiquitous B-Tree](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.96.6637&rep=rep1&type=pdf),” *ACM Computing Surveys*, volume 11, number 2, pages 121137, June 1979. [doi:10.1145/356770.356776](http://dx.doi.org/10.1145/356770.356776)
1. Emmanuel Goossaert: “[Coding for SSDs](http://codecapsule.com/2014/02/12/coding-for-ssds-part-1-introduction-and-table-of-contents/),” *codecapsule.com*, February 12, 2014.
1. C. Mohan and Frank Levine: “[ARIES/IM: An Efficient and High Concurrency Index Management Method Using Write-Ahead Logging](http://www.ics.uci.edu/~cs223/papers/p371-mohan.pdf),” at *ACM International Conference on Management of Data* (SIGMOD), June 1992. [doi:10.1145/130283.130338](http://dx.doi.org/10.1145/130283.130338)
1. Howard Chu: “[LDAP at Lightning Speed]( https://buildstuff14.sched.com/event/08a1a368e272eb599a52e08b4c3c779d),” at *Build Stuff '14*, November 2014.
1. Bradley C. Kuszmaul: “[A Comparison of Fractal Trees to Log-Structured Merge (LSM) Trees](http://insideanalysis.com/wp-content/uploads/2014/08/Tokutek_lsm-vs-fractal.pdf),” *tokutek.com*, April 22, 2014.
1. Manos Athanassoulis, Michael S. Kester, Lukas M. Maas, et al.: “[Designing Access Methods: The RUM Conjecture](http://openproceedings.org/2016/conf/edbt/paper-12.pdf),” at *19th International Conference on Extending Database Technology* (EDBT), March 2016.
[doi:10.5441/002/edbt.2016.42](http://dx.doi.org/10.5441/002/edbt.2016.42)
1. Manos Athanassoulis, Michael S. Kester, Lukas M. Maas, et al.: “[Designing Access Methods: The RUM Conjecture](http://openproceedings.org/2016/conf/edbt/paper-12.pdf),” at *19th International Conference on Extending Database Technology* (EDBT), March 2016. [doi:10.5441/002/edbt.2016.42](http://dx.doi.org/10.5441/002/edbt.2016.42)
1. Peter Zaitsev: “[Innodb Double Write](https://www.percona.com/blog/2006/08/04/innodb-double-write/),” *percona.com*, August 4, 2006.
1. Tomas Vondra: “[On the Impact of Full-Page Writes](http://blog.2ndquadrant.com/on-the-impact-of-full-page-writes/),” *blog.2ndquadrant.com*, November 23, 2016.
1. Mark Callaghan: “[The Advantages of an LSM vs a B-Tree](http://smalldatum.blogspot.co.uk/2016/01/summary-of-advantages-of-lsm-vs-b-tree.html),” *smalldatum.blogspot.co.uk*, January 19, 2016.
1. Mark Callaghan: “[Choosing Between Efficiency and Performance with RocksDB](http://www.codemesh.io/codemesh/mark-callaghan),” at *Code Mesh*, November 4, 2016.
1. Michi Mutsuzaki: “[MySQL vs. LevelDB](https://github.com/m1ch1/mapkeeper/wiki/MySQL-vs.-LevelDB),” *github.com*, August 2011.
1. Benjamin Coverston, Jonathan Ellis, et al.: “[CASSANDRA-1608: Redesigned Compaction](https://issues.apache.org/jira/browse/CASSANDRA-1608), *issues.apache.org*, July 2011.
1. Igor Canadi, Siying Dong, and Mark Callaghan: “[RocksDB Tuning Guide](https://github.com/facebook/rocksdb/wiki/RocksDB-Tuning-Guide),”
*github.com*, 2016.
1. Igor Canadi, Siying Dong, and Mark Callaghan: “[RocksDB Tuning Guide](https://github.com/facebook/rocksdb/wiki/RocksDB-Tuning-Guide),” *github.com*, 2016.
1. [*MySQL 5.7 Reference Manual*](http://dev.mysql.com/doc/refman/5.7/en/index.html). Oracle, 2014.
1. [*Books Online for SQL Server 2012*](http://msdn.microsoft.com/en-us/library/ms130214.aspx). Microsoft, 2012.
1. Joe Webb: “[Using Covering Indexes to Improve Query Performance](https://www.simple-talk.com/sql/learn-sql-server/using-covering-indexes-to-improve-query-performance/),” *simple-talk.com*, 29 September 2008.
1. Frank Ramsak, Volker Markl, Robert Fenk, et al.: “[Integrating the UB-Tree into a Database System Kernel](http://www.vldb.org/conf/2000/P263.pdf),” at *26th International Conference on Very Large Data Bases* (VLDB), September 2000.
1. The PostGIS Development Group: “[PostGIS 2.1.2dev Manual](http://postgis.net/docs/manual-2.1/),” *postgis.net*, 2014.
1. Robert Escriva, Bernard Wong, and Emin Gün Sirer: “[HyperDex: A Distributed, Searchable Key-Value Store](http://www.cs.princeton.edu/courses/archive/fall13/cos518/papers/hyperdex.pdf),” at *ACM SIGCOMM Conference*, August 2012. [doi:10.1145/2377677.2377681](http://dx.doi.org/10.1145/2377677.2377681)
1. Michael McCandless: “[Lucene's FuzzyQuery Is 100 Times Faster in 4.0](http://blog.mikemccandless.com/2011/03/lucenes-fuzzyquery-is-100-times-faster.html),” *blog.mikemccandless.com*, March 24, 2011.
1. Steffen Heinz, Justin Zobel, and Hugh E. Williams: “[Burst Tries: A Fast, Efficient Data Structure for String Keys](http://citeseer.ist.psu.edu/viewdoc/summary?doi=10.1.1.18.3499),” *ACM Transactions on Information Systems*, volume 20, number 2, pages 192223, April 2002. [doi:10.1145/506309.506312](http://dx.doi.org/10.1145/506309.506312)
1. Klaus U. Schulz and Stoyan Mihov: “[Fast String Correction with Levenshtein Automata](http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.16.652),” *International Journal on Document Analysis and Recognition*, volume 5, number 1, pages 6785, November 2002. [doi:10.1007/s10032-002-0082-8](http://dx.doi.org/10.1007/s10032-002-0082-8)
1. Christopher D. Manning, Prabhakar Raghavan, and Hinrich Schütze: [*Introduction to Information Retrieval*](http://nlp.stanford.edu/IR-book/). Cambridge University Press, 2008. ISBN: 978-0-521-86571-5, available online at *nlp.stanford.edu/IR-book*
1. Michael Stonebraker, Samuel Madden, Daniel J. Abadi, et al.: “[The End of an Architectural Era (Its Time for a Complete Rewrite)](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.137.3697&rep=rep1&type=pdf),” at *33rd International Conference on Very Large Data Bases* (VLDB), September 2007.
1. “[VoltDB Technical Overview White Paper](https://www.voltdb.com/wptechnicaloverview),” VoltDB, 2014.
1. Stephen M. Rumble, Ankita Kejriwal, and John K. Ousterhout: “[Log-Structured Memory for DRAM-Based Storage](https://www.usenix.org/system/files/conference/fast14/fast14-paper_rumble.pdf),” at *12th USENIX Conference on File and Storage Technologies* (FAST), February 2014.
1. Stavros Harizopoulos, Daniel J. Abadi, Samuel Madden, and Michael Stonebraker: “[OLTP Through the Looking Glass, and What We Found There](http://hstore.cs.brown.edu/papers/hstore-lookingglass.pdf),” at *ACM International Conference on Management of Data*
(SIGMOD), June 2008. [doi:10.1145/1376616.1376713](http://dx.doi.org/10.1145/1376616.1376713)
1. Stavros Harizopoulos, Daniel J. Abadi, Samuel Madden, and Michael Stonebraker: “[OLTP Through the Looking Glass, and What We Found There](http://hstore.cs.brown.edu/papers/hstore-lookingglass.pdf),” at *ACM International Conference on Management of Data* (SIGMOD), June 2008. [doi:10.1145/1376616.1376713](http://dx.doi.org/10.1145/1376616.1376713)
1. Justin DeBrabant, Andrew Pavlo, Stephen Tu, et al.: “[Anti-Caching: A New Approach to Database Management System Architecture](http://www.vldb.org/pvldb/vol6/p1942-debrabant.pdf),” *Proceedings of the VLDB Endowment*, volume 6, number 14, pages 19421953, September 2013.
1. Joy Arulraj, Andrew Pavlo, and Subramanya R. Dulloor: “[Let's Talk About Storage & Recovery Methods for Non-Volatile Memory Database Systems](http://www.pdl.cmu.edu/PDL-FTP/NVM/storage.pdf),” at *ACM International Conference on Management of Data* (SIGMOD), June 2015. [doi:10.1145/2723372.2749441](http://dx.doi.org/10.1145/2723372.2749441)
1. Edgar F. Codd, S. B. Codd, and C. T. Salley: “[Providing OLAP to User-Analysts: An IT Mandate](http://www.minet.uni-jena.de/dbis/lehre/ss2005/sem_dwh/lit/Cod93.pdf),” E. F. Codd Associates, 1993.
1. Surajit Chaudhuri and Umeshwar Dayal: “[An Overview of Data Warehousing and OLAP Technology](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/sigrecord.pdf),” *ACM SIGMOD Record*, volume 26, number 1, pages 6574, March 1997. [doi:10.1145/248603.248616](http://dx.doi.org/10.1145/248603.248616)
1. Per-Åke Larson, Cipri Clinciu, Campbell Fraser, et al.: “[Enhancements to SQL Server Column Stores](http://research.microsoft.com/pubs/193599/Apollo3%20-%20Sigmod%202013%20-%20final.pdf),” at *ACM International Conference on Management of Data* (SIGMOD), June 2013.
1. Franz Färber, Norman May, Wolfgang Lehner, et al.: “[The SAP HANA Database An Architecture Overview](http://sites.computer.org/debull/A12mar/hana.pdf),” *IEEE Data Engineering Bulletin*, volume 35, number 1, pages 2833, March 2012.
1. Michael Stonebraker: “[The Traditional RDBMS Wisdom Is (Almost Certainly) All Wrong](http://slideshot.epfl.ch/talks/166),” presentation at *EPFL*, May 2013.
1. Daniel J. Abadi: “[Classifying the SQL-on-Hadoop Solutions](https://web.archive.org/web/20150622074951/http://hadapt.com/blog/2013/10/02/classifying-the-sql-on-hadoop-solutions/),” *hadapt.com*, October 2, 2013.
1. Marcel Kornacker, Alexander Behm, Victor Bittorf, et al.: “[Impala: A Modern, Open-Source SQL Engine for Hadoop](http://pandis.net/resources/cidr15impala.pdf),” at *7th Biennial Conference on Innovative Data Systems Research* (CIDR), January 2015.
1. Sergey Melnik, Andrey Gubarev, Jing Jing Long, et al.: “[Dremel: Interactive Analysis of Web-Scale Datasets](http://research.google.com/pubs/pub36632.html),” at *36th International Conference on Very Large Data Bases* (VLDB), pages
330339, September 2010.
1. Sergey Melnik, Andrey Gubarev, Jing Jing Long, et al.: “[Dremel: Interactive Analysis of Web-Scale Datasets](http://research.google.com/pubs/pub36632.html),” at *36th International Conference on Very Large Data Bases* (VLDB), pages 330339, September 2010.
1. Ralph Kimball and Margy Ross: *The Data Warehouse Toolkit: The Definitive Guide to Dimensional Modeling*, 3rd edition. John Wiley & Sons, July 2013. ISBN: 978-1-118-53080-1
1. Derrick Harris: “[Why Apple, eBay, and Walmart Have Some of the Biggest Data Warehouses Youve Ever Seen](http://gigaom.com/2013/03/27/why-apple-ebay-and-walmart-have-some-of-the-biggest-data-warehouses-youve-ever-seen/),” *gigaom.com*, March 27, 2013.
1. Julien Le Dem: “[Dremel Made Simple with Parquet](https://blog.twitter.com/2013/dremel-made-simple-with-parquet),” *blog.twitter.com*, September 11, 2013.
1. Daniel J. Abadi, Peter Boncz, Stavros Harizopoulos, et al.: “[The Design and Implementation of Modern Column-Oriented Database Systems](http://cs-www.cs.yale.edu/homes/dna/papers/abadi-column-stores.pdf),” *Foundations and Trends in Databases*, volume 5, number 3, pages 197280, December 2013. [doi:10.1561/1900000024](http://dx.doi.org/10.1561/1900000024)
1. Peter Boncz, Marcin Zukowski, and Niels Nes: “[MonetDB/X100: Hyper-Pipelining Query Execution](http://www.cidrdb.org/cidr2005/papers/P19.pdf),”
at *2nd Biennial Conference on Innovative Data Systems Research* (CIDR), January 2005.
1. Jingren Zhou and Kenneth A. Ross: “[Implementing Database Operations Using SIMD Instructions](http://www1.cs.columbia.edu/~kar/pubsk/simd.pdf),”
at *ACM International Conference on Management of Data* (SIGMOD), pages 145156, June 2002.
[doi:10.1145/564691.564709](http://dx.doi.org/10.1145/564691.564709)
1. Michael Stonebraker, Daniel J. Abadi, Adam Batkin, et al.: “[C-Store: A Column-oriented DBMS](http://www.vldb2005.org/program/paper/thu/p553-stonebraker.pdf),”
at *31st International Conference on Very Large Data Bases* (VLDB), pages 553564, September 2005.
1. Peter Boncz, Marcin Zukowski, and Niels Nes: “[MonetDB/X100: Hyper-Pipelining Query Execution](http://www.cidrdb.org/cidr2005/papers/P19.pdf),” at *2nd Biennial Conference on Innovative Data Systems Research* (CIDR), January 2005.
1. Jingren Zhou and Kenneth A. Ross: “[Implementing Database Operations Using SIMD Instructions](http://www1.cs.columbia.edu/~kar/pubsk/simd.pdf),” at *ACM International Conference on Management of Data* (SIGMOD), pages 145156, June 2002. [doi:10.1145/564691.564709](http://dx.doi.org/10.1145/564691.564709)
1. Michael Stonebraker, Daniel J. Abadi, Adam Batkin, et al.: “[C-Store: A Column-oriented DBMS](http://www.vldb2005.org/program/paper/thu/p553-stonebraker.pdf),” at *31st International Conference on Very Large Data Bases* (VLDB), pages 553564, September 2005.
1. Andrew Lamb, Matt Fuller, Ramakrishna Varadarajan, et al.: “[The Vertica Analytic Database: C-Store 7 Years Later](http://vldb.org/pvldb/vol5/p1790_andrewlamb_vldb2012.pdf),” *Proceedings of the VLDB Endowment*, volume 5, number 12, pages 17901801, August 2012.
1. Julien Le Dem and Nong Li: “[Efficient Data Storage for Analytics with Apache Parquet 2.0](http://www.slideshare.net/julienledem/th-210pledem),” at *Hadoop Summit*, San Jose, June 2014.
1. Jim Gray, Surajit Chaudhuri, Adam Bosworth, et al.: “[Data Cube: A Relational Aggregation Operator Generalizing Group-By, Cross-Tab, and Sub-Totals](http://arxiv.org/pdf/cs/0701155.pdf),” *Data Mining and Knowledge Discovery*, volume 1, number 1, pages 2953, March 2007. [doi:10.1023/A:1009726021843](http://dx.doi.org/10.1023/A:1009726021843)