solr億萬級索引優化實踐(三)

       本篇文章主要介紹下如何從客戶端solrJ以及服務端參數配置的角度來提升索引速度。

       solrJ6.0提供的Java客戶端主要有下面幾種接口:HttpSolrClient,ConcurrentUpdateSolrClient,CloudSolrClient。下面分別對這三種接口做一個簡單的比較。HttpSolrClient在定義的時候需要明確指定一個solr節點路徑,他在提交數據的時候也只能提交到這個節點上;ConcurrentUpdateSolrClient接口在同樣是指定具體solr節點路徑的,但不一樣的事,這是個異步提交的模式,即我們在對客戶端添加數據的時候,客戶端會將文檔緩存到內存隊列中,讓隊列中的數據達到一定數量時,客戶端會自動一次性向solr服務器發起一個http請求;CloudSolrClient就比較簡單了,這個在定義時只需要指定zookeeper地址就好了,因爲solr節點註冊時,會將節點信息同步到zookeeper中,在提交數據時,CloudSolrClient會和zookeeper進行通信,發現solr雲中的索引庫,然後利用LBHttpSolrClient來提交數據。通過對比發現,ConcurrentUpdateSolrClient和CloudSolrClient這兩個接口比較優秀,後者比較適用於哈希路由的模式,而前者則比較適合指定路由的模式。

      CloudSolrClient

	public SolrClient CreateCloudSolrClient() {
		CloudSolrClient csClient = new CloudSolrClient(zkUrl);
		csClient.setZkConnectTimeout(zkConnectTimeOut);
		csClient.setZkClientTimeout(zkClientTimeOut);
		csClient.setDefaultCollection(collectionName);
		csClient.setParallelUpdates(true);
		csClient.setRequestWriter(new BinaryRequestWriter());
		csClient.connect();
		return csClient;
	}

       ConcurrentUpdateSolrClient

public SolrClient CreateHttpClient() {
		ModifiableSolrParams params = new ModifiableSolrParams();
		params.set(HttpClientUtil.PROP_MAX_CONNECTIONS, 128);
		params.set(HttpClientUtil.PROP_MAX_CONNECTIONS_PER_HOST, 32);
		params.set(HttpClientUtil.PROP_FOLLOW_REDIRECTS, false);
		params.set(HttpClientUtil.PROP_ALLOW_COMPRESSION, true);
		params.set(HttpClientUtil.PROP_USE_RETRY, true);
		HttpClient httpClient = HttpClientUtil.createClient(params);
		httpLists.add(httpClient);
		ConcurrentUpdateSolrClient client = new ConcurrentUpdateSolrClient(solrUrl + "/" + collectionName, 50, 1);
		client.setConnectionTimeout(zkClientTimeOut);
		client.setSoTimeout(zkConnectTimeOut);
		client.setPollQueueTime(500);
		client.setParser(new BinaryResponseParser());
		client.setRequestWriter(new BinaryRequestWriter());
		return client;
	}

        上篇文章介紹過,在多線程配合指定路由的模式下,集羣的性能能夠水平拓展,這個時候再配合ConcurrentUpdateSolrClient客戶端,單個節點速度也會有比較大的提升。需要注意的是,有兩個參數需要慎重設置,第一個是隊列大小,這個指的是隊列中最多存儲多少個請求之後向服務器進行提交,另一個是線程數,表示內部開幾個線程來提交數據。這兩個參數需要根據自己應用程序的JVM來設置,如果設置的過大,會導致內存溢出。

         由於是異步請求的方式,所以如果在建立索引的過程中出現了異常,異常信息是不會拋給應用程序的,後來通過調試源碼發現,solrJ自己在內部處理了這個異常(其實什麼都沒做,預留了一個空方法),我們可以在選擇修改源碼來捕捉異常數據,或者將此異常拋出,由應用程序來捕捉異常,持久化異常數據。


          在服務器端,我們也可以通過一些優化,來提高建立索引速度。

          1   段合併

        solr的索引是由段組成,更新索引的時候是寫入一個段的信息,幾個段共同組成一個索引,在solr優化索引的時候或其他的時候,solr的段是會合並的。所以我們可以對相關參數進行控制,對段的大小以及合併的頻率進行調整,來提交系統資源利用效率。

        mergeFactor這個參數是合併因子,只當內存中有N個文檔時則合併成一個段,當存在N個段文件時則合併成一個新的段文件。

        minMergeSize,指定最小的合併段大小,如果段的大小小於這個值,則可以參加合併。

        maxMergeSize,當一個段的大小大於這個值的時候就不參與合併了。

        maxMergeDocs,當文檔數據量大於這個的時候,這個段就不參與合併了。

在實際場景中,應該根據物理機的資源,來配置這些參數,適量加大mergeFactor參數,來降低合併頻率,頻繁的段合併會消耗大量系統資源。

     

         2  自動生成ID

         在Solr中,每一個索引,都要有一個唯一的ID,類似於關係型數據庫表中的主鍵。我們在創建文檔的時候,需要自己生成一串UUID來標示文檔,但是由於ID比較長,在索引過程中,會佔用一些額外的網速和內存等資源,我們可以控制在服務器端讓solr自己生成UUID,具體配置步驟如下:

        1)修改schema.xml文件,將ID字段修改爲UUID類型

 <field name="id" type="uuid" indexed="true" stored="true" required="true" multiValued="false" /> 
 <fieldType name="uuid" class="solr.UUIDField" indexed="true" />

        2)配置solrconfig.xml文件,添加更新策略配置,生成UUID

<updateRequestProcessorChain name="uuid">
     <processor class="solr.UUIDUpdateProcessorFactory">
          <str name="fieldName">id</str>
     </processor>
     <processor class="solr.LogUpdateProcessorFactory" />
     <processor class="solr.DistributedUpdateProcessorFactory" />
     <processor class="solr.RunUpdateProcessorFactory" />
</updateRequestProcessorChain>

        3)配置requestHandler

<requestHandler name="/dataimport" class="solr.DataImportHandler">
    <lst name="defaults">
      <str name="config">tika-data-config.xml</str>
      <str name="update.chain">uuid</str>    
   </lst>
</requestHandler>

<requestHandler name="/update" class="solr.UpdateRequestHandler">
       <lst name="defaults">
        <str name="update.chain">uuid</str>
       </lst>  
</requestHandler>

  <!-- for back compat with clients using /update/json and /update/csv -->  
  <requestHandler name="/update/json" class="solr.JsonUpdateRequestHandler">
        <lst name="defaults">
         <str name="stream.contentType">application/json</str>
         <str name="update.chain">uuid</str>
        </lst>
  </requestHandler>
  <requestHandler name="/update/csv" class="solr.CSVRequestHandler">
        <lst name="defaults">
         <str name="stream.contentType">application/csv</str>
         <str name="update.chain">uuid</str>
        </lst>
  </requestHandler>

  <requestHandler name="/update/extract"
                  startup="lazy"
                  class="solr.extraction.ExtractingRequestHandler" >
    <lst name="defaults">
      <str name="xpath">/xhtml:html/xhtml:body/descendant:node()</str>
      <str name="capture">content</str>
      <str name="fmap.meta">attr_meta_</str>
      <str name="uprefix">attr_</str>
      <str name="lowernames">true</str>
      <str name="update.chain">uuid</str>
    </lst>
</requestHandler>
    這樣solr即可自動生成UUID,不需要再客戶端額外生成。


       



        

         

     

       

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章