Skip to content

加速 lucene 索引建立速度 ImproveIndexingSpeed

本文 只是简单的翻译,原文http://wiki.apache.org/lucene-java/ImproveIndexingSpeed

* Be sure you really need to speed things up.

Many of the ideas here are simple to try, but others will necessarily add some complexity to your application. So be sure your indexing speed is indeed too slow and the slowness is indeed within Lucene.

* 请确认你真的需要更快的索引速度

这里的很多想法都非常容易尝试,但也有一些会给你的程序带来额外的复杂度。所以请确认你的搜索速度真的慢到不能忍受,并且慢的原因的确是因为lucene。

*Make sure you are using the latest version of Lucene.

* 确认你在使用最新的Lucene版本。

* Use a local filesystem.

Remote filesystems are typically quite a bit slower for indexing. If your index needs to be on the remote fileysystem, consider building it first on the local filesystem and then copying it up to the remote filesystem.

* 尽量使用本地文件系统

远程文件系统一般来说都会降低索引速度。如果索引必须分布在远程服务器,请尝试先在本地生成索引,然后分发到远程服务器上。

* Get faster hardware, especially a faster IO system.

* 使用更快的硬件设备,特别是更快的IO设备

* Open a single writer and re-use it for the duration of your indexing session.

* 在索引期间复用单一的IndexWriter实例

* Flush by RAM usage instead of document count.

For Lucene <= 2.2: call writer.ramSizeInBytes() after every added doc then call flush() when it’s using too much RAM. This is especially good if you have small docs or highly variable doc sizes. You need to first set maxBufferedDocs large enough to prevent the writer from flushing based on document count. However, don’t set it too large otherwise you may hit LUCENE-845. Somewhere around 2-3X your “typical” flush count should be OK.

For Lucene >= 2.3: IndexWriter can flush according to RAM usage itself. Call writer.setRAMBufferSizeMB() to set the buffer size. Be sure you don’t also have any leftover calls to setMaxBufferedDocs since the writer will flush “either or” (whichever comes first).

* 按照内存消耗Flush代替根据文档数量Flush

在Lucene 2.2之前的版本,可以在每次添加文档后调用ramSizeInBytes方法,当索引消耗过多的内存时,然后在调用flush()方法。这样做在索引大量小文档或者文档大小不定的情况下尤为有效。你必须先把maxBufferedDocs参数设置足够大,以防止writer基于文档数量flush。但是注意,别把这个值设置的太大,否则你将遭遇Lucene-845号BUG。不过这个BUG已经在2.3版本中得到解决。
在 Lucene2.3之后的版本。IndexWriter可以自动的根据内存消耗调用flush()。你可以通过 writer.setRAMBufferSizeMB()来设置缓存大小。当你打算按照内存大小flush后,确保没有在别的地方设置 MaxBufferedDocs值。否则flush条件将变的不确定(谁先符合条件就按照谁)。

* Use as much RAM as you can afford.

More RAM before flushing means Lucene writes larger segments to begin with which means less merging later. Testing in LUCENE-843 found that around 48 MB is the sweet spot for that content set, but, your application could have a different sweet spot.

* 在你能承受的范围内使用更多的内存

在 flush前使用更多的内存意味着Lucene将在索引时生成更大的segment,也意味着合并次数也随之减少。在Lucene-843中测试,大概 48MB内存可能是一个比较合适的值。但是,你的程序可能会是另外一个值。这跟不同的机器也有一定的关系,请自己多加测试,选择一个权衡值。

* Turn off compound file format.

Call setUseCompoundFile(false). Building the compound file format takes time during indexing (7-33% in testing for LUCENE-888). However, note that doing this will greatly increase the number of file descriptors used by indexing and by searching, so you could run out of file descriptors if mergeFactor is also large.

* 关闭复合文件格式

调用setUseCompoundFile(false)可以关闭复合文件选项。生成复合文件将消耗更多的时间(经过Lucene-888测试,大概会增加 7%-33%的时间)。但是请注意,这样做将大大的增加搜索和索引使用的文件句柄的数量。如果合并因子也很大的话,你可能会出现用光文件句柄的情况。

* Re-use Document and Field instances

As of Lucene 2.3 there are new setValue(…) methods that allow you to change the value of a Field. This allows you to re-use a single Field instance across many added documents, which can save substantial GC cost.

It’s best to create a single Document instance, then add multiple Field instances to it, but hold onto these Field instances and re-use them by changing their values for each added document. For example you might have an idField, bodyField, nameField, storedField1, etc. After the document is added, you then directly change the Field values (idField.setValue(…), etc), and then re-add your Document instance.

Note that you cannot re-use a single Field instance within a Document, and, you should not change a Field’s value until the Document containing that Field has been added to the index. See Field for details.

* 重用Document和Field实例

在lucene 2.3中,新增了一个叫setValue的方法,可以允许你改变字段的值。这样的好处是你可以在整个索引进程中复用一个Filed实例。这将极大的减少GC负担。
最好创建一个单一的Document实例,然后添加你想要的字段到文档中。同时复用添加到文档的Field实例,通用调用相应的SetValue方法改变相应的字段的值。然后重新将Document添加到索引中。
注意:你不能在一个文档中多个字段共用一个Field实例,在文档添加到索引之前,Field的值都不应该改变。也就是说如果你有3个字段,你必须创建3个Field实例,然后再之后的Document添加过程中复用它们。

