<?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; solr</title>
	<atom:link href="http://www.kafka0102.com/tag/solr/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>Solr复制bug一例：Unable to move index file from tempfile to indexfile</title>
		<link>http://www.kafka0102.com/2010/11/414.html</link>
		<comments>http://www.kafka0102.com/2010/11/414.html#comments</comments>
		<pubDate>Wed, 24 Nov 2010 15:37:28 +0000</pubDate>
		<dc:creator>kafka0102</dc:creator>
				<category><![CDATA[solr]]></category>
		<category><![CDATA[solr replication]]></category>
		<category><![CDATA[solr 复制]]></category>

		<guid isPermaLink="false">http://www.kafka0102.com/?p=414</guid>
		<description><![CDATA[22日下午3时多，收到搜索系统的报警邮件，错误日志如下：
[2010-11-22 15:16:14][ERROR][pool-6-thread-1][SnapPuller.java(650)]Unable to move index file from: /indexpath/index.20101122031500/_21.frq to: /indexpath/index.20101122031000/_21.frq
	SnapPuller是Solr复制用到的一个类，我对它做了一些修改，所以把它挪到我的代码里了。报错的代码片断如下：]]></description>
			<content:encoded><![CDATA[<p>22日下午3时多，收到搜索系统的报警邮件，错误日志如下：<br />
<font color="blue"><br />
[2010-11-22 15:16:14][ERROR][pool-6-thread-1][SnapPuller.java(650)]Unable to move index file from: /indexpath/index.20101122031500/_21.frq to: /indexpath/index.20101122031000/_21.frq<br />
</font><br />
	SnapPuller是Solr复制用到的一个类，我对它做了一些修改，所以把它挪到我的代码里了。报错的代码片断如下：</p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">private</span> <span style="color: #000066; font-weight: bold;">boolean</span> copyAFile<span style="color: #009900;">&#40;</span><span style="color: #000000; font-weight: bold;">final</span> <span style="color: #003399;">File</span> tmpIdxDir, <span style="color: #000000; font-weight: bold;">final</span> <span style="color: #003399;">File</span> indexDir, <span style="color: #000000; font-weight: bold;">final</span> <span style="color: #003399;">String</span> fname, <span style="color: #000000; font-weight: bold;">final</span> List<span style="color: #339933;">&lt;</span>String<span style="color: #339933;">&gt;</span> copiedfiles<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #000000; font-weight: bold;">final</span> <span style="color: #003399;">File</span> indexFileInTmpDir <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>tmpIdxDir, fname<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #000000; font-weight: bold;">final</span> <span style="color: #003399;">File</span> indexFileInIndex <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>indexDir, fname<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #000000; font-weight: bold;">final</span> <span style="color: #000066; font-weight: bold;">boolean</span> success <span style="color: #339933;">=</span> indexFileInTmpDir.<span style="color: #006633;">renameTo</span><span style="color: #009900;">&#40;</span>indexFileInIndex<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>success<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
      LOG.<span style="color: #006633;">error</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;Unable to move index file from: &quot;</span> <span style="color: #339933;">+</span> indexFileInTmpDir
          <span style="color: #339933;">+</span> <span style="color: #0000ff;">&quot; to: &quot;</span> <span style="color: #339933;">+</span> indexFileInIndex<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><span style="color: #000000; font-weight: bold;">final</span> <span style="color: #003399;">String</span> f <span style="color: #339933;">:</span> copiedfiles<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #000000; font-weight: bold;">final</span> <span style="color: #003399;">File</span> indexFile <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>indexDir, f<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>indexFile.<span style="color: #006633;">exists</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
          indexFile.<span style="color: #006633;">delete</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span>
      <span style="color: #009900;">&#125;</span>
      delTree<span style="color: #009900;">&#40;</span>tmpIdxDir<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
      <span style="color: #000000; font-weight: bold;">return</span> <span style="color: #000066; font-weight: bold;">false</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
    <span style="color: #000000; font-weight: bold;">return</span> <span style="color: #000066; font-weight: bold;">true</span><span style="color: #339933;">;</span>
  <span style="color: #009900;">&#125;</span></pre></div></div>

<p>  	错误现象表明，indexFileInTmpDir.renameTo(indexFileInIndex)失败了。在这个复制过程中，这种失败最大的可能性就是indexFileInIndex所在的indexDir不存在。查看索引数据目录，确实没有找到index.20101122031000文件夹。不过，从库的index.properties文件却记录“index=index.20101122031000”，也就是说，当前提供查询服务的索引目录就是index.20101122031000。查看进程打开的句柄，进程确实保持着index.20101122031000目录下的索引文件句柄，但它们也确实被删除了。</p>
<p>  	这里补充一下背景，搜索系统使用的是最新solr1.4.1版本，从库每5分钟同步一次主库。所以，当上面的错误出现以后，后续每5分钟就又会出现类似的错误，使得复制总是失败。Solr复制的部分原理是：从库默认的索引目录是index，在正常复制时，从库只是对该目录里的索引文件做修改（添加新的，删除旧的），但当从库索引提交点（IndexCommit）的generation大于主库时，会发生索引文件的全copy，也就是抛弃当前使用的索引目录，而是使用新生成的临时索引目录作为新的索引目录，并修改index.properties，临时目录的命名格式就是取当前的一时间戳（也就是bug所在，但到这里我还没发现这个bug）。Solr wiki也有提到，从库的IndexCommit.generation大于的bad case就是外界对从库做了不适当的提交。而在我的系统里，这种现象是由于重建索引造成的，系统重建索引没有使用DIH，而是在不影响更新时单独线程重建，建完后覆盖旧的索引，这造成重建后的索引的generation会比从库小，所以每次重建从库都会使用新的索引目录。</p>
<p>  	回到这个问题上来，临时的解决办法就是重启从库，因为重启后从库读index.properties发现index.20101122031000并不存在，默认就会使用index目录的索引，而index目录的generation会和主库不一致，就产生全复制，短时间内恢复成最新的索引，但这样可能后续还会触发到bug。但问题并没有解决，我继续查看主从库的日志，再没有发现其他错误信息。而这个问题之前是没有出现的，所以可能是最近的什么操作引起的。而最近的操作就是老高22号凌晨重建了索引，我对照了重建完成时间，是凌晨3点多，而从库的index.20101122031000应该就是之后复制生成的。</p>
<p>  	google之错误信息，找到https://issues.apache.org/jira/browse/SOLR-1822，报bug的人出现和我一样的错误！！不过他也不清楚索引目录怎么没了，他是建议发现目录没了就新建一个，这也是可以曲线解决问题的（我想了一下，这种方法应该不会产生句柄泄漏）。可问题是，好端端的一个索引目录怎么就没了。我继续查看从库复制相关的log，发现在下午3点10分之前都是有正常的复制log的。初想一下，复制过程中发生删除的地方也就是和过期的IndexCommit相关联的索引文件被删除，但也不会删除目录阿！！！莫不是被系统外界下的黑手？</p>
<p>  	回家的地铁上就在琢磨，复制过程有删除目录的操作吗？有！！！复制的最后会删除临时目录！！！那会不会是临时目录和目标索引目录重名了，结果复制后把索引目录给删除了？回家再回看错误日志，这临时索引目录名称是：index.20101122031500，而出错当时是下午的15:15,而不是凌晨的03:15，查找创建临时索引目录的代码：</p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;"> <span style="color: #000000; font-weight: bold;">private</span> <span style="color: #003399;">File</span> createTempindexDir<span style="color: #009900;">&#40;</span><span style="color: #000000; font-weight: bold;">final</span> SolrCore core<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #000000; font-weight: bold;">final</span> <span style="color: #003399;">String</span> tmpIdxDirName <span style="color: #339933;">=</span> <span style="color: #0000ff;">&quot;index.&quot;</span> <span style="color: #339933;">+</span> <span style="color: #000000; font-weight: bold;">new</span> <span style="color: #003399;">SimpleDateFormat</span><span style="color: #009900;">&#40;</span>SnapShooter.<span style="color: #006633;">DATE_FMT</span><span style="color: #009900;">&#41;</span>.<span style="color: #006633;">format</span><span style="color: #009900;">&#40;</span><span style="color: #000000; font-weight: bold;">new</span> <span style="color: #003399;">Date</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #000000; font-weight: bold;">final</span> <span style="color: #003399;">File</span> tmpIdxDir <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>core.<span style="color: #006633;">getDataDir</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>, tmpIdxDirName<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    tmpIdxDir.<span style="color: #006633;">mkdirs</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #000000; font-weight: bold;">return</span> tmpIdxDir<span style="color: #339933;">;</span>
  <span style="color: #009900;">&#125;</span></pre></div></div>

<p>  SnapShooter.DATE_FMT的定义是：</p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;">  <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">static</span> <span style="color: #000000; font-weight: bold;">final</span> <span style="color: #003399;">String</span> DATE_FMT <span style="color: #339933;">=</span> <span style="color: #0000ff;">&quot;yyyyMMddhhmmss&quot;</span><span style="color: #339933;">;</span></pre></div></div>

<p>  注意时间串格式中的小时是“hh”（范围是01-12）而不是“HH”（范围是00-23），这就造成从库如果在当天上午某个时间点全复制，而不幸的，在下午的相应时刻复制又来了，就使得临时索引目录和当前索引目录名称相同，而在复制的最后误删掉当前索引目录。从Bug效果来说，这绝对有破坏性，并且其真的是够低级的。<br />
  我后来check了solr的trunk，发现这个bug已经被修复了，log显示是：<br />
<font color="blue"><br />
  r964312 | koji | 2010-07-15 14:32:22 +0800 (四, 2010-07-15) | 1 行<br />
  SOLR-1995: Use proper format for SimpleDateFormat<br />
</font><br />
  找到https://issues.apache.org/jira/browse/SOLR-1995，发现修复的初衷和我所出现的问题还不一样，它的破坏性也要小的多。话说又说回来，这个bug其实是很隐蔽的，触发的条件是：1）从库要在当天的上午某点进行全复制,如果使用DIH复制，这种情况是不会出现的，而我的系统的全复制通常是在下午，所以之前没出现该问题。2）下午的相应时刻也要发生复制，这要求复制间隔能被12小时整除（我的5分钟间隔中招了），并且当时主库的索引有更新变化（我的系统10多个库当时就有部分库就因为那个时间段没有更新而没有中招）。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kafka0102.com/2010/11/414.html/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>httpclient的并发连接问题</title>
		<link>http://www.kafka0102.com/2010/11/405.html</link>
		<comments>http://www.kafka0102.com/2010/11/405.html#comments</comments>
		<pubDate>Fri, 19 Nov 2010 17:56:18 +0000</pubDate>
		<dc:creator>kafka0102</dc:creator>
				<category><![CDATA[java]]></category>
		<category><![CDATA[httpclient]]></category>
		<category><![CDATA[solr]]></category>

		<guid isPermaLink="false">http://www.kafka0102.com/?p=405</guid>
		<description><![CDATA[	昨天的搜索系统又出状况了，几个库同时重建索引变得死慢。经过一个上午的复现分析，确定问题出现httpclient的使用上。搜索系统在重建索引时，是并发多个线程（默认是8个）不停的从PHP客户端取数据（当然，从另一个角度来说，搜索系统是客户端，PHP端是服务端），取回后放到一个队列里由单独的一个线程更新索引。在测试环境复现发现，对于一个请求，PHP端打印耗时是1-2秒，但搜索端打印在4-6秒。这种耗时差别也就两种可能性，一个是PHP端返回到搜索端接受完耗时太长，另一个就是搜索端在真正发给PHP端数据前等待了很久。因为有了之前的jetty7的困顿，起初我怀疑是传输数据的问题。因为请求数据的代码部分我只是简单的使用了httpclient，所以只能从httpclient着手分析。我想到把PHP端和搜索端的请求起始和结束时间都打出来对照一下，不过在这样做之前我把搜索端的并发请求线程数调到了1,看下单线程情况下效果如何，结果惊奇地发现PHP端和搜索端的耗时相近。所以，可以确定，是httpclient的并发连接处理上可能存在问题。]]></description>
			<content:encoded><![CDATA[<p>	昨天的搜索系统又出状况了，几个库同时重建索引变得死慢。经过一个上午的复现分析，确定问题出现httpclient的使用上（我使用的是3.1这个被广泛使用的遗留版本）。搜索系统在重建索引时，是并发多个线程（默认是8个）不停的从PHP客户端取数据（当然，从另一个角度来说，搜索系统是客户端，PHP端是服务端），取回后放到一个队列里由单独的一个或多个线程更新索引。在测试环境复现发现，对于一个请求，PHP端打印耗时是1-2秒，但搜索端打印在4-6秒。这种耗时差别也就两种可能性，一个是PHP端返回到搜索端接受完耗时太长，另一个就是搜索端在真正发给PHP端数据前等待了很久。因为有了之前的jetty7的困顿，起初我怀疑是传输数据的问题。因为请求数据的代码部分我只是简单的使用了httpclient，所以只能从httpclient着手分析。我想到把PHP端和搜索端的请求起始和结束时间都打出来对照一下，不过在这样做之前我把搜索端的并发请求线程数调到了1,看下单线程情况下效果如何，结果惊奇地发现PHP端和搜索端的耗时相近。所以，可以确定，是httpclient的并发连接处理上可能存在问题。不消说，翻开httpclient API中和配置相关的接口，结果找到HttpConnectionManagerParams类中下面两个函数：</p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;">	<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000066; font-weight: bold;">void</span> setDefaultMaxConnectionsPerHost<span style="color: #009900;">&#40;</span><span style="color: #000066; font-weight: bold;">int</span> maxHostConnections<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000066; font-weight: bold;">void</span> setMaxTotalConnections<span style="color: #009900;">&#40;</span><span style="color: #000066; font-weight: bold;">int</span> maxTotalConnections<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p>	httpclient在处理请求连接方面使用了连接池，它内部实际上有两种连接池，一种是全局的ConnectionPool，一种是每主机（per-host）HostConnectionPool。参数maxHostConnections就HostConnectionPool中表示每主机可保持连接的连接数，maxTotalConnections是ConnectionPool中可最多保持的连接数。每主机的配置类是HostConfiguration，HttpClient有个int executeMethod(final HostConfiguration hostConfiguration, final HttpMethod method)方法可以指定使用哪个HostConfiguration，不过多数情况都是不显示指定HostConfiguration，这样httpclient就用了默认的HostConfiguration=null，也就是说所有的请求可以认为指自同一个主机。如果不设置这两个参数，httpclient自然会用默认的配置，也就是MultiThreadedHttpConnectionManager中的：</p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;">    <span style="color: #008000; font-style: italic; font-weight: bold;">/** The default maximum number of connections allowed per host */</span>
    <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">static</span> <span style="color: #000000; font-weight: bold;">final</span> <span style="color: #000066; font-weight: bold;">int</span> DEFAULT_MAX_HOST_CONNECTIONS <span style="color: #339933;">=</span> <span style="color: #cc66cc;">2</span><span style="color: #339933;">;</span>   <span style="color: #666666; font-style: italic;">// Per RFC 2616 sec 8.1.4</span>
&nbsp;
    <span style="color: #008000; font-style: italic; font-weight: bold;">/** The default maximum number of connections allowed overall */</span>
    <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">static</span> <span style="color: #000000; font-weight: bold;">final</span> <span style="color: #000066; font-weight: bold;">int</span> DEFAULT_MAX_TOTAL_CONNECTIONS <span style="color: #339933;">=</span> <span style="color: #cc66cc;">20</span><span style="color: #339933;">;</span></pre></div></div>

<p>    默认的maxHostConnections大小只有2,也就是说，在我并发8个线程请求数据时，实际上会有6个线程处于等待被调度,这也就解释上面的现象了。再看看上面的注释，我从http://www.faqs.org/rfcs/rfc2616.html找到从了RFC 2616 sec 8.1.4  Practical Considerations段落的最后一段：<br />
<font color="blue"><br />
       Clients that use persistent connections SHOULD limit the number of simultaneous connections that they maintain to a given server. A single-user client SHOULD NOT maintain more than 2 connections with any server or proxy. A proxy SHOULD use up to 2*N connections to  another server or proxy, where N is the number of simultaneously active users. These guidelines are intended to improve HTTP response times and avoid congestion.<br />
</font><br />
	看这叙述，也就表明人家httpclient设置maxHostConnections为2是有根有据的。不过，这种设置显然适合的是浏览器这种客户端，但我相信，多数使用httpclient并不希望有这种默认的限制。而它默认的只有20的maxTotalConnections也太有些吝啬了。我后来浏览solr的客户端server类CommonsHttpSolrServer的代码发现了下面的段落，solr要比我更了解httpclient：</p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;">    _httpClient <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span>client <span style="color: #339933;">==</span> <span style="color: #000066; font-weight: bold;">null</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">?</span> <span style="color: #000000; font-weight: bold;">new</span> HttpClient<span style="color: #009900;">&#40;</span><span style="color: #000000; font-weight: bold;">new</span> MultiThreadedHttpConnectionManager<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">:</span> client<span style="color: #339933;">;</span>
    <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>client <span style="color: #339933;">==</span> <span style="color: #000066; font-weight: bold;">null</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
      <span style="color: #666666; font-style: italic;">// set some better defaults if we created a new connection manager and client</span>
      <span style="color: #666666; font-style: italic;">// increase the default connections</span>
      <span style="color: #000000; font-weight: bold;">this</span>.<span style="color: #006633;">setDefaultMaxConnectionsPerHost</span><span style="color: #009900;">&#40;</span> <span style="color: #cc66cc;">32</span> <span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>  <span style="color: #666666; font-style: italic;">// 2</span>
      <span style="color: #000000; font-weight: bold;">this</span>.<span style="color: #006633;">setMaxTotalConnections</span><span style="color: #009900;">&#40;</span> <span style="color: #cc66cc;">128</span> <span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// 20</span>
    <span style="color: #009900;">&#125;</span></pre></div></div>

<p>    对于httpclient，特别指出的是它的MultiThreadedHttpConnectionManager，看名字好像是多线程并发请求似的，其实不是，但它也确实用到了多线程，那是在发现连接不够用时起个等待线程wait信号，这个名称的含义应该是多线程情况线程安全的HttpConnectionManager。<br />
    使用httpclient还有两点经验，一个是创建的MultiThreadedHttpConnectionManager 实例最好是全局的，否则会有多个连接池，而HttpClient是线程安全的，可以多个实例。另一个是，在处理请求的最后，也就是finnaly里中，要调用method.releaseConnection();回收连接，否则连接池就可能会爆了。</p>
<p>补充：写完之后倒在床上，我又想起了几个问题，这里补充上：<br />
1、系统原先重建索引隐约记得速度还是可以的，为什么现在变慢得如此明显？这有两方面的原因，一个是原来系统取数据是用的单线程（我后来发现单取数据取数据速度跟不上更新索引的速度所以改成多线程），另一个是，当时重建没有一下子同时开启数个库。所以，即便是同样的代码，环境变了，效果也可能变了。当这种改变悄然发生了，程序员却没有捕捉到，才会第一直觉感到问题的诡异。<br />
2、对于长时间不能获得连接的情况，httpclient是否有warn日志报出来？因为我使用了httpclient的getResponseBodyAsStream方法，而它会打出warn日志，所以我是关掉了httpclient的warn级别的。所以，我又检查了httpclient的代码，可惜没看到相关warning log，这点httpclient可以改进下。不过httpclient现在都是4时代了，而我使用的还是3.1的，而3.x已经被停止更新了，所以再采用httpclient可以考虑4版本的，尽管现在能见到的代码几乎都是用的3.x系列的。<br />
3、httpclient的文档是否有特别提到连接数配置的情况？我翻看了一下，确实在关于threading一页中有提到。不过，我等使用它时显然没有完整阅毕它的文档。或许，httpclient给出个明显的最佳实践到能引起使用者的注意，否则误用的情况还是会时有发生。不信，google之。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kafka0102.com/2010/11/405.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>solr拾遗：引用计数</title>
		<link>http://www.kafka0102.com/2010/11/401.html</link>
		<comments>http://www.kafka0102.com/2010/11/401.html#comments</comments>
		<pubDate>Sat, 13 Nov 2010 12:22:07 +0000</pubDate>
		<dc:creator>kafka0102</dc:creator>
				<category><![CDATA[solr]]></category>
		<category><![CDATA[RefCounted]]></category>
		<category><![CDATA[SolrIndexSearcher]]></category>
		<category><![CDATA[SolrQueryRequest]]></category>

		<guid isPermaLink="false">http://www.kafka0102.com/?p=401</guid>
		<description><![CDATA[	据我不完全统计，solr代码中使用引用计数的用途有两种：一个是引用资源，一个是引用对象。技术上来说引用计数的使用没多少可大说的，不过如果没有正确的close获得的资源和对象，泄漏的bug就出现了。]]></description>
			<content:encoded><![CDATA[<p>	据我不完全统计，solr代码中使用引用计数的用途有两种：一个是引用资源，一个是引用对象。技术上来说引用计数的使用没多少可大说的，不过如果没有正确的close获得的资源和对象，泄漏的bug就出现了。</p>
<h2>引用资源</h2>
<p>	solr中供使用的IndexSearcher是SolrIndexSearcher，使用时它是寄存于RefCounted。RefCounted的代码如下：</p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">abstract</span> <span style="color: #000000; font-weight: bold;">class</span> RefCounted<span style="color: #339933;">&lt;</span>Type<span style="color: #339933;">&gt;</span> <span style="color: #009900;">&#123;</span>
  <span style="color: #000000; font-weight: bold;">protected</span> <span style="color: #000000; font-weight: bold;">final</span> Type resource<span style="color: #339933;">;</span>
  <span style="color: #000000; font-weight: bold;">protected</span> <span style="color: #000000; font-weight: bold;">final</span> AtomicInteger refcount <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> AtomicInteger<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
  <span style="color: #000000; font-weight: bold;">public</span> RefCounted<span style="color: #009900;">&#40;</span>Type resource<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #000000; font-weight: bold;">this</span>.<span style="color: #006633;">resource</span> <span style="color: #339933;">=</span> resource<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;">int</span> getRefcount<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> refcount.<span style="color: #006633;">get</span><span style="color: #009900;">&#40;</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: #000000; font-weight: bold;">final</span> RefCounted<span style="color: #339933;">&lt;</span>Type<span style="color: #339933;">&gt;</span> incref<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    refcount.<span style="color: #006633;">incrementAndGet</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #000000; font-weight: bold;">return</span> <span style="color: #000000; font-weight: bold;">this</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: #000000; font-weight: bold;">final</span> Type get<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> resource<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> decref<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;">if</span> <span style="color: #009900;">&#40;</span>refcount.<span style="color: #006633;">decrementAndGet</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">==</span> <span style="color: #cc66cc;">0</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
      close<span style="color: #009900;">&#40;</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;">protected</span> <span style="color: #000000; font-weight: bold;">abstract</span> <span style="color: #000066; font-weight: bold;">void</span> close<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>	SolrIndexSearcher关联于SolrCore。SolrCore成员变量</p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">private</span> RefCounted<span style="color: #339933;">&lt;</span>SolrIndexSearcher<span style="color: #339933;">&gt;</span> _searcher</pre></div></div>

<p>表示正在使用的SolrIndexSearcher，而成员变量</p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">private</span> <span style="color: #000000; font-weight: bold;">final</span> LinkedList<span style="color: #339933;">&lt;</span>RefCounted<span style="color: #339933;">&lt;</span>SolrIndexSearcher<span style="color: #339933;">&gt;&gt;</span> _searchers</pre></div></div>

<p>保持了未close的SolrIndexSearcher列表。在主从模式下，当新的索引到来时，solr会创建了新的</p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;">RefCounted<span style="color: #339933;">&lt;</span>SolrIndexSearcher<span style="color: #339933;">&gt;</span></pre></div></div>

<p>实例赋为_searcher并添加到_searchers。上面的RefCounted的close方法是抽象的，在SolrCore中有其实现：</p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;">  <span style="color: #000000; font-weight: bold;">private</span> RefCounted<span style="color: #339933;">&lt;</span>SolrIndexSearcher<span style="color: #339933;">&gt;</span> newHolder<span style="color: #009900;">&#40;</span>SolrIndexSearcher newSearcher<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    RefCounted<span style="color: #339933;">&lt;</span>SolrIndexSearcher<span style="color: #339933;">&gt;</span> holder <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> RefCounted<span style="color: #339933;">&lt;</span>SolrIndexSearcher<span style="color: #339933;">&gt;</span><span style="color: #009900;">&#40;</span>newSearcher<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
      <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000066; font-weight: bold;">void</span> close<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;">try</span> <span style="color: #009900;">&#123;</span>
          <span style="color: #000000; font-weight: bold;">synchronized</span><span style="color: #009900;">&#40;</span>searcherLock<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
            <span style="color: #666666; font-style: italic;">// it's possible for someone to get a reference via the _searchers queue</span>
            <span style="color: #666666; font-style: italic;">// and increment the refcount while RefCounted.close() is being called.</span>
            <span style="color: #666666; font-style: italic;">// we check the refcount again to see if this has happened and abort the close.</span>
            <span style="color: #666666; font-style: italic;">// This relies on the RefCounted class allowing close() to be called every</span>
            <span style="color: #666666; font-style: italic;">// time the counter hits zero.</span>
            <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>refcount.<span style="color: #006633;">get</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">&gt;</span> <span style="color: #cc66cc;">0</span><span style="color: #009900;">&#41;</span> <span style="color: #000000; font-weight: bold;">return</span><span style="color: #339933;">;</span>
            _searchers.<span style="color: #006633;">remove</span><span style="color: #009900;">&#40;</span><span style="color: #000000; font-weight: bold;">this</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
          <span style="color: #009900;">&#125;</span>
          resource.<span style="color: #006633;">close</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span> <span style="color: #000000; font-weight: bold;">catch</span> <span style="color: #009900;">&#40;</span><span style="color: #003399;">IOException</span> e<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
          log.<span style="color: #006633;">error</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;Error closing searcher:&quot;</span> <span style="color: #339933;">+</span> SolrException.<span style="color: #006633;">toStr</span><span style="color: #009900;">&#40;</span>e<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span>
      <span style="color: #009900;">&#125;</span>
    <span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span>
    holder.<span style="color: #006633;">incref</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;">// set ref count to 1 to account for this._searcher</span>
    <span style="color: #000000; font-weight: bold;">return</span> holder<span style="color: #339933;">;</span>
  <span style="color: #009900;">&#125;</span></pre></div></div>

<p>  可以看到，当调用RefCounted的close方法，如果引用计数refcount不大于0,则会调用SolrIndexSearcher的close方法来释放其保持的文件句柄，并且将其从_searchers中移除。<br />
  再来看查询时对SolrIndexSearcher的使用。每个请求是要构造SolrQueryRequest，看下SolrQueryRequestBase代码片断：</p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;">  <span style="color: #000000; font-weight: bold;">protected</span> RefCounted<span style="color: #339933;">&lt;</span>SolrIndexSearcher<span style="color: #339933;">&gt;</span> searcherHolder<span style="color: #339933;">;</span>
  <span style="color: #000000; font-weight: bold;">public</span> SolrIndexSearcher getSearcher<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;">if</span><span style="color: #009900;">&#40;</span>core <span style="color: #339933;">==</span> <span style="color: #000066; font-weight: bold;">null</span><span style="color: #009900;">&#41;</span> <span style="color: #000000; font-weight: bold;">return</span> <span style="color: #000066; font-weight: bold;">null</span><span style="color: #339933;">;</span><span style="color: #666666; font-style: italic;">//a request for a core admin will no have a core</span>
    <span style="color: #666666; font-style: italic;">// should this reach out and get a searcher from the core singleton, or</span>
    <span style="color: #666666; font-style: italic;">// should the core populate one in a factory method to create requests?</span>
    <span style="color: #666666; font-style: italic;">// or there could be a setSearcher() method that Solr calls</span>
    <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>searcherHolder<span style="color: #339933;">==</span><span style="color: #000066; font-weight: bold;">null</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
      searcherHolder <span style="color: #339933;">=</span> core.<span style="color: #006633;">getSearcher</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
    <span style="color: #000000; font-weight: bold;">return</span> searcherHolder.<span style="color: #006633;">get</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  <span style="color: #009900;">&#125;</span>
  <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000066; font-weight: bold;">void</span> close<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;">if</span> <span style="color: #009900;">&#40;</span>searcherHolder<span style="color: #339933;">!=</span><span style="color: #000066; font-weight: bold;">null</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
      searcherHolder.<span style="color: #006633;">decref</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
      searcherHolder <span style="color: #339933;">=</span> <span style="color: #000066; font-weight: bold;">null</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
  <span style="color: #009900;">&#125;</span></pre></div></div>

<p> 当调用SolrQueryRequestBase的getSearcher()时，如果是第一次调用，会转而调用core.getSearcher()，其会使得到的searcherHolder的引用计数增一。而对称的，SolrQueryRequestBase的close()方法使searcherHolder的引用计数减一，一增一减平衡了。这也使得当新的索引到来时，仍旧提供查询服务的SolrIndexSearcher不会立即关闭，直到其引用计数减为0才关闭。所以，在使用SolrQueryRequest时，要确保请求的最后调用其close方法，否则那些无用的SolrIndexSearcher就不会被释放，直到句柄耗尽或者OOM掉。</p>
<h2>引用对象</h2>
<p>	引用对象的典型使用是SolrCore，看下相关代码：</p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">private</span> <span style="color: #000000; font-weight: bold;">final</span> AtomicInteger refCount <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> AtomicInteger<span style="color: #009900;">&#40;</span><span style="color: #cc66cc;">1</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
  <span style="color: #000000; font-weight: bold;">final</span> <span style="color: #000066; font-weight: bold;">void</span> open<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    refCount.<span style="color: #006633;">incrementAndGet</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  <span style="color: #009900;">&#125;</span>
  <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000066; font-weight: bold;">void</span> close<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #000066; font-weight: bold;">int</span> count <span style="color: #339933;">=</span> refCount.<span style="color: #006633;">decrementAndGet</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>count <span style="color: #339933;">&gt;</span> <span style="color: #cc66cc;">0</span><span style="color: #009900;">&#41;</span> <span style="color: #000000; font-weight: bold;">return</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// close is called often, and only actually closes if nothing is using it.</span>
    <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>count <span style="color: #339933;">&lt;</span> <span style="color: #cc66cc;">0</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
      log.<span style="color: #006633;">error</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;Too many close [count:{}] on {}. Please report this exception to solr-user@lucene.apache.org&quot;</span>, count, <span style="color: #000000; font-weight: bold;">this</span> <span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
      <span style="color: #000000; font-weight: bold;">return</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
    <span style="color: #666666; font-style: italic;">//释放对象和资源</span>
  <span style="color: #009900;">&#125;</span></pre></div></div>

<p>	引用计数就是refCount了。SolrCore实例的获得是通过调用CoreContainer的 SolrCore getCore(String name)得到，其实现是：</p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">public</span> SolrCore getCore<span style="color: #009900;">&#40;</span><span style="color: #003399;">String</span> name<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #000000; font-weight: bold;">synchronized</span><span style="color: #009900;">&#40;</span>cores<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
      SolrCore core <span style="color: #339933;">=</span> cores.<span style="color: #006633;">get</span><span style="color: #009900;">&#40;</span>name<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
      <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>core <span style="color: #339933;">!=</span> <span style="color: #000066; font-weight: bold;">null</span><span style="color: #009900;">&#41;</span>
        core.<span style="color: #006633;">open</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;">// increment the ref count while still synchronized</span>
      <span style="color: #000000; font-weight: bold;">return</span> core<span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
  <span style="color: #009900;">&#125;</span></pre></div></div>

<p>  也就是说CoreContainer显示的open了SolrCore，所以在得到SolrCore实例后，也需要显示的close它。因为SolrQueryRequestBase用到了SolrCore，所以在处理请求的最后，要确保调用了SolrCore的close方法。当然，对于查询端，SolrCore实例在整个生命周期内通常并不会真正被close，除非显示的调用了reload等操作。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kafka0102.com/2010/11/401.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>solr拾遗：CopyField</title>
		<link>http://www.kafka0102.com/2010/10/394.html</link>
		<comments>http://www.kafka0102.com/2010/10/394.html#comments</comments>
		<pubDate>Sat, 30 Oct 2010 15:51:46 +0000</pubDate>
		<dc:creator>kafka0102</dc:creator>
				<category><![CDATA[solr]]></category>
		<category><![CDATA[copyfiled]]></category>

		<guid isPermaLink="false">http://www.kafka0102.com/?p=394</guid>
		<description><![CDATA[	solr的index schema中，除了支持基本数值类型的field，还支持一些特别的field，比如较常用的CopyField。以下面的schema配置片断为例：

&#60;schema name=&#34;eshequn.post.db_post.0&#34; version=&#34;1.1&#34;
    xmlns:xi=&#34;http://www.w3.org/2001/XInclude&#34;&#62;
     &#60;fields&#62;
     	&#60;!-- for title --&#62;
        &#60;field name=&#34;t&#34; type=&#34;text&#34; indexed=&#34;true&#34; stored=&#34;false&#34; /&#62;
        &#60;!-- for abstract --&#62;
        &#60;field name=&#34;a&#34; type=&#34;text&#34; [...]]]></description>
			<content:encoded><![CDATA[<p>	solr的index schema中，除了支持基本数值类型的field，还支持一些特别的field，比如较常用的CopyField。以下面的schema配置片断为例：</p>

<div class="wp_syntax"><div class="code"><pre class="xml" style="font-family:monospace;"><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;schema</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;eshequn.post.db_post.0&quot;</span> <span style="color: #000066;">version</span>=<span style="color: #ff0000;">&quot;1.1&quot;</span></span>
<span style="color: #009900;">    <span style="color: #000066;">xmlns:xi</span>=<span style="color: #ff0000;">&quot;http://www.w3.org/2001/XInclude&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>
     <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;fields<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
     	<span style="color: #808080; font-style: italic;">&lt;!-- for title --&gt;</span>
        <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;field</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;t&quot;</span> <span style="color: #000066;">type</span>=<span style="color: #ff0000;">&quot;text&quot;</span> <span style="color: #000066;">indexed</span>=<span style="color: #ff0000;">&quot;true&quot;</span> <span style="color: #000066;">stored</span>=<span style="color: #ff0000;">&quot;false&quot;</span> <span style="color: #000000; font-weight: bold;">/&gt;</span></span>
        <span style="color: #808080; font-style: italic;">&lt;!-- for abstract --&gt;</span>
        <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;field</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;a&quot;</span> <span style="color: #000066;">type</span>=<span style="color: #ff0000;">&quot;text&quot;</span> <span style="color: #000066;">indexed</span>=<span style="color: #ff0000;">&quot;true&quot;</span> <span style="color: #000066;">stored</span>=<span style="color: #ff0000;">&quot;false&quot;</span> <span style="color: #000000; font-weight: bold;">/&gt;</span></span>
        <span style="color: #808080; font-style: italic;">&lt;!-- for title and abstract --&gt;</span>
        <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;field</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;ta&quot;</span> <span style="color: #000066;">type</span>=<span style="color: #ff0000;">&quot;text&quot;</span> <span style="color: #000066;">indexed</span>=<span style="color: #ff0000;">&quot;true&quot;</span> <span style="color: #000066;">stored</span>=<span style="color: #ff0000;">&quot;false&quot;</span> <span style="color: #000066;">multiValued</span>=<span style="color: #ff0000;">&quot;true&quot;</span><span style="color: #000000; font-weight: bold;">/&gt;</span></span>
    <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/fields<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
    <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;copyField</span> <span style="color: #000066;">source</span>=<span style="color: #ff0000;">&quot;t&quot;</span> <span style="color: #000066;">dest</span>=<span style="color: #ff0000;">&quot;ta&quot;</span> <span style="color: #000000; font-weight: bold;">/&gt;</span></span>
    <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;copyField</span> <span style="color: #000066;">source</span>=<span style="color: #ff0000;">&quot;a&quot;</span> <span style="color: #000066;">dest</span>=<span style="color: #ff0000;">&quot;ta&quot;</span> <span style="color: #000000; font-weight: bold;">/&gt;</span></span>
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/schema<span style="color: #000000; font-weight: bold;">&gt;</span></span></span></pre></div></div>

<p>	字段t是文章的标题，字段a是文章的摘要，字段ta是文章标题和摘要的联合。添加索引文档时，只需要传入t和a字段的内容，solr会自动索引ta字段。这算不上多高级的功能，不过如果让你来实现这个功能，你会怎么做呢？我接手的搜索系统原来就有类似的功能，它的做法是，将t和a字段的文本合并，塞到ta字段，无可厚非的做法。不过，有人注意到lucene的Document类提供的public final Field[] getFields(String name)类似函数不？也就是说，lucene中的一个name可以对应多个Field。solr在添加索引时，会检查field name是不是copyField集合中的source，是的话就以其value构造dest field。如果dest由多个source构成，就需要将其指定为multiValued。</p>
<p>	对于查询来说，如果查询字段要来自多个字段，一种选择是使用CopyField，化多个字段为一个字段，缺点是不能区分各个字段的重要度差别。比如文章的标题和摘要，标题就要比摘要重要性更强，如果有这方面的要求，可以选择查询多个字段的做法。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kafka0102.com/2010/10/394.html/feed</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>solr filter query的误用</title>
		<link>http://www.kafka0102.com/2010/10/374.html</link>
		<comments>http://www.kafka0102.com/2010/10/374.html#comments</comments>
		<pubDate>Fri, 22 Oct 2010 02:06:01 +0000</pubDate>
		<dc:creator>kafka0102</dc:creator>
				<category><![CDATA[solr]]></category>
		<category><![CDATA[filter query]]></category>

		<guid isPermaLink="false">http://www.kafka0102.com/?p=374</guid>
		<description><![CDATA[在我整理solr SolrIndexSearcher性能问题分析的时候，我就在想，我是不是误用了SolrIndexSearcher，才出现我所以为的性能问题。当我想到其实现特点，我恍惚确定确实是这样的，根源就是我误用 filter query。上篇文章暂且放下，这篇做个补充。如我上文所述，fq=fid:1这个条件匹配的文档数会很多，不过，如果开启了filter cache，那么只会在第一次调用时慢些，后续的调用都会命中cache而提升速度，而通过query预热是可以解决初次调用的低速问题。所以，如果要使用filter query，就要开启filter cache，并确保filter cache能容纳所有的filter query。这也需要对应用的查询特点做好分析。以fq=atm:[int_time1 TO int_time2]为例，我之前是将它放到filter query中，不过因为每次查询的int_time1和int_time2都几乎不相同，使得总不能命中filter cache，严重影响了查询性能。另一方面，我在做压力测试时也发现，当filter query结果充满了filter cache，最终使得程序内存耗尽。]]></description>
			<content:encoded><![CDATA[<p>在我整理<a title="Permanent Link to solr SolrIndexSearcher性能问题分析" rel="bookmark" href="../2010/10/366.html">solr  SolrIndexSearcher性能问题分析</a>的时候，我就在想，我是不是误用了SolrIndexSearcher，才出现我所以为的性能问题。当我想到其实现特点，我恍惚确定确实是这样的，根源就是我误用filter query。上篇文章暂且放下，这篇做个补充。如我上文所述，fq=fid:1这个条件匹配的文档数会很多，不过，如果开启了filter cache，那么只会在第一次调用时慢些，后续的调用都会命中cache而提升速度，而通过query预热是可以解决初次调用的低速问题。所以，如果要使用filter query，就要开启filter cache，并确保filter cache能容纳所有的filter query。这也需要对应用的查询特点做好分析。以fq=atm:[int_time1 TO int_time2]为例，我之前是将它放到filter query中，不过因为每次查询的int_time1和int_time2都几乎不相同，使得总不能命中filter cache，严重影响了查询性能。另一方面，我在做压力测试时也发现，当filter query结果充满了filter cache，最终使得程序内存耗尽。</p>
<p>对filter query的使用，还有个注意点，就是solr是按每个fq参数缓存结果的，所以fq=fid:1、fq=fid:2、fq=(fid:1 OR fid:2)是3个缓存项。而像atm:[int_time1 TO int_time2]这样的范围查询，如果个数不是可枚举的，就不要使用它作为fq。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kafka0102.com/2010/10/374.html/feed</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>solr SolrIndexSearcher性能问题分析</title>
		<link>http://www.kafka0102.com/2010/10/366.html</link>
		<comments>http://www.kafka0102.com/2010/10/366.html#comments</comments>
		<pubDate>Thu, 21 Oct 2010 15:22:52 +0000</pubDate>
		<dc:creator>kafka0102</dc:creator>
				<category><![CDATA[solr]]></category>
		<category><![CDATA[lucene]]></category>
		<category><![CDATA[性能]]></category>

		<guid isPermaLink="false">http://www.kafka0102.com/?p=366</guid>
		<description><![CDATA[基于solr的新搜索系统已经使用了有段时间，不过之前上的几个库都很小，也没发现什么性能问题。最近上了thread和post库后，性能问题显现出来。这段时间解决的性能问题有好几个，本文只着重于SolrIndexSearcher的search问题。thread库索引大小有400M多，记录数有400多万，原来的基于lucene的系统的查询处理耗时一般在10-30ms，而同样的请求，新系统耗时在50-100ms。而post库索引大小有 6G多，记录数有3000多万，旧系统的查询处理时间一般在30-80ms，而新系统往往需要400-800ms，性能差距还是很明显的。
因为SolrIndexSearcher是基于lucene的IndexSearcher，想必应该是青出于蓝胜于蓝，并且其wiki上给出的性能数据也很不错，所以当我把新系统做完后并没有去细致的测试其性能。但现在系统的性能相比之下确实有些差，并且因为要做多个库的整合搜索，就要避免像thread 这样耗时长的库就成为短板。总之，问题需要定位和解决。]]></description>
			<content:encoded><![CDATA[<h2>问题背景</h2>
<p>基于solr的新搜索系统已经使用了有段时间，不过之前上的几个库都很小，也没发现什么性能问题。最近上了thread和post库后，性能问题显现出来。这段时间解决的性能问题有好几个，本文只着重于SolrIndexSearcher的search问题。thread库索引大小有400M多，记录数有400多万，原来的基于lucene的系统的查询处理耗时一般在10-30ms，而同样的请求，新系统耗时在50-100ms。而post库索引大小有6G多，记录数有3000多万，旧系统的查询处理时间一般在30-80ms，而新系统往往需要400-800ms，性能差距还是很明显的。</p>
<p>因为SolrIndexSearcher是基于lucene的IndexSearcher，想必应该是青出于蓝胜于蓝，并且其wiki上给出的性能数据也很不错，所以当我把新系统做完后并没有去细致的测试其性能。但现在系统的性能相比之下确实有些差，并且因为要做多个库的整合搜索，就要避免像thread这样耗时长的库就成为短板。总之，问题需要定位和解决。</p>
<h2>原因分析</h2>
<p>分析thread应用给搜索server发来的query，query除了包含用户输入的文本，还有两个filter query:1)是fq=fid:1，fid是int型，表示选择某个版块;2）是fq=atm:[int_time1 TO int_time2]，atm是int型，表示帖子发表的时间，默认int_time2是请求时的时间，int_time1是比int_time2小6个月的起始时间。实际测试发现，当去掉两个fq，处理耗时很短（即便有sort返回结果数很多），而两个fq同时存在或则其中某一个存在时，耗时都会变长，尤其是匹配的结果总数较多时。<br />
因为query分为filter query和text query，基于lucene的IndexSearcher实现方式可以有两种：1）是将各filter query和text query联合起来构成一个大的Query（就是个BooleanQuery，BooleanClause之间是MUST关系）；2）是将filter query转成Filter，和text query区分开。旧系统使用的就是方式2。因为Filter是基于Query构造的，所以两种方式按说性能应该差不多。</p>
<p>翻看SolrIndexSearcher的代码，它除了继承IndexSearcher的一系列search方法，还增加了search(QueryResult qr, QueryCommand cmd)，也是我使用的接口。我当然可以认为，solr的这个search方法会对查询过程做了更高效的实现。除去为提升性能（或者降低性能）引入的三种cache代码，以及一些细枝末节的分支代码，SolrIndexSearcher的search方法主要处理过程其实也简单：</p>
<p>1）将一或多个fq构成List&lt;Query&gt;，然后调用getDocSet(final List&lt;Query&gt; queries)得到DocSet，DocSet存放的是匹配的doc id列表，getDocSet方法内部就是遍历queries，对每一个query执行getPositiveDocSet(final Query q)得到该query的DocSet，然后将得到的多个DocSet做合并，得到满足所有fq的DocSet。</p>
<p>2）由DocSet的Filter getTopFilter()方法得到Filter，再联合TopFieldCollector或者TopScoreDocCollector，以及由text query构造的Qeury，调用lucene的void search(Query query, Filter filter, Collector results)，再结合一些扫尾工作，完成一次查询过程。</p>
<p>单看这个过程，也很难发现问题在哪。为了跟踪整个查询的执行路径，我索性将SolrIndexSearcher从solr源码中提取出来，这其间也是费了一些周折，因为SolrIndexSearcher使用了一些包级别的代码并且我需要构造的SolrIndexSearcher2不能直接从SolrQueryRequest获得。通过加入一些代码段的耗时统计，发现上述1）耗时占70%以上，再分析发现，问题出在查询fq的DocSet getDocSetNC(final Query query, final DocSet filter)方法中使用的DocSetCollector。DocSetCollector的代码如下：</p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">class</span> DocSetCollector <span style="color: #000000; font-weight: bold;">extends</span> Collector <span style="color: #009900;">&#123;</span>
  <span style="color: #000066; font-weight: bold;">int</span> pos<span style="color: #339933;">=</span><span style="color: #cc66cc;">0</span><span style="color: #339933;">;</span>
  OpenBitSet bits<span style="color: #339933;">;</span>
  <span style="color: #000000; font-weight: bold;">final</span> <span style="color: #000066; font-weight: bold;">int</span> maxDoc<span style="color: #339933;">;</span>
  <span style="color: #000000; font-weight: bold;">final</span> <span style="color: #000066; font-weight: bold;">int</span> smallSetSize<span style="color: #339933;">;</span>
  <span style="color: #000066; font-weight: bold;">int</span> base<span style="color: #339933;">;</span>
  <span style="color: #666666; font-style: italic;">// in case there aren't that many hits, we may not want a very sparse</span>
  <span style="color: #666666; font-style: italic;">// bit array.  Optimistically collect the first few docs in an array</span>
  <span style="color: #666666; font-style: italic;">// in case there are only a few.</span>
  <span style="color: #000000; font-weight: bold;">final</span> <span style="color: #000066; font-weight: bold;">int</span><span style="color: #009900;">&#91;</span><span style="color: #009900;">&#93;</span> scratch<span style="color: #339933;">;</span>
  DocSetCollector<span style="color: #009900;">&#40;</span><span style="color: #000066; font-weight: bold;">int</span> smallSetSize, <span style="color: #000066; font-weight: bold;">int</span> maxDoc<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #000000; font-weight: bold;">this</span>.<span style="color: #006633;">smallSetSize</span> <span style="color: #339933;">=</span> smallSetSize<span style="color: #339933;">;</span>
    <span style="color: #000000; font-weight: bold;">this</span>.<span style="color: #006633;">maxDoc</span> <span style="color: #339933;">=</span> maxDoc<span style="color: #339933;">;</span>
    <span style="color: #000000; font-weight: bold;">this</span>.<span style="color: #006633;">scratch</span> <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> <span style="color: #000066; font-weight: bold;">int</span><span style="color: #009900;">&#91;</span>smallSetSize<span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
  <span style="color: #009900;">&#125;</span>
  <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000066; font-weight: bold;">void</span> collect<span style="color: #009900;">&#40;</span><span style="color: #000066; font-weight: bold;">int</span> doc<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>
    doc <span style="color: #339933;">+=</span> base<span style="color: #339933;">;</span>
    <span style="color: #666666; font-style: italic;">// optimistically collect the first docs in an array</span>
    <span style="color: #666666; font-style: italic;">// in case the total number will be small enough to represent</span>
    <span style="color: #666666; font-style: italic;">// as a small set like SortedIntDocSet instead...</span>
    <span style="color: #666666; font-style: italic;">// Storing in this array will be quicker to convert</span>
    <span style="color: #666666; font-style: italic;">// than scanning through a potentially huge bit vector.</span>
    <span style="color: #666666; font-style: italic;">// FUTURE: when search methods all start returning docs in order, maybe</span>
    <span style="color: #666666; font-style: italic;">// we could have a ListDocSet() and use the collected array directly.</span>
    <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>pos <span style="color: #339933;">&amp;</span>lt<span style="color: #339933;">;</span> scratch.<span style="color: #006633;">length</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
      scratch<span style="color: #009900;">&#91;</span>pos<span style="color: #009900;">&#93;</span><span style="color: #339933;">=</span>doc<span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span> <span style="color: #000000; font-weight: bold;">else</span> <span style="color: #009900;">&#123;</span>
      <span style="color: #666666; font-style: italic;">// this conditional could be removed if BitSet was preallocated, but that</span>
      <span style="color: #666666; font-style: italic;">// would take up more memory, and add more GC time...</span>
      <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>bits<span style="color: #339933;">==</span><span style="color: #000066; font-weight: bold;">null</span><span style="color: #009900;">&#41;</span> bits <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> OpenBitSet<span style="color: #009900;">&#40;</span>maxDoc<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
      bits.<span style="color: #006633;">fastSet</span><span style="color: #009900;">&#40;</span>doc<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
    pos<span style="color: #339933;">++;</span>
  <span style="color: #009900;">&#125;</span>
&nbsp;
  <span style="color: #000000; font-weight: bold;">public</span> DocSet getDocSet<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;">if</span> <span style="color: #009900;">&#40;</span>pos<span style="color: #339933;">&amp;</span>lt<span style="color: #339933;">;=</span>scratch.<span style="color: #006633;">length</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
      <span style="color: #666666; font-style: italic;">// assumes docs were collected in sorted order!</span>
      <span style="color: #000000; font-weight: bold;">return</span> <span style="color: #000000; font-weight: bold;">new</span> SortedIntDocSet<span style="color: #009900;">&#40;</span>scratch, pos<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span> <span style="color: #000000; font-weight: bold;">else</span> <span style="color: #009900;">&#123;</span>
      <span style="color: #666666; font-style: italic;">// set the bits for ids that were collected in the array</span>
      <span style="color: #000000; font-weight: bold;">for</span> <span style="color: #009900;">&#40;</span><span style="color: #000066; font-weight: bold;">int</span> i<span style="color: #339933;">=</span><span style="color: #cc66cc;">0</span><span style="color: #339933;">;</span> i</pre></div></div>

<p>可以看到，DocSetCollector使用两个变量来保存collect的doc id，当collect的doc数小于smallSetSize时使用数组scratch，否则使用OpenBitSet bits。getDocSetNC中构造DocSetCollector的代码是：DocSetCollector collector = new DocSetCollector(maxDoc()&gt;&gt;6, maxDoc());。问题就出在OpenBitSet，以thread用例来说，满足fq=fid:1的doc数有200万之多（总doc数有400万）,而fq=atm:[int_time1 TO int_time2]有时也有百万之多，这使得DocSetCollector在collect时使用OpenBitSet.fastSet(int index)置位。尽管OpenBitSet要比BitSet高效，但我在本机测试发现，OpenBitSet200万次的fastSet需要50ms，所以不难理解，匹配fq的文档数越多，DocSetCollector就越慢，进而SolrIndexSearcher就越慢。不过，另一方面，当多个fq的DocSet做合并后，实际有效的DocSet大小可能很小，而再和text query做合并后，得到的DocSet就会更小。所以，当索引的文档多时，solr的这种处理效率上就低得多。</p>
<p>我粗略浏览了lucene的相关代码，lucence在通过各种Scorer操作匹配Query的结果时没有使用OpenBitSet，而是主要使用队列、堆等集合来操作匹配结果的收集、合并等操作（各种Scorer的具体实现没有细看，有时间再看吧），而Collector.collect的会是最后真正匹配的结果。从实际测试的效果来看，性能要比solr提升数倍。最后，我也就放弃了search(QueryResult qr, QueryCommand cmd)，而是使用lucene的，虽然不能使用上solr的三种cache，但性能还是令人满意的。</p>
<p><font color="red">updated：该文分析问题的角度上出现了偏差，<a href="http://www.kafka0102.com/2010/10/374.html">solr filter query的误用</a>做了一些补充和修正。</font></p>
]]></content:encoded>
			<wfw:commentRss>http://www.kafka0102.com/2010/10/366.html/feed</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>[Solr实践]自定义SolrEventListener实现searcher的autowarm策略</title>
		<link>http://www.kafka0102.com/2010/09/326.html</link>
		<comments>http://www.kafka0102.com/2010/09/326.html#comments</comments>
		<pubDate>Sun, 05 Sep 2010 11:50:40 +0000</pubDate>
		<dc:creator>kafka0102</dc:creator>
				<category><![CDATA[solr]]></category>

		<guid isPermaLink="false">http://www.kafka0102.com/?p=326</guid>
		<description><![CDATA[	Solr的searcher autowarm（预热）有两个时机，一个是系统启动时（firstSearcher），一个是使用新的searcher替换旧的searcher时（newSearcher）。Solr支持在solrconfig.xml中对SolrCore配置SolrEventListener来实现自定义的autowarm。通常来说，Solr提供的默认实现QuerySenderListener就够用了。在我的需求中，希望solrconfig.xml中配置的SolrEventListener是针对多个SolrCore的，这要是因为我的多个SolrCore共用了一个solrconfig.xml配置。就配置autowarm的查询query来说，简单的就是配置一个常见的query，但如果系统有排序查询（sort），可以配置适宜的sort条件以预热lucene的fieldCache。下面是我自定义的SolrEventListener，效果是，如果SolrCore没有配置query，就使用default的，否则使用自己的。]]></description>
			<content:encoded><![CDATA[<p>	Solr的searcher autowarm（预热）有两个时机，一个是系统启动时（firstSearcher），一个是使用新的searcher替换旧的searcher时（newSearcher）。Solr支持在solrconfig.xml中对SolrCore配置SolrEventListener来实现自定义的autowarm。通常来说，Solr提供的默认实现QuerySenderListener就够用了。在我的需求中，希望solrconfig.xml中配置的SolrEventListener是针对多个SolrCore的，这要是因为我的多个SolrCore共用了一个solrconfig.xml配置。就配置autowarm的查询query来说，简单的就是配置一个常见的query，但如果系统有排序查询（sort），可以配置适宜的sort条件以预热lucene的fieldCache。下面是我自定义的SolrEventListener，效果是，如果SolrCore没有配置query，就使用default的，否则使用自己的。<br />
	实现代码修改自Solr的QuerySenderListener，代码如下：</p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">class</span> TubaQuerySenderListener <span style="color: #000000; font-weight: bold;">implements</span> SolrEventListener <span style="color: #009900;">&#123;</span>
&nbsp;
  <span style="color: #000000; font-weight: bold;">private</span> <span style="color: #000000; font-weight: bold;">static</span> <span style="color: #000000; font-weight: bold;">final</span> Logger logger <span style="color: #339933;">=</span> LoggerFactory
  .<span style="color: #006633;">getLogger</span><span style="color: #009900;">&#40;</span>TubaQuerySenderListener.<span style="color: #000000; font-weight: bold;">class</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
  <span style="color: #000000; font-weight: bold;">protected</span> <span style="color: #000000; font-weight: bold;">final</span> SolrCore core<span style="color: #339933;">;</span>
  <span style="color: #000000; font-weight: bold;">protected</span> List<span style="color: #339933;">&lt;</span>NamedList<span style="color: #339933;">&gt;</span> queryArgs<span style="color: #339933;">;</span>
&nbsp;
  <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000066; font-weight: bold;">void</span> init<span style="color: #009900;">&#40;</span><span style="color: #000000; font-weight: bold;">final</span> NamedList args<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #000000; font-weight: bold;">final</span> <span style="color: #003399;">String</span> coreName <span style="color: #339933;">=</span> core.<span style="color: #006633;">getName</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    queryArgs <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span>List<span style="color: #339933;">&lt;</span>NamedList<span style="color: #339933;">&gt;</span><span style="color: #009900;">&#41;</span>args.<span style="color: #006633;">get</span><span style="color: #009900;">&#40;</span>coreName<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>queryArgs <span style="color: #339933;">==</span> <span style="color: #000066; font-weight: bold;">null</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
      queryArgs <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span>List<span style="color: #339933;">&lt;</span>NamedList<span style="color: #339933;">&gt;</span><span style="color: #009900;">&#41;</span>args.<span style="color: #006633;">get</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;default&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
      <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>queryArgs <span style="color: #339933;">==</span> <span style="color: #000066; font-weight: bold;">null</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        logger.<span style="color: #006633;">warn</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;TubaQuerySenderListener not valid for core:&quot;</span><span style="color: #339933;">+</span>coreName<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
      <span style="color: #009900;">&#125;</span>
    <span style="color: #009900;">&#125;</span>
    logger.<span style="color: #006633;">info</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;core[&quot;</span><span style="color: #339933;">+</span>coreName<span style="color: #339933;">+</span><span style="color: #0000ff;">&quot;]register TubaQuerySenderListener : &quot;</span> <span style="color: #339933;">+</span> queryArgs<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> TubaQuerySenderListener<span style="color: #009900;">&#40;</span><span style="color: #000000; font-weight: bold;">final</span> SolrCore core<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #000000; font-weight: bold;">this</span>.<span style="color: #006633;">core</span> <span style="color: #339933;">=</span> core<span style="color: #339933;">;</span>
  <span style="color: #009900;">&#125;</span>
&nbsp;
  <span style="color: #008000; font-style: italic; font-weight: bold;">/**
   * Add the {@link org.apache.solr.common.params.EventParams#EVENT} with either the {@link org.apache.solr.common.params.EventParams#NEW_SEARCHER}
   * or {@link org.apache.solr.common.params.EventParams#FIRST_SEARCHER} values depending on the value of currentSearcher.
   * &lt;p/&gt;
   * Makes a copy of NamedList and then adds the parameters.
   *
   *
   * @param currentSearcher If null, add FIRST_SEARCHER, otherwise NEW_SEARCHER
   * @param nlst The named list to add the EVENT value to
   */</span>
  <span style="color: #000000; font-weight: bold;">protected</span> NamedList addEventParms<span style="color: #009900;">&#40;</span><span style="color: #000000; font-weight: bold;">final</span> SolrIndexSearcher currentSearcher, <span style="color: #000000; font-weight: bold;">final</span> NamedList nlst<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #000000; font-weight: bold;">final</span> NamedList result <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> NamedList<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    result.<span style="color: #006633;">addAll</span><span style="color: #009900;">&#40;</span>nlst<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>currentSearcher <span style="color: #339933;">!=</span> <span style="color: #000066; font-weight: bold;">null</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
      result.<span style="color: #006633;">add</span><span style="color: #009900;">&#40;</span>EventParams.<span style="color: #006633;">EVENT</span>, EventParams.<span style="color: #006633;">NEW_SEARCHER</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span> <span style="color: #000000; font-weight: bold;">else</span> <span style="color: #009900;">&#123;</span>
      result.<span style="color: #006633;">add</span><span style="color: #009900;">&#40;</span>EventParams.<span style="color: #006633;">EVENT</span>, EventParams.<span style="color: #006633;">FIRST_SEARCHER</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
    <span style="color: #000000; font-weight: bold;">return</span> result<span style="color: #339933;">;</span>
  <span style="color: #009900;">&#125;</span>
&nbsp;
  @Override
  <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000066; font-weight: bold;">void</span> newSearcher<span style="color: #009900;">&#40;</span><span style="color: #000000; font-weight: bold;">final</span> SolrIndexSearcher newSearcher, <span style="color: #000000; font-weight: bold;">final</span> SolrIndexSearcher currentSearcher<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>queryArgs <span style="color: #339933;">==</span> <span style="color: #000066; font-weight: bold;">null</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: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
    <span style="color: #000000; font-weight: bold;">final</span> SolrIndexSearcher searcher <span style="color: #339933;">=</span> newSearcher<span style="color: #339933;">;</span>
    <span style="color: #000000; font-weight: bold;">for</span> <span style="color: #009900;">&#40;</span><span style="color: #000000; font-weight: bold;">final</span> NamedList nlst <span style="color: #339933;">:</span> queryArgs<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
      <span style="color: #000000; font-weight: bold;">try</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #666666; font-style: italic;">// bind the request to a particular searcher (the newSearcher)</span>
        <span style="color: #000000; font-weight: bold;">final</span> NamedList params <span style="color: #339933;">=</span> addEventParms<span style="color: #009900;">&#40;</span>currentSearcher, nlst<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #000000; font-weight: bold;">final</span> LocalSolrQueryRequest req <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> LocalSolrQueryRequest<span style="color: #009900;">&#40;</span>core,params<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
          @Override <span style="color: #000000; font-weight: bold;">public</span> SolrIndexSearcher getSearcher<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> searcher<span style="color: #339933;">;</span> <span style="color: #009900;">&#125;</span>
          @Override <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000066; font-weight: bold;">void</span> close<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span> <span style="color: #009900;">&#125;</span>
        <span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span>
&nbsp;
        <span style="color: #000000; font-weight: bold;">final</span> SolrQueryResponse rsp <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> SolrQueryResponse<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        core.<span style="color: #006633;">execute</span><span style="color: #009900;">&#40;</span>core.<span style="color: #006633;">getRequestHandler</span><span style="color: #009900;">&#40;</span>req.<span style="color: #006633;">getParams</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>.<span style="color: #006633;">get</span><span style="color: #009900;">&#40;</span>CommonParams.<span style="color: #006633;">QT</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span>, req, rsp<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #666666; font-style: italic;">// Retrieve the Document instances (not just the ids) to warm</span>
        <span style="color: #666666; font-style: italic;">// the OS disk cache, and any Solr document cache.  Only the top</span>
        <span style="color: #666666; font-style: italic;">// level values in the NamedList are checked for DocLists.</span>
        <span style="color: #000000; font-weight: bold;">final</span> NamedList values <span style="color: #339933;">=</span> rsp.<span style="color: #006633;">getValues</span><span style="color: #009900;">&#40;</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><span style="color: #000066; font-weight: bold;">int</span> i<span style="color: #339933;">=</span><span style="color: #cc66cc;">0</span><span style="color: #339933;">;</span> i<span style="color: #339933;">&lt;</span>values.<span style="color: #006633;">size</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> i<span style="color: #339933;">++</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
          <span style="color: #000000; font-weight: bold;">final</span> <span style="color: #003399;">Object</span> o <span style="color: #339933;">=</span> values.<span style="color: #006633;">getVal</span><span style="color: #009900;">&#40;</span>i<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
          <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>o <span style="color: #000000; font-weight: bold;">instanceof</span> DocList<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
            <span style="color: #000000; font-weight: bold;">final</span> DocList docs <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span>DocList<span style="color: #009900;">&#41;</span>o<span style="color: #339933;">;</span>
            <span style="color: #000000; font-weight: bold;">for</span> <span style="color: #009900;">&#40;</span><span style="color: #000000; font-weight: bold;">final</span> DocIterator iter <span style="color: #339933;">=</span> docs.<span style="color: #006633;">iterator</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> iter.<span style="color: #006633;">hasNext</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
              newSearcher.<span style="color: #006633;">doc</span><span style="color: #009900;">&#40;</span>iter.<span style="color: #006633;">nextDoc</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
            <span style="color: #009900;">&#125;</span>
          <span style="color: #009900;">&#125;</span>
        <span style="color: #009900;">&#125;</span>
        req.<span style="color: #006633;">close</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
      <span style="color: #009900;">&#125;</span> <span style="color: #000000; font-weight: bold;">catch</span> <span style="color: #009900;">&#40;</span><span style="color: #000000; font-weight: bold;">final</span> <span style="color: #003399;">Exception</span> e<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #666666; font-style: italic;">// do nothing... we want to continue with the other requests.</span>
        <span style="color: #666666; font-style: italic;">// the failure should have already been logged.</span>
        logger.<span style="color: #006633;">warn</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;&quot;</span>,e<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
      <span style="color: #009900;">&#125;</span>
    <span style="color: #009900;">&#125;</span>
    logger.<span style="color: #006633;">info</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;core[&quot;</span><span style="color: #339933;">+</span>core.<span style="color: #006633;">getName</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">+</span><span style="color: #0000ff;">&quot;]TubaQuerySenderListener newSearcher done.&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  <span style="color: #009900;">&#125;</span>
&nbsp;
  @Override
  <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000066; font-weight: bold;">void</span> postCommit<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;">throw</span> <span style="color: #000000; font-weight: bold;">new</span> <span style="color: #003399;">UnsupportedOperationException</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  <span style="color: #009900;">&#125;</span>
&nbsp;
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>	solrconfig.xml中的配置示例如下，其中firstSearcher和newSearcher的配置是一样的，不过这不意味着它们是必须一样的。</p>

<div class="wp_syntax"><div class="code"><pre class="xml" style="font-family:monospace;">	<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;query<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
		<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;listener</span> <span style="color: #000066;">event</span>=<span style="color: #ff0000;">&quot;firstSearcher&quot;</span></span>
<span style="color: #009900;">			<span style="color: #000066;">class</span>=<span style="color: #ff0000;">&quot;com.tintintech.tuba.search.TubaQuerySenderListener&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>
			<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;arr</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;default&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>
				<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;lst<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
					<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;str</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;q&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>手机<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/str<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
					<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;str</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;start&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>0<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/str<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
					<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;str</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;rows&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>10<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/str<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
				<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/lst<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
			<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/arr<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
			<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;arr</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;core1&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>
				<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;lst<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
					<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;str</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;q&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>手机<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/str<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
					<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;str</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;start&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>0<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/str<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
					<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;str</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;rows&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>10<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/str<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
					<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;str</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;sort&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>at desc<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/str<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
				<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/lst<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
			<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/arr<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
		<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/listener<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
&nbsp;
		<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;listener</span> <span style="color: #000066;">event</span>=<span style="color: #ff0000;">&quot;newSearcher&quot;</span></span>
<span style="color: #009900;">			<span style="color: #000066;">class</span>=<span style="color: #ff0000;">&quot;com.tintintech.tuba.search.TubaQuerySenderListener&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>
			<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;arr</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;default&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>
				<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;lst<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
					<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;str</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;q&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>手机<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/str<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
					<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;str</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;start&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>0<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/str<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
					<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;str</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;rows&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>10<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/str<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
				<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/lst<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
			<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/arr<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
			<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;arr</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;core1&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>
				<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;lst<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
					<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;str</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;q&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>手机<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/str<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
					<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;str</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;start&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>0<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/str<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
					<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;str</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;rows&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>10<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/str<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
					<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;str</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;sort&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>at desc<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/str<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
				<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/lst<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
			<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/arr<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
		<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/listener<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
	<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/query<span style="color: #000000; font-weight: bold;">&gt;</span></span></span></pre></div></div>

]]></content:encoded>
			<wfw:commentRss>http://www.kafka0102.com/2010/09/326.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Solr之困</title>
		<link>http://www.kafka0102.com/2010/08/319.html</link>
		<comments>http://www.kafka0102.com/2010/08/319.html#comments</comments>
		<pubDate>Sat, 21 Aug 2010 20:36:37 +0000</pubDate>
		<dc:creator>kafka0102</dc:creator>
				<category><![CDATA[solr]]></category>

		<guid isPermaLink="false">http://www.kafka0102.com/?p=319</guid>
		<description><![CDATA[重写公司的站内搜索。经过前期一段时间对lucene和solr的熟悉，最后决定使用Solr作为新系统的基础框架。现在已经是第一阶段开发的后期，核心代码行数有11000+（不包含admin及client等）。现已实现的功能要比已有系统要丰富些，但综合比较两个系统总的代码量，其实新系统并不多得太多。新系统使用Solr代替了已有系统实现的部分功能，这减少了新系统的代码量，同是新系统实现了已有系统不具有的功能，也增加了一些代码量。开发的这段时间，因为新系统中很多代码是独立于Solr的，所以和Solr的交互也是时断时续，以使得即便到了开发后期我还能发现Solr实现的一些细节带给我的困扰。]]></description>
			<content:encoded><![CDATA[<p>重写公司的站内搜索。经过前期一段时间对lucene和solr的熟悉，最后决定使用Solr作为新系统的基础框架。现在已经是第一阶段开发的后期，核心代码行数有11000+（不包含admin及client等）。现已实现的功能要比已有系统要丰富些，但综合比较两个系统总的代码量，其实新系统并不多得太多。新系统使用Solr代替了已有系统实现的部分功能，这减少了新系统的代码量，同是新系统实现了已有系统不具有的功能，也增加了一些代码量。开发的这段时间，因为新系统中很多代码是独立于Solr的，所以和Solr的交互也是时断时续，以使得即便到了开发后期我还能发现Solr实现的一些细节带给我的困扰。</p>
<p>抛开我所做的系统来说，如果要选择一个站内搜索解决方案，Solr在某些场景下可能是个很不错的选择。因为Solr提供了Web server支持通过Http来更新索引、重建索引、查询等功能，如果需求对上Solr，甚至可以不需要基于Solr做二次开发就直接满足需要。多美妙的事情阿。不过，如果你需要些高级功能，那么可能你需要基于Solr做些工作了。比如，如果索引库很大，可以将索引库拆成多个shard，查询时对多个shard进行，这个功能Solr是支持的；不过，建索引的事情就需要自己搞定了，比如在Solr前面加个Proxy（或者只是个库函数），在建索引时根据特定的策略提交到不同的shard上。这个其实也还好了，但如果我需要一个涉及到多个索引库（各索引库有不同的schema）的查询，比如要做整站搜索，那么Solr的shard查询就用不上了，因为它必须要求各shard的schema一致。而我要做的实际是个通用搜索，这样的问题就有些接踵而至了。尽管和Solr磨合的过程花了不少时间，涉及到对它提供的功能、设计、源码的理解等等，并且有时还要妥协它开发，有时还要舍弃它已实现的功能而另起炉灶。但不可否认的是，对于初涉站内搜索开发的我来说，使用Solr并不是太坏的选择，从中也学到了Solr优秀的地方，同时也看到它不足的地方，都是收获。本文会简单的总结下个人在应用Solr过程中一些不是很爽的地方，爽的地方姑且按下不表。</p>
<p>Solr实现上有个核心东西，就是SolrCore。每个SolrCore对应着一个索引库，几乎所有的操作都是针对单个SolrCore进行的，似乎Solr的初衷就是如此，并没有考虑到多个SolrCore之间的关联。所以，可以看到的是，每个SolrQueryRequest都会关联到一个SolrCore，SolrRequestHandler的获得也是从SolrCore取得的。这糟糕的设计使得，当需要对多个SolrCore做管理时，Solr不得不做出CoreAdminHandler，它虽然实现了SolrRequestHandler接口，但它是脱离于SolrCore的，使得配置上也和其他handler不一样。而Solr的shard查询的支持就更糟糕，它要求shard的SolrCore的schema都是一致的，而不能查询异构的SolrCore。为了解决这个问题，我在Solr基础上加了个VirtualCore（这个概念现在看起来不是很好，或许IndexCore会更好些），VirtualCore里面可以包含一个或多个SolrCore，而很多操作就不是针对SolrCore而是针对VirtualCore了。比如索引库index被拆分成index.0、index.1、index.2，无论索引还是查询，客户端只需要向系统针对core=index进行请求，无需关心index被系统拆分成几个库，这些库被如何分布，系统会通过配置把这些事情做好。对于整站多个库的联合查询，就是针对多个VirtualCore进行，可以通过配置指定各个VirtualCore的请求参数而不需要像Solr那样有严格的约束。</p>
<p>引入了VirtualCore，使得Solr的一些实现不能得手的使用上。首当其冲的就是它的SearchHandler，我不得不在它的基础上重写了一个，它的shard请求异常处理策略也很有问题，如果shard请求中的某个出现异常，它就不会返回结果，这样做的好处是保证返回结果的全局准确性，但却降低了可用性。这里也需要考虑到查询结果cache的问题，如果在Solr前面加了查询结果Cache，那么Solr这种准确性要求就是有必要的。但在我的实现中，是可以有多少shard返回就处理多少，但在异常的情况下就不做查询结果cache处理。</p>
<p>VirtualCore也使得Solr强悍的DIH也用不上了，但即便没有VirtualCore，DIH也很难解决单点提交多个shard索引的问题。DIH直接对索引的SolrCore做重建索引处理，并没有对重建索引过程提供灵活的hook（尽管它确实提供了一些hook）。就我的需求来说，我希望每索引一个文档同时会根据一定的策略来更新摘要数据库，我浏览了DIH的文档和代码，似乎很难做到。而且，DIH是直接在现有索引上做重建的，如果重建时间很长或者出现问题，使得同时进来的更新索引被阻塞，就会影响到正常的服务。</p>
<p>Solr对配置文件的把握上也不够好。Solr对solrconfig.xml文件提供了Java属性值替换配置文件变量，但solr.xml却没有支持，使得线上线下配置文件中充斥着不同的绝对路径。也有好的一方面，比如schema.xml支持Xinclude，使得多个索引库的schema.xml可以共用相同的field type定义。不过，如果多个索引库的schema能集中在一个文件而不是散落成多个文件，管理起来会更方便。这样的问题同时也存在于solrconfig.xml，尽管solrconfig.xml大多数项的配置都是通用的，不过多个索引库时，searcher的warm请求参数可能就会不一样，这使得我在考虑安排时间改写它的默认Lisnter的实现。</p>
<p>Solr的索引复制有一个细节，那就是master和slave保持长连接，master通过调用OutputStream的flush方法不断把数据发送给slave，如果使用Servlet容器，通过Servlet得到OutputStream这样做没什么问题，但如果使用Netty作为服务器框架，并且使用Netty的http实现，那就实现不了这个效果。这使得我不得不放弃Netty改用Jetty了。</p>
<p>再回到查询上，Solr的SearchHandler只会得到doc id list，而不会得到需要的所请求的字段内容，它是在ResponseWriter输出时根据doc id从IndexReader得到需要的字段。在我的设计中，索引只会存储逻辑主键id，得到逻辑主键id后再从另外的摘要库把其他字段取回（或者就是返回id列表给客户端），但我显然需要在ResponseWriter输出前做完这些事情，这使得我并不得不修改request需要返回的字段列表为空。而这个ResponseWriter是需要和SolrCore的schema绑定的，结果对于并不存在的VirtualCore，我还不得不使用上配置为空并且没有索引的fake schema蒙混过去。</p>
<p>还是关于配置，Solr复制slave端配置的master url需要指定参数core，这使得每个SolrCore都有不同的master url而不能共用一个solrconfig.xml，而我真的很希望它们能共用一个solrconfig.xml。其实这个core参数在ReplicationHandler中完全可以得到，Solr没这么做的一个可能的原因是，它支持的请求url格式是http://host/corename/qt?xx=dd，把corename作为url path的一部分让我用起来很不爽，所以我把请求的格式格式改成：http://host/qt?core=aaa&amp;xx=dd，并出下策把Solr和复制相关的代码拷过来，增加了几行代码完事。</p>
<p>问题当然还有，但就像上面提到的，遇到问题总要找个解决方案，尽管有的方案看起来有些二。在回想上面提到的问题之后，我对现在完成的产出的可用性有些怀疑，我到现在还没有完整的测试过这个系统，所以，它还需要我更仔细的打磨。值得庆幸的是，随着对Solr了解的深入，我能更好的驾驭它了。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kafka0102.com/2010/08/319.html/feed</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>[Solr源码分析]LRUCache和FastLRUCache实现分析</title>
		<link>http://www.kafka0102.com/2010/08/293.html</link>
		<comments>http://www.kafka0102.com/2010/08/293.html#comments</comments>
		<pubDate>Sun, 08 Aug 2010 16:01:42 +0000</pubDate>
		<dc:creator>kafka0102</dc:creator>
				<category><![CDATA[cache]]></category>
		<category><![CDATA[solr]]></category>
		<category><![CDATA[HashMap]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[源码分析]]></category>

		<guid isPermaLink="false">http://www.kafka0102.com/?p=293</guid>
		<description><![CDATA[	在 [Solr 实践]Solr Cache使用介绍及分析 一文我有对Solr的LRUCache和FastLRUCache做了一些介绍，本文在此基础对其实现做些补充。
1、LRUCache的实现分析
	在分析LRUCache前先对LinkedHashMap做些介绍。LinkedHashMap继承于HashMap，它使用了一个双向链表来存储Map中的Entry顺序关系，这种顺序有两种，一种是LRU顺序，一种是插入顺序，这可以由其构造函数public LinkedHashMap(int initialCapacity,float loadFactor,                   boolean accessOrder)指定。所以，对于get、put、remove等操作，LinkedHashMap除了要做HashMap做的事情，还做些调整Entry顺序链表的工作。
	以get操作为例，如果是LRU顺序（accessOrder为true），Entry的recordAccess方法就调整get到的Entry到链表的头部去：

   public V get&#40;Object key&#41; &#123;
        Entry&#60;K,V&#62; e = &#40;Entry&#60;K,V&#62;&#41;getEntry&#40;key&#41;;
        if &#40;e [...]]]></description>
			<content:encoded><![CDATA[<p>	在 <a href="http://www.kafka0102.com/2010/08/267.html" target="blank">[Solr 实践]Solr Cache使用介绍及分析</a> 一文我有对Solr的LRUCache和FastLRUCache做了一些介绍，本文在此基础对其实现做些补充。</p>
<h2>1、LRUCache的实现分析</h2>
<p>	在分析LRUCache前先对LinkedHashMap做些介绍。LinkedHashMap继承于HashMap，它使用了一个双向链表来存储Map中的Entry顺序关系，这种顺序有两种，一种是LRU顺序，一种是插入顺序，这可以由其构造函数public LinkedHashMap(int initialCapacity,float loadFactor,                   boolean accessOrder)指定。所以，对于get、put、remove等操作，LinkedHashMap除了要做HashMap做的事情，还做些调整Entry顺序链表的工作。<br />
	以get操作为例，如果是LRU顺序（accessOrder为true），Entry的recordAccess方法就调整get到的Entry到链表的头部去：</p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;">   <span style="color: #000000; font-weight: bold;">public</span> V get<span style="color: #009900;">&#40;</span><span style="color: #003399;">Object</span> key<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        Entry<span style="color: #339933;">&lt;</span>K,V<span style="color: #339933;">&gt;</span> e <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span>Entry<span style="color: #339933;">&lt;</span>K,V<span style="color: #339933;">&gt;</span><span style="color: #009900;">&#41;</span>getEntry<span style="color: #009900;">&#40;</span>key<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>e <span style="color: #339933;">==</span> <span style="color: #000066; font-weight: bold;">null</span><span style="color: #009900;">&#41;</span>
            <span style="color: #000000; font-weight: bold;">return</span> <span style="color: #000066; font-weight: bold;">null</span><span style="color: #339933;">;</span>
        e.<span style="color: #006633;">recordAccess</span><span style="color: #009900;">&#40;</span><span style="color: #000000; font-weight: bold;">this</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #000000; font-weight: bold;">return</span> e.<span style="color: #006633;">value</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span></pre></div></div>

<p>    对于put来说，LinkedHashMap重写了addEntry方法：</p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;">   <span style="color: #000066; font-weight: bold;">void</span> addEntry<span style="color: #009900;">&#40;</span><span style="color: #000066; font-weight: bold;">int</span> hash, K key, V value, <span style="color: #000066; font-weight: bold;">int</span> bucketIndex<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        createEntry<span style="color: #009900;">&#40;</span>hash, key, value, bucketIndex<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #666666; font-style: italic;">// Remove eldest entry if instructed, else grow capacity if appropriate</span>
        Entry<span style="color: #339933;">&lt;</span>K,V<span style="color: #339933;">&gt;</span> eldest <span style="color: #339933;">=</span> header.<span style="color: #006633;">after</span><span style="color: #339933;">;</span>
        <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>removeEldestEntry<span style="color: #009900;">&#40;</span>eldest<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
            removeEntryForKey<span style="color: #009900;">&#40;</span>eldest.<span style="color: #006633;">key</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span> <span style="color: #000000; font-weight: bold;">else</span> <span style="color: #009900;">&#123;</span>
            <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>size <span style="color: #339933;">&gt;=</span> threshold<span style="color: #009900;">&#41;</span>
                resize<span style="color: #009900;">&#40;</span><span style="color: #cc66cc;">2</span> <span style="color: #339933;">*</span> table.<span style="color: #006633;">length</span><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>    addEntry中调用了boolean removeEldestEntry(Map.Entry<K,V> eldest)方法，默认实现一直返回false，也就是默认的Map是没有容量限制的。LinkedHashMap的子类可以复写该方法，当当前的size大于阈值时返回true，这样LinkedHashMap就可以从Entry顺序链表中删除最旧的Entry。这使得LinkedHashMap具有了Cache的功能，可以存储限量的元素，并具有两种可选的元素淘汰策略（LRU和FIFO），其中的LRU是最常用的。<br />
	Solr的LRUCache是基于LinkedHashMap实现的，所以LRUCache的实现真的很简单，这里列出其中核心的代码片断：</p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;">    <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #003399;">Object</span> init<span style="color: #009900;">&#40;</span><span style="color: #000000; font-weight: bold;">final</span> <span style="color: #003399;">Map</span> args, <span style="color: #003399;">Object</span> persistence, <span style="color: #000000; font-weight: bold;">final</span> CacheRegenerator regenerator<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
	<span style="color: #666666; font-style: italic;">//一堆解析参数参数初始化的代码</span>
	<span style="color: #666666; font-style: italic;">//map map    </span>
    map <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> LinkedHashMap<span style="color: #009900;">&#40;</span>initialSize, 0.75f, <span style="color: #000066; font-weight: bold;">true</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
      @Override
      <span style="color: #000000; font-weight: bold;">protected</span> <span style="color: #000066; font-weight: bold;">boolean</span> removeEldestEntry<span style="color: #009900;">&#40;</span><span style="color: #000000; font-weight: bold;">final</span> <span style="color: #003399;">Map.<span style="color: #006633;">Entry</span></span> eldest<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>size<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">&gt;</span> limit<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
          <span style="color: #666666; font-style: italic;">// increment evictions regardless of state.</span>
          <span style="color: #666666; font-style: italic;">// this doesn't need to be synchronized because it will</span>
          <span style="color: #666666; font-style: italic;">// only be called in the context of a higher level synchronized block.</span>
          evictions<span style="color: #339933;">++;</span>
          stats.<span style="color: #006633;">evictions</span>.<span style="color: #006633;">incrementAndGet</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
          <span style="color: #000000; font-weight: bold;">return</span> <span style="color: #000066; font-weight: bold;">true</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span>
        <span style="color: #000000; font-weight: bold;">return</span> <span style="color: #000066; font-weight: bold;">false</span><span style="color: #339933;">;</span>
      <span style="color: #009900;">&#125;</span>
    <span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span>
    <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>persistence<span style="color: #339933;">==</span><span style="color: #000066; font-weight: bold;">null</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
      <span style="color: #666666; font-style: italic;">// must be the first time a cache of this type is being created</span>
      persistence <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> CumulativeStats<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
    stats <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span>CumulativeStats<span style="color: #009900;">&#41;</span>persistence<span style="color: #339933;">;</span>
    <span style="color: #000000; font-weight: bold;">return</span> persistence<span style="color: #339933;">;</span>
  <span style="color: #009900;">&#125;</span>
&nbsp;
    <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #003399;">Object</span> put<span style="color: #009900;">&#40;</span><span style="color: #000000; font-weight: bold;">final</span> <span style="color: #003399;">Object</span> key, <span style="color: #000000; font-weight: bold;">final</span> <span style="color: #003399;">Object</span> value<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #000000; font-weight: bold;">synchronized</span> <span style="color: #009900;">&#40;</span>map<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
      <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>state <span style="color: #339933;">==</span> State.<span style="color: #006633;">LIVE</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        stats.<span style="color: #006633;">inserts</span>.<span style="color: #006633;">incrementAndGet</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
      <span style="color: #009900;">&#125;</span>
      <span style="color: #666666; font-style: italic;">// increment local inserts regardless of state???</span>
      <span style="color: #666666; font-style: italic;">// it does make it more consistent with the current size...</span>
      inserts<span style="color: #339933;">++;</span>
      <span style="color: #000000; font-weight: bold;">return</span> map.<span style="color: #006633;">put</span><span style="color: #009900;">&#40;</span>key,value<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;">public</span> <span style="color: #003399;">Object</span> get<span style="color: #009900;">&#40;</span><span style="color: #000000; font-weight: bold;">final</span> <span style="color: #003399;">Object</span> key<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #000000; font-weight: bold;">synchronized</span> <span style="color: #009900;">&#40;</span>map<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
      <span style="color: #000000; font-weight: bold;">final</span> <span style="color: #003399;">Object</span> val <span style="color: #339933;">=</span> map.<span style="color: #006633;">get</span><span style="color: #009900;">&#40;</span>key<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
      <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>state <span style="color: #339933;">==</span> State.<span style="color: #006633;">LIVE</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #666666; font-style: italic;">// only increment lookups and hits if we are live.</span>
        lookups<span style="color: #339933;">++;</span>
        stats.<span style="color: #006633;">lookups</span>.<span style="color: #006633;">incrementAndGet</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>val<span style="color: #339933;">!=</span><span style="color: #000066; font-weight: bold;">null</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
          hits<span style="color: #339933;">++;</span>
          stats.<span style="color: #006633;">hits</span>.<span style="color: #006633;">incrementAndGet</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span>
      <span style="color: #009900;">&#125;</span>
      <span style="color: #000000; font-weight: bold;">return</span> val<span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
  <span style="color: #009900;">&#125;</span></pre></div></div>

<p>  可以看到，LRUCache对读写操作直接加的互斥锁，多线程并发读写时会有锁的竞争问题。通常来说，Cache系统的读要远多于写，不能并发读是有些不够友好。不过，相比于Solr中其它耗时的操作来说，LRUCache的串行化读往往不会成为系统的瓶颈。LRUCache的优点是，直接套用LinkedHashMap，实现简单，缺点是，因为LinkedHashMap的get操作需要操作Entry顺序链表，所以必须对整个操作加锁。</p>
<h2>2、FastLRUCache的实现分析</h2>
<p>	Solr1.4引入FastLRUCache作为另一种可选的实现。FastLRUCache放弃了LinkedHashMap，而是使用现在很多Java Cache实现中使用的ConcurrentHashMap。但ConcurrentHashMap只提供了高性能的并发存取支持，并没有提供对淘汰数据的支持，所以FastLRUCache主要需要做的就是这件事情。FastLRUCache的存取操作都在ConcurrentLRUCache中实现，所以我们直接过渡到ConcurrentLRUCache的实现。<br />
	ConcurrentLRUCache的存取操作代码如下：</p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;">  <span style="color: #000000; font-weight: bold;">public</span> V get<span style="color: #009900;">&#40;</span><span style="color: #000000; font-weight: bold;">final</span> K key<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #000000; font-weight: bold;">final</span> CacheEntry<span style="color: #339933;">&lt;</span>K,V<span style="color: #339933;">&gt;</span> e <span style="color: #339933;">=</span> map.<span style="color: #006633;">get</span><span style="color: #009900;">&#40;</span>key<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>e <span style="color: #339933;">==</span> <span style="color: #000066; font-weight: bold;">null</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
      <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>islive<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        stats.<span style="color: #006633;">missCounter</span>.<span style="color: #006633;">incrementAndGet</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
      <span style="color: #009900;">&#125;</span>
      <span style="color: #000000; font-weight: bold;">return</span> <span style="color: #000066; font-weight: bold;">null</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
    <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>islive<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
      e.<span style="color: #006633;">lastAccessed</span> <span style="color: #339933;">=</span> stats.<span style="color: #006633;">accessCounter</span>.<span style="color: #006633;">incrementAndGet</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
    <span style="color: #000000; font-weight: bold;">return</span> e.<span style="color: #006633;">value</span><span style="color: #339933;">;</span>
  <span style="color: #009900;">&#125;</span>
&nbsp;
  <span style="color: #000000; font-weight: bold;">public</span> V remove<span style="color: #009900;">&#40;</span><span style="color: #000000; font-weight: bold;">final</span> K key<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #000000; font-weight: bold;">final</span> CacheEntry<span style="color: #339933;">&lt;</span>K,V<span style="color: #339933;">&gt;</span> cacheEntry <span style="color: #339933;">=</span> map.<span style="color: #006633;">remove</span><span style="color: #009900;">&#40;</span>key<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>cacheEntry <span style="color: #339933;">!=</span> <span style="color: #000066; font-weight: bold;">null</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
      stats.<span style="color: #006633;">size</span>.<span style="color: #006633;">decrementAndGet</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
      <span style="color: #000000; font-weight: bold;">return</span> cacheEntry.<span style="color: #006633;">value</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
    <span style="color: #000000; font-weight: bold;">return</span> <span style="color: #000066; font-weight: bold;">null</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: #003399;">Object</span> put<span style="color: #009900;">&#40;</span><span style="color: #000000; font-weight: bold;">final</span> K key, <span style="color: #000000; font-weight: bold;">final</span> V val<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>val <span style="color: #339933;">==</span> <span style="color: #000066; font-weight: bold;">null</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: #000066; font-weight: bold;">null</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
    <span style="color: #000000; font-weight: bold;">final</span> CacheEntry e <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> CacheEntry<span style="color: #009900;">&#40;</span>key, val, stats.<span style="color: #006633;">accessCounter</span>.<span style="color: #006633;">incrementAndGet</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #000000; font-weight: bold;">final</span> CacheEntry oldCacheEntry <span style="color: #339933;">=</span> map.<span style="color: #006633;">put</span><span style="color: #009900;">&#40;</span>key, e<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #000066; font-weight: bold;">int</span> currentSize<span style="color: #339933;">;</span>
    <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>oldCacheEntry <span style="color: #339933;">==</span> <span style="color: #000066; font-weight: bold;">null</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
      currentSize <span style="color: #339933;">=</span> stats.<span style="color: #006633;">size</span>.<span style="color: #006633;">incrementAndGet</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span> <span style="color: #000000; font-weight: bold;">else</span> <span style="color: #009900;">&#123;</span>
      currentSize <span style="color: #339933;">=</span> stats.<span style="color: #006633;">size</span>.<span style="color: #006633;">get</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
    <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>islive<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
      stats.<span style="color: #006633;">putCounter</span>.<span style="color: #006633;">incrementAndGet</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span> <span style="color: #000000; font-weight: bold;">else</span> <span style="color: #009900;">&#123;</span>
      stats.<span style="color: #006633;">nonLivePutCounter</span>.<span style="color: #006633;">incrementAndGet</span><span style="color: #009900;">&#40;</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;">// Check if we need to clear out old entries from the cache.</span>
    <span style="color: #666666; font-style: italic;">// isCleaning variable is checked instead of markAndSweepLock.isLocked()</span>
    <span style="color: #666666; font-style: italic;">// for performance because every put invokation will check until</span>
    <span style="color: #666666; font-style: italic;">// the size is back to an acceptable level.</span>
    <span style="color: #666666; font-style: italic;">// There is a race between the check and the call to markAndSweep, but</span>
    <span style="color: #666666; font-style: italic;">// it's unimportant because markAndSweep actually aquires the lock or returns if it can't.</span>
    <span style="color: #666666; font-style: italic;">// Thread safety note: isCleaning read is piggybacked (comes after) other volatile reads</span>
    <span style="color: #666666; font-style: italic;">// in this method.</span>
    <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>currentSize <span style="color: #339933;">&gt;</span> upperWaterMark <span style="color: #339933;">&amp;&amp;</span> <span style="color: #339933;">!</span>isCleaning<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
      <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>newThreadForCleanup<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #000000; font-weight: bold;">new</span> <span style="color: #003399;">Thread</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
          @Override
          <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000066; font-weight: bold;">void</span> run<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
            markAndSweep<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
          <span style="color: #009900;">&#125;</span>
        <span style="color: #009900;">&#125;</span>.<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: #009900;">&#125;</span> <span style="color: #000000; font-weight: bold;">else</span> <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>cleanupThread <span style="color: #339933;">!=</span> <span style="color: #000066; font-weight: bold;">null</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
        cleanupThread.<span style="color: #006633;">wakeThread</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
      <span style="color: #009900;">&#125;</span> <span style="color: #000000; font-weight: bold;">else</span> <span style="color: #009900;">&#123;</span>
        markAndSweep<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
      <span style="color: #009900;">&#125;</span>
    <span style="color: #009900;">&#125;</span>
    <span style="color: #000000; font-weight: bold;">return</span> oldCacheEntry <span style="color: #339933;">==</span> <span style="color: #000066; font-weight: bold;">null</span> <span style="color: #339933;">?</span> <span style="color: #000066; font-weight: bold;">null</span> <span style="color: #339933;">:</span> oldCacheEntry.<span style="color: #006633;">value</span><span style="color: #339933;">;</span>
  <span style="color: #009900;">&#125;</span></pre></div></div>

<p>	所有的操作都是直接调用map（ConcurrentHashMap）的。看下put中的代码，当map容量达到上限并且没有其他线程在清理数据（currentSize > upperWaterMark &#038;&#038; !isCleaning），就调用markAndSweep方法清理数据，可以有3种方式做清理工作：1）在该线程同步执行，2）即时启动新线程异步执行，3）提供单独的清理线程，即时唤醒它异步执行。</p>
<p>	markAndSweep方法那是相当的冗长，这里就不罗列出来。下面叙述下它的思路。</p>
<p>	对于ConcurrentLRUCache中的每一个元素CacheEntry，它有个属性lastAccessed，表示最后访问的数值大小。ConcurrentLRUCache中的stats.accessCounter是全局的自增整数，当put或get Entry时，Entry的lastAccessed会被更新成新自增得到的accessCounter。		ConcurrentLRUCache淘汰数据就是淘汰那些lastAccessed较小的Entry。因为ConcurrentLRUCache没有维护以lastAccessed排序的Entry链表（否则就是LRUCache了），所以淘汰数据时就需要遍历整个Map中的元素来淘汰合适的Entry。这是不是要扯上排序呢？其实不用那么大动干戈。</p>
<p>	这里定义几个变量，wantToKeep表示Map中需要保留的Entry个数，wantToRemove表示需要删除的个数（wantToRemove=map.size-wantToKeep),newestEntry是最大的lastAccessed值（初始是stats.accessCounter），这三个变量初始都是已知的，oldestEntry表示最小的lastAccessed，这个是未知的，可以在遍历Entry时通过比较递进到最小。Map中的Entry有3种:(a)是可以立刻判断出可以被淘汰的，也就是lastAccessed<(oldestEntry+wantToRemove)的，（b）是可以立刻判断出可以被保留的，也就是lastAccessed>(newestEntry-1000)的，（c）除上述两者之外的就是不能准确判断是否需要被淘汰的。对于遍历一趟Map中的Entry来说，极好的情况是如果淘汰掉满足（a）的Entry后Map大小降到了wantToKeep，这种情况的典型代表是对Cache只有get和put操作，使得lastAccessed在Map中能保持连续；极坏的情况是，可能满足（a）的Entry不够多甚至没有。但遍历一趟Map至少有一个效果是，会把需要处理的Entry范围缩小到满足（c）的。如此反复迭代，一定使得Map容量调到wantToKeep。而对这个淘汰，也要考虑一个现实情况是，wantToKeep往往是接近于map.size（比如等于0.9*map.size）的，如果remove操作不是很多，那么并不需要很多次遍历就可以完成清理工作。</p>
<p>	ConcurrentLRUCache淘汰数据的基本思想如上所述。它的执行过程可以分为3个阶段。第一个阶段就是遍历Map中的每个Entry，如果满足（a）就remove，满足（b）则跳过，满足（c）则放到新map中。一遍下来后，如果map.size还大于wantToKeep，第二个阶段就再重复上述过程（实现上，Solr用了个变量numPasses，似乎想做个开关控制遍历几次，当前就固定成一次）。完了如果map.size还大于wantToKeep，第三阶段再遍历一遍Map，但这次使用PriorityQueue来提取出还需要再淘汰的N个最old的Entry，这样一次下来就收工了。需要补充一点，上面提到的wantToKeep在代码中是acceptableWaterMark和lowerWaterMark，也就是如果遍历后达到acceptableWaterMark就算完成，但操作是按lowerWaterMark的要求来。</p>
<p>	这个算法的时间复杂度是2n+kln(k)（k值在实际大多数情况下会很小），相比于直接的堆排，通常会更快些。</p>
<h2>3、总结</h2>
<p>	LRUCache和FastLRUCache两种Cache实现是两种很不同的思路。两者的相同点是，都使用了现成的Map来维护数据。不同点是如何来淘汰数据。LRUCache（也就是LinkedHashMap）格外维护了一个结构，在做存取操作时同时更新该结构，优点在于淘汰操作是O(1)的，缺点是需要对存取操作加互斥锁。FastLRUCache正相反，它没有额外维护新的结构，可以由ConcurrentHashMap支持并发读，但put操作中如果需要淘汰数据，淘汰过程是O(n)的，因为整个过程不加锁，这也只会影响该次put的性能，而FastLRUCache也可选成起独立线程异步执行来降低影响。而另一个Cache实现Ehcache，它在淘汰数据就是同步的，不过它限定了每次淘汰数据的大小（通常都少于5个），所以同步情况下性能不会太受影响。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kafka0102.com/2010/08/293.html/feed</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>[Solr实践]Solr Cache使用介绍及分析</title>
		<link>http://www.kafka0102.com/2010/08/267.html</link>
		<comments>http://www.kafka0102.com/2010/08/267.html#comments</comments>
		<pubDate>Sat, 31 Jul 2010 21:15:27 +0000</pubDate>
		<dc:creator>kafka0102</dc:creator>
				<category><![CDATA[solr]]></category>
		<category><![CDATA[solr cache]]></category>
		<category><![CDATA[solr 实践]]></category>

		<guid isPermaLink="false">http://www.kafka0102.com/?p=267</guid>
		<description><![CDATA[	本文将介绍Solr查询中涉及到的Cache使用及相关的实现。Solr查询的核心类就是SolrIndexSearcher，每个core通常在同一时刻只由当前的SolrIndexSearcher供上层的handler使用（当切换SolrIndexSearcher时可能会有两个同时提供服务），而Solr的各种Cache是依附于SolrIndexSearcher的，SolrIndexSearcher在则Cache生，SolrIndexSearcher亡则Cache被清空close掉。Solr中的应用Cache有filterCache、queryResultCache、documentCache等，这些Cache都是SolrCache的实现类，并且是SolrIndexSearcher的成员变量，各自有着不同的逻辑和使命，下面分别予以介绍和分析。]]></description>
			<content:encoded><![CDATA[<p>	本文将介绍Solr查询中涉及到的Cache使用及相关的实现。Solr查询的核心类就是SolrIndexSearcher，每个core通常在同一时刻只由当前的SolrIndexSearcher供上层的handler使用（当切换SolrIndexSearcher时可能会有两个同时提供服务），而Solr的各种Cache是依附于SolrIndexSearcher的，SolrIndexSearcher在则Cache生，SolrIndexSearcher亡则Cache被清空close掉。Solr中的应用Cache有filterCache、queryResultCache、documentCache等，这些Cache都是SolrCache的实现类，并且是SolrIndexSearcher的成员变量，各自有着不同的逻辑和使命，下面分别予以介绍和分析。</p>
<h2>1、SolrCache接口实现类</h2>
<p>	Solr提供了两种SolrCache接口实现类：solr.search.LRUCache和solr.search.FastLRUCache。FastLRUCache是1.4版本中引入的，其速度在普遍意义上要比LRUCache更fast些。<br />
	下面是对SolrCache接口主要方法的注释：</p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">interface</span> SolrCache <span style="color: #009900;">&#123;</span>
  <span style="color: #008000; font-style: italic; font-weight: bold;">/**
   * Solr在解析配置文件构造SolrConfig实例时会初始化配置中的各种CacheConfig，
   * 在构造SolrIndexSearcher时通过SolrConfig实例来newInstance SolrCache，
   * 这会调用init方法。参数args就是和具体实现（LRUCache和FastLRUCache）相关的
   * 参数Map，参数persistence是个全局的东西，LRUCache和FastLRUCache用其来统计
   * cache访问情况（因为cache是和SolrIndexSearcher绑定的，所以这种统计就需要个
   * 全局的注入参数），参数regenerator是autowarm时如何重新加载cache，
   * CacheRegenerator接口只有一个被SolrCache warm方法回调的方法：
   * boolean regenerateItem(SolrIndexSearcher newSearcher,
   * SolrCache newCache, SolrCache oldCache, Object oldKey, Object oldVal)
   */</span>
  <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #003399;">Object</span> init<span style="color: #009900;">&#40;</span><span style="color: #003399;">Map</span> args, <span style="color: #003399;">Object</span> persistence, CacheRegenerator regenerator<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  <span style="color: #008000; font-style: italic; font-weight: bold;">/** :TODO: copy from Map */</span>
  <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000066; font-weight: bold;">int</span> size<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  <span style="color: #008000; font-style: italic; font-weight: bold;">/** :TODO: copy from Map */</span>
  <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #003399;">Object</span> put<span style="color: #009900;">&#40;</span><span style="color: #003399;">Object</span> key, <span style="color: #003399;">Object</span> value<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  <span style="color: #008000; font-style: italic; font-weight: bold;">/** :TODO: copy from Map */</span>
  <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #003399;">Object</span> get<span style="color: #009900;">&#40;</span><span style="color: #003399;">Object</span> key<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  <span style="color: #008000; font-style: italic; font-weight: bold;">/** :TODO: copy from Map */</span>
  <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000066; font-weight: bold;">void</span> clear<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  <span style="color: #008000; font-style: italic; font-weight: bold;">/**
   * 新创建的SolrIndexSearcher autowarm方法，该方法的实现就是遍历已有cache中合适的
   * 范围（因为通常不会把旧cache中的所有项都重新加载一遍），对每一项调用regenerator的
   * regenerateItem方法来对searcher加载新cache项。
   */</span>
  <span style="color: #000066; font-weight: bold;">void</span> warm<span style="color: #009900;">&#40;</span>SolrIndexSearcher searcher, SolrCache old<span style="color: #009900;">&#41;</span> <span style="color: #000000; font-weight: bold;">throws</span> <span style="color: #003399;">IOException</span><span style="color: #339933;">;</span>
  <span style="color: #008000; font-style: italic; font-weight: bold;">/** Frees any non-memory resources */</span>
  <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000066; font-weight: bold;">void</span> close<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<h3>1.1、solr.search.LRUCache </h3>
<p>LRUCache可配置参数如下：<br />
1）size：cache中可保存的最大的项数，默认是1024<br />
2）initialSize：cache初始化时的大小，默认是1024。<br />
3）autowarmCount：当切换SolrIndexSearcher时，可以对新生成的SolrIndexSearcher做autowarm（预热）处理。autowarmCount表示从旧的SolrIndexSearcher中取多少项来在新的SolrIndexSearcher中被重新生成，如何重新生成由CacheRegenerator实现。在当前的1.4版本的Solr中，这个autowarmCount只能取预热的项数，将来的4.0版本可以指定为已有cache项数的百分比，以便能更好的平衡autowarm的开销及效果。如果不指定该参数，则表示不做autowarm处理。<br />
	实现上，LRUCache直接使用LinkedHashMap来缓存数据，由initialSize来限定cache的大小，淘汰策略也是使用LinkedHashMap的内置的LRU方式，读写操作都是对map的全局锁，所以并发性效果方面稍差。</p>
<h3>1.2、solr.search.FastLRUCache</h3>
<p>在配置方面，FastLRUCache除了需要LRUCache的参数，还可有选择性的指定下面的参数：<br />
1）minSize：当cache达到它的最大数，淘汰策略使其降到minSize大小，默认是0.9*size。<br />
2）acceptableSize：当淘汰数据时，期望能降到minSize，但可能会做不到，则可勉为其难的降到acceptableSize，默认是0.95*size。<br />
3）cleanupThread：相比LRUCache是在put操作中同步进行淘汰工作，FastLRUCache可选择由独立的线程来做，也就是配置cleanupThread的时候。当cache大小很大时，每一次的淘汰数据就可能会花费较长时间，这对于提供查询请求的线程来说就不太合适，由独立的后台线程来做就很有必要。<br />
	实现上，FastLRUCache内部使用了ConcurrentLRUCache来缓存数据，它是个加了LRU淘汰策略的ConcurrentHashMap，所以其并发性要好很多，这也是多数Java版Cache的极典型实现。</p>
<h2>2、filterCache</h2>
<p>	filterCache存储了无序的lucene document id集合，该cache有3种用途：<br />
	1）filterCache存储了filter queries(&#8220;fq&#8221;参数)得到的document id集合结果。Solr中的query参数有两种，即q和fq。如果fq存在，Solr是先查询fq（因为fq可以多个，所以多个fq查询是个取结果交集的过程），之后将fq结果和q结果取并。在这一过程中，filterCache就是key为单个fq（类型为Query），value为document id集合（类型为DocSet）的cache。对于fq为range query来说，filterCache表现出其有价值的一面。<br />
	2）filterCache还可用于facet查询（http://wiki.apache.org/solr/SolrFacetingOverview），facet查询中各facet的计数是通过对满足query条件的document id集合（可涉及到filterCache）的处理得到的。因为统计各facet计数可能会涉及到所有的doc id，所以filterCache的大小需要能容下索引的文档数。<br />
	3）如果solfconfig.xml中配置了&lt;useFilterForSortedQuery/&gt;，那么如果查询有filter（此filter是一需要过滤的DocSet，而不是fq，我未见得它有什么用），则使用filterCache。<br />
下面是filterCache的配置示例：</p>

<div class="wp_syntax"><div class="code"><pre class="xml" style="font-family:monospace;">    <span style="color: #808080; font-style: italic;">&lt;!-- Internal cache used by SolrIndexSearcher for filters (DocSets),</span>
<span style="color: #808080; font-style: italic;">         unordered sets of *all* documents that match a query.</span>
<span style="color: #808080; font-style: italic;">         When a new searcher is opened, its caches may be prepopulated</span>
<span style="color: #808080; font-style: italic;">         or &quot;autowarmed&quot; using data from caches in the old searcher.</span>
<span style="color: #808080; font-style: italic;">         autowarmCount is the number of items to prepopulate.  For LRUCache,</span>
<span style="color: #808080; font-style: italic;">         the prepopulated items will be the most recently accessed items.</span>
<span style="color: #808080; font-style: italic;">      --&gt;</span>
    <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;filterCache</span></span>
<span style="color: #009900;">      <span style="color: #000066;">class</span>=<span style="color: #ff0000;">&quot;solr.LRUCache&quot;</span></span>
<span style="color: #009900;">      <span style="color: #000066;">size</span>=<span style="color: #ff0000;">&quot;16384&quot;</span></span>
<span style="color: #009900;">      <span style="color: #000066;">initialSize</span>=<span style="color: #ff0000;">&quot;4096&quot;</span></span>
<span style="color: #009900;">      <span style="color: #000066;">autowarmCount</span>=<span style="color: #ff0000;">&quot;4096&quot;</span><span style="color: #000000; font-weight: bold;">/&gt;</span></span></pre></div></div>

<p>      对于是否使用filterCache及如何配置filterCache大小，需要根据应用特点、统计、效果、经验等各方面来评估。对于使用fq、facet的应用，对filterCache的调优是很有必要的。</p>
<h2>3、queryResultCache</h2>
<p>	顾名思义，queryResultCache是对查询结果的缓存（SolrIndexSearcher中的cache缓存的都是document id set），这个结果就是针对查询条件的完全有序的结果。下面是它的配置示例：</p>

<div class="wp_syntax"><div class="code"><pre class="xml" style="font-family:monospace;">	    <span style="color: #808080; font-style: italic;">&lt;!-- queryResultCache caches results of searches - ordered lists of</span>
<span style="color: #808080; font-style: italic;">         document ids (DocList) based on a query, a sort, and the range</span>
<span style="color: #808080; font-style: italic;">         of documents requested.</span>
<span style="color: #808080; font-style: italic;">      --&gt;</span>
    <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;queryResultCache</span></span>
<span style="color: #009900;">      <span style="color: #000066;">class</span>=<span style="color: #ff0000;">&quot;solr.LRUCache&quot;</span></span>
<span style="color: #009900;">      <span style="color: #000066;">size</span>=<span style="color: #ff0000;">&quot;16384&quot;</span></span>
<span style="color: #009900;">      <span style="color: #000066;">initialSize</span>=<span style="color: #ff0000;">&quot;4096&quot;</span></span>
<span style="color: #009900;">      <span style="color: #000066;">autowarmCount</span>=<span style="color: #ff0000;">&quot;1024&quot;</span><span style="color: #000000; font-weight: bold;">/&gt;</span></span></pre></div></div>

<p>      缓存的key是个什么结构呢？就是下面的类（key的hashcode就是QueryResultKey的成员变量hc）：</p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">public</span> QueryResultKey<span style="color: #009900;">&#40;</span>Query query, List<span style="color: #339933;">&lt;</span>Query<span style="color: #339933;">&gt;</span> filters, Sort sort, <span style="color: #000066; font-weight: bold;">int</span> nc_flags<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #000000; font-weight: bold;">this</span>.<span style="color: #006633;">query</span> <span style="color: #339933;">=</span> query<span style="color: #339933;">;</span>
    <span style="color: #000000; font-weight: bold;">this</span>.<span style="color: #006633;">sort</span> <span style="color: #339933;">=</span> sort<span style="color: #339933;">;</span>
    <span style="color: #000000; font-weight: bold;">this</span>.<span style="color: #006633;">filters</span> <span style="color: #339933;">=</span> filters<span style="color: #339933;">;</span>
    <span style="color: #000000; font-weight: bold;">this</span>.<span style="color: #006633;">nc_flags</span> <span style="color: #339933;">=</span> nc_flags<span style="color: #339933;">;</span>
    <span style="color: #000066; font-weight: bold;">int</span> h <span style="color: #339933;">=</span> query.<span style="color: #006633;">hashCode</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>filters <span style="color: #339933;">!=</span> <span style="color: #000066; font-weight: bold;">null</span><span style="color: #009900;">&#41;</span> h <span style="color: #339933;">^=</span> filters.<span style="color: #006633;">hashCode</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    sfields <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span><span style="color: #000000; font-weight: bold;">this</span>.<span style="color: #006633;">sort</span> <span style="color: #339933;">!=</span><span style="color: #000066; font-weight: bold;">null</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">?</span> <span style="color: #000000; font-weight: bold;">this</span>.<span style="color: #006633;">sort</span>.<span style="color: #006633;">getSort</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">:</span> defaultSort<span style="color: #339933;">;</span>
    <span style="color: #000000; font-weight: bold;">for</span> <span style="color: #009900;">&#40;</span>SortField sf <span style="color: #339933;">:</span> sfields<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
      <span style="color: #666666; font-style: italic;">// mix the bits so that sortFields are position dependent</span>
      <span style="color: #666666; font-style: italic;">// so that a,b won't hash to the same value as b,a</span>
      h <span style="color: #339933;">^=</span> <span style="color: #009900;">&#40;</span>h <span style="color: #339933;">&lt;&lt;</span> <span style="color: #cc66cc;">8</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">|</span> <span style="color: #009900;">&#40;</span>h <span style="color: #339933;">&gt;&gt;&gt;</span> <span style="color: #cc66cc;">25</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>   <span style="color: #666666; font-style: italic;">// reversible hash</span>
      <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>sf.<span style="color: #006633;">getField</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">!=</span> <span style="color: #000066; font-weight: bold;">null</span><span style="color: #009900;">&#41;</span> h <span style="color: #339933;">+=</span> sf.<span style="color: #006633;">getField</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>.<span style="color: #006633;">hashCode</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
      h <span style="color: #339933;">+=</span> sf.<span style="color: #006633;">getType</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
      <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>sf.<span style="color: #006633;">getReverse</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> h<span style="color: #339933;">=</span>~h<span style="color: #339933;">;</span>
      <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>sf.<span style="color: #006633;">getLocale</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">!=</span><span style="color: #000066; font-weight: bold;">null</span><span style="color: #009900;">&#41;</span> h<span style="color: #339933;">+=</span>sf.<span style="color: #006633;">getLocale</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>.<span style="color: #006633;">hashCode</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
      <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>sf.<span style="color: #006633;">getFactory</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">!=</span><span style="color: #000066; font-weight: bold;">null</span><span style="color: #009900;">&#41;</span> h<span style="color: #339933;">+=</span>sf.<span style="color: #006633;">getFactory</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>.<span style="color: #006633;">hashCode</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
    hc <span style="color: #339933;">=</span> h<span style="color: #339933;">;</span>
  <span style="color: #009900;">&#125;</span></pre></div></div>

<p>  因为查询参数是有start和rows的，所以某个QueryResultKey可能命中了cache，但start和rows却不在cache的document id set范围内。当然，document id set是越大命中的概率越大，但这也会很浪费内存，这就需要个参数：queryResultWindowSize来指定document id set的大小。Solr中默认取值为50,可配置，WIKI上的解释很深简单明了：</p>

<div class="wp_syntax"><div class="code"><pre class="xml" style="font-family:monospace;">      <span style="color: #808080; font-style: italic;">&lt;!-- An optimization for use with the queryResultCache.  When a search</span>
<span style="color: #808080; font-style: italic;">         is requested, a superset of the requested number of document ids</span>
<span style="color: #808080; font-style: italic;">         are collected.  For example, of a search for a particular query</span>
<span style="color: #808080; font-style: italic;">         requests matching documents 10 through 19, and queryWindowSize is 50,</span>
<span style="color: #808080; font-style: italic;">         then documents 0 through 50 will be collected and cached.  Any further</span>
<span style="color: #808080; font-style: italic;">         requests in that range can be satisfied via the cache.</span>
<span style="color: #808080; font-style: italic;">    --&gt;</span>
    <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;queryResultWindowSize<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>50<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/queryResultWindowSize<span style="color: #000000; font-weight: bold;">&gt;</span></span></span></pre></div></div>

<p>  相比filterCache来说，queryResultCache内存使用上要更少一些，但它的效果如何就很难说。就索引数据来说，通常我们只是在索引上存储应用主键id，再从数据库等数据源获取其他需要的字段。这使得查询过程变成，首先通过solr得到document id set，再由Solr得到应用id集合，最后从外部数据源得到完成的查询结果。如果对查询结果正确性没有苛刻的要求，可以在Solr之外独立的缓存完整的查询结果（定时作废），这时queryResultCache就不是很有必要，否则可以考虑使用queryResultCache。当然，如果发现在queryResultCache生命周期内，query重合度很低，也不是很有必要开着它。</p>
<h2>4、documentCache</h2>
<p>	又顾名思义，documentCache用来保存&lt;doc_id,document&gt;对的。如果使用documentCache，就尽可能开大些，至少要大过&lt;max_results&gt; * &lt;max_concurrent_queries&gt;，否则因为cache的淘汰，一次请求期间还需要重新获取document一次。也要注意document中存储的字段的多少，避免大量的内存消耗。<br />
	下面是documentCache的配置示例：</p>

<div class="wp_syntax"><div class="code"><pre class="xml" style="font-family:monospace;">	<span style="color: #808080; font-style: italic;">&lt;!-- documentCache caches Lucene Document objects (the stored fields for each document).</span>
<span style="color: #808080; font-style: italic;">      --&gt;</span>
    <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;documentCache</span></span>
<span style="color: #009900;">      <span style="color: #000066;">class</span>=<span style="color: #ff0000;">&quot;solr.LRUCache&quot;</span></span>
<span style="color: #009900;">      <span style="color: #000066;">size</span>=<span style="color: #ff0000;">&quot;16384&quot;</span></span>
<span style="color: #009900;">      <span style="color: #000066;">initialSize</span>=<span style="color: #ff0000;">&quot;16384&quot;</span><span style="color: #000000; font-weight: bold;">/&gt;</span></span></pre></div></div>

<h2>5、User/Generic Caches</h2>
<p>Solr支持自定义Cache，只需要实现自定义的regenerator即可，下面是配置示例：</p>

<div class="wp_syntax"><div class="code"><pre class="xml" style="font-family:monospace;">    <span style="color: #808080; font-style: italic;">&lt;!-- Example of a generic cache.  These caches may be accessed by name</span>
<span style="color: #808080; font-style: italic;">         through SolrIndexSearcher.getCache(),cacheLookup(), and cacheInsert().</span>
<span style="color: #808080; font-style: italic;">         The purpose is to enable easy caching of user/application level data.</span>
<span style="color: #808080; font-style: italic;">         The regenerator argument should be specified as an implementation</span>
<span style="color: #808080; font-style: italic;">         of solr.search.CacheRegenerator if autowarming is desired.</span>
<span style="color: #808080; font-style: italic;">    --&gt;</span>
    <span style="color: #808080; font-style: italic;">&lt;!--</span>
<span style="color: #808080; font-style: italic;">    &lt;cache name=&quot;yourCacheNameHere&quot;</span>
<span style="color: #808080; font-style: italic;">      class=&quot;solr.LRUCache&quot;</span>
<span style="color: #808080; font-style: italic;">      size=&quot;4096&quot;</span>
<span style="color: #808080; font-style: italic;">      initialSize=&quot;2048&quot;</span>
<span style="color: #808080; font-style: italic;">      autowarmCount=&quot;4096&quot;</span>
<span style="color: #808080; font-style: italic;">      regenerator=&quot;org.foo.bar.YourRegenerator&quot;/&gt;</span>
<span style="color: #808080; font-style: italic;">    --&gt;</span></pre></div></div>

<h2>6、The Lucene FieldCache</h2>
<p>	lucene中有相对低级别的FieldCache，Solr并不对它做管理，所以，lucene的FieldCache还是由lucene的IndexSearcher来搞。</p>
<h2>7、autowarm</h2>
<p>	上面有提到autowarm，autowarm触发的时机有两个，一个是创建第一个Searcher时（firstSearcher），一个是创建个新Searcher（newSearcher）来代替当前的Searcher。在Searcher提供请求服务前，Searcher中的各个Cache可以做warm处理，处理的地方通常是SolrCache的init方法，而不同cache的warm策略也不一样。<br />
	1）filterCache：filterCache注册了下面的CacheRegenerator，就是由旧的key查询索引得到新值put到新cache中。</p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;">	      solrConfig.<span style="color: #006633;">filterCacheConfig</span>.<span style="color: #006633;">setRegenerator</span><span style="color: #009900;">&#40;</span>
              <span style="color: #000000; font-weight: bold;">new</span> CacheRegenerator<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;">public</span> <span style="color: #000066; font-weight: bold;">boolean</span> regenerateItem<span style="color: #009900;">&#40;</span>SolrIndexSearcher newSearcher, SolrCache newCache, SolrCache oldCache, <span style="color: #003399;">Object</span> oldKey, <span style="color: #003399;">Object</span> oldVal<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>
                  newSearcher.<span style="color: #006633;">cacheDocSet</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#40;</span>Query<span style="color: #009900;">&#41;</span>oldKey, <span style="color: #000066; font-weight: bold;">null</span>, <span style="color: #000066; font-weight: bold;">false</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
                  <span style="color: #000000; font-weight: bold;">return</span> <span style="color: #000066; font-weight: bold;">true</span><span style="color: #339933;">;</span>
                <span style="color: #009900;">&#125;</span>
              <span style="color: #009900;">&#125;</span>
      <span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p>      2）queryResultCache：queryResultCache的autowarm不在SolrCache的init（也就是说，不是去遍历已有的queryResultCache中的query key执行查询），而是通过SolrEventListener接口的void newSearcher(SolrIndexSearcher newSearcher, SolrIndexSearcher currentSearcher)方法，来执行配置中特定的query查询，达到显示的预热lucene FieldCache的效果。<br />
      queryResultCache的配置示例如下：</p>

<div class="wp_syntax"><div class="code"><pre class="xml" style="font-family:monospace;">          <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;listener</span> <span style="color: #000066;">event</span>=<span style="color: #ff0000;">&quot;newSearcher&quot;</span> <span style="color: #000066;">class</span>=<span style="color: #ff0000;">&quot;solr.QuerySenderListener&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>
      <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;arr</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;queries&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>
        <span style="color: #808080; font-style: italic;">&lt;!-- seed common sort fields --&gt;</span>
        <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;lst<span style="color: #000000; font-weight: bold;">&gt;</span></span></span> <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;str</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;q&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>anything<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/str<span style="color: #000000; font-weight: bold;">&gt;</span></span></span> <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;str</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;sort&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>name desc price desc populartiy desc<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/str<span style="color: #000000; font-weight: bold;">&gt;</span></span></span> <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/lst<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
      <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/arr<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
    <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/listener<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
    <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;listener</span> <span style="color: #000066;">event</span>=<span style="color: #ff0000;">&quot;firstSearcher&quot;</span> <span style="color: #000066;">class</span>=<span style="color: #ff0000;">&quot;solr.QuerySenderListener&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>
      <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;arr</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;queries&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>
        <span style="color: #808080; font-style: italic;">&lt;!-- seed common sort fields --&gt;</span>
        <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;lst<span style="color: #000000; font-weight: bold;">&gt;</span></span></span> <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;str</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;q&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>anything<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/str<span style="color: #000000; font-weight: bold;">&gt;</span></span></span> <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;str</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;sort&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>name desc, price desc, populartiy desc<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/str<span style="color: #000000; font-weight: bold;">&gt;</span></span></span> <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/lst<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
        <span style="color: #808080; font-style: italic;">&lt;!-- seed common facets and filter queries --&gt;</span>
        <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;lst<span style="color: #000000; font-weight: bold;">&gt;</span></span></span> <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;str</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;q&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>anything<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/str<span style="color: #000000; font-weight: bold;">&gt;</span></span></span> 
              <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;str</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;facet.field&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>category<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/str<span style="color: #000000; font-weight: bold;">&gt;</span></span></span> 
              <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;str</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;fq&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>inStock:true<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/str<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
              <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;str</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;fq&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>price:[0 TO 100]<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/str<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
        <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/lst<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
      <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/arr<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
    <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/listener<span style="color: #000000; font-weight: bold;">&gt;</span></span></span></pre></div></div>

<p>    3）documentCache：因为新索引的document id和索引文档的对应关系发生变化，所以documentCache没有warm的过程，落得白茫茫一片真干净。<br />
    尽管autowarm很好，也要注意autowarm带来的开销，这需要在实际中检验其warm的开销，也要注意Searcher的切换频率，避免因为warm和切换影响Searcher提供正常的查询服务。</p>
<h2>8、参考文章</h2>
<p>http://wiki.apache.org/solr/SolrCaching</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kafka0102.com/2010/08/267.html/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>[Solr源码分析]Solr复制类ReplicationHandler实现简要分析</title>
		<link>http://www.kafka0102.com/2010/07/249.html</link>
		<comments>http://www.kafka0102.com/2010/07/249.html#comments</comments>
		<pubDate>Sat, 24 Jul 2010 16:42:27 +0000</pubDate>
		<dc:creator>kafka0102</dc:creator>
				<category><![CDATA[solr]]></category>
		<category><![CDATA[lucene]]></category>
		<category><![CDATA[源码分析]]></category>

		<guid isPermaLink="false">http://www.kafka0102.com/?p=249</guid>
		<description><![CDATA[在上一文《solr ReplicationHandler使用介绍》的基础上，本文接着对solr的ReplicationHandler实现细节做些分析，这个分析原则上没有摘取大段代码，窃以为摘了代码后未见得有很好的阐述效果，但不摘取后窃又发现，阐述的效果依旧不好。归结起来，还是窃的表达不够深入浅出所致。闲言少叙，直接上内容。]]></description>
			<content:encoded><![CDATA[<p>在上一文《<a title="Permanent Link to solr ReplicationHandler使用介绍" rel="bookmark" href="http://www.kafka0102.com/2010/07/244.html">solr  ReplicationHandler使用介绍</a>》的基础上，本文接着对solr的ReplicationHandler实现细节做些分析，这个分析原则上没有摘取大段代码，窃以为摘了代码后未见得有很好的阐述效果，但不摘取后窃又发现，阐述的效果依旧不好。归结起来，还是窃的表达不够深入浅出所致。闲言少叙，直接上内容。</p>
<h2>1、master的工作</h2>
<p>对于ReplicationHandler的复制功能来说，核心的问题确定是在一个时间点要复制哪些文件，这就用上了lucene的IndexDeletionPolicy的特性。lucene在初始化时，会调用IndexDeletionPolicy.onInit(List&lt;? extends IndexCommit&gt; commits)方法；lucene在commit（触发的时机也可以是optimize、close，solr在commit时实际上就是close了indexwriter）时，会调用IndexDeletionPolicy.onCommit(List&lt;? extends IndexCommit&gt; commits)。IndexCommit对象中保存了该次提交关联的文件列表等信息，这使得solr中的复制过程中，slave可以从master得到文件列表后跟本地文件做比较，跳过不变的文件，下载新文件，并删除无用的文件。IndexDeletionPolicy的两个针对commits的函数，会对当前存在的commits列表做些处理，比如lucene默认的KeepOnlyLastCommitDeletionPolicy会只保留最新的IndexCommit，对那些过时的IndexCommit执行delete操作以将无用的文件删掉。solr中，SolrDeletionPolicy默认也是保留最新一个IndexCommit，但可以设置maxCommitAge、maxCommitsToKeep、maxOptimizedCommitsToKeep来保留更多的IndexCommit。但solr真正使用的IndexDeletionPolicy实现是IndexDeletionPolicyWrapper，它是SolrDeletionPolicy的wrap。在slave从master复制文件的过程中，要保证当前正在复制的IndexCommit点不能被删除，这就用到了IndexDeletionPolicyWrapper中的void setReserveDuration(Long indexVersion, long reserveTime)方法，该方法会在master向slave响应indexversion、filelist命令前、以及每向slave传送5M的索引文件内容时调用，而默认的reserveTime时间是10s，如果慢速网络传输5M数据需要10秒以上，就需要调整该值了。</p>
<p>ReplicationHandler复制文件没有采用rsync，而是使用http，它在读一个文件内容传输到slave时，默认是按照1M大小分段输出内容到slave（http chunked？），并且默认是对每段内容做了checksum，保证传输的内容的正确性。上面提到的setReserveDuration点，主要就是它在packetsWritten % 5 == 0次数后触发一次修改。</p>
<p>ReplicationHandler还可以备份索引文件。由于lucene的索引文件只是追加新文件而不会修改已有文件，所以只要针对一个IndexCommit点做备份，其过程还是很简单的。</p>
<h2>2、slave的工作</h2>
<p>slave启动时会创建SnapPuller对象，SnapPuller会启动一个线程定时的（pollInterval间隔）从master复制数据（fetchLatestIndex方法）。对于一次复制过程，slave和master交互处理细节如下：<br />
1、slave首先向master询问最新的索引版本号（indexversion命令），slave检查得到的latestVersion、latestGeneration有效后，和本地的IndexCommit的getVersion()、getGeneration()比较，如果不相等，则需要往下进行，否则等待下一次调度。</p>
<p>2、slave向master请求之前得到的indexversion下的文件列表（filelist命令，包括索引文件和可选的配置文件）。如果文件列表为空，则返回等待下一次调度。否则，就需要检查哪些文件需要被下载过来。这里做的判断有：1）如果本地的commit.getGeneration() &gt;= latestGeneration，说明本地索引文件被破坏（比如对slave不小心提交了修改索引的命令），需要完全将master的文件复制过来。2）逐个检查文件列表中的文件是否在本地存在，不存在就下载下来。</p>
<p>3、对于下载文件内容，对应命令是filecontent。下载的文件显然需要放到临时目录中，这个临时目录和已有的索引目录（默认名字index）在同一数据目录下，只是命名为index.&lt;时间戳&gt;。下载完毕后，copy数据有两种情况：1）如果是完全下载，则不需要将临时目录中的文件copy到已有目录中，而是修改数据目录中的index.properties，标识索引目录为新生成的临时目录，而旧索引目录并不会被删除，可以手工删掉，当然，通常是不应该出现slave的Generation大于master的异常情况。2）通常就是把临时索引目录的文件copy到旧索引目录，copy时要把segments_N放到最后copy，避免copy中途出现异常造成数据被毁。</p>
<p>4、当新索引和可选的配置文件copy完毕之后，slave会对solrcore的UpdateHandler做commit操作，这会close掉indexwriter并强制重启新的indexsearcher提供服务。同时，如果solrcore的UpdateHandler是DirectUpdateHandler2（不应该不是），会强制调用handler.forceOpenWriter()来删除旧的无用的索引文件，并调用replicationHandler.refreshCommitpoint()来更新slave的indexCommitPoint。</p>
<p>5、如果索引复制失败，slave会向数据目录下的replication.properties输出复制失败的信息。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kafka0102.com/2010/07/249.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>[Solr实践]Solr复制类ReplicationHandler使用介绍</title>
		<link>http://www.kafka0102.com/2010/07/244.html</link>
		<comments>http://www.kafka0102.com/2010/07/244.html#comments</comments>
		<pubDate>Sat, 24 Jul 2010 14:30:51 +0000</pubDate>
		<dc:creator>kafka0102</dc:creator>
				<category><![CDATA[solr]]></category>
		<category><![CDATA[lucene]]></category>
		<category><![CDATA[ReplicationHandler]]></category>

		<guid isPermaLink="false">http://www.kafka0102.com/?p=244</guid>
		<description><![CDATA[solr1.4中引入ReplicationHandler代替外部脚本来复制索引数据，ReplicationHandler使得复制索引数据更自动化。对于使用者来说，只要简单的配置好，就可以一劳永逸的享受solr的复制功能了。下面介绍其使用相关内容。]]></description>
			<content:encoded><![CDATA[<p>	solr1.4中引入ReplicationHandler代替外部脚本来复制索引数据，ReplicationHandler使得复制索引数据更自动化。对于使用者来说，只要简单的配置好，就可以一劳永逸的享受solr的复制功能了。下面介绍其使用相关内容。</p>
<h2>1、配置</h2>
<p>	ReplicationHandler是个RequestHandler，如果需要使用它，也就是在solrconfig.xml中配置它，下面介绍ReplicationHandler的配置参数。</p>
<h3>1.1、Master</h3>
<p>	master的配置示例如下：</p>

<div class="wp_syntax"><div class="code"><pre class="xml" style="font-family:monospace;"><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;requestHandler</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;/replication&quot;</span> <span style="color: #000066;">class</span>=<span style="color: #ff0000;">&quot;solr.ReplicationHandler&quot;</span> <span style="color: #000000; font-weight: bold;">&gt;</span></span>
    <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;lst</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;master&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>
        <span style="color: #808080; font-style: italic;">&lt;!--Replicate on 'startup' and 'commit'. 'optimize' is also a valid value for replicateAfter. --&gt;</span>
        <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;str</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;replicateAfter&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>startup<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/str<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
        <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;str</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;replicateAfter&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>commit<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/str<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
&nbsp;
        <span style="color: #808080; font-style: italic;">&lt;!--Create a backup after 'optimize'. Other values can be 'commit', 'startup'. It is possible to have multiple entries of this config string.  Note that this is just for backup, replication does not require this. --&gt;</span>
        <span style="color: #808080; font-style: italic;">&lt;!-- &lt;str name=&quot;backupAfter&quot;&gt;optimize&lt;/str&gt; --&gt;</span>
&nbsp;
        <span style="color: #808080; font-style: italic;">&lt;!--If configuration files need to be replicated give the names here, separated by comma --&gt;</span>
        <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;str</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;confFiles&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>schema.xml,stopwords.txt,elevate.xml<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/str<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
       <span style="color: #808080; font-style: italic;">&lt;!--The default value of reservation is 10 secs.See the documentation below . Normally , you should not need to specify this --&gt;</span>
        <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;str</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;commitReserveDuration&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>00:00:10<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/str<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
    <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/lst<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/requestHandler<span style="color: #000000; font-weight: bold;">&gt;</span></span></span></pre></div></div>

<p>	说明：<br />
	1)replicateAfter可取startup、commit、optimize，表示触发复制的时机。使用中，这三个值都可以配上。<br />
	2)backupAfter表示备份时机，如果需要备份，solr会在配置的时机自动生成备份。<br />
	3)confFiles表示在复制时需要复制到slave的文件列表。<br />
	4)commitReserveDuration默认是10秒，这个值通常你通常不需要修改，除非你的网络慢到传输5M数据需要10秒以上的时间。</p>
<h3>1.2、Slave</h3>
<p>	Slave的配置示例如下：</p>

<div class="wp_syntax"><div class="code"><pre class="xml" style="font-family:monospace;"><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;requestHandler</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;/replication&quot;</span> <span style="color: #000066;">class</span>=<span style="color: #ff0000;">&quot;solr.ReplicationHandler&quot;</span> <span style="color: #000000; font-weight: bold;">&gt;</span></span>
    <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;lst</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;slave&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>
&nbsp;
        <span style="color: #808080; font-style: italic;">&lt;!--fully qualified url for the replication handler of master . It is possible to pass on this as a request param for the fetchindex command--&gt;</span>
        <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;str</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;masterUrl&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>http://master_host:port/solr/corename/replication<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/str<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>  
&nbsp;
        <span style="color: #808080; font-style: italic;">&lt;!--Interval in which the slave should poll master .Format is HH:mm:ss . If this is absent slave does not poll automatically. </span>
<span style="color: #808080; font-style: italic;">         But a fetchindex can be triggered from the admin or the http API --&gt;</span>
        <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;str</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;pollInterval&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>00:00:20<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/str<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>  
        <span style="color: #808080; font-style: italic;">&lt;!-- THE FOLLOWING PARAMETERS ARE USUALLY NOT REQUIRED--&gt;</span>
        <span style="color: #808080; font-style: italic;">&lt;!--to use compression while transferring the index files. The possible values are internal|external</span>
<span style="color: #808080; font-style: italic;">         if the value is 'external' make sure that your master Solr has the settings to honour the accept-encoding header.</span>
<span style="color: #808080; font-style: italic;">         see here for details http://wiki.apache.org/solr/SolrHttpCompression</span>
<span style="color: #808080; font-style: italic;">         If it is 'internal' everything will be taken care of automatically. </span>
<span style="color: #808080; font-style: italic;">         USE THIS ONLY IF YOUR BANDWIDTH IS LOW . THIS CAN ACTUALLY SLOWDOWN REPLICATION IN A LAN--&gt;</span>
        <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;str</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;compression&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>internal<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/str<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
        <span style="color: #808080; font-style: italic;">&lt;!--The following values are used when the slave connects to the master to download the index files. </span>
<span style="color: #808080; font-style: italic;">         Default values implicitly set as 5000ms and 10000ms respectively. The user DOES NOT need to specify </span>
<span style="color: #808080; font-style: italic;">         these unless the bandwidth is extremely low or if there is an extremely high latency--&gt;</span>
        <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;str</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;httpConnTimeout&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>5000<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/str<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
        <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;str</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;httpReadTimeout&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>10000<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/str<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
&nbsp;
        <span style="color: #808080; font-style: italic;">&lt;!-- If HTTP Basic authentication is enabled on the master, then the slave can be configured with the following --&gt;</span>
        <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;str</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;httpBasicAuthUser&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>username<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/str<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
        <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;str</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;httpBasicAuthPassword&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>password<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/str<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
     <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/lst<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/requestHandler<span style="color: #000000; font-weight: bold;">&gt;</span></span></span></pre></div></div>

<p>	说明：上面的参数也不需要太多解释，其中pollInterval参数表明slave从master复制数据的频率。如果对实时性要求不高，通常5-10分钟即可，也避免slave的indexsearcher频繁的切换，同时，master的commit频率也可相对保持一致。</p>
<h2>2、HTTP API</h2>
<p>	solr的ReplicationHandler提供了一系列http命令（参数command），支持的可选值如下：<br />
	1）indexversion：slave从master获取最新的索引点信息。<br />
	2）filecontent：slave从master下载指定文件的内容。<br />
	3）filelist：slave从master获取指定indexversion的索引文件列表（及需要复制的配置文件）。<br />
	4）backup：备份索引。如果担心索引有损坏的可能性，可以定期备份索引。<br />
	5）fetchindex：手动复制数据，和slave自动复制相当。<br />
	6）disablepoll：停止slave的复制。<br />
	7）enablepoll：开启slave的复制。<br />
	8）abortfetch：终止slave上正在进行的下载文件过程。<br />
	9）commits：show当前仍旧保留的IndexCommit信息。<br />
	10）details：show slave当前的复制细节信息。<br />
	11）enablereplication：启动master对所有slave的复制功能<br />
	12）disablereplication：关闭master对所有slave的复制功能</p>
<h2>4、性能</h2>
<p>solr的ReplicationHandler使用http的分段连续的下载索引文件数据，而代替经典的rsync，solr wiki上给出的性能测试对比图如下：<br />
<a href="http://www.kafka0102.com/wp-content/uploads/2010/07/transfer_time.png"><img src="http://www.kafka0102.com/wp-content/uploads/2010/07/transfer_time.png" alt="" title="transfer_time" width="800" height="600" class="aligncenter size-full wp-image-245" /></a><br />
<br/><br />
可以看到，性能方面差别不大，不必有太多的担心。</p>
<h2>4、参考文章</h2>
<p>http://wiki.apache.org/solr/SolrReplication</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kafka0102.com/2010/07/244.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>分享Sify.com的架构经验</title>
		<link>http://www.kafka0102.com/2010/05/144.html</link>
		<comments>http://www.kafka0102.com/2010/05/144.html#comments</comments>
		<pubDate>Sat, 15 May 2010 17:27:11 +0000</pubDate>
		<dc:creator>kafka0102</dc:creator>
				<category><![CDATA[architecture]]></category>
		<category><![CDATA[Sify.com]]></category>
		<category><![CDATA[solr]]></category>
		<category><![CDATA[架构]]></category>

		<guid isPermaLink="false">http://www.kafka0102.com/?p=144</guid>
		<description><![CDATA[今天分享的网站架构来自于Sify.com Architecture - A Portal at 3900 Requests Per Second（该标题有标题党嫌疑），对英文熟稔并不屑于我的中文简述的可以跳过该文。Sify.com是印度的一家portal网站，应该是信息集成类网站。它给出的月 pv是1.5亿次，每秒请求数是3900次（应该是针对所有服务的页面请求，包括异步的，并且是高峰的，否则就和pv对不上了）。按规模来说，算是个中等规模的网站，不过它的架构却是很值得说道的。]]></description>
			<content:encoded><![CDATA[<p>今天分享的网站架构来自于<a href="http://highscalability.com/blog/2010/5/10/sifycom-architecture-a-portal-at-3900-requests-per-second.html " target="_blank">Sify.com Architecture - A Portal at 3900 Requests Per Second</a>（该标题有标题党嫌疑），对英文熟稔并不屑于我的中文简述的可以跳过该文。Sify.com是印度的一家portal网站，应该是信息集成类网站。它给出的月pv是1.5亿次，每秒请求数是3900次（应该是针对所有服务的页面请求，包括异步的，并且是高峰的，否则就和pv对不上了）。按规模来说，算是个中等规模的网站，不过它的架构却是很值得说道的。</p>
<h2>网站架构</h2>
<p>1、为了节约机器资源并最大化利用机器资源，Sify.com也广泛采用了虚拟机。对于同一服务，Sify.com将其部署在多台机器上，使一台机器挂掉后仍有其他机器提供服务。对于冗余和负载均衡，目前Sify.com是将其做成配置手工修改，将来可能会自动化管理和扩展。对于运维来说，别说中小网站，就是一些大网站也没有做到自动化运维，很多还是收到报警后人肉解决。尽管自动化运维很酷，但从成本角度来说，大多中小网站不必追求这点，把监控做好，能在最快时间发现问题并解决问题就够了。</p>
<p>2、Sify.com的存储很特别，它没有使用通用的数据库，它的关系数据存在检索系统中，全文数据存在分布式文件系统中。这种围绕检索系统+kv文件系统的解决方案，我以前也读到过一篇文章。Sify.com的检索系统基于Solr，我最近也在看Solr，打算重做的检索系统也是基于Solr。Sify.com的查询会从检索系统检索出id，如果需要全文，再从文件系统（它用的是GFS，该GFS不是google的，而是一个开源的集群文件系统，我也不是很了解的说，感兴趣的可以访问http://sourceware.org/cluster/gfs/参观）取出全文内容。这个策略也是通常的检索系统处理策略。</p>
<p>3、Sify.com的提交操作采用异步处理模式，就是提交到ActiveMQ/Camel，然后分发到相应的服务处理。其中的Camel是开源的ESB实现，我也不甚了解。Sify.com各服务间似乎都是走HTTP协议，很有爱的表现。</p>
<h2>展望未来</h2>
<p>Sify.com提了一些愿景，我就不一一罗列，其中最吸引我的是，它打算采用Drools这个规则引擎做Cache失效处理。它提到Cache来源是Akamai和Varnish，所以失效的应该是来自CDN和本地的页面内容Cache。因为同一个URL请求可能会引起多个Cache项失效，而这种前端页面Cache失效让业务逻辑代码处理也不够友好，有个统一处理的系统在管理和操作上就方便很多。它的做法是，对于引起Cache失效的URL，将URL等信息打到日志中去，由Drools根据配置的规则来执行Cache失效策略。感觉上来说，还是很有意思的做法。</p>
<h2>经验教训</h2>
<p>Sify.com总结的经验都是和使用的软件的缺陷相关的，总结如下：</p>
<p>1、首先是ActiveMQ，把Sify.com搞得很狼狈。我目前对ActiveMQ只有初步的了解，也没什么发言权。它提到ActiveMQ的问题有两个：1）ActiveMQ耗尽socket资源的问题，尽管ActiveMQ5.0声称解决了，但Sify.com还是没看到效果，使得ActiveMQ需要不断重启，最后折中解决了，部署两台ActiveMQ，轮流切换，这解决方案真是够山寨的。2）它使用Topic订阅发布机制时有4个消费者，结果经常ActiveMQ会hang住数个小时，折中的解决是使用4个Queue代替（够囧），但运行还是不稳定，时有抛出异常或内存溢出的情况。按说ActiveMQ也够成熟了，也不知是真有问题还是Sify.com自己没用好。公司也有使用ActiveMQ，也打算有时间会对ActiveMQ和JMS做些深入的研究。</p>
<p>2、Solr的问题有3个：1）Solr有时会对请求没有响应或超时，就需要重启解决问题，这个可能我需要关注下。2）Solr对复杂查询（比如加NOT查询）处理慢，这个应该是lucene的问题了，只能尽可能规避，把复杂查询拆成多个简单查询后再合并处理。3）对实时检索的需求，目前Solr没有提供该功能，Sify.com打算采用LinkedIn的Zoie（有Solr plugin），不过因为Sify.com索引数据的主键是字母+数字组合，而Zoie的是数字的，需要Sify.com做些扩展工作。我前段时间也有专门对Zoie做了一些分析。</p>
<p>3、GFS锁问题造成GFS不能访问，显然是个很严重的臭虫，升级版本解决问题。</p>
<p>4、Sify.com使用Lighty和PHP合作的不是很愉快，说是PHP_FCGI不稳定，会使进程hang住CPU飚满，这个我没什么经验，也不发言。</p>
<h2>总结</h2>
<p>夜已深，睡意浓，到此为止。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kafka0102.com/2010/05/144.html/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>

