Lucene.net(4.8.0) 学习问题记录四: IndexWriter 索引的优化以及思考
前言:目前自己在做使用Lucene.net和PanGu分词实现全文检索的工作,不过自己是把别人做好的项目进行迁移。因为项目整体要迁移到ASP.NET Core 2.0版本,而Lucene使用的版本是3.6.0 ,PanGu分词也是对应Lucene3.6.0版本的。不过好在Lucene.net 已经有了Core 2.0版本(4.8.0 bate版),而PanGu分词,目前有人正在做,貌似已经做完,只是还没有测试~,Lucene升级的改变我都会加粗表示。
Lucene.net 4.8.0
https://github.com/apache/lucenenet
PanGu分词(可以直接使用的)
https://github.com/SilentCC/Lucene.Net.Analysis.PanGu
JIEba分词(可以直接使用的)
https://github.com/SilentCC/JIEba-netcore2.0
Lucene.net 4.8.0 和之前的Lucene.net 3.6.0 改动还是相当多的,这里对自己开发过程遇到的问题,做一个记录吧,希望可以帮到和我一样需要升级Lucene.net的人。我也是第一次接触Lucene ,也希望可以帮助初学Lucene的同学。
一,优化建索引速度的方法总结
1.IndexWriter的简介
http://www.cnblogs.com/dacc123/p/8228298.html
在这片博文,介绍了IndexWriter, 也提到了IndexWriter优化索引速度的方法,但是觉得还比较片面,所以重新写一篇优化索引速度的博文。
2.优化索引速度的方法
2.1 使用更快的硬件设备
使用更快的硬件设备,使用固态硬盘代替普通的硬盘,会提高索引的读写速度。
2.2 设置IndexWriter的RAMBufferSizeMB
设置IndexWriter的RAMBufferSizeMB 使得IndexWriter能利用更多的内存,应对海量的数据,这样IndexWriter的flush的操作也会减少。当然同样的设置还有设置IndexWriter的MaxBufferedDocs ,推荐使用RAMBufferSizeMB 。并且关闭,autoCommit = false。
2.3 设置IndexWriter的MergeFactor
IndexWriter的MergeFactor决定了当索引中segment数量达到多少时,就将这些segment合并成一个大的segment文件。设置MergeFactor越大,IndexWriter将会减少合并的操作,可以提高索引的速度。带来的结果是索引文件中会有很多的segment文件,需要优化,否则会影响搜索速度。所以需要选择一个合适的MergeFactor
2.4 关闭复合文件格式
setUseCompoundFile(false)。生成复合文件会消耗更多的时间,关闭复合文件格式会导致增加搜索和索引使用的文件句柄的数量。
2.5 使用单例的IndexWriter
多线程创建索引使用唯一的IndexWriter,而不是每次都创建一个IndexWriter。
2.6 使用更快的分词器
事实上建索引的时间大部分都花在了分词的时间上,一个好的分词器,将大大减少索引的时间。而关于分词器,我会再写一篇博文去研究,下面给出一个不通分词器的性能测试demo:
https://github.com/ysc/cws_evaluation
2.7 加快获取文档的时间
很多建索引速度慢的原因不是出在Lucene上,而是获取文档的速度太慢,所以一个很好的快速获取文档的机制很重要。
2.8 分布建索引
如果索引文件非常大,那么可以考虑分布建索引,再把这些分段索引合并起来。IndexWriter.AddIndexes()用来把不同文件夹中的索引合并到一个文件夹中,且合并之后的索引是最优的,也就是Optimize(1)之后的索引。当然分布索引放在不同的服务器上,效率才是翻倍的。
二,优化建索引速度的思考
1.分布建索引(伪)
我在做搜索的时候,42个G的索引文件,需要7个小时从头到尾重建完,这里包括了我获取文档的时间,以及接口通信的时间。然后优化Optimize(1),这也需要1~2个小时的时间。于是我想将重建索引的速度继续降低。通过上面的设置IndexWriter的参数的方法,时间虽然有减少,但是效果不明显。这也表示着,如果你建索引的时间已经大大的无法让你承受,那么修改IndexWriter的RAMBufferSizeMB,MergeFactor等等参数,也是无济于事的。Lucene的索引性能不会因为改了几个参数而得到显著的提升。所以这个时候我们就需要从分词器,分布建索引,以及整个重建索引的机制下手。
由于公司只给我了一台服务器,所以我选择在这台服务器上跑了两个相同的应用在不同的文件夹分布建索引,再调用IndexWriter.Addindexes合并索引,虽然是在同一台服务器上,分两个应用确实可以最大的发挥cpu的效率,最终6个小时后,两段索引都建完了。而合并需要半个小时,让我惊喜的是合并之后的索引已经是十分完美的。原本9个小时的索引重建工作也缩短到了6个半小时。这里要提一下IndexWriter.AddIndexes(),有两个函数.
//只是把一些列文件夹中的索引,复制到同一个文件夹中,并不会合并他们, IndexWriter.AddIndexes(Directory[] d) //把一系列文件中的索引,合并到同一个文件中,在合并时,子文件夹中不能有IndexWriter在操作 IndexWriter.AddIndexes(IndexReader[] i)
显然选择第二个是比较好的。
如果再有一台服务器,那么效率则是会翻倍的。
2. 选择新的分词器
我使用的是PanGu分词器,根据官网上的指标:
Core Duo 1.8 GHz 下单线程 分词速度为 390K 字符每秒,2线程分词速度为 690K 字符每秒。
在上面的链接中,我发现了很多分词速度更快的分词器,比如JIEba分词器,Word分词器。但是并不适合选择那些快速分词模式的分词器,因为搜索引擎最重要的是搜索效果,而不是你后台建索引的速度。一般的搜索引擎在建索引的时候会选择细粒度分词,也就是将词分的越细越好,分的词越多越好,这样可以提高召回率。当然带来的负面作用是分词的速度下降。所以建索引的时候还是选择那些细粒度的分词模式是最好的,而搜索的时候可以用粗粒度的分词模式,索引速度已经是次要的。
另外,推荐使用JIEba分词,JIEba分词的分词效果确实比PanGu分词要好。在PanGu分词打开多元分词开关之后二者的分词效果对比:
测试样例:小明硕士毕业于中国科学院计算所,后在日本京都大学深造 结巴分词(搜索引擎模式):小明/ 硕士/ 毕业/ 于/ 中国/ 科学/ 学院/ 科学院/ 中国科学院/ 计算/ 计算所/ ,/ 后/ 在/ 日本/ 京都/ 大学/ 日本京都大学/ 深造 盘古分词(开启多元分词开关): 小 明 硕士 毕业 于 中国科学院 计算所 后 在 日本 京都 大学 深造
3.更改重建索引机制
我的建索引机制是,每次向提供数据的接口请求200个文档,然后把200个文档调用写索引接口,返回正确写入后,再去请求下一个200个文档。我修改了这样的机制,使用一个文档队列,长度为10000.请求文档不需要等写索引返回成功直到队列满,写索引直接出队列知道队列为空。并且写在一个应用中。速度如何还没有测试。
4.根本错误
现在搜索引擎的机制是每天晚上重建索引,因为这个机制,给我带来了很多麻烦,所以才有优化建索引的需求。很显然一个好的搜索引擎肯定不是每晚重建索引,应该是实时的维护索引,但是由于应用比较多,实现起来开发成本很高,所以暂时只能这样先凑合。将在不久的将来,使用事件总线,彻底解决维护索引的机制。
三,结语
随着用户的增多,索引量的不断变大,分布式的搜索引擎,也必不可少,这篇博文值得推荐看看