mirror of
https://github.com/Vonng/ddia.git
synced 2024-12-06 15:20:12 +08:00
chapter 3 still in progress
This commit is contained in:
parent
a40ce46024
commit
1ec79062e7
34
ch3.md
34
ch3.md
@ -308,11 +308,11 @@ B树在数据库体系结构中是非常根深蒂固的,为许多工作负载
|
||||
索引中的键是查询搜索的内容,而其值可以是以下两种情况之一:它可以是所讨论的实际行(文档,顶点),也可以是对存储在别处的行的引用。在后一种情况下,行被存储的地方被称为**堆文件(heap file)**,并且存储的数据没有特定的顺序(它可以是仅追加的,或者可以跟踪被删除的行以便用新数据覆盖它们后来)。堆文件方法很常见,因为它避免了在存在多个二级索引时复制数据:每个索引只引用堆文件中的一个位置,实际的数据保存在一个地方。
|
||||
在不更改键的情况下更新值时,堆文件方法可以非常高效:只要新值不大于旧值,就可以覆盖该记录。如果新值更大,情况会更复杂,因为它可能需要移到堆中有足够空间的新位置。在这种情况下,要么所有的索引都需要更新,以指向记录的新堆位置,或者在旧堆位置留下一个转发指针【5】。
|
||||
|
||||
在某些情况下,从索引到堆文件的额外跳跃对读取来说性能损失太大,因此可能希望将索引行直接存储在索引中。这被称为聚集索引。例如,在MySQL的InnoDB存储引擎中,表的主键总是一个聚簇索引,二级索引用主键(而不是堆文件中的位置)【31】。在SQL Server中,可以为每个表指定一个聚簇索引【32】。
|
||||
在某些情况下,从索引到堆文件的额外跳跃对读取来说性能损失太大,因此可能希望将索引行直接存储在索引中。这被称为聚集索引。例如,在MySQL的InnoDB存储引擎中,表的主键总是一个聚集索引,二级索引则引用主键(而不是堆文件中的位置)【31】。在SQL Server中,可以为每个表指定一个聚集索引【32】。
|
||||
|
||||
在 **聚集索引(clustered index)** (在索引中存储所有行数据)和 **非聚集索引(nonclustered index)** (仅在索引中存储对数据的引用)之间的折衷被称为 **包含列的索引(index with included columns)**或**覆盖索引(covering index)**,其存储表的一部分在索引内【33】。这允许通过单独使用索引来回答一些查询(这种情况叫做:索引 **覆盖(cover)** 了查询)【32】。
|
||||
|
||||
与任何类型的数据重复一样,聚簇和覆盖索引可以加快读取速度,但是它们需要额外的存储空间,并且会增加写入开销。数据库还需要额外的努力来执行事务保证,因为应用程序不应该因为重复而导致不一致。
|
||||
与任何类型的数据重复一样,聚集和覆盖索引可以加快读取速度,但是它们需要额外的存储空间,并且会增加写入开销。数据库还需要额外的努力来执行事务保证,因为应用程序不应看到任何因为重复而导致的不一致。
|
||||
|
||||
#### 多列索引
|
||||
|
||||
@ -339,23 +339,23 @@ SELECT * FROM restaurants WHERE latitude > 51.4946 AND latitude < 51.5079
|
||||
|
||||
例如,全文搜索引擎通常允许搜索一个单词以扩展为包括该单词的同义词,忽略单词的语法变体,并且搜索在相同文档中彼此靠近的单词的出现,并且支持各种其他功能取决于文本的语言分析。为了处理文档或查询中的拼写错误,Lucene能够在一定的编辑距离内搜索文本(编辑距离1意味着添加,删除或替换了一个字母)【37】。
|
||||
|
||||
正如“[在SSTables中创建LSM树](#在SSTables中创建LSM树)”中所提到的,Lucene为其词典使用了一个类似于SSTable的结构。这个结构需要一个小的内存索引,告诉查询在排序文件中哪个偏移量需要查找关键字。在LevelDB中,这个内存中的索引是一些键的稀疏集合,但在Lucene中,内存中的索引是键中字符的有限状态自动机,类似于trie 【38】。这个自动机可以转换成Levenshtein自动机,它支持在给定的编辑距离内有效地搜索单词【39】。
|
||||
正如“[在SSTables中创建LSM树](#在SSTables中创建LSM树)”中所提到的,Lucene为其词典使用了一个类似于SSTable的结构。这个结构需要一个小的内存索引,告诉查询需要在排序文件中哪个偏移量查找键。在LevelDB中,这个内存中的索引是一些键的稀疏集合,但在Lucene中,内存中的索引是键中字符的有限状态自动机,类似于trie 【38】。这个自动机可以转换成Levenshtein自动机,它支持在给定的编辑距离内有效地搜索单词【39】。
|
||||
|
||||
其他的模糊搜索技术正朝着文档分类和机器学习的方向发展。有关更多详细信息,请参阅信息检索教科书,例如【40】。
|
||||
|
||||
#### 在内存中存储一切
|
||||
|
||||
本章到目前为止讨论的数据结构都是对磁盘限制的回答。与主内存相比,磁盘处理起来很尴尬。对于磁盘和SSD,如果要在读取和写入时获得良好性能,则需要仔细地布置磁盘上的数据。但是,我们容忍这种尴尬,因为磁盘有两个显著的优点:它们是耐用的(它们的内容在电源关闭时不会丢失),并且每GB的成本比RAM低。
|
||||
本章到目前为止讨论的数据结构都是对磁盘限制的回答。与主内存相比,磁盘处理起来很尴尬。对于磁盘和SSD,如果要在读取和写入时获得良好性能,则需要仔细地布置磁盘上的数据。但是,我们容忍这种尴尬,因为磁盘有两个显著的优点:它们是持久的(它们的内容在电源关闭时不会丢失),并且每GB的成本比RAM低。
|
||||
|
||||
随着RAM变得更便宜,每GB的成本价格被侵蚀了。许多数据集不是那么大,所以将它们全部保存在内存中是非常可行的,可能分布在多个机器上。这导致了内存数据库的发展。
|
||||
随着RAM变得更便宜,每GB成本的论据被侵蚀了。许多数据集不是那么大,所以将它们全部保存在内存中是非常可行的,可能分布在多个机器上。这导致了内存数据库的发展。
|
||||
|
||||
某些内存中的键值存储(如Memcached)仅用于缓存,在重新启动计算机时丢失的数据是可以接受的。但其他内存数据库的目标是持久性,可以通过特殊的硬件(例如电池供电的RAM),将更改日志写入磁盘,将定时快照写入磁盘或通过复制内存来实现,记忆状态到其他机器。
|
||||
某些内存中的键值存储(如Memcached)仅用于缓存,在重新启动计算机时丢失的数据是可以接受的。但其他内存数据库的目标是持久性,可以通过特殊的硬件(例如电池供电的RAM),将更改日志写入磁盘,将定时快照写入磁盘或者将内存中的状态复制到其他机器上。
|
||||
|
||||
内存数据库重新启动时,需要从磁盘或通过网络从副本重新加载其状态(除非使用特殊的硬件)。尽管写入磁盘,它仍然是一个内存数据库,因为磁盘仅用作耐久性附加日志,读取完全由内存提供。写入磁盘也具有操作优势:磁盘上的文件可以很容易地由外部实用程序进行备份,检查和分析。
|
||||
内存数据库重新启动时,需要从磁盘或通过网络从副本重新加载其状态(除非使用特殊的硬件)。尽管写入磁盘,它仍然是一个内存数据库,因为磁盘仅出于持久性目的进行日志追加,读取完全由内存提供。写入磁盘也具有运维优势:磁盘上的文件可以很容易地由外部实用程序进行备份,检查和分析。
|
||||
|
||||
诸如VoltDB,MemSQL和Oracle TimesTen等产品是具有关系模型的内存数据库,供应商声称,通过消除与管理磁盘上的数据结构相关的所有开销,他们可以提供巨大的性能改进【41,42】。 RAM Cloud是一个开源的内存键值存储器,具有持久性(对存储器中的数据以及磁盘上的数据使用日志结构化方法)【43】。 Redis和Couchbase通过异步写入磁盘提供了较弱的持久性。
|
||||
|
||||
反直觉的是,内存数据库的性能优势并不是因为它们不需要从磁盘读取的事实。即使是基于磁盘的存储引擎也可能永远不需要从磁盘读取,因为操作系统缓存最近在内存中使用了磁盘块。相反,它们更快的原因在于省去了将内存数据结构编码为磁盘数据结构的开销。【44】。
|
||||
反直觉的是,内存数据库的性能优势并不是因为它们不需要从磁盘读取的事实。即使是基于磁盘的存储引擎也可能永远不需要从磁盘读取,因为操作系统在内存中缓存了最近使用的磁盘块。相反,它们更快的原因在于省去了将内存数据结构编码为磁盘数据结构的开销。【44】。
|
||||
|
||||
除了性能,内存数据库的另一个有趣的领域是提供难以用基于磁盘的索引实现的数据模型。例如,Redis为各种数据结构(如优先级队列和集合)提供了类似数据库的接口。因为它将所有数据保存在内存中,所以它的实现相对简单。
|
||||
|
||||
@ -367,19 +367,19 @@ SELECT * FROM restaurants WHERE latitude > 51.4946 AND latitude < 51.5079
|
||||
|
||||
## 事务处理还是分析?
|
||||
|
||||
在早期业务数据对处理过程中,一次典型的数据库写入通常与一笔 *商业交易(commercial transaction)* 相对应:卖个货,向供应商下订单,支付员工工资等等。但随着数据库应用至那些不涉及到钱的领域,术语 **交易/事务(transaction)** 仍留了下来,用于指代一组读写操作构成的逻辑单元。
|
||||
在早期业务数据处理过程中,一次典型的数据库写入通常与一笔 *商业交易(commercial transaction)* 相对应:卖个货,向供应商下订单,支付员工工资等等。但随着数据库应用至那些不涉及到钱的领域,术语 **交易/事务(transaction)** 仍留了下来,用于指代一组读写操作构成的逻辑单元。
|
||||
|
||||
事务不一定具有ACID(原子性,一致性,隔离性和持久性)属性。事务处理只是意味着允许客户端进行低延迟读取和写入 —— 而不是批量处理作业,而这些作业只能定期运行(例如每天一次)。我们在[第7章](ch7.md)中讨论ACID属性,在[第10章](ch10.md)中讨论批处理。
|
||||
|
||||
即使数据库开始被用于许多不同类型的博客文章,游戏中的动作,地址簿中的联系人等等,基本访问模式仍然类似于处理业务事务。应用程序通常使用索引通过某个键查找少量记录。根据用户的输入插入或更新记录。由于这些应用程序是交互式的,因此访问模式被称为 **在线事务处理(OLTP, OnLine Transaction Processing)** 。
|
||||
即使数据库开始被用于许多不同类型的数据,比如博客文章的评论,游戏中的动作,地址簿中的联系人等等,基本访问模式仍然类似于处理商业交易。应用程序通常使用索引通过某个键查找少量记录。根据用户的输入插入或更新记录。由于这些应用程序是交互式的,这种访问模式被称为 **在线事务处理(OLTP, OnLine Transaction Processing)** 。
|
||||
|
||||
但是,数据库也开始越来越多地用于数据分析,这些数据分析具有非常不同的访问模式。通常,分析查询需要扫描大量记录,每个记录只读取几列,并计算汇总统计信息(如计数,总和或平均值),而不是将原始数据返回给用户。例如,如果您的数据是一个销售交易表,那么分析查询可能是:
|
||||
|
||||
* 一月份每个商店的总收入是多少?
|
||||
* 在最近的推广活动中卖了多少香蕉?
|
||||
* 在最近的推广活动中多卖了多少香蕉?
|
||||
* 哪个牌子的婴儿食品最常与X品牌的尿布同时购买?
|
||||
|
||||
这些查询通常由业务分析师编写,并提供给帮助公司管理层做出更好决策(商业智能)的报告。为了区分这种使用数据库的事务处理模式,它被称为**在线分析处理(OLAP, OnLine Analytice Processing)**。【47】。OLTP和OLAP之间的区别并不总是清晰的,但是一些典型的特征在[表3-1]()中列出。
|
||||
这些查询通常由业务分析师编写,并提供给帮助公司管理层做出更好决策(商业智能)的报告。为了将这种使用数据库的模式和事务处理区分开,它被称为**在线分析处理(OLAP, OnLine Analytice Processing)**。【47】。OLTP和OLAP之间的区别并不总是清晰的,但是一些典型的特征在[表3-1]()中列出。
|
||||
|
||||
**表3-1 比较交易处理和分析系统的特点**
|
||||
|
||||
@ -431,17 +431,17 @@ Teradata,Vertica,SAP HANA和ParAccel等数据仓库供应商通常使用昂
|
||||
|
||||
通常情况下,事实被视为单独的事件,因为这样可以在以后分析中获得最大的灵活性。但是,这意味着事实表可以变得非常大。像苹果,沃尔玛或eBay这样的大企业在其数据仓库中可能有几十PB的交易历史,其中大部分实际上是表【56】。
|
||||
|
||||
事实表中的一些列是属性,例如产品销售的价格和从供应商那里购买的成本(允许计算利润余额)。事实表中的其他列是对其他表(称为维表)的外键引用。由于事实表中的每一行都表示一个事件,因此这些维度代表事件的发生地点,时间,方式和原因。
|
||||
事实表中的一些列是属性,例如产品销售的价格和从供应商那里购买的成本(允许计算利润余额)。事实表中的其他列是对其他表(称为维度表)的外键引用。由于事实表中的每一行都表示一个事件,因此这些维度代表事件的发生地点,时间,方式和原因。
|
||||
|
||||
例如,在[图3-9](img/fig3-9.md)中,其中一个维度是已售出的产品。 `dim_product` 表中的每一行代表一种待售产品,包括**库存单位(SKU)**,说明,品牌名称,类别,脂肪含量,包装尺寸等。`fact_sales` 表中的每一行都使用外部表明在特定交易中销售了哪些产品。 (为了简单起见,如果客户一次购买几种不同的产品,则它们在事实表中被表示为单独的行)。
|
||||
|
||||
即使日期和时间通常使用维度表来表示,因为这允许对日期(诸如公共假期)的附加信息进行编码,从而允许查询区分假期和非假期的销售。
|
||||
甚至日期和时间也通常使用维度表来表示,因为这允许对日期的附加信息(诸如公共假期)进行编码,从而允许查询区分假期和非假期的销售。
|
||||
|
||||
“星型模式”这个名字来源于这样一个事实,即当表关系可视化时,事实表在中间,由维表包围;与这些表的连接就像星星的光芒。
|
||||
“星型模式”这个名字来源于这样一个事实,即当表关系可视化时,事实表在中间,由维度表包围;与这些表的连接就像星星的光芒。
|
||||
|
||||
这个模板的变体被称为雪花模式,其中尺寸被进一步分解为子尺寸。例如,品牌和产品类别可能有单独的表格,并且 `dim_product` 表格中的每一行都可以将品牌和类别作为外键引用,而不是将它们作为字符串存储在 `dim_product` 表格中。雪花模式比星形模式更规范化,但是星形模式通常是首选,因为分析师使用它更简单【55】。
|
||||
这个模板的变体被称为雪花模式,其中维度被进一步分解为子维度。例如,品牌和产品类别可能有单独的表格,并且 `dim_product` 表格中的每一行都可以将品牌和类别作为外键引用,而不是将它们作为字符串存储在 `dim_product` 表格中。雪花模式比星形模式更规范化,但是星形模式通常是首选,因为分析师使用它更简单【55】。
|
||||
|
||||
在典型的数据仓库中,表格通常非常宽泛:事实表格通常有100列以上,有时甚至有数百列【51】。维度表也可以是非常宽的,因为它们包括可能与分析相关的所有元数据——例如,`dim_store` 表可以包括在每个商店提供哪些服务的细节,它是否具有店内面包房,店面面积,商店第一次开幕的日期,最后一次改造的时间,离最近的高速公路的距离等等。
|
||||
在典型的数据仓库中,表格通常非常宽:事实表格通常有100列以上,有时甚至有数百列【51】。维度表也可以是非常宽的,因为它们包括可能与分析相关的所有元数据——例如,`dim_store` 表可以包括在每个商店提供哪些服务的细节,它是否具有店内面包房,店面面积,商店第一次开张的日期,最后一次改造的时间,离最近的高速公路的距离等等。
|
||||
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user