听说过时序数据库吗?( 二 )

  • Elasticsearch 从1.0开始支持aggregation,基本上有了普通SQL的聚合能力 。从 2.0 开始支持 pipeline aggregation,可以支持类似SQL sub query的嵌套聚合的能力 。这种聚合能力相比Crate.io,Solr等同门师兄弟要强大得多 。
  • 如何快速检索?
    Elasticsearch是通过Lucene的倒排索引技术实现比关系型数据库更快的过滤 。特别是它对多条件的过滤支持非常好,比如年龄在18和30之间,性别为女性这样的组合查询 。倒排索引很多地方都有介绍,但是其比关系型数据库的b-tree索引快在哪里?到底为什么快呢?
    笼统的来说,b-tree索引是为写入优化的索引结构 。当我们不需要支持快速的更新的时候,可以用预先排序等方式换取更小的存储空间,更快的检索速度等好处,其代价就是更新慢 。要进一步深入的化,还是要看一下Lucene的倒排索引是怎么构成的 。
    听说过时序数据库吗?

    文章插图
     
    这里有好几个概念 。我们来看一个实际的例子,假设有如下的数据:
    听说过时序数据库吗?

    文章插图
     
    这里每一行是一个document 。每个document都有一个docid 。那么给这些document建立的倒排索引就是:
    年龄:
    听说过时序数据库吗?

    文章插图
     
    性别:
    听说过时序数据库吗?

    文章插图
     
    可以看到,倒排索引是per field的,一个字段由一个自己的倒排索引 。18,20这些叫做 term,而[1,3]就是posting list 。Posting list就是一个int的数组,存储了所有符合某个term的文档id 。那么什么是term dictionary 和 term index?
    假设我们有很多个term,比如:
    Carla,Sara,Elin,Ada,Patty,Kate,Selena
    如果按照这样的顺序排列,找出某个特定的term一定很慢,因为term没有排序,需要全部过滤一遍才能找出特定的term 。排序之后就变成了:
    Ada,Carla,Elin,Kate,Patty,Sara,Selena
    这样我们可以用二分查找的方式,比全遍历更快地找出目标的term 。这个就是 term dictionary 。有了term dictionary之后,可以用 logN 次磁盘查找得到目标 。但是磁盘的随机读操作仍然是非常昂贵的(一次random access大概需要10ms的时间) 。所以尽量少的读磁盘,有必要把一些数据缓存到内存里 。但是整个term dictionary本身又太大了,无法完整地放到内存里 。于是就有了term index 。term index有点像一本字典的大的章节表 。比如:
    A开头的term ……………. Xxx页
    C开头的term ……………. Xxx页
    E开头的term ……………. Xxx页
    如果所有的term都是英文字符的话,可能这个term index就真的是26个英文字符表构成的了 。但是实际的情况是,term未必都是英文字符,term可以是任意的byte数组 。而且26个英文字符也未必是每一个字符都有均等的term,比如x字符开头的term可能一个都没有,而s开头的term又特别多 。实际的term index是一棵trie 树:
    听说过时序数据库吗?

    文章插图
     
    例子是一个包含 "A", "to", "tea", "ted", "ten", "i", "in", 和 "inn" 的 trie 树 。这棵树不会包含所有的term,它包含的是term的一些前缀 。通过term index可以快速地定位到term dictionary的某个offset,然后从这个位置再往后顺序查找 。再加上一些压缩技术(搜索 Lucene Finite State Transducers) term index 的尺寸可以只有所有term的尺寸的几十分之一,使得用内存缓存整个term index变成可能 。整体上来说就是这样的效果 。
    听说过时序数据库吗?

    文章插图
     
    现在我们可以回答“为什么Elasticsearch/Lucene检索可以比mysql快了 。Mysql只有term dictionary这一层,是以b-tree排序的方式存储在磁盘上的 。检索一个term需要若干次的random access的磁盘操作 。而Lucene在term dictionary的基础上添加了term index来加速检索,term index以树的形式缓存在内存中 。从term index查到对应的term dictionary的block位置之后,再去磁盘上找term,大大减少了磁盘的random access次数 。
    额外值得一提的两点是:term index在内存中是以FST(finite state transducers)的形式保存的,其特点是非常节省内存 。Term dictionary在磁盘上是以分block的方式保存的,一个block内部利用公共前缀压缩,比如都是Ab开头的单词就可以把Ab省去 。这样term dictionary可以比b-tree更节约磁盘空间 。


    推荐阅读