Apache Solr - 企业级搜索平台

项目简介

Apache Solr是一个基于Apache Lucene的开源企业级搜索平台,提供分布式索引、复制和负载均衡查询、自动故障转移和恢复、集中式配置等功能。Solr不仅仅是一个搜索引擎,更是一个完整的搜索服务器,提供REST风格的API、管理界面和强大的配置选项。

主要特性

  • 分布式搜索:SolrCloud提供分布式索引和查询
  • 实时索引:支持近实时的文档索引更新
  • 分面导航:强大的分面搜索和导航功能
  • 高亮显示:搜索结果高亮和摘要生成
  • 拼写检查:自动拼写纠正和建议
  • 地理空间搜索:位置和地理信息检索

项目原理

核心架构

1
2
3
4
5
6
7
8
9
10
Solr架构
├── SolrCloud (分布式模式)
│ ├── ZooKeeper Ensemble
│ ├── Solr Nodes
│ └── Collections/Shards
├── Solr Core (单机模式)
│ ├── Schema Definition
│ ├── Index Directory
│ └── Request Handlers
└── Lucene Engine

核心概念

  • Collection:逻辑上的索引单元,可分布在多个节点
  • Shard:Collection的物理分割,支持水平扩展
  • Replica:Shard的副本,提供高可用性
  • Core:Solr的基本运行实例

使用场景

1. 电子商务搜索

为在线商店提供商品搜索、分面导航和推荐功能。

2. 企业内容搜索

搜索企业内部文档、邮件、知识库等内容。

3. 日志分析

对应用日志、系统日志进行索引和搜索分析。

4. 地理位置服务

基于地理位置的搜索和推荐服务。

具体案例

案例1:基本配置和操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!-- schema.xml -->
<schema name="product_catalog" version="1.6">
<fieldType name="text_cn" class="solr.TextField">
<analyzer type="index">
<tokenizer class="solr.StandardTokenizerFactory"/>
<filter class="solr.LowerCaseFilterFactory"/>
</analyzer>
</fieldType>

<field name="id" type="string" indexed="true" stored="true" required="true"/>
<field name="name" type="text_cn" indexed="true" stored="true"/>
<field name="price" type="pdouble" indexed="true" stored="true"/>
<field name="category" type="string" indexed="true" stored="true" multiValued="true"/>

<uniqueKey>id</uniqueKey>
</schema>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// Java客户端操作
public class SolrExample {
private SolrClient solrClient;

public SolrExample(String solrUrl) {
this.solrClient = new HttpSolrClient.Builder(solrUrl).build();
}

// 添加文档
public void addDocuments() throws Exception {
SolrInputDocument doc = new SolrInputDocument();
doc.addField("id", "PROD001");
doc.addField("name", "智能手机");
doc.addField("price", 2999.99);
doc.addField("category", Arrays.asList("电子产品", "手机"));

solrClient.add(doc);
solrClient.commit();
}

// 基本搜索
public void search(String queryString) throws Exception {
SolrParams params = new ModifiableSolrParams()
.set("q", queryString)
.set("rows", 10)
.set("fl", "id,name,price,score");

QueryResponse response = solrClient.query(params);
SolrDocumentList documents = response.getResults();

for (SolrDocument doc : documents) {
System.out.println("ID: " + doc.getFieldValue("id"));
System.out.println("名称: " + doc.getFieldValue("name"));
System.out.println("价格: " + doc.getFieldValue("price"));
}
}

// 分面搜索
public void facetSearch() throws Exception {
ModifiableSolrParams params = new ModifiableSolrParams();
params.set("q", "*:*");
params.set("facet", true);
params.add("facet.field", "category");
params.add("facet.field", "brand");

QueryResponse response = solrClient.query(params);

for (FacetField facetField : response.getFacetFields()) {
System.out.println("分面: " + facetField.getName());
for (FacetField.Count count : facetField.getValues()) {
System.out.println(" " + count.getName() + ": " + count.getCount());
}
}
}
}

案例2:SolrCloud集群配置

1
2
3
4
5
6
7
8
9
10
11
12
# 启动SolrCloud集群
ZK_HOST="zk1:2181,zk2:2181,zk3:2181"

# 上传配置
./solr zk upconfig -n myconfig -d ./configsets/sample -z $ZK_HOST

# 启动节点
./solr start -cloud -p 8983 -z $ZK_HOST -m 2g
./solr start -cloud -p 8984 -z $ZK_HOST -m 2g

# 创建集合
curl "http://localhost:8983/solr/admin/collections?action=CREATE&name=products&numShards=2&replicationFactor=2&collection.configName=myconfig"

案例3:高级查询功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 高亮搜索
public void highlightSearch(String query) throws Exception {
ModifiableSolrParams params = new ModifiableSolrParams();
params.set("q", query);
params.set("hl", true);
params.add("hl.fl", "name,description");
params.set("hl.simple.pre", "<mark>");
params.set("hl.simple.post", "</mark>");

QueryResponse response = solrClient.query(params);
Map<String, Map<String, List<String>>> highlighting = response.getHighlighting();

for (SolrDocument doc : response.getResults()) {
String id = (String) doc.getFieldValue("id");
if (highlighting.containsKey(id)) {
Map<String, List<String>> fieldHighlights = highlighting.get(id);
for (Map.Entry<String, List<String>> entry : fieldHighlights.entrySet()) {
System.out.println("高亮字段 " + entry.getKey() + ": " + entry.getValue());
}
}
}
}

// 地理空间搜索
public void geoSearch(double lat, double lon, double distance) throws Exception {
ModifiableSolrParams params = new ModifiableSolrParams();
params.set("q", "*:*");
params.set("fq", String.format("{!geofilt pt=%f,%f sfield=location d=%f}", lat, lon, distance));
params.set("sort", String.format("geodist(location,%f,%f) asc", lat, lon));

QueryResponse response = solrClient.query(params);
// 处理地理搜索结果
}

性能优化建议

1. 索引优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!-- solrconfig.xml 优化配置 -->
<indexConfig>
<ramBufferSizeMB>100</ramBufferSizeMB>
<maxBufferedDocs>1000</maxBufferedDocs>
<useCompoundFile>false</useCompoundFile>
</indexConfig>

<updateHandler class="solr.DirectUpdateHandler2">
<autoCommit>
<maxTime>15000</maxTime>
<openSearcher>false</openSearcher>
</autoCommit>
<autoSoftCommit>
<maxTime>1000</maxTime>
</autoSoftCommit>
</updateHandler>

2. 查询优化

1
2
3
4
5
6
7
8
9
10
// 使用过滤查询提高性能
params.set("q", "text:搜索词");
params.add("fq", "category:电子产品"); // 过滤查询会被缓存
params.add("fq", "price:[100 TO 1000]");

// 限制返回字段
params.set("fl", "id,name,price"); // 只返回需要的字段

// 使用查询结果缓存
params.set("cache", true);

3. 硬件配置建议

内存配置

  • JVM堆内存:服务器内存的50-75%
  • 操作系统缓存:剩余内存用于文件系统缓存

存储配置

  • 使用SSD提高I/O性能
  • 分离索引和日志存储

网络配置

  • 万兆以太网减少网络延迟
  • 合理配置分片和副本分布

Apache Solr作为企业级搜索平台,其强大的搜索功能和灵活的配置选项使其成为构建复杂搜索应用的理想选择。通过合理的架构设计和性能优化,Solr可以为企业提供高效、可靠的搜索服务。

版权所有,如有侵权请联系我