1. 程式人生 > >solr(一)配置檔案詳解與整合zookeeper叢集

solr(一)配置檔案詳解與整合zookeeper叢集

一 . 概念 
 1) 全文檢索 :  先對非結構化資料建立索引, 在對索引進行搜尋的過程就是全文檢索
 2) 方法 : 順序索引(windows檔案查詢, linux的grep命令)
      索引 : 從非結構化資料中提取然後重新組織的資訊
    (eg : 字典的字是非結構化資料, 拼音表和偏旁部首表就是索引)
 3) 順序查詢 : 速度慢, 順序查詢是檔案包含了哪些字串 , 是檔案-> 字串
      索引查詢 : 有字串找檔案, 二者方向相反, 也成反向索引
 
 字串1 --> 文件編號的列表1詞典 | 倒排表
 字串2 --> 文件編號的列表2(優勢 : 一次建立索引, 多次使用)
 字串3 --> 文件編號的列表3
 字串4 --> 文件編號的列表4
 
二. 概念

   1. Document : solr的基本單元, 一個描述某一個物件的資料集 , 包括物體的組成成分,結構, 操作時間等
   2. Field : Document由field組成, field描述物件的屬性
  
 三 . 配置檔案
  1. solr.xml

        (1)單個core的配置 : 
      <solr>
	<solrcloud>
			<str name="host">${host:}</str>
			<int name="hostPort">${jetty.port:8983}</int>
			<str name="hostContext">${hostContext:solr}</str>
			<int name="zkClientTimeout">${zkClientTimeout:30000}</int>
			<bool name="genericCoreNodeNames">${genericCoreNodeNames:true}</bool>
	</solrcloud>
	<shardHandlerFactory name="shardHandlerFactory"class="HttpShardHandlerFactory">
			<int name="socketTimeout">${socketTimeout:0}</int>
			<int name="connTimeout">${connTimeout:0}</int>
	</shardHandlerFactory>
      </solr>   
          (2) 多個core的配置 
	<solr persistent="false">
		<cores adminPath="/admin/cores" host="${host:}" hostPort="${jetty.port:8983}" hostContext="${hostContext:solr}">
			<core name="firstcore " instanceDir="core01" />
			<core name="secondcore" instanceDir="core02" />
		</cores>
	</solr>

   2. solrconfig.xml : 

      1) 在每個collection下有一個, mutilconfig裡的collection有一個, 用來配置requestHandler
      2) 資料庫匯入時 , 配置資料庫的檔案 dataimport
           a) <dataDir> : solr索引資料存放位置 
 <dataDir>${solr.data.dir:/home/lj/software/solrdata}</dataDir>
           b) <directoryFactory> : 配置工廠類, 以實現在dataDir中存放索引
<directoryFactory name="DirectoryFactory"  class="${solr.directoryFactory:solr.NRTCachingDirectoryFactory}">
          c) <schemaFactory> : 配置索引是用到的schema.xml
1. <schemaFactory class="ClassicIndexSchemaFactory"/>
預設為schma.xml
2. <schemaFactory class="ManagedIndexSchemaFactory">()
<bool name="mutable">true</bool>
<str name="managedSchemaResourceName">managed-schema</str>
</schemaFactory>
詳解見"example裡的solrconfig.xml"
(4) <indexConfig> : 配置lucene的index writer, 預設即可
(5) <updatehandler> : 配置如何更新索引
傳送到solr中的資料不能立刻被查到, 這些資料還需經過commit的過程. 這個過程會有延遲, 並且在此過程中, 不能處理
新的"commit", 以免資料被覆蓋
1. commit : 
2. soft commit : 資料儲存且立刻被查到, 這種方式不會等到background merge完畢. 當伺服器突然宕機, solr不會知道已經儲存的資料放在哪裡
3. auto commit : 

