hbase lucene_将Lucene与HBase集成

hbase lucene

从购物网站到社交网络再到兴趣点,搜索在几乎任何现代应用中都起着关键作用。 Lucene搜索库是实现搜索引擎的当今事实上的标准。 苹果,IBM,Attlassian(Jira),Wolfram会使用它,选择您喜欢的公司[1]。 结果,任何允许提高Lucene的可伸缩性和性能的实现方式都引起了人们的极大兴趣。

Lucene快速入门

Lucene中的可搜索实体表示为包含字段及其值的文档。 每个字段值都包含一个或多个可搜索元素-术语。 Lucene搜索基于包含有关可搜索文档信息的倒排索引。 与普通索引不同,在普通索引中,您可以查找文档以了解文档包含哪些字段,而在反向索引中,您可以查找字段的术语以了解文档中包含的所有文档。

图1给出了高级Lucene体系结构[2]。它的主要组件是IndexSearcher,IndexReader,IndexWriter和Directory。 IndexSearcher实现搜索逻辑。 IndexWriter为每个插入的文档编写反向索引。 IndexReader读取索引内容以支持IndexSearcher。 IndexReader和IndexWriter都依赖Directory,Directory提供用于操作索引数据集的API,这些API直接模仿文件系统API。

图1:高级Lucene架构

标准的Lucene发行版包含几种目录实现,包括基于文件系统和基于内存的[1]

基于标准文件系统的后端(目录实现)的缺点是索引增长导致性能下降。 使用了多种技术来克服此问题,包括负载平衡和索引分片-在多个Lucene实例之间拆分索引。 尽管功能强大,但是分片的使用使整个实现体系结构复杂化,并且需要有关预期文档的一定数量的先验知识才能正确地分区Lucene索引。

另一种方法是允许索引后端本身正确地分片数据并基于这样的后端构建实现。 这样的后端之一可以是noSQL数据库。 在本文中,我们将描述基于HBase [4]的实现。

实施方法

如[3]中所述,Lucene在较高级别上对两个不同的数据集进行操作:

  • 索引数据集将所有“字段/术语”对(以及诸如术语频率,位置等附加信息)以及包含这些术语的文档保留在适当的字段中。
  • 文档数据集存储所有文档,包括存储的字段等。

如前所述,直接实现目录接口并不总是将Lucene移植到新后端的最简单(最方便)的方法。 结果,几个Lucene端口,包括来自Lucene contrib的有限的内存索引支持。 模块Lucandra [5]和HBasene [6]采用了不同的方法[2],而不是改写目录,而是改写了高级Lucene的类-IndexReader和IndexWriter,从而绕过了Directory API(图2)。

图2:不带文件系统的后端集成Lucene

尽管这种方法通常需要更多的工作[2],但它会导致更强大的实现,从而可以充分利用后端的本机功能。

本文介绍的实现[2]遵循这种方法。

整体架构

总体实现(图3)基于用作内存中缓存的基于内存的后端,以及将该缓存与HBase后端同步的机制。

图3:基于HBase的Lucene实现的总体架构

该实现试图平衡两个相互冲突的需求-性能:内存缓存中的HBase可以通过最小化用于搜索和文档检索的HBase读取量来极大地提高性能; 和可扩展性:能够运行所需数量的Lucene实例以支持不断增长的搜索客户端数量。 后者需要最小化缓存的生存时间,以使内容与HBase实例(thruth的单个副本)同步。 通过对实时参数实现可配置的缓存时间,限制每个Lucene实例中的缓存存在,可以实现一种折衷方案。

内存缓存中的基础数据模型

如前所述,内部Lucene数据模型基于两个主要数据集–索引和文档,它们被实现为两个模型– IndexMemoryModel和DocumentMemoryModel。 在我们的实现中,读写操作(IndexReader / IndexWriter)都是通过内存缓存完成的,但是它们的实现却大不相同。 对于读取,高速缓存首先检查所需的数据是否在内存中并且是否不过期[3],以及是否直接使用了它。 否则,缓存将从HBase读取/刷新数据,然后将其返回给IndexReader。 另一方面,对于写操作,数据无需存储在内存中即可直接写入HBase。 尽管这可能会导致实际数据可用性上的延迟,但它使实现变得非常简单-我们无需担心哪个缓存可提供新的/更新的数据。 通过设置适当的缓存过期时间,可以控制此延迟以符合业务需求。

