首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件架构设计 >

Lucene使用的一点体会

2012-11-05 
Lucene应用的一点体会Lucene应用(我用的是Lucene2.1.0,有些观点有可能也不太正确)1.多线程索引,共享同一个

Lucene应用的一点体会
Lucene应用(我用的是Lucene2.1.0,有些观点有可能也不太正确)

1.多线程索引,共享同一个IndexWriter对象

这种方式效率很慢,主要原因是因为:

java 代码?

  1. public?void?addDocument(Document?doc,?Analyzer?analyzer)?throws?IOException?{??
  2. SegmentInfo?newSegmentInfo?=?buildSingleDocSegment(doc,?analyzer);??
  3. synchronized?(this)?{??
  4. ramSegmentInfos.addElement(newSegmentInfo);//这句很占用效率??
  5. maybeFlushRamSegments();??
  6. }??
  7. }??
ramSegmentInfos 是一个SegmentInfos 对象,这个对象extends Vector,Vector的addElement是同步的。这个可能是导致效率慢的主要原因吧.

2 多线程索引,? 先写到RAMDirectory,再一次性写到FSDirectory

功能:首先向RAMDirectory里写,当达到1000个Document後,再向FSDirectory里写。

当多线程执行时,会大量报java.lang.NullPointerException

自己写的多线程索引的类为(IndexWriterServer,该对象只在Server启动时初始化一次):

代码
  1. public?class?IndexWriterServer{??
  2. private?static?IndexWriter?indexWriter?=?null;??
  3. ??????
  4. ????//private?String?indexDir?;//索引目录;??
  5. ??????
  6. ????private?static??CJKAnalyzer?analyzer?=?null;??
  7. ??????
  8. ????private?static?RAMDirectory?ramDir?=?new?RAMDirectory();??
  9. ??????
  10. ????private?static?IndexWriter?ramWriter?=?null;??
  11. ??????
  12. ????private?static?int?diskFactor?=?0;//内存中现在有多少Document??
  13. ??????
  14. ????private?static?long?ramToDistTime?=?0;//内存向硬盘写需要多少时间??
  15. ??????
  16. ????private?int?initValue?=?1000;//内存中达到多少Document,才向硬盘写??
  17. ??????
  18. ????private?static?IndexItem?[]indexItems?=?null;??
  19. ??????
  20. ????public?IndexWriterServer(String?indexDir){??
  21. ????????initIndexWriter(indexDir);??
  22. ????}??
  23. ????public?void?initIndexWriter(String?indexDir){??
  24. ????????boolean?create?=?false;//是否创建新的??
  25. ??????????
  26. ????????analyzer?=?new?CJKAnalyzer();??
  27. ??????????
  28. ????????Directory?directory?=?this.getDirectory(indexDir);??
  29. ????????//判断是否为索引目录??
  30. ????????if(!IndexReader.indexExists(indexDir)){??
  31. ????????????create?=?true;??
  32. ????????}??
  33. ??????????
  34. ????????indexWriter?=?getIndexWriter(directory,create);??
  35. ??????????
  36. ????????try{??
  37. ????????????ramWriter?=?new?IndexWriter(ramDir,?analyzer,?true);??
  38. ????????}catch(Exception?e){??
  39. ????????????logger.info(e);??
  40. ????????}??
  41. ??????????
  42. ????????indexItems?=?new?IndexItem[initValue+2];??
  43. ????}??
  44. ??
  45. /**?
  46. ?????*?生成单个Item索引?
  47. ?????*/??
  48. ????public?boolean?generatorItemIndex(IndexItem?item,?Current?__current)?throws?DatabaseError,?RuntimeError{??
  49. ????????boolean?isSuccess?=?true;//是否索引成功??
  50. ??????????
  51. ????????try{??
  52. ??????????????
  53. ????????????Document?doc?=?getItemDocument(item);??
  54. ??????????????
  55. ????????????ramWriter.addDocument(doc);//关键代码,错误就是从这里报出来的??
  56. ??????????????
  57. ????????????indexItems[diskFactor]?=?item;//为数据挖掘使用??
  58. ????????????diskFactor?++;??
  59. ????????????if((diskFactor?%?initValue)?==?0){??
  60. ????????????????ramToDisk(ramDir,ramWriter,indexWriter);??
  61. ????????????????//ramWriter?=?new?IndexWriter(ramDir,?analyzer,?true);??
  62. ????????????????diskFactor?=?0;??
  63. ??????????????????
  64. ????????????????//数据挖掘??
  65. ????????????????isSuccess?=?MiningData();??
  66. ??
  67. ??????????????????
  68. ????????????}??
  69. ??????????????
  70. ????????????doc?=?null;??
  71. ??????????????
  72. ??
  73. ????????????logger.info("generator?index?item?link:"?+?item.itemLink??+"?success");??
  74. ????????}catch(Exception?e){??
  75. ????????????logger.info(e);??
  76. ????????????e.printStackTrace();??
  77. ??????
  78. ????????????logger.info("generator?index?item?link:"?+?item.itemLink??+"?faiture");??
  79. ????????????isSuccess?=?false;??
  80. ????????}finally{??
  81. ????????????item?=?null;??
  82. ????????}??
  83. ??????????
  84. ????????return?isSuccess;??
  85. ????}??
  86. ??
  87. public??void?ramToDisk(RAMDirectory?ramDir,?IndexWriter?ramWriter,IndexWriter?writer){??
  88. ????????try{??
  89. ????????????ramWriter.close();//关键代码,把fileMap赋值为null了??
  90. ????????????ramWriter?=?new?IndexWriter(ramDir,?analyzer,?true);//重新构建一个ramWriter对象。因为它的fileMap为null了,但是好像并没有太大作用??
  91. ????????????Directory?ramDirArray[]?=?new?Directory[1];??
  92. ????????????ramDirArray[0]?=?ramDir;??
  93. ????????????mergeDirs(writer,?ramDirArray);??
  94. ????????}catch(Exception?e){??
  95. ????????????logger.info(e);??
  96. ????????}??
  97. ????}??
  98. ????/**?
  99. ?????*?将内存里的索引信息写到硬盘里?
  100. ?????*?@param?writer?
  101. ?????*?@param?ramDirArray?
  102. ?????*/???
  103. ????public??void?mergeDirs(IndexWriter?writer,Directory[]?ramDirArray){??
  104. ????????try?{??
  105. ????????????writer.addIndexes(ramDirArray);??
  106. ????????????//optimize();??
  107. ????????}?catch?(IOException?e)?{??
  108. ????????????logger.info(e);??
  109. ????????}??
  110. ????}??
  111. ??????
  112. }??