<updateHandler class="solr.DirectUpdateHandler2">
<updateLog>
<str name="dir">${solr.ulog.dir:}</str>
</updateLog>
<autoCommit> 
  <maxTime>${solr.autoCommit.maxTime:15000}</maxTime> 
  <openSearcher>false</openSearcher>  //會自動提交, 但不會立刻看到索引變化
</autoCommit>
<autoSoftCommit> 
<maxTime>${solr.autoSoftCommit.maxTime:-1}</maxTime> 
</autoSoftCommit>
</updateHandler>
(6) 查詢設定<query>
1. caches : 超過一定時間為改變的索引就會形成cache, 以便更快的查詢
新index searcher也會在老searcher的caches基礎上建立, 當新searcher建立完畢, 老searcher關閉
實現類 : 1) LRUCache
2) FastLRUCache
3) LFUCache
 
(1) filterCache 
<filterCache class="solr.FastLRUCache"
size="512"
initialSize="512"
autowarmCount="0"/>
...
   ... 更多見文件
2. requestDispatcher
配置http響應處理
<requestDispatcher handleSelect="false" >
<requestParsers enableRemoteStreaming="true" 
multipartUploadLimitInKB="2048000"
formdataUploadLimitInKB="2048"
addHttpRequestToContext="false"/>
<httpCaching never304="true" />
 </requestDispatcher>

3. requestHandler
http://10.1.198.56:8983/solr/iap_app_log_201501/select?q=ss (正確)  http請求不加#
http://10.1.198.56:8983/solr/#/iap_app_log_201501/select?q=ss (錯誤)
  2. schemal.xml : 
    1) fieldType : 
<1>  四大內容 : name, class , 
 TextField的欄位型別需要analyzer類
 其他屬性(sortMissingLast)
<fieldType name="string" class="solr.StrField" sortMissingLast="true" />
<fieldType name="text_greek" class="solr.TextField">
<analyzer class="org.apache.lucene.analysis.el.GreekAnalyzer"/>
</fieldType>
<2>solr的貨幣型別
<fieldType name="currency" class="solr.CurrencyField" precisionStep="8" defaultCurrency="USD" currencyConfig="currency.xml" />
currency.xml檔案中定義了利率
2) 規定fields
<field name="id" type="string" indexed="true" stored="true" required="true" multiValued="false" /> 
3) 複製fields
<copyField source="cat" dest="text"/> 
   <field name="text" type="text_general" indexed="true" stored="false" multiValued="true"/>
cat的內容回作為附加載入text值的後面, 存在text中
4) 動態fields
<dynamicField name="*_is" type="int"    indexed="true"  stored="true"  multiValued="true"/>
5) unique key : 
<uniqueKey>id</uniqueKey>
6) defaultSearchField : 不推薦
7) solrQueryParser : 
<solrQueryParser defaultOperator="OR"/> 不推薦, 預設or

四. 分析器
(一)分析其包括分詞器和過濾器 , 分詞器和過濾器組合形成分析器, 他們可以形成鏈式處理. 這種處理在建立索引, 分析查詢語句時都使用
 分詞器Tokenizer : 把field的資料分成詞彙單元, 或者每個token
 過濾器filter : 過濾器把token stream中的每個詞變形, 丟棄, 增加
 
(二) schema.xml配置分析器
當fieldType為"solr.TextField"時, 才配置分析器, 
      1. 配置org.apache.lucene.analysis.Analyzer的子類
<fieldtype name="nametext" class="solr.TextField">  
<analyzer class="org.apache.lucene.analysis.WhitespaceAnalyzer"/>  
</fieldtype>  
 2. 分別配置tokenizer和filter
<fieldtype name="text" class="solr.TextField">  
 <analyzer>  
<tokenizer class="solr.StandardTokenizerFactory"/>  
<filter class="solr.StandardFilterFactory"/>  
<filter class="solr.LowerCaseFilterFactory"/>  
<filter class="solr.StopFilterFactory"/>  
<filter class="solr.PorterStemFilterFactory"/>  
 </analyzer>  
</fieldtype>  

