在所有先进的应用程序中,不管是购物站点还是社交网络乃至风景名胜站点,搜索都扮演着关键的角色。Lucene搜索程序库事实上已经成为实现搜索引擎的标准。苹果、IBM、Attlassian(Jira)、Wolfram以及很多大家喜欢的公司【1】都使用了这种技术。因此,大家对任何能够提升Lucene的可伸缩性和性能的实现都很感兴趣。 Lucene简介 Lucene中可搜索的实体都表现为文档(document),它由字段(field)和值(value)组成。每个字段值都由一个或多个可搜索的元素——即词汇(term)——组成。Lucene搜索基于反向索引,其中包含了关于可搜索文档的信息。在使用正常索引时,你可以搜索文档,以了解它包含哪些字段,但使用反向索引与之不同,你会搜索字段的词汇,以了解所有包括该词汇的文档。 图1显示的是高层次的Lucene架构【2】。它的主要组件包括IndexSearcher、IndexReader、IndexWriter 和Directory。IndexSearcher实现了搜索逻辑。IndexWriter为每个插入的文档写入反向索引。IndexReader会在IndexSearcher的支持下读取索引的内容。IndexReader和IndexWriter都依赖于Directory,它会提供操作索引数据集的API,而该API会直接模拟文件系统API。 图1: 高层次的Lucene架构 标准的Lucene分发包中有多个目录实现,包括基于文件系统和基于内存的[1]。 标准基于文件系统的后端的缺点在于,随着索引增加性能会下降。人们使用了各种不同的技术来解决这个问题,包括负载均衡和索引分片(index sharding)——在多个Lucene实例之间切分索引。尽管分片功能很强大,但它让总体的实现架构变得更复杂,并且需要大量对期望文档的预测知识,才能对Lucene索引进行合适地分片。 另一种不同的方法是,让索引后端自身对数据进行正确地分片,并基于这样的后端构建出实现。这种后端可以是NoSQL数据库。在本文中我们会描述基于HBase的实现【4】。 实现方法 正如在【3】中所说明的,在高层次上,Lucene会操作两个单独的数据集: 正如我们已经在上面提到的,想要把Lucene移植到新的后端中,直接实现directory接口并不总会是最简单(最方便)的方法。所以,很多对Lucene的移植,包括从Lucene的contrib.module支持的优先内存索引、Lucandra【5】和HBasene【6】分别采用了不同的方法【2】,不仅重写了directory,还重写了高级的Lucene类——IndexReader和IndexWriter,从而绕开了Directory的API(如图2)。 图2: 将Lucene和没有文件系统的后端整合 尽管这种方法通常需要更多工作【2】,但是它能够带来更强大的实现,让我们可以完全利用后端的本地功能。 文中所展现的实现[2]也遵循了这种方法。 总体架构 总体上的实现(如图3)是在基于内存的后端之上构建的,并将其用作内存缓存、同步缓存和HBase后端的机制。 图3: 基于HBase的Lucene实现的总体架构 该实现试图平衡两种相互冲突的需求,性能: 在内存中,缓存能够最小化HBase用于搜索和文件返回的数据读取量,从而极大提升性能;可伸缩性: 按照需要运行为多个Lucene示例以支持日益增长的搜索客户端的能力。后者需要最小化缓存的生命周期,从而和HBase实例(上面提到实例的副本)中的内容同步。通过为活动参数实现可配置的缓存时间,限制每个Lucene实例中展现的缓存,我们可以达成一种折中方案。 内存缓存中的底层数据模型 正如之前所提到的,内部Lucene数据模型基于两种主要的数据集——索引和文档,它们会被实现为两种模型——IndexMemoryModel和DocumentMemoryModel。在我们的实现中,读写操作(IndexReader/IndexWriter)都是通过内存缓存完成的,但是它们的实现有很大区别。对于读取操作,缓存首先会检查所需要的数据是否在内存中,并且没有过期[3],如果那样的话就会直接使用。否则缓存就会从HBase读取或者刷新数据,然后把它返回给IndexReader。相反,对于写操作,数据会直接写入到HBase,而不会在内存中存储。尽管这在实际的数据可用性方面会有延迟,但是它会让实现过程非常简单——我们不需要考虑把新建或者更新的数据发送给哪个缓存。这里的延迟可以通过设置合适的缓存过期时间来控制,从而符合业务需求。 IndexMemoryModel 索引内存模型的类图如图4所示。 图4: IndexMemoryModel类图 在这个实现中: DocumentMemoryModel 文档内存模型的类图如图5所示。 图5: DocumentMemoryModel类图 在这个实现中: LuceneDocumentNormMemoryModel 正如在【9】中说明的,规范(norm)是用于表现文档或字段的加权因子,从而提供更好的搜索结果排序,这需要耗费大量内存。类的实现基于对映射的映射(map of maps),内部映射会存储规范和文档的映射关系,而外部映射会存储规范和字段的映射关系。 尽管规范信息的键值是字段名称,从而可以添加到LuceneIndexMemoryModel类中,但是我们还是决定把对规范的管理实现为单独的类——LuceneDocumentNormMemoryModel。这么做的原因在于,在Lucene使用规范是可选的操作。 IndexWriter 有了之前所描述的底层内存模型,实现索引写入程序就很简单了。因为Lucene不会定义IndexWriter接口,所以想要实现IndexWriter,我们需要实现所有标准Lucene实现中的方法。这个类的主要内容在于addDocument方法。这个方法会遍历所有文档的字段。对于每个字段,方法都会检查它是否可以令牌化(tokenized),并使用特定的分析器来做到这一点。这个方法还会更新所有三种内存结构——索引、文档和(可选的)规范,它们会为新增的文档存储信息。 IndexReader IndexReader会实现Lucene核心所提供的IndexReader接口。因为Hbase中所获得的列表和单独的读操作相比要快很多,所以我们使用一些方法来扩展这个类,从而可以读取多个文档。类本身没有把更多的处理转交给几个类,它会对其进行管理: HBase表 以上提出的解决方案基于两个主要的HBase表——Index表(图6)和document表(图7)。 图6: HBase的Index表 图7: HBase的document表 如果需要支持规范的话,可选择实现第三个表(图8)。 图8: HBase的norm表 HBase的Index表(图6)会完成实现的主要工作。这个表对每个Lucene实例所知道的字段/词汇组合都设置了入口,其中包含一个列族(column family)——documents族。这个列族为包含这个字段或词汇的所有文档都包含了一列(名称是文档的ID)。每个列的内容都是TermDocument类的值。 HBase的document表(图7)存储了文档本身、对索引或规范的向后引用,它会为文档处理引用这些文档以及一些Lucene使用的附加信息。它对所有Lucene实例知道的文档都设置了入口(row)。每个文档都通过文档ID(键值)唯一标识,并包含两个列族——字段族和索引族。字段列族针对所有存储在Lucene中的文档字段包含一列(名称为字段的名称)。列的值是值的类型(字符串或者字符数组)和值本身的组合。索引列族为每个引用这个文档的索引包含了一列(名称是字段或者术语)。列的值包括给定字段/词汇在文档的使用频率、位置和偏移量。 HBase的norm表(图8)为每个字段存储了文档的规范。它对所有Lucene实例知道的每个字段(键值)都设置了入口(行)。每行都只包含一个列族——规范族。这个族对每个需要存储给定字段规范的文档都有一列(名称是文档ID)。 数据格式 最终的设计方案确定了在HBase中存储数据的数据格式。对于这个实现,我们基于性能、结果数据的最小规模以及和Hadoop的紧密整合程度选择了Avro【10】。 实现主要使用的数据结构是TermDocument(代码1)、文档的FieldData(代码2)和DocumentTermFrequency(代码3)。 代码1 词汇文档AVRO定义 代码2 字段数据AVRO定义 代码3 TermDocumentFrequency的AVRO定义 结论 本文中描述的简单实现完全支持所有Lucene功能,针对Lucene核心和普通模块的单元测试都验证了这一点。我们可以将它作为基础,构建可扩展性很强的搜索实现,支持HBase固有的可扩展性以及完全对称的设计,让我们可以添加任意数量服务于HBase数据的进程。它还可以避免需要关闭打开状态的Lucene索引读取程序,就可以包含新的索引数据,那会经过一定延迟之后为用户所用,而延迟是通过活动参数的缓存时间所控制的。在下一篇文章中我们会展示如何扩展这个实现,以包含地理搜索支持。Lucene和HBase的集成
{
"type" : "record",
"name" : "TermDocument",
"namespace" : "com.navteq.lucene.hbase.document",
"fields" : [ {
"name" : "docFrequency",
"type" : "int"
}, {
"name" : "docPositions",
"type" : ["null", {
"type" : "array",
"items" : "int"
}]
} ]
}
{
"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" ]
} ]
}
}
} ]
}
{
"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"
} ]
}
}]
} ]
}
发表评论
-
四层和七层负载均衡的区别
2015-03-13 13:27 365(一) 简单理解四层和七层负载均衡: ① 所 ... -
Solr搜索服务架构图
2013-02-21 18:33 1361... -
Spring多数据源的配置和使用
2012-09-12 17:27 1073Spring多数据源的配置和使用 最近开 ... -
Java多播通讯框架 JGroups
2012-08-20 12:10 1320Java多播通讯框架 JGroups JGroups ... -
Terrocotta - 基于JVM的Java应用集群解决方案
2012-08-17 11:14 917Terrocotta - 基于JVM的Java ... -
MongoDB基本管理命令
2012-08-16 10:23 859MongoDB基本管理命令 MongoDB是一个NoSQ ... -
hessian demo和hessian与spring整合demo
2012-08-13 11:52 1640hessian demo和hessian与spring ... -
C++著名程序库的比较和学习经验
2012-07-25 10:35 732C++著名程序库的比较和学习经验 内容目录:1、C++各 ... -
可伸缩性最佳实践:来自eBay的经验
2012-07-05 08:53 702可伸缩性最佳实践: ... -
各种java序列化工具性能对比
2012-07-04 13:16 1750各种java序列化工具性能对比 看到一个很不错的工具ht ... -
深入探讨 Java 类加载器
2012-07-03 17:32 642深入探讨 Java 类加载 ... -
Servlet 工作原理解析
2012-06-27 15:05 706Servlet 工作原理解析 简介: Web 技 ... -
HTTP协议header头域
2012-06-27 10:04 909HTTP(HyperTextTransferPr ... -
深入研究Servlet线程安全性问题
2012-06-21 13:49 0摘 要:介绍了Servlet多 ... -
Java类加载原理解析
2012-06-12 13:33 593Java类加载原理解析 ... -
ffmpeg源码及相关开发资料下载,好文章积攒
2012-06-04 16:12 735FFMpeg0.6版源码下载:来自:http://sour ... -
分布式文件系统FastDFS架构剖析
2012-05-31 11:34 702FastDFS是一款类Google FS的开源分布式文件系 ... -
NoSQL数据库探讨
2012-05-24 09:16 543随着互联网web2.0网站的兴起,非关系型的数据库现在成了一个 ... -
用nosql轻松打造千万级数据量的微博系统
2012-05-23 11:38 852其实微博是一个结构相对简单,但数据量却是很庞大的一种产品. ...
相关推荐
2. **安装步骤**: 详细说明如何将光盘上的资源正确地集成到Lucene和Nutch环境中。 3. **使用示例**: 提供使用这些资源的实际操作例子,如创建索引、运行查询等。 4. **故障排查**: 针对常见问题和错误提供解决方案,...
通过将 HBase 与 Solr 集成,可以实现 HBase 的二级索引,从而提高查询性能。 CDH(Cloudera Distribution of Hadoop)是一款基于 Hadoop 的大数据处理平台,提供了完整的数据处理解决方案。通过使用 CDH,我们可以...
作者Nishant Garg凭借其在软件架构和开发领域的丰富经验,特别是在Java、Java Enterprise Edition、SOA、Spring、Hibernate、Hadoop、Hive、Flume、Sqoop、Oozie、Spark、Shark、YARN、Impala、Kafka、Storm、Solr/...
这可能意味着用户在搭建或者升级一个集成了HBase和Solr的环境,可能是在为大数据分析平台提供既有的搜索和数据存储功能。 综上所述,这个压缩包包含的知识点主要有: 1. HBase:分布式NoSQL数据库,基于Hadoop,...
当 Solr 集成 HBase 时,Solr 可以作为 HBase 的索引层,提供快速的全文搜索和高级查询功能,而 HBase 则负责存储和管理大量结构化和半结构化数据。这种结合的优势包括: 1. **实时搜索**:通过 Solr 的实时索引,...
了解如何在Hadoop集群上安装和配置HBase,以及如何将HBase作为数据存储层集成到Hadoop生态系统中。 6. 分布式存储系统的优化。针对不同的应用场景,例如海量数据处理、实时增量搜索、图形计算和交互式计算等,理解...
HBase是一个分布式的、面向列的NoSQL数据库,适合存储大量结构化和半结构化的数据,而Solr则是Apache Lucene项目的一个子项目,提供了一个高性能、可扩展的全文检索和分析引擎。当我们在HBase上构建二级索引时,通常...
3. **利用Lucene**:每个RegionServer可以使用Lucene来保存和检索数据,这种方式的优点是灵活性较高,但同时也带来了实现复杂性和性能方面的挑战。 ### HBase for Solr的实现原理及应用场景 HBase for Solr是一种...
总的来说,通过HBase的Coprocessor和Solr的集成,我们可以实现对HBase数据的复杂查询和全文搜索,极大地扩展了HBase的应用范围。这个过程虽然相对复杂,但一旦设置好,就能带来显著的查询性能提升,对于需要高效检索...
4. **集成HBase和Solr**:配置HBase的`hbase-site.xml`,启用HBase-Solr连接器(如`hbase.indexer.solr.zookeeper.quorum`等),使HBase能够自动将数据同步到Solr。 5. **实时同步**:当HBase中的数据发生变化时,...
### Java大数据内容_7Flume、Kafka、Sqoop、Lucene #### 一、Flume 入门 ##### 1.1 Flume 概述 Flume 是一个分布式的、可靠的、高可用的日志采集系统,主要用于收集、汇总和移动大量的日志数据。它由 Cloudera ...
6. X-PackLight Analysis:提供了轻分析能力,可集成大数据分析工具,如Spark,以执行复杂的数据处理和分析任务。 在企业级特性方面,阿里云HBase还具备了高数据可靠性,通过SLA保障了99.9%的单集群可用性,甚至...
这份压缩包文件包含了一系列关于大数据技术的PPT和PDF文档,主要聚焦于Spark、HBase、HDFS的二次开发以及相关的技术,如Hive、Kafka、Solr和MapReduce等。以下是这些资源中涉及的主要知识点: 1. **Spark二次开发**...