* Re-use a single Token instance in your analyzer

Analyzers often create a new Token for each term in sequence that needs to be indexed from a Field. You can save substantial GC cost by re-using a single Token instance instead.

* 在你的分析器Analyzer中使用一个单一的Token实例

在分析器中共享一个单一的token实例也将缓解GC的压力。

* Use the char[] API in Token instead of the String API to represent token Text

As of Lucene 2.3, a Token can represent its text as a slice into a char array, which saves the GC cost of new’ing and then reclaiming String instances. By re-using a single Token instance and using the char[] API you can avoid new’ing any objects for each term. See Token for details.

* 在Token中使用char[]接口来代替String接口来表示数据

在Lucene 2.3中,Token可以使用char数组来表示他的数据。这样可以避免构建字符串以及GC回收字符串的消耗。通过配合使用单一Token实例和使用char[]接口你可以避免创建新的对象。

* Use autoCommit=false when you open your IndexWriter

In Lucene 2.3 there are substantial optimizations for Documents that use stored fields and term vectors, to save merging of these very large index files. You should see the best gains by using autoCommit=false for a single long-running session of IndexWriter. Note however that searchers will not see any of the changes flushed by this IndexWriter until it is closed; if that is important you should stick with autoCommit=true instead or periodically close and re-open the writer.

* 设置autoCommit为false

在Lucene 2.3中对拥有存储字段和Term向量的文档进行了大量的优化,以节省大索引合并的时间。你可以将单一复用的IndexWriter实例的 autoCommit设置为false来见证这些优化带来的好处。注意这样做将导致searcher在IndexWriter关闭之前不会看到任何索引的更新。如果你认为这个对你很重要,你可以继续将autoCommit设置为true,或者周期性的打开和关闭你的writer。

* Instead of indexing many small text fields, aggregate the text into a single “contents” field and index only that (you can still store the other fields).

* 如果你要索引很多小文本字段,如果没有特别需求,建议你将这些小文本字段合并为一个大的contents字段,然后只索引contents。(当然你也可以继续存储那些字段)

* Increase mergeFactor, but not too much.

Larger mergeFactors defers merging of segments until later, thus speeding up indexing because merging is a large part of indexing. However, this will slow down searching, and, you will run out of file descriptors if you make it too large. Values that are too large may even slow down indexing since merging more segments at once means much more seeking for the hard drives.

* 加大mergeFactor合并因子,但不是越大越好

大的合并因子将延迟segment的合并时间,这样做可以提高索引速度,因为合并是索引很耗时的一个部分。但是,这样做将降低你的搜索速度。同时,你有可能会用光你的文件句柄如果你把合并因子设置的太大。值太大了设置可能降低索引速度,因为这意味着将同时合并更多的segment,将大大的增加硬盘的负担。

* Turn off any features you are not in fact using.

If you are storing fields but not using them at query time, don’t store them. Likewise for term vectors. If you are indexing many fields, turning off norms for those fields may help performance.

* 关闭所有你实际上没有使用的功能

如果你存储了字段,但是在查询时根本没有用到它们,那么别存储它们。同样Term向量也是如此。如果你索引很多的字段,关闭这些字段的不必要的特性将对索引速度提升产生很大的帮助。

* Use a faster analyzer.

Sometimes analysis of a document takes alot of time. For example, StandardAnalyzer is quite time consuming, especially in Lucene version <= 2.2. If you can get by with a simpler analyzer, then try it.

* 使用一个更快的分析器

有时间分析文档将消耗很长的时间。举例来说,StandardAnalyzer就比较耗时,尤其在Lucene 2.3版本之前。你可以尝试使用一个更简单更快但是符合你需求的分析器。

* Speed up document construction.

Often the process of retrieving a document from somewhere external (database, filesystem, crawled from a Web site, etc.) is very time consuming.

* 加速文档的构建时间

在通常的情况下,文档的数据来源可能是外部(比如数据库,文件系统,蜘蛛从网站上的抓取等),这些通常都比较耗时,尽量优化获取它们的性能。

* Don’t optimize unless you really need to (for faster searching).

* 在你真的需要之前不要随意的优化optimize索引(只有在需要更快的搜索速度的时候)

* Use multiple threads with one IndexWriter.

Modern hardware is highly concurrent (multi-core CPUs, multi-channel memory architectures, native command queuing in hard drives, etc.) so using more than one thread to add documents can give good gains overall. Even on older machines there is often still concurrency to be gained between IO and CPU. Test the number of threads to find the best performance point.

* 在多线程中共享一个IndexWriter

最新的硬件都是适合高并发的(多核CPU,多通道内存构架等),所以使用多线程添加文档将会带来不小的性能提升。就算是一台很老的机器,并发添加文档都将更好的利用IO和CPU。多测试并发的线程数目,获得一个临界最优值。

* Index into separate indices then merge.