(三) Tokenizer詳解
1. 工廠類StandardTokenizerFactory必須實現solr.analysis.TokenizerFactory介面, 在工廠類的create()方法中翻譯分析器例項,實現solr和第三方分詞器的結合
<fieldType name="managed_en" class="solr.TextField" positionIncrementGap="100">
 <analyzer>
<tokenizer class="solr.StandardTokenizerFactory"/>
<filter class="solr.ManagedStopFilterFactory" managed="english" />
<filter class="solr.ManagedSynonymFilterFactory" managed="english" />
 </analyzer>
</fieldType>
2. solr實現的TokenizerFactory
1) <tokenizer class="solr.StandardTokenizerFactory"/>
   用空格和分隔符分割字串, 再除去分隔符
eg "a b,c @sina.com" => "a""b""c""sina.com"
2) <tokenizer class="solr.KeywordTokenizerFactory"/>
不對輸入進行分詞
3) <tokenizer class="solr.LetterTokenizerFactory"/>
對所有符號進行分割
eg "i can't"->"i" "can" "t"
4)  solr.LowerCaseTokenizerFactory
用所有非字母符號分詞, 講分得的字母變為小寫, 非字母去除
eg: "I LOVE "=> 'i' 'love' 
.
.
.
(四) Filter詳解
filter會按照配置的順序來對tokenizer產生的tokens進行處理
...
...
(五) CharFilterFactories元件
該元件在tokenizer之前處理字串中的字元, 可以對字元增加, 修改,刪除, 該元件也可以形成鏈式處理
<fieldType name="text_fa" class="solr.TextField" positionIncrementGap="100">
<analyzer>
<charFilter class="solr.PersianCharFilterFactory"/>
<tokenizer class="solr.StandardTokenizerFactory"/>
<filter class="solr.LowerCaseFilterFactory"/>
<filter class="solr.ArabicNormalizationFilterFactory"/>
<filter class="solr.PersianNormalizationFilterFactory"/>
<filter class="solr.StopFilterFactory" ignoreCase="true" words="lang/stopwords_fa.txt" />
</analyzer>
</fieldType>
四. 匯入資料到solr
  1. 從資料庫導Data Import Handler :
1)  solrconfig.xml配置資料庫連線xml  
<requestHandler name="/dataimport" class="org.apache.solr.handler.dataimport.DataImportHandler">
<lst name="defaults">
<str name="config">db-data-config.xml</str>
</lst>
</requestHandler>

2) xml檔案
<dataConfig>
<dataSource name="jdbc" driver="oracle.jdbc.driver.OracleDriver" url="jdbc:oracle:thin:@10.1.242.63:1521:AI4A" user="audit30devc"password="audit30#devc" />
<document>
<entity name="core的邏輯名(顯示), 非真實目錄" dataSource="jdbc" query="select T.*, from dual">
<field name="stdBeginTimeDate" column="STD_BEGIN_DATE" dateTimeFormat="yyyy-MM-dd HH:mm:ss" locale="en" />
</entity>
</document>
</dataConfig>
   
  2. csv檔案
  3. json物件
  4. 從word/pdf匯入 
       1) Solr Cell :  ExtractingRequestHandler 
  5.. 用solrj匯入
  
 
  五. solrCloud
    (1) 每個分片都是索引集合的子集, 但是用http://localhost:8983/solr/collection1/select?*=*得出的是索引全集
    (2) 用bin\solr指令碼啟動, 預設開啟2個shard(8983,7574),每個shard2個副本. 
 <1> 手動建立多節點分片
  (建立叢集之前, 至少執行example單節點solr一次,解壓必要的jar檔案)
   (1) 開啟第一個節點, 讓該節點內建的zookeeper'監控叢集
   (2) 開啟其餘節點, 並讓其餘節點指向第一個節點的zookeeper
1. 步驟
   (1) 複製example, 讓其分成兩個節點node1,node2 
cp -r example node1
cp -r example node2
   (2) -DzkRun開啟node1節點
