使用lucene 缓存机制,搜索结果需要缓存吗?缓存多少合适

&&国之画&&&& &&
版权所有 京ICP备号-2
迷上了代码!Solr 缓存配置(一) | Solr 中文网
本站提供 solr 相关的原创或翻译的文章
& Solr 缓存配置(一)
Solr 缓存配置(一)
[作者: 分类: ]
缓存在 Solr 中充当了一个非常重要的角色,Solr 中主要有这三种缓存:
Filter cache(过滤器缓存),用于保存过滤器(fq 参数)和层面搜索的结果
Document cache(文档缓存),用于保存 lucene 文档存储的字段
Query result(查询缓存),用于保存查询的结果
还有第四种缓存,lucene 内部的缓存,不过该缓存外部无法控制到。
通过这 3 种缓存,可以对 solr 的搜索实例进行调优。调整这些缓存,需要根据索引库中文档的数量,每次查询结果的条数等。
在调整参数前,需要事先得到 solr 示例中的以下信息:
索引中文档的数量
每秒钟搜索的次数
过滤器的数量
一次查询返回最大的文档数量
不同查询和不同排序的个数
这些数量可以在 solr admin 页面的日志模块找到。假设以上的值分别为:
索引中文档的数量:1000000
每秒钟搜索的次数:100
过滤器的数量:200
一次查询返回最大的文档数量:100
不同查询和不同排序的个数:500
然后可以开始修改 solrconfig.xml 中缓存的配置了,第一个是过滤器缓存:
&filterCache class=&solr.FastLRUCache& size=&200& initialSize=&200&
autowarmCount=&100&/&
第二个是查询结果缓存:
&queryResultCache class=&solr.FastLRUCache& size=&500& initialSize=&500& autowarmCount=&250&/&
第三个是文档缓存:
&documentCache class=&solr.FastLRUCache& size=&11000& initialSize=&11000& /&
这几个配置是基于以上的几个假设的值进行调优的。
asdtiang的博客
专业卖衣服
Subversion中文社区
用最少的时间花最少的钱买到最划算的商品
邮件列表订阅:转载时请以超链接形式标明文章原始出处和作者信息及本声明
三、排序过滤和分页
仅仅把东西搜出来是不够的,好的检索工具还应当能够对检索的结果进行排序,优先将最相关的内容送出
或是按照某种规则,将检索结果送出。
1.文档得分规则
文档得分主要是由4部分内容来决定,即tf(词条频率)、idf(反转文档频率)、boost(Field的激励因子)
和lengthNorm(长度因子)
tf:某个关键字在某文档中出现次数的平方根
idf:Math.log(numDocs/docFreq+1)+1.0
(numDocs:表示索引中总共的文档数量,docFreq:当前检索
的关键字的文档总数)
lengthNorm:在lucene的底层实现中,lengthNorm是一种固定值,它无需在索引时指定,只需要跟随创建
索引过程,被写入索引中。
boost:文档的boost值一般情况下默认为1.0,在建立索引时可通过预先人为的指定Document或Field的
boost值,来改变搜索时文档的得分,从而改变文档在搜索结果中的排序位置。
2.使用Sort排序
该类有6种构造函数
public Sort()
public Sort(String field) 默认为降序
public Sort(String field,boolean reverse)
public Sort(String[] fields)
public Sort(SortField field)
public Sort(SortField[] fields)
Sort可以对单一的Field进行排序,但是对多个Field的排序就必须要借助SortField类了。SortField类
是个包装类,它能描述Field和reverse(排序)信息,在搜索结果中有很多记录时,应该正确的指定Field
的类型,这样对排序过程的效率会有很大的提高。
排序有多种
a.按文档得分进行排序:这是lucene默认的排序方式
如:Hits hist=searcher.search(q,Sort.RELEVANCE);
Sort.RELEVANCE表示当前的排序法则
是按照文档的得分进行降序排列
b.按文档的内部ID号来排序:在建立索引的时候,lucene会为每个文档建立一个内部的ID号
如:Hits hits=searcher.search(q,Sort.INDEXORDER)
Sort.INDEXORDER表示排序法则是按
照文档的内部ID号来排序
c.按一个或多个Field来排序:把每个Field封装成SortField,然后以SortField[]数组的形式传入Sort类
注意:比较字符串时,Locale信息的不同,很有可能会影响到比较的结果,因此SortField类有这样的构
造函数,可以帮助指定Locale信息
public SortField(String field,Locale locale)
public SortField(String field,Locale locale,boolean reverse)
当指定Locale后,lucene才可以正确的对字符串进行比较,从而正确的排序。不过大多数情况下,使用Sort
进行排序时,排序的内容多为int型或是日期型。即便需要进行字符串的比较,也最好使用仅包含ASCII的
字符串Field进行,这样可以确保排序的正确,同时,尽量提高排序的效率。
3.搜索时使用过滤器
简单示例:
Java代码 public class MySecurityFilter extends Filter{
public static final int SECURITY=0;
public BitSet bits(IndexReader reader) throws IOException{
//先初始化一个BitSet对象
final BitSet bits=new BitSet(reader.maxDoc());
//将整个集合置为true,表示当前集合内的所有文档都是可以被检索到的
bits.set(0,bits.size()-1);
//构造一个Term对象,代表最高安全级别
Term term=new Term("securitylevel",SECURITY+"");
//从索引中取出具有最高安全级别的文档
TermDocs termDocs=reader.termDocs(term);
//遍历每个文档
while(termDocs.next()){
bits.set(termDocs.doc(),false);
public class MySecurityFilter extends Filter{
public static final int SECURITY=0;
public BitSet bits(IndexReader reader) throws IOException{
//先初始化一个BitSet对象
final BitSet bits=new BitSet(reader.maxDoc());
//将整个集合置为true,表示当前集合内的所有文档都是可以被检索到的
bits.set(0,bits.size()-1);
//构造一个Term对象,代表最高安全级别
Term term=new Term("securitylevel",SECURITY+"");
//从索引中取出具有最高安全级别的文档
TermDocs termDocs=reader.termDocs(term);
//遍历每个文档
while(termDocs.next()){
bits.set(termDocs.doc(),false);
Java代码 public class MySecurityFilter extends Filter{
public static final int SECURITY=0;
public BitSet bits(IndexReader reader) throws IOException{
//先初始化一个BitSet对象
final BitSet bits=new BitSet(reader.maxDoc());
//将整个集合置为true,表示当前集合内的所有文档都是可以被检索到的
bits.set(0,bits.size()-1);
//构造一个Term对象,代表最高安全级别
Term term=new Term("securitylevel",SECURITY+"");
//初始化一个IndexSearcher对象
//查找securitylevel这个Field的值为SECURITY的文档
IndexSearcher searcher=new IndexSearcher(term);
Hits hits=searcher.search(new TermQuery(term));
for(int i=0;i<hits.length();i++){
bits.set(hits.id(i),false);
public class MySecurityFilter extends Filter{
public static final int SECURITY=0;
public BitSet bits(IndexReader reader) throws IOException{
//先初始化一个BitSet对象
final BitSet bits=new BitSet(reader.maxDoc());
//将整个集合置为true,表示当前集合内的所有文档都是可以被检索到的
bits.set(0,bits.size()-1);
//构造一个Term对象,代表最高安全级别
Term term=new Term("securitylevel",SECURITY+"");
//初始化一个IndexSearcher对象
//查找securitylevel这个Field的值为SECURITY的文档
IndexSearcher searcher=new IndexSearcher(term);
Hits hits=searcher.search(new TermQuery(term));
for(int i=0;i<hits.length();i++){
bits.set(hits.id(i),false);
注意:Filter实际上在进行真正的查询之前,已经遍历过一次一遍索引了,因此无
论使用何种Filter,都不应该忽视它对查询效率的影响。
4.在结果中查询(QueryFilter)
在QueryFilter的bits方法中,调用IndexSearcher方法,对注入的Query进行一次
检索,然后在要被返回的BitSet中,将检索结果所对应的文档标记为true,即过滤掉了
所有不再检索结果范围内的文档。
作者BLog:/blog/280679
历史上的今天:
引用地址:
范家晟:佳雪囡:梦の森林:佳雪囡:佳雪囡:佳雪囡:梦の森林:水墨:白粥咸带鱼:佳雪囡:
博客大巴模板设计: | 作者:日月&&&&素材来源:查看: 5613|回复: 2
Lucene与HBase的组合使用及HBasene的分析报告
主题帖子积分
1.Lucene中,以什么的形式作为搜索的主体?
2.Lucene与HBase的组合实现方法是什么?
3.HBase Table的实现有哪两种方式?
Lucene简介  Lucene中,以document的形式作为搜索的主体。document由fieldName和fieldValue所组成,每个fieldValue又可以由一个或多个term元素来组成。基于不同的分词及索引规则,可用于搜索fieldValue的term少于组成fieldValue的term。Lucene的搜索基于反向索引,包含着可用于搜索document的field信息。通过Lucene,可以正向查找document,以便了解其包含哪些field信息;也可以通过反向索引,通过搜索字段的term,来查询包含该term的document。
1.jpg (37.66 KB, 下载次数: 0)
19:44 上传
[ 图1 ]&&Lucene总体架构
  由图1所示,IndexSearcher实现了搜索的逻辑,IndexWriter实现了文档的插入与反向索引的建立,IndexReader由IndexSearcher调用以便读取索引的内容。IndexReader和IndexWriter都依赖于抽象类Directory,Directory提供操作索引数据及的API。  标准的Lucene是基于文件系统和基于内存的。  标准基于文件系统的后端的缺点在于,随着索引增加性能会下降,人们使用了各种不同的技术来解决这个问题,包括负载均衡和索引分片(index sharding,在多个Lucene实例之间切分索引)。尽管分片功能很强大,但它让总体的实现架构变得更复杂,并且需要大量对期望文档的预测知识,才能对Lucene索引进行合适地分片。另一方面,在大数据量的情况下,segment的合并花销巨大;频繁的update数据将使得Lucene对Disk io产生巨大的影响。一个新的数据的update,可能导致一部分根本没有变化的索引被重写很多次,并且可能导致很多的小的index segment,造成了search的性能下降。  Lucene的优势在于索引查找的迅速,而非document的存储。为解决上述问题,基于NoSQL数据库存储索引的后端结构应运而生。
以下,将基于HBase的实现来进行分析。实现方法  在Lucene中,其会操作两个单独的数据集:文档数据集中存储了所有文档,包括存储的字段等。索引数据集中存储了所有字段/词汇/词频/位置等信息,以及包含当前字段的document
  如果要实现将Lucene的后端移植到HBase上,直接构建一个Directory的实现并不会是最简单的。在已有的开源项目中,Lucandra和HBasene均采用了直接重写IndexReader和IndexWriter的方式,直接绕开了Directory的API。此实现并不会重写Lucene的索引查询机制。如若重载IndexSearcher,则可以在使用现有的Lucene索引查询机制上,根据后端的功能增强性能。
2.jpg (29.76 KB, 下载次数: 0)
19:44 上传
[ 图2 ] Lucene的后端重新设计
  图2的设计,可以将Lucene后端与HBase整合起来,将索引数据存储到HBase中,从而利用HBase的大数据存储以及分布式性能。
架构设计  在架构设计上,将HBase用作索引的持久化后端,同时可以如网上所说,基于内存实现一套缓存机制,用来提高数据读取速度。实现一套高效的缓存同步机制,也将有利于数据读写速率的提高。
3.jpg (41.64 KB, 下载次数: 0)
19:44 上传
[ 图3 ] 带有内存缓存以及同步缓存的HBase后端实现
  对于HBase的访问,每一次交互都需要通过以太网,以太网的运行状态将大大影响系统的使用情况,而索引的建立又希望能达到实时且高响应。为了平衡这两种相互冲突的需求,在内存中,缓存能够最小化HBase用于搜索和文件返回的数据读取量,从而极大提升性能;按照需要运行为多个Lucene示例以支持日益增长的搜索客户端的能力。后者需要最小化缓存的生命周期,从而和HBase实例(上面提到实例的副本)中的内容同步。通过为活动参数实现可配置的缓存时间,限制每个Lucene实例中展现的缓存,我们可以达成一种折中方案。  根据上述所描述的结构,对于读操作,首先会检查所需数据是否在内存中且没有过期,如果有效将直接使用,否者将从HBase中获取数据并更新到内存中。而对于写操作,可以简化到直接将数据写入到HBase中,进而不需要考虑是否需要建立或更新缓存这种复杂的问题,这也将提高系统的实时响应性。
HBase Table的实现当前了解到的两种可参考的实现方式:HBasene类型、Lucandra类型。
1-- HBasene其索引表由以下几个column family组成:fm.sequence:记录sequenceId,表示当前添加的第几个document。在执行createLuceneIndexTable时创建该行,且rowKey为segmentId,Column.qulifier为qual.sequence,Column.value=-1。每add一个document,当前segmentId的Column.value将自增1。fm.doc2int:每个document的存储都将被分配一个唯一的id,如果document的Field.Store=YES,则能够通过该id获取到对应的document的全部信息。fm.fields:记录了Field中value的内容,rowKey为documentId,Column.qulifier为FieldName,Column.value为FieldValue的内容。fm.termVector:向量偏移数据,用于模糊查找,记录了偏移量等信息,rowKey为FileldName/Term的组合,Column.qulifier为documentId,Column.value为指向的document中的所有位置偏移量。Column.value的结构为:[A][size][position]……[position]fm.termFrequencies:关键词在每个document中出现的频率,rowKey结构为zfm/FileName/Term,Column.qulifier为documentId,Column.value为出现的次数。
2-- Lucandra在Lucendra中,只有两个ColummFamily来存储数据,分别是TermInfo和Documents&Keyspace Name=&Lucandra&&
& & &ColumnFamily& &Name=&TermInfo& CompareWith=&BytesType& ColumnType=&Super&&&CompareSubcolumnsWith=&BytesType&&&KeysCached=&10%& /&
& & &ColumnFamily Name=&Documents& CompareWith=&BytesType& KeysCached=&10%& /&
&/Keyspace&复制代码
& && &&&TermInfo存储了Lucandra逆向索引的信息,用来存储index的Field信息,其结构如下:& && &&&RowKey:field/term& && &&&SuperColumn.name:documentId& && &&&[ SubColumn.name:&frequencies&&&Column.value:count ]& && &&&[ SubColumn.name:&position&&&Column.value:position vector ]& && &&&[ SubColumn.name:&offsets&&&Column.value:offsets vector ]& && &&&[ SubColumn.name:&norms&&&Column.value:norms vector ]
由于HBase中不存在SuperColumn和SubColumn的概念,我们可以将其简化为:& && &&&RowKey:field/term& && &&&Column.qulifier:documentId& && &&&Column.value:fieldInfo [ 此fieldInfo可以通过AVRO类定义 ]
& && &&&Documents存储了Document数据,其结构如下:& && &&&RowKey:documentId& && &&&Column.name:fieldName& && &&&Column.value:fieldValue
对比:& && &&&由HBasene的表结构可以知道,对于每一个document的更新以及每一次term的查询,都需要操纵多行数据才能实现,逻辑上相对复杂;而Lucandra只需要处理两行(documents及TermInfo各一行)。但是HBasene的优势在于单行的存储结构小,每次与HBase交互只需要传输少量的数据及可,并且不像Lucandra结构一样需要而外的AVRO组件来序列化与反序列化数据。
HBasene的缺陷:& && &&&1、HBasene在三年前已经停止了其项目的更新,开源的支持断开了。& && &&&2、在对HBasene的最新源码分析过程中,首先是发现其设计上保留着segment的概念,但是其设计阻碍着document的查询。其documentId由segmentId/sequenceId组成,每次segment的commit,sequenceId恢复为-1。而search时返回的TopDocs中只包含了sequenceId[int]。& && &&&3、每次添加document时,只有fm.Fields以及fm.doc2int的数据被实时flush到了HBase中,而其他数据需要segment大小到了一定程度[默认1000条document]才commit,容易造成数据缺失。在segment没被commit的情况下,是无法查询到新添加的document的。& && &&&4、HBase Table的设计中,没有考虑到当document中fieldName相同的情况。当fieldName相同的情况下,后面添加的会覆盖前面添加数据的,最后只保留最后添加的一份。& && &&&5、fm.termVector中,Column.value保存的并不是positionVector信息,而是segment中所有document里出现的次数。& && &&&6、fm.termFrequencies只是单纯地存储了,并没有被应用上。& && &&&7、IndexWriter的重写并没有实现所有函数,只实现了最基本的addDocument& && &&&8、对IndexSearch的重写和扩展也不够。
欢迎加入about云群 、 ,云计算爱好者群,关注
主题帖子积分
中级会员, 积分 404, 距离下一级还需 596 积分
中级会员, 积分 404, 距离下一级还需 596 积分
主题帖子积分
高级会员, 积分 3168, 距离下一级还需 1832 积分
高级会员, 积分 3168, 距离下一级还需 1832 积分
1.Lucene中,以什么的形式作为搜索的主体?
2.Lucene与HBase的组合实现方法是什么?
3.HBase Table的实现有哪两种方式?
经常参与各类话题的讨论,发帖内容较有主见
经常帮助其他会员答疑
活跃且尽责职守的版主
站长推荐 /6
about云|新出视频,openstack零基础入门,解决你ping不通外网难题
云计算hadoop视频大全(新增 yarn、flume|storm、hadoop一套视频
视频资料大优惠
大数据零基础由入门到实战
阶段1:hadoop零基础入门基础篇
阶段2:hadoop2入门
阶段3:大数据非hadoop系列课程
阶段4:项目实战篇
阶段5:大数据高级系列应用课程
阶段6:工作实用系列教程
等待验证会员请验证邮箱
新手获取积分方法
Powered by构建高性能分布式搜索引擎(Lucene.Net-内存溢出问题解决篇)一 - 推酷
构建高性能分布式搜索引擎(Lucene.Net-内存溢出问题解决篇)一
在索引数据比较少的时候内存问题不是很明显,但如果索引文件有几个G的时候,内存就会出现溢出问题.这个问题是由于Lucene的内部Bug引起的,如果在搜索时设置了排序,则它会缓存相关数据,但在下一次搜索时,它没有清除内部的一些缓存数据,所以会造成内存溢出.这个问题的解决办法就是改Lucene的源代码.
修改步骤如下:
修改Lucene源代码中Index目录中的IndexReader.cs文件,在其中加上如下代码:
&&&&&&&&&&
public string m_cacheKey =
(这一行代码主要用于Lucene内部用缓存的键值),以后清除缓存就用这个键值.
修改Lucene源代码中Search目录中的IndexSearcher.cs文件,修改其中的Close方法代码如下:
&&&&&&&&& public override void& Close()
&&&&&&&&&&& //清除缓存数据
&&&&&&&&&&&
if (!string.IsNullOrEmpty(reader.m_cacheKey))
&&&&&&&&&&& {
&&&&&&&&&&&&&&& if (null != parators)
&&&&&&&&&&&&&&& {
&&&&&&&&&&&&&&&&&&& parators.ReleaseCache(reader.m_cacheKey);
&&&&&&&&&&&&&&& }
&&&&&&&&&&&&&&& Lucene.Net.Search.FieldCache_Fields.DEFAULT.ReleaseFieldCache(reader.m_cacheKey);
&&&&&&&&&&&&&&& Lucene.Net.Search.ExtendedFieldCache_Fields.EXT_DEFAULT.ReleaseExtendedFieldCache(reader.m_cacheKey);
&&&&&&&&&&&&&&&
// 立即回收垃圾
&&&&&&&&&&&&&&& GC.Collect();
&&&&&&&&&&& }&&&&&&&&&&&&&&&&&&&
&&&&&&&&&&& //关闭读取器
&&&if (closeReader)
&&&&reader.Close();
&&&&&&&&&&
修改Lucene源代码中Search目录中的FieldCache.cs文件,修改其中的接口public interface FieldCache,并添加接口方法,方法代码如下:
//用于清除缓存
&&&&&&& void ReleaseFieldCache(string strCacheKey);
修改Lucene源代码中Search目录中的FieldCacheImpl.cs文件,添加下列方法,
&&&&&&&&&&
& //用于清除缓存
&&&&&&& public void ReleaseFieldCache(string strCacheKey)
&&&&&&&&&&& bytesCache.ReleaseCache(strCacheKey);
&&&&&&&&&&& shortsCache.ReleaseCache(strCacheKey);
&&&&&&&&&&& intsCache.ReleaseCache(strCacheKey);
&&&&&&&&&&& floatsCache.ReleaseCache(strCacheKey);
&&&&&&&&&&& stringsCache.ReleaseCache(strCacheKey);
&&&&&&&&&&& stringsIndexCache.ReleaseCache(strCacheKey);
&&&&&&&&&&& autoCache.ReleaseCache(strCacheKey);
&&&&&&&&&&& customCache.ReleaseCache(strCacheKey);
&&&&&&& 修改内部类:internal abstract class Cache,添加方法如下:
&&&&&&&&&&
//用于清除缓存
&&&&&&&&&&& public void ReleaseCache(string strCacheKey)
&&&&&&&&&&& {
&&&&&&&&&&&&&&& if (null != readerCache)
&&&&&&&&&&&&&&& {
&&&&&&&&&&&&&&&&&&& lock (readerCache.SyncRoot)
&&&&&&&&&&&&&&&&&&& {
&&&&&&&&&&&&&&&&&&&&&&& if (null != readerCache[strCacheKey])
&&&&&&&&&&&&&&&&&&&&&&& {
&&&&&&&&&&&&&&&&&&&&&&&&&&& ((System.Collections.Hashtable)readerCache[strCacheKey]).Clear();
&&&&&&&&&&&&&&&&&&&&&&& }&&&&&&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&&&& }
&&&&&&&&&&&&&&& }
&&&&&&&&&&& }
&&&&&&&&&&& 修改方法Get:
&&&&&&&&&&&&&& public virtual System.Object Get(IndexReader reader, System.Object key)
&&&&System.Collections.IDictionary innerC
&&&&System.Object value_R
&&&&lock (readerCache.SyncRoot)
&&&&&&&&&&&&&&&&&&&
//改键值为静态值
&&&&&&&&&&&&&&&&&&& innerCache = (System.Collections.IDictionary)readerCache[reader.m_cacheKey];
&&&&&//innerCache = (System.Collections.IDictionary) readerCache[reader];
&&&&&if (innerCache == null)
&&&&&&innerCache = new System.Collections.Hashtable();
&&&&&&&&&&&&&&&&&&
&&&&& //改键值为静态值
&&&&&&&&&&&&&&&&&&&&&&& readerCache[reader.m_cacheKey] = innerC
&&&&&&//readerCache[reader] = innerC
&&&&&&value_Renamed =
&&&&&&value_Renamed = innerCache[key];
&&&&&if (value_Renamed == null)
&&&&&&value_Renamed = new CreationPlaceholder();
&&&&&&innerCache[key] = value_R
&&&&if (value_Renamed is CreationPlaceholder)
&&&&&lock (value_Renamed)
&&&&&&CreationPlaceholder progress = (CreationPlaceholder) value_R
&&&&&&if (progress.value_Renamed == null)
&&&&&&&progress.value_Renamed = CreateValue(reader, key);
&&&&&&&lock (readerCache.SyncRoot)
&&&&&&&&innerCache[key] = progress.value_R
&&&&&&return progress.value_R
&&&&return value_R
&&&&&&&&&&
修改Lucene源代码中Search目录中的ExtendedFieldCache.cs文件,修改其中的接口public interface ExtendedFieldCache : FieldCache,并添加接口方法,方法代码如下:
//用于清除缓存
&&&&&&& void ReleaseExtendedFieldCache(string strCacheKey);
修改Lucene源代码中Search目录中的ExtendedFieldCacheImpl.cs文件,添加下列方法,代码如下:
& //用于清除缓存
&&&&&&& public void ReleaseExtendedFieldCache(string strCacheKey)
&&&&&&&&&&& longsCache.ReleaseCache(strCacheKey);
&&&&&&&&&&& doublesCache.ReleaseCache(strCacheKey);
&&&&&&&&&&& autoCache.ReleaseCache(strCacheKey);
通过上述6个步骤,内存溢出问题就可以解决了,但还有一个需要注意的就是在更新索引时,一定要调用如下代码,才能清掉缓存:
//清缓排序缓存数据
IndexSearcher.Close();
IndexReader.Close();
深圳E搜科技(搜索引擎技术钻研者!)
QQ:4724029
已发表评论数()
请填写推刊名
描述不能大于100个字符!
权限设置: 公开
仅自己可见
正文不准确
标题不准确
排版有问题
主题不准确
没有分页内容
图片无法显示
视频无法显示
与原文不一致

我要回帖

更多关于 搜索引擎lucene 的文章

 

随机推荐