一、solr簡介(需要整理 總結)
首先Solr是基於Lucene做的,Solr的目標是打造一款企業級的搜索引擎系統,因此它更接近於我們認識到的搜索引擎系統,它是一個搜索引擎服務,通過各種API可以讓你的應用使用搜索 服務,而不需要將搜索邏輯耦合在應用中。而且Solr可以根據配置文件定義數據解析的方式,更像是一個搜索框架,它也支持主從、熱換庫等操作。
solr還是一種開放源碼的、基於 Lucene Java 的搜索服務器,易於加入到 Web 應用程序中。Solr 提供了層面搜索(就是統計)、命中醒目顯示並且支持多種輸出格式(包括XML/XSLT 和JSON等格式)。它易於安裝和配置,而且附帶了一個基於HTTP 的管理界面。可以使用 Solr 的表現優異的基本搜索功能,也可以對它進行擴展從而滿足企業的需要。Solr的特性包括:
- 高級的全文搜索功能
- 專爲高通量的網絡流量進行的優化
- 基於開放接口(XML和HTTP)的標準
- 綜合的HTML管理界面
- 可伸縮性-能夠有效地複製到另外一個Solr搜索服務器
- 使用XML配置達到靈活性和適配性
- 可擴展的插件體系
Solr 必須運行在Java1.6 或更高版本的Java 虛擬機中,運行標準Solr 服務只需要安裝JRE 即可,但如果需要擴展功能或編譯源碼則需要下載JDK 來完成。
Solr 是一個開源的企業級搜索服務器,底層使用易於擴展和修改的Java 來實現。服務器通信使用標準的HTTP 和XML,所以如果使用Solr 瞭解Java 技術會有用卻不是必須的要求
文檔通過Http利用XML 加到一個搜索集合中。查詢該集合也是通過http收到一個XML/JSON響應來實現。它的主要特性包括:高效、靈活的緩存功能,垂直搜索功能,高亮顯示搜索結果,通過索引複製來提高可用性,提供一套強大Data Schema來定義字段,類型和設置文本分析,提供基於Web的管理界面等。
二、Solr緩存
緩存在 Solr 中充當了一個非常重要的角色,Solr 中主要有這三種緩存:
- Filter cache(過濾器緩存),用於保存過濾器(fq 參數)和層面搜索的結果
- Document cache(文檔緩存),用於保存 lucene 文檔存儲的字段
- Query result(查詢緩存),用於保存查詢的結果
- 還有第四種緩存,lucene 內部的緩存,不過該緩存外部無法控制到。
通過這 3 種緩存,可以對 solr 的搜索實例進行調優。調整這些緩存,需要根據索引庫中文檔的數量,每次查詢結果的條數等。
在調整參數前,需要事先得到 solr 示例中的以下信息: 索引中文檔的數量 每秒鐘搜索的次數 過濾器的數量 一次查詢返回最大的文檔數量,不同查詢和不同排序的個數,這些數量可以在 solr admin 頁面的日誌模塊找到。
假設以上的值分別爲:
索引中文檔的數量:1000000
每秒鐘搜索的次數:100
過濾器的數量:200
一次查詢返回最大的文檔數量:100
不同查詢和不同排序的個數:500
然後可以開始修改 solrconfig.xml 中緩存的配置了
第一個是過濾器緩存:
第二個是查詢結果緩存:
第三個是文檔緩存:
這幾個配置是基於以上的幾個假設 的值進行調優的。
三、solr 安裝
1.安裝背景概要
因爲solr7+ 新增了誇核(solr 跨核概念,是建立在,solr存儲方式的基礎上,因爲使用solr前必須創建Core,Core即爲solr的核,那不同的業務有可能在不同的核中,之前版本是不支持跨核搜索的)搜索功能,寫法如下:
shards=localhost:9095/solr/core0,localhost:9095/solr/core1 ,因爲跨核的功能有可能在實際業務場景中應用到,所以本次是對 Solr7.1.0 安裝部署。在安裝 solr前需要安裝jdk1.8或以上版本,這裏選用tomcat作爲solr的容器,tomcat也建議使用8.0或以上版本
2.下載
關於jdk,tomcat的安裝就不在本文複述,見下鏈接。
jdk1.8 下載與安裝: https://blog.csdn.net/yx1214442120/article/details/55098380
tomcat8 下載與安裝:http://tomcat.apache.org/download-80.cgi ,注意系統兼容,我這裏選擇64位Windows,解壓後配置環境變量,因爲 tomcat默認8080端口容易與oracle 衝突,把端口號改爲8888,地址爲http://localhost:8888/。(5以後就不需要依賴tomcat了,這裏 還採用tomcat 是爲了部署linux 時候可以同樣套用該步驟)
solr7.1.0 下載 (包含過往版本) :http://archive.apache.org/dist/lucene/solr/
3. solr7.1.0 安裝(以下步驟都不可省略,請認真按步驟執行~)
3.1 解壓出solr-7.1.0,將 solr 壓縮包中 solr-7.1.0\server\solr-webapp\文件夾下有個webapp文件夾,將之複製到Tomcat\webapps\目錄下,並改成solr (名稱隨意,爲了後面訪問方便,改爲solr).
3.2 在E盤(根據實際情況)新建一個文件夾:solrhome ,回到tomcat的webapps目錄下,打開tomcat\webapps 下 solr\WEB-INF\web.xml文件。在web-app節點中加入以下代碼,env-entry-value 目錄可根據實際情況自行更替:
<env-entry> <env-entry-name>solr/home</env-entry-name> <env-entry-value>E:\solrhome</env-entry-value> <env-entry-type>java.lang.String</env-entry-type> </env-entry>
* 把 security-constraint 整個標籤註釋:
<!-- <security-constraint>
<web-resource-collection>
<web-resource-name>Disable TRACE</web-resource-name>
<url-pattern>/</url-pattern>
<http-method>TRACE</http-method>
</web-resource-collection>
<auth-constraint/>
</security-constraint>
<security-constraint>
<web-resource-collection>
<web-resource-name>Enable everything but TRACE</web-resource-name>
<url-pattern>/</url-pattern>
<http-method-omission>TRACE</http-method-omission>
</web-resource-collection>
</security-constraint>-->
3.3 將 solr-7.1.0\server\lib\ext 所有jar包,以及solr-7.1.0\server\lib 下 metrics 相關的jar ,以及solr-7.1.0\dist 下 solr-dataimporthandler 相關的jar複製到tomcat的webapps\solr\WEB-INF\lib下
3.4 在tomcat-8.5.31\webapps\solr\WEB-INF 下新建 classes文件夾。然後將solr-7.1.0\server\resources 下 log4j.properties 複製到 classes文件夾中。
3.5 回到解壓的solr-7.1.0目錄,打開文件夾:solr-7.1.0\server\solr,複製所有內容到E:\solrhome,把 solr-7.1.0 下 dist和contrib 文件夾 複製到 E:\solrhome下。 這樣solrhome就和tomcat中的solr 完成了加載關聯。
3.6 運行 apache-tomcat\bin 下 startup.bat 啓動tomcat,看到一下日誌說明啓動成功了。
3.7 訪問solr http://localhost:8888/solr/index.html ,見到如下頁面纔算真正安裝成功。
3.8 下面介紹兩種新建核心的方法,第一種: 下圖新建testCore,這時候可以根據業務,架構創建多個核心,這裏就體現了爲什麼我們要用7+版本的solr ,就是因爲 7+版本支持多核心搜索。這裏需要注意的是,並不是全自動創建~還需要你先建好文件夾,把solrconfig.xml丟進去才行,所以這裏不建議這種方式創建。不過你可以點一下試一試~
第二種 :不通過web頁面,直接手動配置。在 E:\solrhome 下新建testCore/conf
將solr-7.1.0\server\solr\configsets\_default\conf 下所有文件放置到 新建的testCore/conf 下
然後修改 solrconfig.xml ,可以用相對路徑,或者絕對路徑。這裏用了絕對路徑。
<lib dir="E:/solrhome/contrib/extraction/lib" regex=".*\.jar" />
<lib dir="E:/solrhome/dist/" regex="solr-cell-\d.*\.jar" />
<lib dir="E:/solrhome/contrib/clustering/lib/" regex=".*\.jar" />
<lib dir="E:/solrhome/dist/" regex="solr-clustering-\d.*\.jar" />
<lib dir="E:/solrhome/contrib/langid/lib/" regex=".*\.jar" />
<lib dir="E:/solrhome/dist/" regex="solr-langid-\d.*\.jar" />
<lib dir="E:/solrhome/contrib/velocity/lib" regex=".*\.jar" />
<lib dir="E:/solrhome/dist/" regex="solr-velocity-\d.*\.jar" />
然後在solrhome\testCore下新建data文件見。顧名思義這裏放數據的,當然是無法看懂的文件。關於底層的實現方式,見如下鏈接 ,講的比較清晰
https://blog.csdn.net/u014209975/article/details/53263642
這時候還需要 在 solrhome\testCore ,新建 core.properties 文件,寫入以下內容(注意這裏有坑 config=conf/solrconfig.xml後面不能有空格 否則啓動會報錯):
#Written by CorePropertiesLocator
#Sat May 26 16:41:44 CST 2018
name=testCore
config=conf/solrconfig.xml
schema=conf/managed-schema
dataDir=data
3.9 重啓tomcat,再訪問首頁,就可以選擇我們新建的testCore了,後面再建core,可以直接複製這一份改改~我老是把core 打成code~ 真是天生的程序員。這是我的testCode手動創建的。以前用過solr 的朋友可能會發現,conf下面沒有schema.xml,1.6以前是schema.xml ,之後版本用的是managed-schema與schema.xml 基本一致,之後所有提到managed-schema都等同於schema.xml。
4.solr 的配置文件
solr 最主要的配置文件就是上面提到過的solrconfig.xml 以及managed-schema,要對這兩個文件有一定的瞭解,尤其是managed-schema ,才能真正的把solr應用到業務中。
4.1 solrconfig.xml , solrconfig.xml配置文件主要定義了solr的一些處理規則,包括索引數據的存放位置,更新,刪除,查詢的一些規則配置。相當於是基礎配置文件。值得注意的是,文件中不能有中文。~否則會報錯,註釋也不行
4.2 managed-schema是在使用solr建立core時的配置(core連接配置和索引庫),solr根據它確定如何對文檔建立索引到索引庫中,每個core在建立前都需要設計好managed-schema。
詳細的配置講解,網上非常多,也非常專業,就不贅述了,提供一個我之前參考的鏈接,solrconfig.xml 以及 managed-schema 詳細介紹見: https://blog.csdn.net/vtopqx/article/details/73224510
5.solr 的使用
5.1數據導入
5.1.1上面已經介紹了core的創建,下面說下怎麼導入數據。在solrhome\testCore\conf下 創建文件data-config.xml,寫入如下內容:
<dataConfig>
<dataSource type="JdbcDataSource"
driver="com.mysql.jdbc.Driver"
url="jdbc:mysql://123.59.41.168:3306/db_yxj_studio"
user="yxj"
password="yxj@2015" />
<document>
<entity name="solr_test" transformer="DateFormatTransformer"
query="SELECT sbtid as id ,master_intro, tags, srpid, department FROM t_subject WHERE sbtid >= ${dataimporter.request.sbtid}">
</entity>
</document>
</dataConfig>
dataConfig 標籤中,子標籤,dataSource 配置數據源,上面是以mysql爲例,配置了數據源。其中entity 標籤 定義了 操作名稱,以及sql ,其中${dataimporter.request.id}
中自定義了id字段,值得注意的是必須指定id字段,因爲我使用的表中沒有ID 所以 我使用了as的寫法。另外還有一點,要把sql中用到的字段名,在managed-schema中進行編寫fields。以我的sql爲例,managed-schema寫法如下。
<!-- 自定義字段-->
<!-- t_subject -->
<field name="sbtid" type="plong" indexed="true" stored="true"/>
<field name="master_intro" type="string" indexed="true" stored="true"/>
<field name="tags" type="string" indexed="true" stored="true"/>
<field name="srpid" type="plong" indexed="true" stored="true"/>
<field name="department" type="string" indexed="true" stored="true"/>
5.1.2接下來把solrhome\testCore\conf 下的 solrconfig.xml,寫入如下內容:
<requestHandler name="/dataimport" class="org.apache.solr.handler.dataimport.DataImportHandler"> <lst name="defaults"> <str name="config">data-config.xml</str> </lst> </requestHandler>
5.1.3最後 把數據庫驅動丟到apache-tomcat\webapps\solr\WEB-INF\lib 中 ,如果你使用的pgSql 就丟對應的jar~
5.1.4重啓tomcat ,導入數據,並檢索,見下圖
導入成功
5.1.5 檢索,見下圖,整個solr 安裝、配置、使用 就都OK了。最後對查詢語法介紹,引用如下:
https://blog.csdn.net/zhufenglonglove/article/details/51518846
6.solr 分詞器
6.1 K分詞器,因爲IK是開源的分詞器,也支持擴展,網上分詞器很多,使用方法也大同小異。首先要下載IK的jar包。solr7.0+要用5.0+的IK分詞器。
IKAnalyzer2012FF_u1-6.51.jar
可以私信我,CSDN不能,上傳JAR~
6.2 把IK jar包放到放到apache-tomcat\webapps\solr\WEB-INF\lib 下,配置solrhome\testCore\conf 下的managed-schema 文件 ,分別寫入
<field name="text_ik" type="text_ik" indexed="true" stored="true" multiValued="true" />
<!-- copyField就引出了solr的一個全文檢索的概念,如果我要實現一個搜索,而我要搜索的屬性有很多個那麼應該使用如下配置方法-->
<copyField source="master_intro" dest="text_ik"/>
<copyField source="tags" dest="text_ik"/>
<copyField source="department" dest="text_ik"/>
<!--IK分詞器 -->
<fieldType name="text_ik" class="solr.TextField" positionIncrementGap="100">
<analyzer type="index">
<tokenizer class="org.wltea.analyzer.lucene.IKTokenizerFactory" useSmart="false" />
</analyzer>
<analyzer type="query">
<tokenizer class="org.wltea.analyzer.lucene.IKTokenizerFactory" useSmart="false" />
</analyzer>
</fieldType>
1.其中useSmart是指分詞的細粒度,可以分別制定index索引和query查詢的分詞細粒度,建議將index的useSmart設置爲false,這樣就採用最細分詞,是索引更精確,查詢時儘量能匹配,而將query的useSmart設置爲true,採用最大分詞,這樣能夠使查詢出來的結果更符合用戶的需求。
2.positionIncrementGap 一個doc中的屬性有多個值時候,設置每個屬性之間的增量值和multiValued屬性配合使用(避免錯誤匹配)。
6.3重啓tomcat
這裏是未使用分詞器的效果。
選用IK分詞以後,效果很明顯
7.solr 與springboot 的集成(這裏有坑)
7.1 通過solrj 來操作solr,maven 引入 。 注意一下 ,JAR衝突的問題,我發現spring-boot-starter-test 包含的 spring-test-4.3.6.RELEASE 包下面 和solr7.1.0有衝突 ,爲了解決這個問題,只能放棄最新的solr-solrj jar 的使用,改用5.3之後的版本,切兼容springboot的。 等日後springboot升級在做更替。我試驗了 老版本不影響solr的使用。是指在7.1.0版本 有一些方法是棄用的。但還是兼容的。如果你使用的是springMvc可以嘗試用最新版本的jar包。
一般,我都會在這裏找jar: http://mvnrepository.com/
<!--solr客戶端solrj的依賴 -->
<dependency>
<groupId>org.apache.solr</groupId>
<artifactId>solr-solrj</artifactId>
<!--<version>7.1.0</version>-->
</dependency>
7.2 java 實現 增刪改查 ,這裏我用多線程測試過,100W數據用我的破機器,3分鐘就跑完了。
相關參數
#solr
#服務器地址
solr.httpSolrClientUrl=http://localhost:8888/solr/
#等待結果超時 這個參數設定的是HTTP連接成功後,等待讀取數據或者寫數據的最大超時時間,單位爲毫秒 如果設置爲0,則表示永遠不會超時
solr.soTimeOut=5000
#接超時時間 HTTP 超時時間 單位爲毫秒 如果設置爲0,則表示永遠不會超時
solr.connectionTimeout=1000
package com.ysz.studio.solr.service;
import java.util.List;
import java.util.Map;
import org.apache.solr.common.SolrDocumentList;
import org.apache.solr.common.SolrInputDocument;
public interface SolrService {
/**
* 按條件查詢搜索引擎
*
* @author [email protected]
* @param coreName
* @param query
* @return
* @since
*/
public SolrDocumentList querySolrIndex(String coreName, String query);
/**
* 向solr插入數據
*
* @author [email protected]
* @param coreName
* @param input
* @return
* @since
*/
public boolean pushDataIntoSolr(String coreName, List<SolrInputDocument> input);
/**
* 根據ID 刪除數據
*
* @author [email protected]
* @param coreName
* @param ids
* @return
* @since
*/
public boolean deleteSolrByIds(String coreName, List<String> ids);
/**
*
*
* @author [email protected]
* @param coreName
* @param id
* @param maps 屬性名稱,屬性值鍵值對
* @return
* @since
*/
public boolean updateSolrById(String coreName, String id, Map<String, String> maps);
}
package com.ysz.studio.solr.service.impl;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.impl.HttpSolrClient;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.SolrDocumentList;
import org.apache.solr.common.SolrInputDocument;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import com.ysz.studio.solr.service.SolrService;
@Service
public class SolrServiceImpl implements SolrService {
private Logger logger = LoggerFactory.getLogger(SolrServiceImpl.class);
@Value("${solr.httpSolrClientUrl}")
private String httpSolrClientUrl;
// 這個參數設定的是HTTP連接成功後,等待讀取數據或者寫數據的最大超時時間,單位爲毫秒 如果設置爲0,則表示永遠不會超時
@Value("${solr.soTimeOut}")
private int soTimeOut;
// HTTP 超時時間 單位爲毫秒 如果設置爲0,則表示永遠不會超時
@Value("${solr.connectionTimeout}")
private int connectionTimeout;
private static HttpSolrClient solr;
private HttpSolrClient connetHttpSolrClientServer(String coreName) {
HttpSolrClient httpSolrClient = new HttpSolrClient(httpSolrClientUrl + coreName);
httpSolrClient.setConnectionTimeout(connectionTimeout);
httpSolrClient.setSoTimeout(soTimeOut);
// solr服務器地址+庫 JAR包 衝突 導致 無法使用最新的 方法
// new HttpSolrClient.Builder(httpSolrClientUrl +
// coreName).withConnectionTimeout(connectionTimeout).withSocketTimeout(soTimeOut).build();
return httpSolrClient;
}
@Override
public boolean pushDataIntoSolr(String coreName, List<SolrInputDocument> input) {
boolean flag = false;
try {
solr = this.connetHttpSolrClientServer(coreName);
solr.add(input);
solr.commit();
flag = true;
} catch (Exception e) {
e.printStackTrace();
logger.error(e.getMessage());
} finally {
try {
solr.close();
} catch (IOException e) {
e.printStackTrace();
logger.error(e.getMessage());
}
}
return flag;
}
@Override
public SolrDocumentList querySolrIndex(String coreName, String query) {
SolrDocumentList list = null;
try {
solr = this.connetHttpSolrClientServer(coreName);
QueryResponse rsp = null;
SolrQuery queryStr = new SolrQuery(query);
// 高亮顯示 start
// queryStr.setHighlight(true); // 開啓高亮組件或用query.setParam("hl", "true");
// queryStr.addHighlightField("master_intro");// 高亮字段
// queryStr.setHighlightSimplePre("<font color='red'>");// 標記,高亮關鍵字前綴
// queryStr.setHighlightSimplePost("</font>");// 後綴
/*
* 獲取高亮分片數,一般搜索詞可能分佈在文章中的不同位置,其所在一定長度的 語句即爲一個片段,默認爲1,但根據業務需要有時候需要多取出幾個分片。 -
* 此處設置決定下文中titleList, contentList中元素的個數
**/
// queryStr.setHighlight(true).setHighlightSnippets(1);
/*
* 每個分片的最大長度,默認爲100。 適當設置此值,如果太小,高亮的標題可能會顯不全; 設置太大,摘要可能會太長。
**/
// queryStr.setHighlightFragsize(150);
// 高亮顯示 end
// 指定返回哪些字段,用逗號或空格分隔,注意:字段區分大小寫,例如,fl= id,title,sort
// queryStr.addField(field);
// 返回結果的第幾條記錄開始,一般分頁用,默認0開始
// queryStr.setStart((Math.max(page, 1) - 1) * rows);
// // 指定返回結果最多有多少條記錄,默認值爲 10,配合start實現分頁
// queryStr.setRows(rows);
// 排序方式,例如id desc 表示按照 “id” 降序
// List<SortClause> sortList = new ArrayList<SortClause>();
// SortClause s = new SortClause("", ORDER.asc);
// sortList.add(s);
// queryStr.setSorts(sortList);
// 過慮查詢,提供一個可選的篩選器查詢。返回在q查詢符合結果中同時符合的fq條件的查詢結果,例如:q=id:1&fq=sort:[1 TO 5],找關鍵字id爲1
// 的,並且sort是1到5之間的。
// queryStr.setFilterQueries(fq);
// 默認的查詢字段,一般默認指定。
// queryStr.set("df", "master_intro");
// 指定返回結果字段。以空格“ ”或逗號“,”分隔。
// queryStr.set("fl", "master_intro");
rsp = solr.query(queryStr);
list = rsp.getResults();
} catch (IOException | SolrServerException e) {
e.printStackTrace();
logger.error(e.getMessage());
} catch (Exception e) {
e.printStackTrace();
logger.error(e.getMessage());
} finally {
try {
solr.close();
} catch (IOException e) {
e.printStackTrace();
logger.error(e.getMessage());
}
}
return list;
}
@Override
public boolean deleteSolrByIds(String coreName, List<String> ids) {
boolean flag = false;
try {
solr = this.connetHttpSolrClientServer(coreName);
solr.deleteById(ids);
solr.commit();
flag = true;
} catch (Exception e) {
e.printStackTrace();
logger.error(e.getMessage());
} finally {
try {
solr.close();
} catch (IOException e) {
e.printStackTrace();
logger.error(e.getMessage());
}
}
return flag;
}
@Override
public boolean updateSolrById(String coreName, String id, Map<String, String> maps) {
boolean flag = false;
try {
solr = this.connetHttpSolrClientServer(coreName);
Set<String> keys = maps.keySet();
SolrInputDocument doc = new SolrInputDocument();
doc.addField("id", id);
for (String key : keys) {
HashMap<String, Object> oper = new HashMap<String, Object>();
oper.put("set", maps.get(key));
doc.addField(key, oper);
}
solr.add(doc);
solr.commit();
flag = true;
} catch (Exception e) {
e.printStackTrace();
logger.error(e.getMessage());
} finally {
try {
solr.close();
} catch (IOException e) {
e.printStackTrace();
logger.error(e.getMessage());
}
}
return flag;
}
}