cd node1
java -DzkRun              // (開啟節點內建zookeeper)
     -DnumShards=2        // (索引分成2片,shard個數, 機器節點個數)
     -Dbootstrap_confdir=./solr/collection1/conf       //(讓內建zookeeper找到cluster的配置檔案)
     -Dcollection.configName=myconf                       // (為內建zookeeper的配置資訊起別名)
     -jar start.jar
   (3) 開啟node2
 cd node2
 java  -Djetty.port=7574
       -DzkHost=localhost:9983 //(指出第一個節點的zookeeper位置,讓node2向zookeeper報告任務, zookeeper埠必須比solr埠大1000)
       -jar start.jar
    <2>為分片建立高可用
   (1)  基於以上node1,node2兩個分片, 再建立兩個新的節點
cp -r example node3
cp -r example node4
   (2) 開啟新的節點
cd node3 
java -Djetty.port=8900 -DzkHost=localhost:9983 -jar start.jar
    [注] : a) 由於已經在開啟node1的時候, -DnumShards=2指定了分片個數為2, 所以新建立的node會直接作為已存在分片的副本, 二不會成為新的副本
     b) 副本的新增順序 和 leader節點額開啟順序一致,副本會自動掛載成輔助節點
cd node4 
java -Djetty.port=7575 -DzkHost=localhost:9983 -jar start.jar
   [注] : a) shard的副本並不處理任務, 也不會控制資料流向正確的位置, 只是儲存資料
    b) 只要每個shard有一個例項執行, 叢集就可以執行
    c) 這種叢集不能讓node1掛掉, 否則zookeeper掛掉. 在某些情況下, zookeeper掛掉仍然能執行solr叢集, 但不能再接受節點增加和節點切換
  <3>建立多例項zookeeper的solr叢集(Zookeeper Ensemble)
在上面node1~4 的基礎上搭建 ,
    1. 清除上面步驟的zookeeper資料
rm -r node*/solr/zoo_data
    2. 在啟動start.jar時, 指定出所有的zookeeper集合
cd node1
java  -DzkRun  -DnumShards=2  
      -Dbootstrap_confdir=./solr/collection1/conf  
	  -Dcollection.configName=myconf      //  把solrconfig.xml和schema.xml傳送到zookeeper中
      -DzkHost=localhost:9983,localhost:8574,localhost:9900       // 這幾個埠分別比每個節點的埠大1000
      -jar start.jar

cd node2
java  -Djetty.port=7574      //第一個節點已提交了配置檔案到zookeeper, 剩下的直接啟動zookeeper即可
      -DzkRun
      -DnumShards=2
      -DzkHost=localhost:9983,localhost:8574,localhost:9900
      -jar start.jar

cd node3 
java -Djetty.port=8900 
     -DzkRun
     -DnumShards=2
     -DzkHost=localhost:9983,localhost:8574,localhost:9900 -jar start.jar

cd node4        //最後一個節點不開啟zookeeper,所以不用加上-DzkRun
java -Djetty.port=7575 -DzkHost=localhost:9983,localhost:8574,localhost:9900 -jar start.jar
 [注] : a) 啟動的zookeeper集合個數必須大於等於節點個數的一半
  b) 啟動節點時會報錯, 應為zookeeper ensemble並未全部啟動
  c) 這四個節點,只是java啟動引數不同, 節點的檔案相同

[概念]
  1. core : 叢集中每個shard的本地index庫就是core
  2. collection : 叢集中,每個節點的core組成了collection
      core.properties檔案的內容
#Written by CorePropertiesLocator
#Tue Feb 03 23:38:40 PST 2015
numShards=3 
name=mycollection_shard1_replica1
shard=shard1  //分片名
collection=mycollection//用collection名識別一個叢集
coreNodeName=core_node2
  3. shard的 數量一旦初始化不能改變, shard是把一個core分割到多個server或者node上面的一種方式
  4. 請求會分發給所有shard的leader
  5. ZooKeeper提供shard副本中的故障轉移和多個分片的負載均衡
       如果leader宕機了,replica節點中的某一個節點將會自動被選舉成新的leader。在每一個節點啟動之後,它都是自動分配給擁有replica最少的shard。
       當所有shard擁有一樣數量的replica的時候,新的節點會被分配給shard id值最小的shard。
  6. 當一個document傳送到一個節點時, 若節點時副, 則轉發請求到對應的leader, 若是leader, 則看該請求是否是這個shard
 