If you have a very large amount of content to index then you can break your content into N “silos”, index each silo on a separate machine, then use the writer.addIndexesNoOptimize to merge them all into one final index.

* 将文档分组在不同的机器上索引然后再合并

如果你有大量的文本文档需要索引,你可以把你的文档分为若干组,在若干台机器上分别索引不同的组,然后利用writer.addIndexesNoOptimize来将它们合并到最终的一个索引文件中。

* Run a Java profiler.

If all else fails, profile your application to figure out where the time is going. I’ve had success with a very simple profiler called JMP. There are many others. Often you will be pleasantly surprised to find some silly, unexpected method is taking far too much time.

* 运行性能测试程序

如果以上的建议都没有发生效果。建议你运行下性能检测程序,如 JMP。找出你的程序中哪个部分比较耗时。这通常会给你想不到的惊喜。

加速 lucene 的搜索速度 ImproveSearchingSpeed

本文 为简单翻译,原文在:
http://wiki.apache.org/lucene-java/ImproveSearchingSpeed

* Be sure you really need to speed things up.

Many of the ideas here are simple to try, but others will necessarily add some complexity to your application. So be sure your searching speed is indeed too slow and the slowness is indeed within Lucene.

* 请确认你真的需要更快的搜索速度

这里的很多想法都非常容易尝试,但也有一些会给你的程序带来额外的复杂度。所以请确认你的搜索速度真的慢到不能忍受,并且慢的原因的确是因为lucene。

*Make sure you are using the latest version of Lucene.

* 请确认你在使用Lucene的最新版本

*Use a local filesystem.

Remote filesystems are typically quite a bit slower for searching. If the index must be remote, try to mount the remote filesystem as a “readonly” mount. In some cases this could improve performance.

* 尽量使用本地文件系统

远程文件系统一般来说都会降低搜索速度。如果索引必须分布在远程服务器,可以尝试将远程文件系统设置为只读。在某些情况下,这样可以提高性能。

* Get faster hardware, especially a faster IO system.

Flash-based Solid State Drives works very well for Lucene searches. As seek-times for SSD’s are about 100 times faster than traditional platter-based harddrives, the usual penalty for seeking is virtually eliminated. This means that SSD-equipped machines need less RAM for file caching and that searchers require less warm-up time before they respond quickly.

* 使用更快的硬件设备,特别是更快的IO设备

Lucene 搜索可以很好的工作在基于闪存的固态硬盘上。固态硬盘的寻道时间大概比传统的以磁盘为基础的硬盘快100倍。这意味着,配备固态硬盘的机器用于文件缓存的内存将变少,搜索需要较少的热身时间,能够更加迅速作出反应。

* Open the IndexReader with readOnly=true.

This makes a big difference when multiple threads are sharing the same reader, as it removes certain sources of thread contention.

* 以只读方式打开 IndexReader

在多个线程共享同一个 reader 的环境下,这样做会带来很大的改善,因为它减少了部分锁争用。

*On non-Windows platform, using NIOFSDirectory instead of FSDirectory.

This also removes sources of contention when accessing the underlying files. Unfortunately, due to a longstanding bug on Windows in Sun’s JRE (http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6265734 — feel particularly free to go vote for it), NIOFSDirectory gets poor performance on Windows.

* 在非 windows 平台上,使用 NIOFSDirectory 替代 FSDirectory

这样做同样可以减少部分底层文件访问时的锁争用。不幸的是,因为 windows 上 Sun 的 JRE 的一个 bug (http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6265734 ), NIOFSDirectory 在 windows 上性能更差。

* Add RAM to your hardware and/or increase the heap size for the JVM.

For a large index, searching can use alot of RAM. If you don’t have enough RAM or your JVM is not running with a large enough HEAP size then the JVM can hit swapping and thrashing at which point everything will run slowly.

* 加大你的机器内存容量,给Java虚拟机分配更多的内存

索引越大,在搜索时需要使用更多的内存。如果你的机器没有足够大的内存或者你的Java虚拟机没有设置足够大的堆空间,频繁的页面文件交换和虚拟内存的使用将使你的硬盘处于超负荷状态运行,此时,一切的程序都将运行的很慢。

*Use one instance of IndexSearcher.

Share a single IndexSearcher across queries and across threads in your application.

* 在程序中使用一个唯一的IndexSearch实例

在程序的查询中共享一个IndexSearch实例,它是多线程安全的。

*When measuring performance, disregard the first query.

The first query to a searcher pays the price of initializing caches (especially when sorting by fields) and thus will skew your results (assuming you re-use the searcher for many queries). On the other hand, if you re-run the same query again and again, results won’t be realistic either, because the operating system will use its cache to speed up IO operations. On Linux (kernel 2.6.16 and later) you can clean the disk cache using sync ; echo 3 > /proc/sys/vm/drop_caches. See http://linux-mm.org/Drop_Caches for details.

* 当测试搜索速度时,忽略第一次查询时间

第一次搜索将花费部分时间在缓存上(特别在按某个字段排序的情况下),从而可能使你的测试结果不太准确(假设你在多个查询中复用一个 IndexSearch实例)。另一方面来说,如果你一次又一次的重复同一个查询,所得的测试结果也是不准确的。因为操作系统将利用其高速缓存加速IO操 作。在Linux上,你可以使用如下命令清除磁盘高速缓存: echo 3 > /proc/sys/vm/drop_caches.