IndexMemoryModel

索引内存模型的类图如图4所示。

图4:IndexMemoryModel类图

在此实现中:

  • LuceneIndexMemoryModel类包含内存中当前存在的每个字段的FieldTermDocuments类。 它还提供了实施IndexReader / IndexWriter所需的所有内部API。
  • FieldTermDocuments类管理每个字段值的TermDocuments。 通常,对于可扫描的数据库,字段列表和字段值列表组合在一个(字段/项值)可导航列表中。 对于基于内存的缓存实现,我们将它们分为两个单独的映射,以使搜索时间更可预测。
  • TermDocuments类包含每个文档ID的TermDocument类的列表。
  • TermDocument类包含给定文档的索引中存储的信息-文档频率和位置数组。

DocumentMemoryModel

文档存储模型的类图如图5所示。

图5:DocumentMemoryModel类图

在此实现中:

  • LuceneDocumentMemoryModel类包含每个索引文档的DocumentStructure类的映射。
  • DocumentStructure类包含有关单个文档的信息。 对于每个文档,它都包含一个已保存字段的列表以及有关每个索引字段的信息。
  • FieldData类包含为存储的字段保存的信息,包括字段名称,值和二进制/字符串标志。
  • DocumentTermFrequency类包含有关每个索引字段的信息,包括对文档中相应索引结构(字段,术语)术语频率的反向引用,术语在文档中的位置以及距文档开头的偏移量。

LuceneDocumentNormMemoryModel

如[9]中所述,规范用于表示文档/字段的提升因数,从而以使用大量内存为代价提供更好的搜索结果排名。 类的实现基于地图映射,其中内部地图存储文档的规范,而外部地图存储字段的规范图。

尽管规范的信息由字段名称键入,因此可以附加到LuceneIndexMemoryModel类,但我们决定将规范管理作为单独的类LuceneDocumentNormMemoryModel来实现。 其原因是在Lucene中使用规范是可选的。

索引作家

使用上面描述的基础内存模型,索引编写器的实现非常简单。 因为Lucene没有定义IndexWriter接口,所以我们必须通过实现标准Lucene实现中存在的所有方法来实现IndexWriter。 此类的主力军是addDocument方法。 此方法遍历所有文档的字段。 对于每个字段,该方法都会检查是否应对其进行标记化,并使用指定的分析器进行标记。 此方法还更新所有三个内存结构-索引,文档和(可选)存储所添加文档信息的规范。

索引阅读器

IndexReader实现了Lucene核心提供的IndexReader接口。 由于Hbase中的list列表与单个读取相比要快得多,因此我们使用该方法扩展了该类,允许读取多个文档。该类本身并没有将大部分处理工作外包给几个类,该类由它管理:

  • 虽然文档ID通常是字符串,但是Lucene在内部对整数进行运算。 DocIDManager类负责字符串到数字的转换管理。 IndexReader使用ThreadLocalStorage形式的此类,允许在线程结束时自动清除。
  • MemoryTermEnum类扩展了Lucene提供的TermEnum类,并负责扫描字段/术语值。
  • MemoryTermFrequencyVector类实现Lucene提供的TermDocs和TermPositions接口,并负责处理有关给定字段/术语对的文档信息。
  • MemoryTermFrequencyVector类实现了Lucene提供的TermFreqVector和TermPositionVector接口,并负责返回有关给定文档ID的文档字段的频率和位置的信息

HBase表

提议的解决方案基于两个主要的HBase表-索引表(图6)和文档表(图7)。

图6:Hbase索引表

(点击图片放大)

图7:HB​​ase文档故事