六.  手動分割shard (Collection API)
   (1) 當需求改變,一個錯誤的shard數量引數, 會導致要重新建立新的core和重新索引所有的資料, 帶來時間和空進上的麻煩。
   (2) Collection API : 現在允許通過它來把一個shard分開到兩個塊中。原來存在的shard還是會保持原狀,
   (3) 所有分割操作實際上是建立了它的資料的兩個副本作為新的shard
     譯註:這裡應該是把原來shard裡面的資料作為一個副本分開到兩個新的shard裡面去
                 當你一切都準備好了之後,你可以把舊的shard給刪除掉。
    1) 新增collection : http://192.168.2.134:8983/solr/admin/collections?action=CREATE&name=mycollection&numShards=3
    2) 刪除collection : http://192.168.2.134:8983/solr/admin/collections?action=DELETE&name=mycollection
    3) reload : 當zookeeper配置檔案改變時, 要reload一個collection
         http://192.168.2.134:8983/solr/admin/collections?action=RELOAD&name=mycollection
    4) shard二次分片 : 
     (1) zookeeper加入新節點, 形成副本
     (2) 手動分片, 分片後原來的shard1會分片成兩片, 均攤索引
            http://192.168.2.134:8983/solr/admin/collections?action=SPLITSHARD&collection=collection1&shard=shard1
     (3) 分片後, 原分片還在, 要刪除原分片

七. 指定分片上查詢的分散式請求 : Distributed Requests
       1) 查詢整個shard   --->   http://localhost:8983/solr/collection1/select?q=*:*
       2) 指定在某幾個分片上查詢--->http://localhost:8983/solr/collection1/select?q=*:*&shards=localhost:7574/solr,localhost:
       3) 在多個collection上查詢 : 
           http://localhost:8983/solr/collection1/select?collection=collection1,collection2
CloudSolrServer獲取shard叢集資訊
public class Test {
    public static void main(String[] args) {
        String zkHost = "192.168.2.134:9983";   //連結任意一個zookeeper即可, 路徑不帶http  

        CloudSolrServer server = new CloudSolrServer(zkHost);
        server.setDefaultCollection("collection1"); //配置索引collection  
        server.connect();
        System.out.println("The cloud Server has been connected !!!!");

        ZkStateReader zkStateReader = server.getZkStateReader();
        ClusterState cloudState  = zkStateReader.getClusterState(); //獲取叢集資訊,返回shard的json物件  
        System.out.println(cloudState);

        String sql = "name:*";
        SolrQuery query = new SolrQuery();
        query.setQuery(sql);
        query.setStart(0);
        query.setRows(100);     //不設定行數,只顯示前10條  
        try {
            QueryResponse rs = server.query(query, METHOD.POST);
            SolrDocumentList li = rs.getResults();
            for(SolrDocument doc : li){
                System.out.println(doc.getFieldValue("name"));
            }
            System.out.println("一共"+li.getNumFound());
        } catch (SolrServerException e) {  e.printStackTrace(); }
    }
}



 4) 新加入的replicate節點, 會自動同步leader節點的索引資料 

   <四>solr 與 hdfs
    solr支援在hdfs上讀寫索引
java -Dsolr.directoryFactory-HdfsDirectoryFactory
  -Dsolr.lock.type=hdfs
  -Dsolr.hdfs.home=hdfs://host:port/path
/*-Dsolr.data.dir=hdfs://host:port/path
  -Dsolr.updatelog=hdfs://host:port/path*/
  -jar start.jar
   
   設定-XX:MaxDirectMemorySize=20g