* Re-open the IndexSearcher only when necessary.

You must re-open the IndexSearcher in order to make newly committed changes visible to searching. However, re-opening the searcher has a certain overhead (noticeable mostly with large indexes and with sorting turned on) and should thus be minimized. Consider using a so called warming technique which allows the searcher to warm up its caches before the first query hits.

* 只有在必要的时候才重新打开 IndexSearcher

为了获得更新的索引信息,你必须重新打开 IndexSearch。当然,重新打开一个searcher会带来一定的系统开销(注意,这大多发生在大索引以及自定义排序上),所以你应该尽量减少重新构造。你可以考虑在重新构造之后强制进行一次搜索 预热

*Run optimize on your index before searching.

An optimized index has only 1 segment to search which can be much faster than the many segments that will normally be created, especially for a large index. If your application does not often update the index then it pays to build the index, optimize it, and use the optimized one for searching. If instead you are frequently updating the index and then refreshing searchers, then optimizing will likely be too costly and you should decrease mergeFactor instead.

* 在搜索之前调用optimize优化你的索引

一个优化后的索引只含有一个Segment(其实说法不严谨,这也取决于一个Segment最多含有的文档参数),这将比同等情况下含多个 Segment的索引搜索速度更快。特别是在大索引的情况下。如果你的程序不经常更新索引,那么花费一定的时间来优化下,然后使用优化后的索引来进行搜索。如果你的索引更新的频率很高,那么优化索引将会是一个很耗时间的事情,这个时候你可以减少mergeFactor参数。
个人建议,在频繁更新索引的情况下,使用两个索引,一个大的优化好的历史索引,一个小的实时添加的索引(如果数据不大的情况下,直接使用RAMDirectory,然后定时的合并到大索引中)

* Decrease mergeFactor.

Smaller mergeFactors mean fewer segments and searching will be faster. However, this will slow down indexing speed, so you should test values to strike an appropriate balance for your application.

* 减小MergeFactor合并因子的值

更小的合并因子意味着索引中拥有更少的Segment,搜索速度也将更快。但是,这也将降低索引速度。你需要自己测试一个值来平衡二者的关系。(此条只适用于不能经常优化的索引库)

* Limit usage of stored fields and term vectors.

Retrieving these from the index is quite costly. Typically you should only retrieve these for the current “page” the user will see, not for all documents in the full result set. For each document retrieved, Lucene must seek to a different location in various files. Try sorting the documents you need to retrieve by docID order first.

* 限制存储字段的使用以及获取尽可能少的数据

从索引中获取数据是一件很耗时间的事情,你最好只获取用户需要的数据。而不是整个文档中存储的数据。每个文档的取回,lucene都必须去索引文件中不同的地方甚至是不同的文件中查找。可以尝试将你需要的文档先按文档编号排序再获取。

* Use FieldSelector to carefully pick which fields are loaded, and how they are loaded, when you retrieve a document.

* 当你取回文档时,使用FieldSelector仔细的选择哪些字段需要获取,如何获取。

* Don’t iterate over more hits than needed.

Iterating over all hits is slow for two reasons. Firstly, the search() method that returns a Hits object re-executes the search internally when you need more than 100 hits. Solution: use the search method that takes a HitCollector instead. Secondly, the hits will probably be spread over the disk so accessing them all requires much I/O activity. This cannot easily be avoided unless the index is small enough to be loaded into RAM. If you don’t need the complete documents but only one (small) field you could also use the FieldCache class to cache that one field and have fast access to it.

* 不要获取多于你需要的hits

获取更多的搜索结果将会降低搜索速度。有两个原因:其一,search方法在返回Hits对象时,如果超过100个hits,lucene将在内部自动重新执行搜索。解决方案:使用返回HitCollector的Search方法。其二,搜索结果可能分布在硬盘的不同地方,获取他们可能需要很多的IO操作。这个是很难避免的,除非索引比较小,可以直接缓存到内存当中。如果你不需要完整的文档对象,你只需要其中的一个很小的字段,你可以使用 FieldCache类来缓存它,从而达到快速访问的效果。

* When using fuzzy queries use a minimum prefix length.

Fuzzy queries perform CPU-intensive string comparisons – avoid comparing all unique terms with the user input by only examining terms starting with the first “N” characters. This prefix length is a property on both QueryParser and FuzzyQuery – default is zero so ALL terms are compared.

* 当使用 fuzzy 查询时设置一个较小的比较长度 (prefixLength)

Fuzzy查询执行CPU密集型字符串比较,尽量避免将用户查询的Term与所有的 Term进行比较。你可以设置只比较以前N个字符开头的Term。prefixLength在QueryParser以及FuzzyQuery中可以设置。默认值为0,将比较所有的Term。

* Consider using filters.

It can be much more efficient to restrict results to a part of the index using a cached bit set filter rather than using a query clause. This is especially true for restrictions that match a great number of documents of a large index. Filters are typically used to restrict the results to a category but could in many cases be used to replace any query clause. One difference between using a Query and a Filter is that the Query has an impact on the score while a Filter does not.