<script type="text/javascript">render_code();</script>

主要原因大概是因为:在调用ramWriter.close();时,Lucene2.1里RAMDirectory 的close()方法

代码
  1. public?final?void?close()?{??
  2. ???fileMap?=?null;??
  3. ?}??
<script type="text/javascript">render_code();</script>
把fileMap 给置null了,当多线程执行ramWriter.addDocument(doc);时,最终执行RAMDirectory 的方法:代码
  1. public?IndexOutput?createOutput(String?name)?{??
  2. ????RAMFile?file?=?new?RAMFile(this);??
  3. ????synchronized?(this)?{??
  4. ????RAMFile?existing?=?(RAMFile)fileMap.get(name);//fileMap为null,所以报:NullPointerException,??
  5. ??????if?(existing!=null)?{??
  6. ????????sizeInBytes?-=?existing.sizeInBytes;??
  7. ????????existing.directory?=?null;??
  8. ??????}??
  9. ??????fileMap.put(name,?file);??
  10. ????}??
  11. ????return?new?RAMOutputStream(file);??
  12. ??}??
<script type="text/javascript">render_code();</script>

提示:在网上搜索了一下,好像这个是lucene的一个bug(http://www.opensubscriber.com/message/java-user@lucene.apache.org/6227647.html),但是好像并没有给出解决方案。


?

3.多线程索引,每个线程一个IndexWriter对象,每个IndexWriter 绑定一个FSDirectory对象。每个FSDirectory绑定一个本地的磁盘目录(唯一的)。单独开辟一个线程出来监控这些索引线程(监控线程),也就是说负责索引的线程索引完了以后,给这个监控线程的queue里发送一个对象:queue.add(directory);,这个监控现成的queue对象是个全局的。当这个queue的size() > 20 时,监控线程 把这20个索引目录合并(merge):indexWriter.addIndexes(dirs);//合并索引,合并到真正的索引目录里。,合并完了以后,然后删除掉这些已经合并了的目录。

但是这样也有几个bug:

a. 合并线程的速度 小于 索引线程的速度。导致 目录越来越多

b.经常会报一个类似这样的错误:

2007-06-08 10:49:18 INFO [Thread-2] (IndexWriter.java:1070) - java.io.FileNotFoundException: /home/spider/luceneserver/merge/item_d28686afe01f365c5669e1f19a2492c8/_1.cfs (No such file or directory)


?

4.单线程索引,调几个参数後,效率也非常快(索引一条信息大概在6-30 ms之间)。感觉一般的需求单线程就够用了。这些参数如下:

?? private int mergeFactor = 100;//磁盘里达到多少後会自动合并
??? private int maxMergeDocs = 1000;//内存中达到多少会向磁盘写入
??? private int minMergeDocs = 1000;//lucene2.0已经取消了
??? private int maxFieldLength = 2000;//索引的最大文章长度
??? private int maxBufferedDocs = 10000;//这个参数不能要,要不然不会自动合并了


得出的结论是:Lucene的多线程索引会有些问题,如果没有特殊需求,单线程的效率几乎就能满足需求.


?

如果单线程的速度满足不了你的需求,你可以多开几个应用。每个应用都绑定一个FSDirectory,然后通过search时通过RMI去这些索引目录进行搜索。

RMI Server端,关键性代码:


?

java 代码
  1. private?void?initRMI(){??
  2. ????????//第一安全配置??
  3. ????????if?(System.getSecurityManager()?==?null)?{??
  4. ????????????System.setSecurityManager(?new?RMISecurityManager()?);??
  5. ????????}??
  6. ????????//注册??
  7. ????????startRMIRegistry(serverUrl);??
  8. ??????????
  9. ????????SearcherWork?searcherWork?=?new?SearcherWork("//"?+?serverUrl?+?"/"?+?bindName,?directory);??
  10. ??????????
  11. ????????searcherWork.run();??
  12. ??????????
  13. ????}??
  14. ??
  15. public?class?SearcherWork??{??
  16. //???Logger??
  17. ????private?static?Logger?logger?=?Logger.getLogger(SearcherWork.class);??
  18. ????private?String?serverUrl?=null;??
  19. ????private?Directory?directory?=null;??
  20. ??
  21. ????public?SearcherWork(){??
  22. ??????????
  23. ????}??
  24. ??????
  25. ????public?SearcherWork(String?serverUrl,?Directory?directory){??
  26. ????????this.serverUrl?=?serverUrl;??
  27. ????????this.directory?=?directory;??
  28. ????}??
  29. ??????
  30. ????public?void?run(){??
  31. ????????try{??
  32. ?????????????Searchable?searcher?=?new?IndexSearcher(directory);??
  33. ?????????????SearchService?service?=?new?SearchService(searcher);??
  34. ?????????????Naming.rebind(serverUrl,?service);??
  35. ?????????????logger.info("RMI?Server?bind?"?+?serverUrl?+?"?success");??
  36. ??????????????
  37. ????????}catch(Exception?e){??
  38. ????????????logger.info(e);??
  39. ????????????System.out.println(e);??
  40. ????????}??
  41. ????}??
  42. ??????
  43. ??
  44. }??
  45. ??
  46. public?class?SearchService?extends?RemoteSearchable?implements?Searchable?{??
  47. ??????
  48. ????public?SearchService?(Searchable?local)?throws?RemoteException?{??
  49. ????????super(local);??
  50. ????}??
  51. }??

客户端关键性代码:

java 代码?
  1. RemoteLuceneConnector?rlc=?new?RemoteLuceneConnector();??
  2. RemoteSearchable[]?rs=?rlc.getRemoteSearchers();??
  3. MultiSearcher?multi?=?new?MultiSearcher(rs);??
  4. Hits?hits?=?multi.search(new?TermQuery(new?Term("content","中国")));??

?


1 楼 sg552 2007-06-11   楼主想说些什么?


LUCENE到底是好?还是不好?
还是在普及LUCENE知识??? 2 楼 rtdb 2007-06-11   支持一下楼主,这种个人应用上的体会,是有价值的。 3 楼 restart 2007-06-11   楼主,光看你的第一点“多线程索引,共享同一个IndexWriter对象”就严重错误了! 4 楼 ttitfly 2007-06-11   只是我个人测试的结果,有些地方不对请指点,请问restart错在什么地方? 5 楼 yfmine 2007-06-12   引用
private int maxMergeDocs = 1000;//内存中达到多少会向磁盘写入

Determines the largest number of documents ever merged by addDocument().

引用
    private int minMergeDocs = 1000;//lucene2.0已经取消了
    private int maxBufferedDocs = 10000;//这个参数不能要,要不然不会自动合并了

minMergeDocs没有取消,就是改成setMaxBufferedDocs了。

引用
ramWriter = new IndexWriter(ramDir, analyzer, true);//重新构建一个ramWriter对象。因为它的fileMap为null了,但是好像并没有太大作用 

lz你的代码有问题,这个是参数……

在实际中,我会用多线程开多个IndexWriter分别索引到不同的目录,IndexWriter被设计成线程安全的,有同步机制,多线程使用同一个IndexWriter,应该没有多大提高,和lz说的3比较类似,但通常不会合并(索引目录是按数据库表分的)。索引的速度和I/O关系比较大,要么是写入磁盘比较慢,要么是从数据源读取慢,通过调整maxBufferedDocs,写入索引文件这一端应该不会成为瓶颈。但调整到10000很可能服务器没那么大的内存。一般都是1000左右

热点排行