Design Data-Intensive Applications 读书笔记八 其他索引结构

其他索引结构

我们目前讨论的是键值索引,用于关系型数据库汇总的主键索引。一个主键唯一标志关系型数据库中的一行,或者是文档数据库中的一个文档,后正式图数据库中的一个顶点。一个记录可以通过主键来引用这一条记录。

第二索引也是很常见的。在关系型数据库中,你可以在一张表格上创建多个第二索引,这对于执行join操作很有助益。第二索引可以使用键值索引来构建,主要的区别就是键可能不是唯一的,可能有多行(文档,节点)有相同的键。这可以用两个方法解决:1、给索引中的每个值检录一个列的标志符的列表(类似全文索引中的检索列表)2、将B-tree或者日志结构索引用于第二索引

在索引中存值

索引中的键用于搜索,值能表示两件事:可以是真实的一行(文档,节点),或者是存储在其他地方的行的引用。在后一种场景中,行数据被存储的地方称为堆文件,它存储数据没有什么特定的顺序(可能是只增的,可能是跟踪删掉的数据为了后续写入新数据来覆盖它们)。对文件方案使用普遍因为它避免了使用多个次要索引时产生的多个重复冗余数据,每个索引指向了对文件中的位置,真实的数据只保存在一个地方。

当不用修改键而更新值时,堆文件方案很有效:如果新值不比旧值更大,记录可以在合适的地方被覆写。如果新值更大,场景会很复杂,因为需要移至堆文件中的有足够空间的新位置。这个情况下,需要更新所有的索引来指向堆文件中的新位置或者是将指向旧位置的指针转而指向到新位置。

在一些场景中,从索引到堆文件的额外跳转回降低读取性能,所以有种方法是直接在索引中存数据。这就是集群索引(clustered index)。例如在MySQL的InnoDB 存储引擎中,表的主键经常是聚合索引,次要索引指向主键(而不是堆文件中位置)。

一个聚合索引(在索引中存储所有的行数据)对比非聚合索引(只在数据中存储对数据的引用)就是覆盖索引(covering index)或者是包含列的索引(index with included cilumns),在索引中存储了表的一些列。这允许一些查询只使用索引(这种场景下,索引被称为覆盖了查询)。

聚合索引能加速度操作,但是需要额外的存储空间,会增加些操作。数据库也需要做额外工作来保证一致性,因为应用不应该看见因为冗余造成的不一致。

 

多列索引

目前索引只讨论了键值一对一地情况。如果我们需要查询表中多列,这是不够的。

多列索引最常见的类型就是串级索引,就是简单的将一个字段添加到另一个字段后面(顺序由字段定义决定)。就类似电话簿,用姓和名指向一个号码。可以用姓名指向一个人,如果只是用姓,就没用了。

使用多维索引查询多列更为常见,这对于地理数据尤为重要。比如使用经纬度搜索一个餐馆。当用户再地图上搜索餐馆时,网站需要找到在用户视区地图范围内的所有餐馆,这需要一个二维查询:

标准的B-tree或者LSM-tree不能有效应对这种查询:你可以给出特定维度范围内(任意经度)的餐馆,或者是特定经度范围(南极和北极间的任何位置)的餐馆,不能同时满足条件。

一个方案是用空间填充曲线将一个二维定位转化为一个数,然后使用一般的B-tree索引。更一般的做法是使用空间索引R-trees例如PostgreSQL的自生成搜索树来标记设施。

一个想法就是多维索引不仅用于地理定位。例如,一个商业网站可以用三维索引(红黄蓝)来检索特定颜色范围的商品。或者是使用一个二维索引(日期,气温)来检索2013年气温在25至30度的观测数据。

 

全文搜索和模糊索引

目前讨论的都是假设你有确定的值或者确定的范围用于检索,然后返回有序值。不会考虑你要检索的是相似值,比如误拼写的词。类似的模型查询需要不同的技术。

比如,全文搜索引擎允许检索一个检索单词扩展至同义词来忽略单词的语法变化,以及搜索相同文档中单词出现次数,和支持文本的语言学分析。为了处理文档或者查询中的错误,Lucene能够在文本中搜索特定编辑距离内的单词(编辑距离为1意味着一个多了,少了或者替换了一个字母)。

之前提到过,Lucene用类似SSTable存储单词字典。这个结构需要一个小型的内存中索引告诉查询要从有序文件中哪个位置找到键。在LevelDB中,内存中的索引是键的稀疏集合,但是在Lucece中,内存索引是关于键的字符的有限状态自动装置。这个自动装置能够转换至Levenshtein automaton,能够支持给定编辑距离的单词的高效搜索。

 

在内存中保存所有

这个章节讨论的数据结构都是为了应对磁盘的局限。对比内存,磁盘很难处理。无论是机械磁盘还是固态硬盘,磁盘上的数据都需要小心对待才能有好的读写性能。我们容忍磁盘的缺陷是因为磁盘有两个显著优势:持久化(断电后不会丢数据)和廉价(每GB的单价比RAM低)。

不过RAM变得越来越廉价。很多数据库也不大,全部放在内存中也很宽裕,也能潜在地支持分布式,这就促进了内存数据库的发展。

一些内存中的键值存储,比如Memcached,就是只用来缓存,如果机器重启,数据就会丢失。但是有一些数据库的目标是持久化,可以使用特殊硬件(自带电池的RAM),将数据写入磁盘,或者是将快照写入磁盘,或者是将内存状态写入其他机器。

当内存数据库重启时,需要重新加载状态,可以从磁盘或者是从网络加载备份。尽管它会写入磁盘,但是仍然是内存数据库,因为磁盘只是用来只增的日志用来持久化,读取完全是在内存中操作。写入磁盘有一个好处:磁盘上的文件能够使用内存工具备份,检查和分析。

反直觉的是,内存数据库性能上的优势不是因为它们不需要从磁盘读取。即便基于磁盘的存储引擎也可能永远不需要从磁盘读取,只要有足够的内存,因为操作系统会将常用的磁盘的块缓存至内存,它们快是因为可以避免将内存数据结构编码至能写入磁盘的格式这个开支。

除性能外,内存数据库提供了磁盘结构索引很难实现的数据模型。比如,Redis给不同数据结构提供了类似数据库的接口,如优先队列和set。因为它将所有数据保存在内存中,它实现起来相对容易。

最近的研究表明内存数据库架构可以扩展来支持比可用内存更大的数据集而不需要重新应对磁盘架构的开支。这就是反缓存(anti-caching)方案,它会在没有足够内存的时候从内存中排除最少使用的数据至磁盘,然后在未来需要的时候重新加载回来。这类似操作系统中的虚拟内存和交换文件,但是数据库能够比操作系统更高效地管理内存,因为它的操作粒度是独立记录而不是整块内存页。但是这个方案仍然需要在内存中维持完整的索引。

存储引擎未来的方向可能是非易失性内存(non-volatile memory)。目前仍然是新的领域,值得关注。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值