如果需要支持Lucene规范,则可以实现可选的第三张表(图8)。

图8:HBase规范表

HBase索引表(图6)是实现的主力军。 该表具有Lucene实例已知的每个字段/术语组合的条目(行),其中包含一个列族-文档族。 此列族包含每个包含此字段/术语的文档的列(称为文档ID)。 每列的内容是TermDocument类的值。

HBase文档表(图7)存储文档本身,向后引用索引/规范,引用这些文档以及Lucene用于文档处理的一些其他簿记信息。 它具有Lucene实例已知的每个文档的条目(行)。 每个文档都由文档ID(键)唯一标识,并包含两个列族-字段族和索引族。 “字段”列族包含Lucene存储的每个文档字段的列(称为字段名)。 列值由值类型(字符串或字节数组)和值本身组成。 索引列族包含一个引用此文档的每个索引的列(命名为字段/术语)。 列值包括文档频率,给定字段/术语的位置和偏移量。

HBase规范表(图8)存储每个字段的文档规范。 对于Lucene实例已知的每个字段(键),它都有一个条目(行)。 每行包含一个列族-规范族。 对于每个需要为其存储给定字段规范的文档,此族都有一个列(称为文档ID)。

资料格式

最终的设计决策是确定用于在HBase中存储数据的数据格式。 对于此实现,我们基于其性能,所生成数据的最小大小以及与Hadoop的紧密集成选择了Avro [10]。

实现使用的主要数据结构是TermDocument(清单1),Document的FieldData(清单2)和DocumentTermFrequency(清单3)

{
  "type" : "record",
  "name" : "TermDocument",
  "namespace" : "com.navteq.lucene.hbase.document",
  "fields" : [ {
    "name" : "docFrequency",
    "type" : "int"
  }, {
    "name" : "docPositions",
    "type" : ["null", {
      "type" : "array",
      "items" : "int "
   }]
  } ]
}

清单1术语文档AVRO定义

{
  "type" : "record",
  "name" : "FieldsData",
  "namespace " : "com.navteq.lucene.hbase.document",
  "fields" : [ {
    "name" : "fieldsArray",
    "type" : {
      "type" : "array",
      "items" : {
        "type" : "record",
        "name" : "singleField",
        "fields" : [ {
          "name" : "binary",
          "type" : "boolean"
        }, {
          "name" : "data",
          "type" : [ "string", "bytes" ]
        } ]
      }
    }
  } ]
}

清单2字段数据AVRO定义

{
  "type" : "record",
  "name" : "TermDocumentFrequency",
  "namespace " : "com.navteq.lucene.hbase.document",
  "fields" : [ {
    "name" : "docFrequency",
    "type" : " int "
  }, {
    "name" : "docPositions",
    "type" : ["null",{
      "type" : "array",
      "items" : " int "
    }]
  }, {
    "name" : "docOffsets",
    "type" : ["null",{
      "type" : "array",
      "items" : {
        "type" : "record",
        "name" : "TermsOffset",
        "fields" : [ {
          "name" : "startOffset",
          "type" : " int "
        }, {
          "name" : "endOffset",
          "type" : " int "
        } ]
      }
    }]
  } ]
}

清单3 TermDocumentFrequency AVRO定义

结论

本文描述的简单实现完全支持所有Lucene功能,这已通过Lucene核心模块和contrib模块的许多单元测试进行了验证。 它可以用作利用HBase固有的可伸缩性及其完全对称的设计构建非常可伸缩的搜索实现的基础,从而允许添加任意数量的服务于HBase数据的进程。 它还避免了关闭打开的Lucene索引读取器以合并新索引数据的必要性,该数据将自动提供给用户,而延迟可能会受到缓存生存时间参数的控制。 在下一篇文章中,我们将展示如何扩展此实现以合并地理空间搜索支持。

翻译自: https://www.infoq.com/articles/LuceneHbase/?topicPageSponsorship=c1246725-b0a7-43a6-9ef9-68102c8d48e1

hbase lucene

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值