* 考虑使用filters

有时候我们的查询将限制部分索引中的记录,这时候使用一个经过缓存了的bit set filter将比使用查询子句更有效,尤其在一个大索引中。过滤器经常用在查询分类结果上。它可以用查询子句限制来替换,区别在于使用Query将影响文档的得分,而Filter不会。

* Find the bottleneck.

Complex query analysis or heavy post-processing of results are examples of hidden bottlenecks for searches. Profiling with a tool such as VisualVM helps locating the problem.

* 找到瓶颈所在

复杂查询分析或大结果集的处理就是搜索可能的瓶颈,使用类似 VisualVM 的工具可以帮助定位问题的所在。

基于 lucene 的站内搜索 - 阶段性成果介绍

imobile 站内搜索 —— 基于 lucene 的站内搜索,阶段性成果介绍

关键词:准实时搜索,及时更新,快速重建,可配置,可监控,高性能

实现:分离读写,分离索引和存储,拆分大小库,新索引 reopen,新索引预热

Hoto 团子美食

文字的韵味,加上美食的诱惑, hoto 团子在芒种这一天,开始内测了!

欢迎 follow 团子的 twitter:
@projectdango

关于 hoto 团子: http://www.hoto.cn/about

慢隐于市,你是否有坐下来看时光消逝的勇气?

你是否怀念这样的晨?
在市场里踩着从竹筐里漏下的时蔬的新露水,
栀子花和小菜叶被快手翻拣的大妈拨倒在地上,
身边的老太太慢吞吞张望地散发着慈祥的香味,
小女孩钻进人群捡起一只和猫妈失散的小绒球,
吆喝的大叔夫妇在砧板前互相擦着油亮的手。

你是否拥有这样的爱?
她/他正在那个橘色灯光的喷香小厨房里忙碌,
小砂煲里咕噜着好几个小时不间断的文火,
昨天的皮蛋瘦肉和今天的冬瓜排骨舒缓你脾气,
你偏爱的有点脆木耳和不很辣青椒已切丝,
白软米饭蒸架上浇红腐乳汁的米粉肉软糯,
心里还惦记饭后递你一杯不加水的鲜榨果汁。

你是否踩着这样的黄昏?
暂别教室或办公室的灼烈和细碎逃进拥挤的空旷超市,
在冷气柜的凉意跟膝盖打招呼后,
你抱着整桶的冰淇林和整块的快乐逃离,
转战糖果的明亮、巧克力的浓郁还有抹茶的致命吸引。
牌子、盒子、味道、枕头、电影、自制小点和分享的人,
共同构筑你美味的习惯和冷暖。

你是否享受这样的时光?
成为城市间美味小店的拥趸过着甘心为口腹踩点的日子,
熟悉每一家小食的秘密招牌,挑战每一味无敌的小酱汁
青睐摆放水灵灵绿色植物的餐厅,
听老板讲故事或讲故事给老板听,
被对面gg/mm/小朋友调戏或调戏对面的小朋友/gg/mm,
被每个陌生人鉴定为居家旅行、白头偕老的不二人选。

你是否徜徉这样的国度?
想对饮食习惯、奇异食物报以好奇心地尝试和尊重,
而飞机上粉色的骆驼奶只欣赏它的颜色猜测它的味道,
找寻一位参照传统方式烹饪的地道侠客——
因为你欣赏他的坚持,
找寻一位能画出味道的永不放弃新食神——
因为你欣赏他的不韧。

谢谢你的到来,
我们是 Dango (团子)–
一个发现和分享美食的平台。
我们和你一样,期待它舒开明亮,容纳你每一丝细小的欢乐琐碎的幸福。
我们希望通过每一个人贡献的微薄力量
而将 Dango 构筑成为一个充满爱和美好的应许之地。
欢迎加入团子大家族,带着属于你的味道,开始你的柔软之旅。

JAVA 实现 grep + tail 功能

在 linux shell 下经常使用的一个命令组合: grep [-v] “filter” filePath | tail -n needLines,用来查看文件中符合条件的最后 n 行记录,尤其在监控 server 的 log 的时候。
现在有一个需求:从 web 管理后台向 search server 发送一个 stat 命令,查看搜索索引的各项统计信息,其中就包括索引更新,查询的 log 的最后 n 条记录。于是需要在 search server 里实现这样一个功能。

PS:如果是 php,我肯定会选用 exec 一条外部 shell 命令,获取命令的输出 的方式来做,但 java,只好自己动手,重复发明轮子了。
PS2:stl 的 rotate 代码真是太精炼了。有空需要多看看这样的代码,能从中学到很多“ how code talks ”

