<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>kafka0102的边城客栈 &#187; LinkedIn</title>
	<atom:link href="http://www.kafka0102.com/tag/linkedin/feed" rel="self" type="application/rss+xml" />
	<link>http://www.kafka0102.com</link>
	<description>要有最朴素的生活与最遥远的梦想，即使明日天寒地冻、路远马亡。</description>
	<lastBuildDate>Sat, 18 Jun 2011 04:20:42 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>使用Zoie构建实时检索系统</title>
		<link>http://www.kafka0102.com/2010/05/119.html</link>
		<comments>http://www.kafka0102.com/2010/05/119.html#comments</comments>
		<pubDate>Sat, 08 May 2010 17:46:59 +0000</pubDate>
		<dc:creator>kafka0102</dc:creator>
				<category><![CDATA[search]]></category>
		<category><![CDATA[LinkedIn]]></category>
		<category><![CDATA[zoie]]></category>
		<category><![CDATA[实时检索]]></category>

		<guid isPermaLink="false">http://www.kafka0102.com/?p=119</guid>
		<description><![CDATA[ Zoie是LinkedIn开源的实时检索系统，它本身用于LinkedIn的用户profile检索。且不说专业的搜索引擎，大多数的站内检索基本都不是实时的，一般都会有几分钟的延迟。但LinkedIn认为，用户profile信息的检索需要实时的效果，因为如果搜索结果不正确或不理想会很影响用户体验。所以，LinkedIn的Zoie实现了秒级别的实时检索效果。Zoie在LinkedIn跑了有两年多时间，所以这个开源项目可以说比较成熟的，这个项目提供了较为丰富的功能和工具，除了核心的API外，它还提供了管理和监控工具、嵌入jetty的Web server demo、一些实用的DataProvider等。对于社区类需要提供用户信息检索的系统来说，使用Zoie或许是个很不错的选择。]]></description>
			<content:encoded><![CDATA[<p>Zoie是LinkedIn开源的实时检索系统，它本身用于LinkedIn的用户profile页检索。且不说专业的搜索引擎，大多数的站内检索基本都不是实时的，一般都会有几分钟的延迟。但LinkedIn认为，用户profile信息的检索需要实时的效果，因为如果搜索结果不正确或不理想会很影响用户体验。所以，LinkedIn的Zoie实现了秒级别的实时检索效果。Zoie在LinkedIn跑了有两年多时间，这个开源项目可以说是起点就比较成熟的，这个项目提供了较为丰富的功能和工具，除了核心的API外，它还提供了管理和监控工具、嵌入jetty的Web server demo、一些实用的DataProvider等。实现上自然是基于lucene了，也使用了一些我需要回顾或者了解的库和框架。对于社区类需要提供用户信息检索的系统来说，使用Zoie或许是个很不错的选择。</p>
<p>下面将要介绍的使用Zoie API来编写检索demo，并且为了省事，我的code sample直接来自于Zoie的Wiki（<a href="http://snaprojects.jira.com/wiki/display/ZOIE/Code+Samples" target="blank">http://snaprojects.jira.com/wiki/display/ZOIE/Code+Samples</a>），但会对其中的使用做些中文版的说明。由于Zoie是个很有针对性的实现，所以相比于Solr这样的通用系统来说，Zoie的代码要“直白”很多，这也促使我拿出精力把它的核心代码研究了一番。总结下来有两点：1）要写一个基于lucene的精致的检索系统需要对lucene有深入的理解，针对实时性检索需求，Zoie本身就对lucene做了很多扩展。2）Zoie的代码结构和代码风格有些糟糕，我看到git里有.project文件，所以可见其作者应该也是基于eclipse开发的，可他的代码就不能好好format下？并且包名和类层次也很诡异。闲言少叙（让我再次想起俞平伯先生），下面罗列下使用Zoie API的code sample。</p>
<p>在检索server端，需要索引的数据总要以一种结构表示，比如一个MAP或POJO之类的。让Zoie来索引数据，就需要Zoie知道你需要索引哪些Field到Document（lucene里的Document）里。说白了，要提供一份要索引的数据及将数据转换成Document结构的接口。这里的数据假定是一个Data：</p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">class</span> Data<span style="color: #009900;">&#123;</span>
<span style="color: #000066; font-weight: bold;">long</span> id<span style="color: #339933;">;</span>
<span style="color: #003399;">String</span> content<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>转换数据的工作由实现ZoieIndexableInterpreter接口的类实现，代码片断如下：</p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">class</span> DataIndexable <span style="color: #000000; font-weight: bold;">implements</span> ZoieIndexable <span style="color: #009900;">&#123;</span>
  <span style="color: #000000; font-weight: bold;">private</span> Data _data<span style="color: #339933;">;</span>
  <span style="color: #000000; font-weight: bold;">public</span> DataIndexable<span style="color: #009900;">&#40;</span>Data data<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    _data <span style="color: #339933;">=</span> data<span style="color: #339933;">;</span>
  <span style="color: #009900;">&#125;</span>
&nbsp;
  <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000066; font-weight: bold;">long</span> getUID<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #000000; font-weight: bold;">return</span> _data.<span style="color: #006633;">id</span><span style="color: #339933;">;</span>
  <span style="color: #009900;">&#125;</span>
&nbsp;
  <span style="color: #000000; font-weight: bold;">public</span> IndexingReq<span style="color: #009900;">&#91;</span><span style="color: #009900;">&#93;</span> buildIndexingReqs<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #666666; font-style: italic;">// it is possible we want to map 1 data object to multiple lucene documents</span>
    <span style="color: #666666; font-style: italic;">// but not for this example</span>
    <span style="color: #003399;">Document</span> doc <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> <span style="color: #003399;">Document</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    doc.<span style="color: #006633;">add</span><span style="color: #009900;">&#40;</span><span style="color: #000000; font-weight: bold;">new</span> <span style="color: #003399;">Field</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;content&quot;</span>,_data.<span style="color: #006633;">content</span>,Store.<span style="color: #006633;">NO</span>,Index.<span style="color: #006633;">ANALYZED</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #666666; font-style: italic;">// no need to add the id field, Zoie will manage the id for you</span>
    <span style="color: #000000; font-weight: bold;">return</span> <span style="color: #000000; font-weight: bold;">new</span> IndexingReq<span style="color: #009900;">&#91;</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#123;</span><span style="color: #000000; font-weight: bold;">new</span> IndexingReq<span style="color: #009900;">&#40;</span>doc<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span>
  <span style="color: #009900;">&#125;</span>
&nbsp;
  <span style="color: #666666; font-style: italic;">// the following methods in this example are kind of hacky,</span>
  <span style="color: #666666; font-style: italic;">// but it is designed to be used when information needed to determine whether documents</span>
  <span style="color: #666666; font-style: italic;">// are to be deleted and/or skipped are only known at runtime</span>
&nbsp;
  <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000066; font-weight: bold;">boolean</span> isDeleted<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #000000; font-weight: bold;">return</span> <span style="color: #0000ff;">&quot;_MARKED_FOR_DELETE&quot;</span>.<span style="color: #006633;">equals</span><span style="color: #009900;">&#40;</span>_data.<span style="color: #006633;">content</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  <span style="color: #009900;">&#125;</span>
&nbsp;
  <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000066; font-weight: bold;">boolean</span> isSkip<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
    <span style="color: #000000; font-weight: bold;">return</span> <span style="color: #0000ff;">&quot;_MARKED_FOR_SKIP&quot;</span>.<span style="color: #006633;">equals</span><span style="color: #009900;">&#40;</span>_data.<span style="color: #006633;">content</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span>
&nbsp;
<span style="color: #000000; font-weight: bold;">class</span> DataIndexableInterpreter <span style="color: #000000; font-weight: bold;">implements</span> ZoieIndexableInterpreter <span style="color: #009900;">&#123;</span>
  <span style="color: #000000; font-weight: bold;">public</span> ZoieIndexable interpret<span style="color: #009900;">&#40;</span>Data src<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
    <span style="color: #000000; font-weight: bold;">return</span> <span style="color: #000000; font-weight: bold;">new</span> DataIndexable<span style="color: #009900;">&#40;</span>src<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>针对上面的代码，我还需要做一些解释。可以看出的是，Zoie在建索引时，需要使用public IndexingReq[] buildIndexingReqs()方法来取出数据，在这个方法里，需要自己build document结构。而方法public long getUID()表明，Zoie需要索引的数据包含uid（可以认为是唯一性主键的概念，而不必须是实际意义的uid），并且这个uid不要在buildIndexingReqs()方法里做成Field，Zoie自己会处理uid，实际上它内部是将uid和lucene的doc id对应上了。这有什么用处呢？对于像用户profile情景，是假设没有删除profile而只有add或update的情况，这使得Zoie在内存里记录了uid和doc id的对应关系，当检查发现是update时，就标记该doc需要在search时过滤掉。对于需要删除数据的场景，一个折中的方法是仅保留uid而将其他Field都去掉。还有那个isDeleted()方法，不要以为它是提供要删除的doc的判断的，它的作用和isSkip差不多，是在Zoie建索引时临时的将当前的Data去掉而不建它的索引（这需要外部修改Data的内容才行），就好像你发现建的数据有问题想中止操作一样，但可想见的是，你根本不知道Zoie建索引到什么阶段了，这个接口的用处就值得怀疑了。</p>
<p>下面再来构建IndexDecorator。Zoie提供的这个接口是允许客户端（相对于Zoie来说是客户端）对给定的ZoieIndexReader装饰成自定义的IndexReader。就像demo给的那样，除非有必要，否则默认实现就可以了。</p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">class</span> MyDoNothingFilterIndexReader <span style="color: #000000; font-weight: bold;">extends</span> FilterIndexReader <span style="color: #009900;">&#123;</span>
<span style="color: #000000; font-weight: bold;">public</span> MyDoNothingFilterIndexReader<span style="color: #009900;">&#40;</span>IndexReader reader<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
<span style="color: #000000; font-weight: bold;">super</span><span style="color: #009900;">&#40;</span>reader<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span>
&nbsp;
<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000066; font-weight: bold;">void</span> updateInnerReader<span style="color: #009900;">&#40;</span>IndexReader inner<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
in <span style="color: #339933;">=</span> inner<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span>
&nbsp;
<span style="color: #000000; font-weight: bold;">class</span> MyDoNothingIndexReaderDecorator <span style="color: #000000; font-weight: bold;">implements</span> IndexReaderDecorator <span style="color: #009900;">&#123;</span>
&nbsp;
  <span style="color: #000000; font-weight: bold;">public</span> MyDoNothingIndexReaderDecorator decorate<span style="color: #009900;">&#40;</span>ZoieIndexReader indexReader<span style="color: #009900;">&#41;</span> <span style="color: #000000; font-weight: bold;">throws</span> <span style="color: #003399;">IOException</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #000000; font-weight: bold;">return</span> <span style="color: #000000; font-weight: bold;">new</span> MyDoNothingFilterIndexReader<span style="color: #009900;">&#40;</span>indexReader<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  <span style="color: #009900;">&#125;</span>
&nbsp;
  <span style="color: #000000; font-weight: bold;">public</span> MyDoNothingIndexReaderDecorator redecorate<span style="color: #009900;">&#40;</span>MyDoNothingIndexReaderDecorator decorated,
                                                    ZoieIndexReader copy<span style="color: #009900;">&#41;</span> <span style="color: #000000; font-weight: bold;">throws</span> <span style="color: #003399;">IOException</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #666666; font-style: italic;">// underlying segment has not changed, just change the inner reader</span>
&nbsp;
    decorated.<span style="color: #006633;">updateInnerReader</span><span style="color: #009900;">&#40;</span>copy<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #000000; font-weight: bold;">return</span> decorated<span style="color: #339933;">;</span>
  <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>下面是build Zoie的核心：ZoieSystem。indexingSystem的start()方法将启动新的线程准备接收数据来建索引。</p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;"><span style="color: #666666; font-style: italic;">// index directory</span>
<span style="color: #003399;">File</span> idxDir <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> <span style="color: #003399;">File</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;myIdxDir&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #666666; font-style: italic;">// create an analyzer</span>
Analyzer analyzer <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> StandardAnalyzer<span style="color: #009900;">&#40;</span>Version.<span style="color: #006633;">LUCENE_CURRENT</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #666666; font-style: italic;">// create similarity</span>
Similarity similarity <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> DefaultSimilarity<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
ZoieIndexableInterpreter myInterpreter <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> DataIndexableInterpreter<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
IndexReaderDecorator decorator <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> MyDoNothingIndexReaderDecorator<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
ZoieSystem indexingSystem <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> ZoieSystem<span style="color: #009900;">&#40;</span>idxDir,         <span style="color: #666666; font-style: italic;">// index direcotry</span>
                                           myInterpreter,  <span style="color: #666666; font-style: italic;">// my interpreter</span>
                                           decorator,      <span style="color: #666666; font-style: italic;">// index decorator</span>
                                           analyzer,       <span style="color: #666666; font-style: italic;">// my analyzer</span>
                                           similarity,     <span style="color: #666666; font-style: italic;">// my similarity</span>
                                           <span style="color: #cc66cc;">1000</span>,           <span style="color: #666666; font-style: italic;">// # events to hold in mem before flushing to disk</span>
                                           <span style="color: #cc66cc;">300000</span>,         <span style="color: #666666; font-style: italic;">// time(ms) to wait before flushing to disk</span>
                                           <span style="color: #000066; font-weight: bold;">true</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>          <span style="color: #666666; font-style: italic;">// true for realtime</span>
&nbsp;
indexingSystem.<span style="color: #006633;">start</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>  <span style="color: #666666; font-style: italic;">// ready to accept indexing even</span></pre></div></div>

<p>下面给出建索引和查询的使用片断，两者可分别在不同线程执行。<br />
索引线程的代码片断：</p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;"><span style="color: #000066; font-weight: bold;">long</span> batchVersion <span style="color: #339933;">=</span> <span style="color: #cc66cc;">0</span><span style="color: #339933;">;</span>
<span style="color: #000000; font-weight: bold;">while</span><span style="color: #009900;">&#40;</span><span style="color: #000066; font-weight: bold;">true</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
  Data<span style="color: #009900;">&#91;</span><span style="color: #009900;">&#93;</span> data <span style="color: #339933;">=</span> buildDataEvents<span style="color: #009900;">&#40;</span>...<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// build a batch of data object to index</span>
&nbsp;
  <span style="color: #666666; font-style: italic;">// construct a collection of indexing events</span>
  <span style="color: #003399;">ArrayList</span> eventList <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> <span style="color: #003399;">ArrayList</span><span style="color: #009900;">&#40;</span>data.<span style="color: #006633;">length</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  <span style="color: #000000; font-weight: bold;">for</span> <span style="color: #009900;">&#40;</span>Data datum <span style="color: #339933;">:</span> data<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
    eventList.<span style="color: #006633;">add</span><span style="color: #009900;">&#40;</span><span style="color: #000000; font-weight: bold;">new</span> DataEvent<span style="color: #009900;">&#40;</span>batchVersion,datum<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  <span style="color: #009900;">&#125;</span>
&nbsp;
  <span style="color: #666666; font-style: italic;">// do indexing</span>
  indexingSystem.<span style="color: #006633;">consume</span><span style="color: #009900;">&#40;</span>events<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
 <span style="color: #666666; font-style: italic;">// increment my version</span>
  batchVersion<span style="color: #339933;">++;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>上面的buildDataEvents方法是产生数据的方法，比如接收客户端请求的数据（也许它还会阻塞在那？），或者是查询数据库的数据之类的。ZoieSystem的consume方法就是用来消费数据建索引，并且是可以批量处理的。</p>
<p>再看看查询线程的代码片断：</p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;">	<span style="color: #666666; font-style: italic;">// get the IndexReaders</span>
List<span style="color: #339933;">&amp;</span>gt<span style="color: #339933;">;</span> readerList <span style="color: #339933;">=</span> indexingSystem.<span style="color: #006633;">getIndexReaders</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #666666; font-style: italic;">// MyDoNothingFilterIndexReader instances can be obtained by calling</span>
<span style="color: #666666; font-style: italic;">// ZoieIndexReader.getDecoratedReaders()</span>
&nbsp;
<span style="color: #666666; font-style: italic;">// combine the readers</span>
MultiReader reader <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> MultiReader<span style="color: #009900;">&#40;</span>readerList.<span style="color: #006633;">toArray</span><span style="color: #009900;">&#40;</span><span style="color: #000000; font-weight: bold;">new</span> IndexReader<span style="color: #009900;">&#91;</span>readerList.<span style="color: #006633;">size</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span>,<span style="color: #000066; font-weight: bold;">false</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #666666; font-style: italic;">// do search</span>
IndexSearcher searcher <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> IndexSearcher<span style="color: #009900;">&#40;</span>reader<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
Query q <span style="color: #339933;">=</span> buildQuery<span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;myquery&quot;</span>,indexingSystem.<span style="color: #006633;">getAnalyzer</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
TopDocs docs <span style="color: #339933;">=</span> searcher.<span style="color: #006633;">search</span><span style="color: #009900;">&#40;</span>q,<span style="color: #cc66cc;">10</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #666666; font-style: italic;">// return readers</span>
indexingSystem.<span style="color: #006633;">returnIndexReaders</span><span style="color: #009900;">&#40;</span>readerList<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p>懒得解释了，跟使用lucene的查询接口差不多。</p>
<p>上面的代码稍加完善就可以真正跑起来了。Zoie也提供了独立的Server功能，但它的那个Server很有可能是你不想要的，而使用上述的嵌入式接口来实现一个Server显然已经极大简化工作了。Zoie还提供了DataProvider机制及实用的实现，对于像通过数据库数据来新建或重建索引，实现自定义的DataProvider就ok了。</p>
<p>总结来说，Zoie是个不错的开源项目，会适合一些站内检索需求。至于它的发展如何就很难说了，LinkedIn开源的几个项目都没什么发展，和Facebook形成了较为鲜明的对比，所以Zoie是否会持续发展下去也很难说，需要看社区及其作者是否能推动这个项目。不过，鉴于这个系统并不复杂，应用及扩展成本是可以控制的。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kafka0102.com/2010/05/119.html/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>一周技术文档分享</title>
		<link>http://www.kafka0102.com/2010/02/46.html</link>
		<comments>http://www.kafka0102.com/2010/02/46.html#comments</comments>
		<pubDate>Thu, 11 Feb 2010 09:14:40 +0000</pubDate>
		<dc:creator>kafka0102</dc:creator>
				<category><![CDATA[other]]></category>
		<category><![CDATA[HipHop]]></category>
		<category><![CDATA[LinkedIn]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[social game]]></category>

		<guid isPermaLink="false">http://www.kafka0102.com/?p=46</guid>
		<description><![CDATA[1、SYNCHRONIZING DATABASES IN DIFFERENT GEOGRAPHIC LOCATIONS

http://highscalability.com/blog/2007/12/7/synchronizing-databases-in-different-geographic-locations.html

翻出很久之前的一篇文章，该文提出了跨地域同步数据的问题。就Mysql来说，如果数据需要在两个地域传输，使用双主复制模式是较为简单的方法。如果mysql能支持多主复制模式，多地域的数据复制或许就解决了。但多主复制很难搞，这种多点并发复制很难自动化的处理提交冲突的问题。]]></description>
			<content:encoded><![CDATA[<p>1、SYNCHRONIZING DATABASES IN DIFFERENT GEOGRAPHIC LOCATIONS</p>
<p><a href="http://highscalability.com/blog/2007/12/7/synchronizing-databases-in-different-geographic-locations.html">http://highscalability.com/blog/2007/12/7/synchronizing-databases-in-different-geographic-locations.html</a></p>
<p>翻出很久之前的一篇文章，该文提出了跨地域同步数据的问题。就Mysql来说，如果数据需要在两个地域传输，使用双主复制模式是较为简单的方法。如果mysql能支持多主复制模式，多地域的数据复制或许就解决了。但多主复制很难搞，这种多点并发复制很难自动化的处理提交冲突的问题。</p>
<p>2、LinkedIn Search: A Look Beneath the Hood</p>
<p><a href="http://thenoisychannel.com/2010/01/31/linkedin-search-a-look-beneath-the-hood/">http://thenoisychannel.com/2010/01/31/linkedin-search-a-look-beneath-the-hood/#</a></p>
<p>这篇揭示LinkedIn的检索系统PPT很不错，尽管我对检索了解不多。对检索结果，LinkedIn没有采用通常的cache方式，而是重新计算。Cache检索结果的好处是提高性能，但无法保证cache数据的真实性，而LinkedIn期望每一次的检索结果都是准确的数据。LinkedIn采用数据库分区、大内存缓存DB数据的方式提高DB查询效率，以至于像双向好友关系也是实时查询的。LinkedIn对Lucene打的实时检索补丁，也很值得研究。</p>
<p>3、<a href="http://developers.facebook.com/news.php?blog=1&amp;story=358">HipHop for PHP: Move Fast</a></p>
<p><a href="http://developers.facebook.com/news.php?blog=1&amp;story=358">http://developers.facebook.com/news.php?blog=1&amp;story=358</a></p>
<p>Facebook爆出的HipHop对PHP社区绝对是爆炸性的消息，如果它开源的话，估计会被众多公司研究和采用。HipHop原理是将PHP代码翻译成C++代码，再编译成二进制程序执行。这一方式和LiteXml类似，都是为了克服脚本语言解释过程的低性能，而在生产环境迂回成编译性程序执行。不过，HipHop会因为C++语言的限制而失掉一些PHP语法功能，尽管不是很重要的说。我倒是很期望PHP能做成虚拟机，不失开发效率，也一样能提高解释执行的性能。</p>
<p>4、<a href="http://highscalability.com/blog/2010/2/8/how-farmville-scales-to-harvest-75-million-players-a-month.html">HOW FARMVILLE SCALES TO HARVEST 75 MILLION PLAYERS A MONTH</a></p>
<p><a href="http://highscalability.com/blog/2010/2/8/how-farmville-scales-to-harvest-75-million-players-a-month.html?utm_source=feedburner&amp;utm_medium=feed&amp;utm_campaign=Feed:+HighScalability+(High+Scalability)&amp;utm_content=Google+Reader">http://highscalability.com/blog/2010/2/8/how-farmville-scales-to-harvest-75-million-players-a-month.html?utm_source=feedburner&amp;utm_medium=feed&amp;utm_campaign=Feed:+HighScalability+(High+Scalability)&amp;utm_content=Google+Reader</a></p>
<p>该文分享了一个social game的技术经验，但没有什么太细节的东西。只有最后大众化的LESSONS LEARNED可以汇总下：</p>
<p>1）对于写多读少的social game，采用in-memory架构，异步提交数据是王道。</p>
<p>2）系统内的组件间低耦合，即便某个组件挂掉，也不会完全影响整个系统的使用。当系统出现性能等问题时，可以关闭一些不必要的功能来缓解问题。</p>
<p>3）缓存Facebook的数据。</p>
<p>4）对新功能做好性能预估及应对方案。</p>
<p>5）抽样部分数据来做分析。</p>
<p>5、When should you store serialized objects in the database?</p>
<p><a href="http://www.mysqlperformanceblog.com/2010/01/21/when-should-you-store-serialized-objects-in-the-database/">http://www.mysqlperformanceblog.com/2010/01/21/when-should-you-store-serialized-objects-in-the-database/</a></p>
<p>Peter的文章总会跟来一堆人的争论。Peter反对在mysql中使用无模式的blob字段，它给出的理由主要有：</p>
<p>1）查询时需要获取整个数据，即便只需要其中部分。</p>
<p>3）blob字段在更新时需要复制的数据更大，并且非定长会造成数据碎片。</p>
<p>3）如MIN, MAX, AVG函数不能使用。</p>
<p>4）blob字段内容没有类型约束检查，需要程序保证，这可能带来潜在的问题。</p>
<p>以用户信息这样的数据类型来说，它的典型特点是：1）一个id对应着一堆属性信息。2）需要针对属性字段做查询条件。这种情况的最简单的做法是，建立一张多属性的schema表和一堆属性索引，但这会带来属性查询的低性能。所以，像FriendFeed、Twitter等，是将属性数据和索引分开。在此基础上，问题只是属性数据的存储是要还是不要schema。如果要schema，peter提到的问题都不是问题，但也带来新的问题：1）属性增加需要修改表结构，这是很折腾人的事情。2）如果属性很多，表结构需要的列数会很多，一个解决方法是，可以将属性分成几类分别存储。实际上，如果在mysql前面有cache（或者Mysql自己的cache够大）来缓存主用户信息，而更新又不是很频繁，blob存储是没什么太大问题的。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kafka0102.com/2010/02/46.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

