基本參數介紹
- q:查詢的關鍵字,此參數最為重要,例如
q=id:1,默認為q=*:* - fl:指定返回哪些字段,用逗號或空格分隔,注意字段區分大小寫,例如
fl=id,title,sort - start:返回結果的第幾條記錄開始,一般分頁用,默認 0 開始
- rows:指定返回結果最多有多少條記錄,默認值為 10,配合 start 實現分頁
- sort:排序方式,例如
id desc表示按照 id 降序,多個字段:score desc, price asc - wt:(writer type) 指定輸出格式,有 xml, json, php 等
- fq:(filter query) 過濾查詢,提供一個可選的篩選器查詢,例如:
q=id:1&fq=sort:[1 TO 5] - df:默認的查詢字段,一般默認指定
- qt:(query type) 指定哪個類型來處理查詢請求,一般不用指定,默認是 standard
- indent:返回的結果是否縮進,默認關閉,用
indent=true開啟
查詢語法
:指定字段查指定值,如返回所有值*:*?表示單個任意字符的通配*表示多個任意字符的通配(不能在檢索的項開始使用)~表示模糊檢索,如roam~將找到 foam 和 roams;roam~0.8檢索返回相似度在 0.8 以上的記錄AND、||布爾操作符OR、&&布爾操作符NOT、!、-排除操作符+存在操作符,要求符號後的項必須在文檔中存在( )用於構成子查詢[]包含範圍檢索,如date:[201507 TO 201510]包含頭尾{}不包含範圍檢索,如date:{201507 TO 201510}不包含頭尾
Solr 本質
Solr 本質上還是搜尋引擎,因此優先還是 index 其後才是 store。
Partial update 也是先把資料拉回來重新 index 後 store。
順序:index 先,然後 store
實戰案例:2024618 大戰
具體問題
- 大總管 job 機 64 台:一秒鐘處理一條 queue
- 大總管 job 機 128 台:兩秒鐘處理一條 queue
- 機器開兩倍,速度完全沒起來!


具體發現
- 機器量大起來時:查詢、更新都會有影響,尤其更新影響更大。查詢其實沒有太大影響,是更新後的 commit 造成的。
- Solr 為了資料絕不遺漏:在 shard 沒有全部完全起來前,不可查、不可更新、不可刪除。But 可以新增,新增的資料也可刪除,等到其他機器復活後會自動 Sync。
- 當 shard 尚未完全準備就緒時(例如:應該有 3 個副本,但僅有 1 個處於 active 狀態):
- ✓ 可以插入新的文件 (doc)
- ✗ 不可以進行查詢 (query)
- ✗ 不可以刪除 (delete)
- ✗ 不可以更新現有的文件 (update)
- Zookeeper 內有 Solr 相關設定,可從 Zookeeper get Solr 的設定進而查詢到 Solr 資料。
Commit vs Soft Commit
Commit(硬提交)
- 功能:將所有緩存中的更改(如新增、更新或刪除的文檔)寫入到索引的持久存儲中
- 性能:較低,因為涉及將數據寫入磁碟,過程相對較慢
- 可見性:所有的更改在 commit 後立即對查詢可見
- 索引更新:真正更新索引,需要進行這一操作
Soft Commit(軟提交)
- 功能:僅將文檔的變更從緩存刷新到內存索引,而不會將它們寫入磁碟
- 性能:較高,執行速度比普通的 commit 快
- 可見性:變更會立即對查詢可見,但實際的持久化仍需執行普通的 commit
- 資料安全性:若系統故障,未進行正常 commit 的變更將會丟失
實務建議
- 以訂單的 case 來說,新增需要及時的 commit,但是更新可以使用 soft commit 來爭取速度跟時間
- 測試案例顯示:開 64 台跟 96 台有沒有及時 commit,回應時間是有明顯差異的,但是查詢差異不大
⚠️ Soft Commit 的坑
soft commit 的啟用會讓頂層快取失效(filter queryResult)跟 document 不一致,造成 query 跟 get 的方法查同一個 ID 資料不一致!
Solr 評分機制 (Scoring)
使用評分的條件
- 需要用
q,不能用fq defType: edismax- 欄位要用切詞性質(IK 等等),如果是 qf
- 設定欄位計算方式(qf、mm、bf 等等)
- 排序按照 score
- 欄位的部分要看 score:
fl=*,score
Java 範例
SolrQuery solrQuery = new SolrQuery("東南西北");
solrQuery.set("defType", "edismax");
solrQuery.set("qf", "total_amount_ik^2 payment_ik^3");
// 設置要返回的字段,包括 score
solrQuery.setFields("*,score");
solrQuery.setRows(10);
solrQuery.setSort("score", SolrQuery.ORDER.desc);
var datas = _orderDao.query(solrQuery);
for (var da : datas) {
System.out.println(da.getOrderId());
}

qf 算法
boost * idf * tf
idf = log(1 + (N - n + 0.5) / (n + 0.5))
tf = freq / (freq + k1 * (1 - b + b * dl / avgdl))
qf vs bf 總結
- qf:會經過 TF-IDF 計算權重,取最高的一個分數來計
- bf:直接增加權重分數
注意:Solr 有時候 index 會卡住影響 idf 跟 tf 的分數。如果只有一個欄位進行 qf 沒有問題,但是如果有兩個取高者,卡住的就會有相對影響。
整體順序
q >> fq 資料進行 qf 然後加上 bf,最後 sort 並且 fl out
自訂 Request Handler
步驟
- 自己包一個 jar 檔
- 放到 Solr 的 lib 裡面
- 到對應的 collection 上設定路徑跟對應的 class
Gradle 設定檔
plugins {
id 'java'
id 'com.github.johnrengelman.shadow' version '7.1.2'
}
group 'com.yourpackage'
version '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.apache.solr:solr-core:8.11.1'
implementation 'org.apache.solr:solr-solrj:8.11.1'
annotationProcessor 'org.apache.solr:solr-core:8.11.1'
annotationProcessor 'org.apache.solr:solr-solrj:8.11.1'
}
shadowJar {
archiveClassifier.set('all')
manifest {
attributes(
'Main-Class': 'com.solrtest.MyCustomRequestHandler',
'Implementation-Title': project.name,
'Implementation-Version': project.version
)
}
}
sourceCompatibility = '1.8'
targetCompatibility = '1.8'
Java Handler 範例
package com.solrtest;
import org.apache.solr.handler.component.SearchHandler;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.response.SolrQueryResponse;
public class MyCustomRequestHandler extends SearchHandler {
@Override
public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
String param = req.getParams().get("paramName");
ModifiableSolrParams params = new ModifiableSolrParams(req.getParams());
params.set("defType", "edismax");
params.set("qf", "data_ik^2");
super.handleRequestBody(req, rsp);
rsp.add("response", "handleRequestBody: " + param);
}
@Override
public String getDescription() {
return "Custom Search Handler";
}
@Override
public String getName() {
return "MyCustomRequestHandler";
}
}
Solr 設定 (solrconfig.xml)
<requestHandler name="/customSearch" class="com.solrtest.MyCustomRequestHandler">
<lst name="defaults">
<str name="echoParams">explicit</str>
<int name="rows">10</int>
</lst>
<arr name="components">
<str>query</str>
<str>facet</str>
</arr>
</requestHandler>
測試查詢
http://localhost:8080/solr/new_core/customSearch?indent=true¶mName=123&q.op=OR&q=*:*
發佈留言