[code=java]
      /**
	 *  implenment: grep [-v] "filter" filePath | tail -n needLines
	 *
	 *  if filter start with '-', means exclude mode "grep -v", others include mode "grep"
	 *
	 *  NOTE. filter no regex support for now
	 *
	 * @param filePath
	 * @param needLines
	 * @param filter
	 * @return String[] strs
	 */
	public static String[] tailFromFile(String filePath, int needLines, String filter){

		boolean filtermod = (filter != null && filter.length() > 0) ? true : false;
		boolean exclusive = (filtermod && filter.startsWith("-")) ? true : false;

		if (needLines <= 0 || needLines >= 50){
			needLines = 10;
		}

		String[] strs = new String[needLines];

		try {
			RandomAccessFile raf = new RandomAccessFile(filePath, "rb");

			int point = 0;
			String s;
			while ((s = raf.readLine()) != null){
				if (filtermod){
					if (exclusive){
						if (s.contains(filter.substring(1))){
							continue;
						}
					}
					else {
						if (! s.contains(filter)){
							continue;
						}
					}
				}

				strs[point] = s;
				if (++ point >= needLines){
					point = 0;
				}
			}

			// need a rotate
			if (strs[needLines -1] != null){
				// stl rotate algrithm
				// see : http://www.cplusplus.com/reference/algorithm/rotate/
				int first  = 0;
				int middle = point;
				int last   = needLines;
				int next   = middle;
				while (first != next)
				{
					//swap (*first++,*next++);
					s = strs[first];
					strs[first] =  strs[next];
					strs[next]  =  s;
					first += 1;
					next  += 1;

					if (next == last){
						next = middle;
					}
					else if (first == middle){
						middle = next;
					}
				}
			}

		} catch (Exception e) {
			log.warn("read from file error: " + filePath, e);
			return null;
		}

		return strs;
	}
[/code]

FULIN.ORG 重回怀抱

20号从 paypal 上直接付美元 $50,但抢注的那个gg一直没有给转域名。26号终于收到邮件,通知去 MaxRegistrar.com 完成域名转移确认。顺便给续费到了 2019 年 (Cost:$84.51,但招行通知扣了 $82.56 ?

接下来需要找一个靠谱点的 nameserver ,然后把域名 cname 到 tangfl.yo2.cn 上来。

whois:

Domain ID:D155788332-LROR
Domain Name:FULIN.ORG
Created On:02-Apr-2009 14:30:36 UTC
Last Updated On:26-May-2009 03:46:47 UTC
Expiration Date:02-Apr-2019 14:30:36 UTC
Sponsoring Registrar:Go France Domains, Inc. (R1555-LROR)
Status:TRANSFER PROHIBITED
Status:RENEWPERIOD
Registrant ID:CR3356765
Registrant Name:tang fulin
Registrant Organization:Beijing Imobile
Registrant Street1:Chaoyang shuangjin
Registrant Street2:
Registrant Street3:
Registrant City:Beijing
Registrant State/Province:Beijing
Registrant Postal Code:100875
Registrant Country:CN
Registrant Phone:+86.8613811201949
Registrant Phone Ext.:
Registrant FAX:
Registrant FAX Ext.:
Registrant Email:tangfulin@126.com
Admin ID:CR3356767
Admin Name:tang fulin
Admin Organization:Beijing Imobile
Admin Street1:Chaoyang shuangjin
Admin Street2:
Admin Street3:
Admin City:Beijing
Admin State/Province:Beijing
Admin Postal Code:100875
Admin Country:CN
Admin Phone:+86.8613811201949
Admin Phone Ext.:
Admin FAX:
Admin FAX Ext.:
Admin Email:tangfulin@126.com
Tech ID:CR3356766
Tech Name:tang fulin
Tech Organization:Beijing Imobile
Tech Street1:Chaoyang shuangjin
Tech Street2:
Tech Street3:
Tech City:Beijing
Tech State/Province:Beijing
Tech Postal Code:100875
Tech Country:CN
Tech Phone:+86.8613811201949
Tech Phone Ext.:
Tech FAX:
Tech FAX Ext.:
Tech Email:tangfulin@126.com
Name Server:NS1.SEDOPARKING.COM
Name Server:NS2.SEDOPARKING.COM
Name Server:

Lucene 索引删除测试

测试代码:http://code.google.com/p/fulin/source/browse/JAVA/lucene/imobile/search2/src/search/test/IndexTest.java

结论:

1。lucene 索引删除条目的时候(不 调用 optimize),会修改索引目录的以下文件:segments.gen, segments_N, ***.del

2。lucene 索引目录发生改变后,如果不 reopen index reader,则改变对于 searcher 来说是不可见的。(甚至可以将 idx 目录删除,searcher 仍然能返回结果。测试:idx 目录大小为 1.2G,删除目录后, searcher 搜索热门词仍然正常返回结果,返回结果条数超过4万条)

3。索引拆分大小库后,大库可以不用滚动,而是在同一个目录上进行 reopen ,以减少磁盘 IO 及搜索预热延迟

如何做有道难题的迷?

有道难题之解迷游戏http://www.youdao.com/nanti/mi/),老高昨天给我发链接的时候稍微看了看,可是不太擅长于做这种脑筋急转弯类的题目,所以也就没有深究。唯一让我有点兴趣的,无非是好奇后面那些题目都是些什么罢了(初始状态下只能看到开头3道题目,每解出一道,就能多看一道)。

因为好奇,所以想了想这样的情况该如何解决。首先,肯定不能按部就班的猜迷——如果能猜谜,我还想这么多干嘛;接下考虑的就是如何破解。题目是 flash 格式的,那么无非有两种情况:1,答案就在加密了的客户端内部;2,每次回答都 post 到服务器端进行验证。用 httpFox 抓了一下包,没有发现 post 调用,那么情况就很明显了。

可惜,我不是 flash 破解高手。但,高手是肯定有的。Ctrl + A 看看,哈哈。

ps,有道可能考虑到流量压力的问题,才会把答案放置在 flash 内部的吧。不过本来就是一个做出来吸引眼球的产品,也的确不必那么较真罢了。

ps2, 网易有道难题 http://www.youdao.com/nanti/ 与百度之星 http://astar.baidu.com/ 还真相似啊!

ps3,网易有道难题居然是跟 TopCoder 合作的

<?xml version=”1.0″ encoding=”utf-8″ ?>
<data radius=”220″ a=”320″ b=”170″ skey=”youdao”>
<question src=”questions/q1oioqzvveoalz.fa.swf” answer=”o” lowcase=”true” visible=”true”>一样的人物</question>
<question src=”questions/q80afzfdqrezxc0-rwq.f0.swf” answer=”bomb” lowcase=”true” visible=”true”>湖边的回忆</question>
<question src=”questions/q4098azvhlaql.f-fq53.swf” answer=”0441″ visible=”true”>危险之地</question>
<question src=”questions/q3zlllweafl342laozl.swf” answer=”@($” visible=”true” locked=”true”>火星文</question>
<question src=”questions/q5zpaqa.eop2-f-qe4.swf” answer=”也可能” visible=”true” locked=”true”>博客中的线索</question>
<question src=”questions/q6pkltix.04.-af.swf” answer=”本机地址” visible=”true” locked=”true”>IT码农的留言</question>
<question src=”questions/q70a9fdalqrexc65o.vz.swf” answer=”search engine” lowcase=”true” visible=”true” locked=”true”>曲径通幽</question>
<question src=”questions/q909qalzxovaltazt-fq.fq.swf” answer=”为” visible=”true” locked=”true”>手机词典的帮助</question>
<question src=”questions/q10090zvalzp-f.4.swf” answer=”3624087915″ visible=”true” locked=”true”>古诗中的数字</question>
<question src=”questions/q2098alzraz.5.ao.swf” answer=”12355331″ visible=”true” locked=”true”>彩铃包月</question>
<question src=”questions/q1109zgflqre0f-aw.w2.swf” answer=”2月18日||二月十八日” visible=”true” locked=”true”>和智玲的聊天</question>
<question src=”questions/q120z0fda2r.z0f-a2.swf” answer=”cctv” lowcase=”true” visible=”true” locked=”true”>黑客是怎样炼成的</question>
<question src=”questions/q13-zf0w2rzlf0.f43.swf” answer=”圆周率||祖冲之” visible=”true” open=”15″ locked=”true”>Morse的登录</question>
<question src=”questions/q1409falz-fa.2aof.swf” answer=”0731-5310163″ visible=”true” locked=”true”>错误的号码</question>
<question src=”questions/q160z-af.4er0zafwe.swf” answer=”LOVE” visible=”true” locked=”true”>数学之美</question>
<question src=”questions/q170z.gzzf-32zflgpqert.swf” answer=”ONLMK” lowcase=”true” visible=”false”>残破的画卷</question>
</data>

Lucene 索引滚动流程设计

Lucene 索引滚动流程设计

TangFulin <tangfulin@gmail.com>

一. Index Writer:

1. 这里的 Writer 包括 Index Updater 和 Index Rebuilder ,但 Rebuilder 产生的索引文件不直接传送给 Searcher 使用,
而是覆盖 Updater 的索引,由 Updater 统一处理后续的流程

2. IndexUpdaterScheduler 每隔一段时间会设置 copy out timer 标识。

3. Updater 每次处理完一批 xml 文件后会查看 copy out timer 标识是否已经被设置,
如果是,则将当前的索引拷贝一份到 src-snap 目录下 yyyyMMddHHmm 格式的子目录中

4. Updater 为单线程,每次处理完一批 xml 后都会调用 optimizeAndCloseIdx ,所以可以保证 idx 数据是完整的

5. 拷贝数据完成后,在该子目录下 touch 一个 copy.done.flag 标识文件

二. Index Transfer:(rsync src-snap 中的新索引到 Index Searcher)

1. 监控 src-snap 目录下是否有新的子目录出现,且子目录下是否有 copy.done.flag 标识

2. 发现新目录后,两阶段传送数据:
2.1 首先将目录下的数据传送到 Index Searcher,如果出错则停止,成功则继续进行下一步
2.2 touch 一个 trans.done.flag 标识文件,并传送到 Index Searcher

3. 成功传送目录后删除 src-snap 下的源目录

三. Index Searcher

1. MySearcherRoller 定期检查 dest 目录下是否有新的索引目录,且该目录下有 trans.done.flag 标识文件

2. 如果发现多个索引目录,则比较目录的 lastModifyTime,选择最新的一个目录,其他目录标识为 skipped

3. 如果当前可用索引 List 无空位置,则关闭最老的一个索引(并将目录标识为 closed),腾出空间。如果关闭失败(可能有未完成的请求仍然在使用它),则跳过本次处理流程

4. 打开最新的索引,预热新打开的索引,将新打开的索引放入可用索引 List

5. 将内部 pointer 指向最新打开的索引。接下来 Searcher 调用 getLuceneSearcher 的时候,即返回最新的索引给新的请求,未完成的老的请求仍然使用旧的有效的索引

6. 当前可用索引 List 容量为 4 ,即 MySearcherRoller 同时打开了 4 份索引,如果要打开第 5 份索引,则需要关闭最老的一个。

7. 当前使用计数器的方式来检查某一个索引是否有未完成的请求。每次 Searcher 调用 getLuceneSearcher,则计数器加1,调用 returnLuceneSearcher,则计数器减1。

四. Index Cleaner

1. 监控 Searcher 的 dest 目录,查找含有 skipped 标识或者 closed 标识的目录,并删除

五. Index Monitor

1. 监控以上服务或进程是否存在,如果不存在,则报警(当前还未实现)并尽量自动重启

2. 监控 log (当前还未实现)

参考:http://www.opensubscriber.com/message/lucene-user@jakarta.apache.org/802222.html

强大的bash

假设我们定义了一个变量为:

file=/dir1/dir2/dir3/my.file.txt

我们可以用 ${ } 分别替换获得不同的值:

  • ${file#*/}:拿掉第一条 / 及其左边的字串:dir1/dir2/dir3/my.file.txt
  • ${file##*/}:拿掉最后一条 / 及其左边的字串:my.file.txt
  • ${file#*.}:拿掉第一个 . 及其左边的字串:file.txt
  • ${file##*.}:拿掉最后一个 . 及其左边的字串:txt
  • ${file%/*}:拿掉最后一条 / 及其右边的字串:/dir1/dir2/dir3
  • ${file%/*}:拿掉第一条 / 及其右边的字串:(空值)
  • ${file%.*}:拿掉最后一个 . 及其右边的字串:/dir1/dir2/dir3/my.file
  • ${file%%.*}:拿掉第一个 . 及其右边的字串:/dir1/dir2/dir3/my

记忆的方法为

  • # 是去掉左边(在键盘上 # 在 $ 之左边)
  • % 是去掉右边(在键盘上 % 在 $ 之右边)
  • 单一符号是最小匹配;两个符号是最大匹配。

还可以按下标取指定长度的子串:

  • ${file:0:5}:提取最左边的 5 个字?:/dir1
  • ${file:5:5}:提取第 5 个字右边的连续 5 个字:/dir2

我们也可以对变量值里的字串作替换:

  • ${file/dir/path}:将第一个 dir 提换为 path:/path1/dir2/dir3/my.file.txt
  • ${file//dir/path}:将全部 dir 提换为 path:/path1/path2/path3/my.file.txt

利用 ${ } 还可针对不同的变数状态赋值(没设定、空值、非空值):

  • ${file-my.file.txt} :假如 $file 为空值,则使用 my.file.txt 作默认值。(保留没设定及非空值)
  • ${file:-my.file.txt} :假如 $file 没有设定或为空值,则使用 my.file.txt 作默认值。 (保留非空值)
  • ${file+my.file.txt} :不管 $file 为何值,均使用 my.file.txt 作默认值。 (不保留任何值)
  • ${file:+my.file.txt} :除非 $file 为空值,否则使用 my.file.txt 作默认值。 (保留空值)
  • ${file=my.file.txt} :若 $file 没设定,则使用 my.file.txt 作默认值,同时将 $file 定义为非空值。 (保留空值及非空值)
  • ${file:=my.file.txt} :若 $file 没设定或为空值,则使用 my.file.txt 作默认值,同时将 $file 定义为非空值。 (保留非空值)
  • ${file?my.file.txt} :若 $file 没设定,则将 my.file.txt 输出至 STDERR。 (保留空值及非空值))
  • ${file:?my.file.txt} :若 $file 没设定或为空值,则将 my.file.txt 输出至 STDERR。 (保留非空值)

还有,${#var} 可计算出变量值的长度:

  • ${#file} 可得到 27 ,因为 /dir1/dir2/dir3/my.file.txt 刚好是 27 个字?…

接下来介绍一下  bash 的数组(array)处理方法:
一般而言,A=”a b c def” 这样的变量只是将 $A 替换为一个单一的字串,
但是改为 A=(a b c def) ,则是将 $A 定义为数组。
bash 的数组替换方法可参考如下方法:

  • ${A[@]} 或 ${A[*]} 可得到 a b c def (全部数组)
  • ${A[0]} 可得到 a (第一个数组),${A[1]} 则为第二个数组…
  • ${#A[@]} 或 ${#A[*]} 可得到 4 (全部数组数量)
  • ${#A[0]} 可得到 1 (即第一个数组(a)的长度),${A[3]} 可得到 3 (第一个数组(def)的长度)
  • A[3]=xyz 则是将第 4 个数组重新定义为 xyz …

一句话总结:bash 很强大!

参考:http://www.linuxsir.org/bbs/showthread.php?threadid=92866