Elasticsearch 是一个分布式搜索和分析引擎,对于开发者而言,它可以理解为高性能的全文搜索引擎,能够将数据转换为可快速检索的索引,支持模糊匹配、分词查询和聚合统计等功能。
核心概念解析
与关系型数据库类比可以快速理解 Elasticsearch 的基本结构:
| Elasticsearch |
MySQL |
说明 |
| Index |
Database |
数据集合,如"文章库" |
| Document |
Row |
单条数据记录 |
| Field |
Column |
文档属性字段 |
| Mapping |
Schema |
字段类型定义 |
| DSL查询 |
SQL查询 |
JSON格式的查询语句 |
这种对应关系有助于快速掌握 Elasticsearch 的数据模型设计思路。
环境部署方案
安装包部署方式
步骤1:获取安装包
# 官方下载(网络可能较慢)
wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.15.1-windows-x86_64.zip
# 推荐使用国内镜像
# 华为镜像:https://mirrors.huaweicloud.com/elasticsearch/7.15.1/
步骤2:配置调整
# 解压安装包
unzip elasticsearch-7.15.1-windows-x86_64.zip
cd elasticsearch-7.15.1
# 内存配置调整(config/jvm.options)
-Xms512m
-Xmx512m
# 跨域配置(config/elasticsearch.yml)
http.cors.enabled: true
http.cors.allow-origin: "*"
步骤3:服务启动
# Windows环境
bin\elasticsearch.bat
# Linux环境
./bin/elasticsearch -d
常见问题处理:
启动时出现 max virtual memory areas vm.max_map_count [65530] is too low 错误时:
# Linux系统解决方案
sudo sysctl -w vm.max_map_count=262144
# Windows系统在配置文件中添加
bootstrap.memory_lock: false
验证部署结果:访问 http://localhost:9200 查看版本信息。
容器化部署方案
使用Docker部署能够快速搭建测试环境:
基础开发环境配置:
docker run -d \
--name es-7.15.1 \
-p 9200:9200 \
-p 9300:9300 \
-e "discovery.type=single-node" \
-e "ES_JAVA_OPTS=-Xms512m -Xmx512m" \
-e "http.cors.enabled=true" \
-e "http.cors.allow-origin=*" \
elasticsearch:7.15.1
生产环境推荐配置:
# 创建数据目录
mkdir -p /data/elasticsearch/{data,logs,config}
chmod 777 /data/elasticsearch/data
# 准备配置文件
echo "http.cors.enabled: true
http.cors.allow-origin: \"*\"
cluster.name: my-es-cluster
node.name: node-1" > /data/elasticsearch/config/elasticsearch.yml
# 启动容器
docker run -d \
--name es-7.15.1-prod \
-p 9200:9200 \
-p 9300:9300 \
-e "discovery.type=single-node" \
-e "ES_JAVA_OPTS=-Xms1g -Xmx1g" \
-v /data/elasticsearch/data:/usr/share/elasticsearch/data \
-v /data/elasticsearch/logs:/usr/share/elasticsearch/logs \
-v /data/elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml \
--ulimit memlock=-1:-1 \
--ulimit nofile=65536:65536 \
elasticsearch:7.15.1
Docker Compose 集成方案:
创建 docker-compose.yml 文件:
version: '3.8'
services:
elasticsearch:
image: elasticsearch:7.15.1
container_name: es-7.15.1
environment:
- discovery.type=single-node
- ES_JAVA_OPTS=-Xms512m -Xmx512m
- http.cors.enabled=true
- http.cors.allow-origin=*
ports:
- "9200:9200"
- "9300:9300"
volumes:
- es-data:/usr/share/elasticsearch/data
- ./elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml
ulimits:
memlock:
soft: -1
hard: -1
nofile:
soft: 65536
hard: 65536
kibana:
image: kibana:7.15.2
container_name: kibana-7.15.1
ports:
- "5601:5601"
environment:
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
depends_on:
- elasticsearch
volumes:
es-data:
启动命令:docker-compose up -d
部署方式对比:
| 对比项 |
安装包部署 |
Docker部署 |
| 学习成本 |
较高 |
较低 |
| 资源占用 |
直接占用宿主机 |
容器额外开销 |
| 数据持久化 |
本地目录 |
需挂载volume |
| 环境隔离 |
无隔离 |
完全隔离 |
| 推荐场景 |
生产环境 |
开发测试 |
实战案例:文章搜索系统
假设现有博客系统的文章表结构:
CREATE TABLE article (
id BIGINT PRIMARY KEY,
title VARCHAR(200),
content TEXT,
author VARCHAR(50),
create_time DATETIME
);
使用 Elasticsearch 实现标题和内容的全文搜索,支持分页、高亮显示和时间排序。
索引创建与映射配置
通过 Kibana Dev Tools 执行:
PUT /article_index
{
"mappings": {
"properties": {
"id": { "type": "long" },
"title": { "type": "text", "analyzer": "ik_max_word" },
"content": { "type": "text", "analyzer": "ik_max_word" },
"author": { "type": "keyword" },
"createTime": { "type": "date", "format": "yyyy-MM-dd HH:mm:ss" }
}
}
}
关键配置点:中文搜索需要使用 IK 分词器,默认的 standard 分词器会将中文按字切分,影响搜索准确性。
在 SpringBoot 项目中引入依赖:
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.15.1</version>
</dependency>
配置客户端连接:
@Configuration
public class EsConfig {
@Bean
public RestHighLevelClient esClient() {
return new RestHighLevelClient(
RestClient.builder(
new HttpHost("localhost", 9200, "http")
)
);
}
}
文档写入操作:
@Service
public class ArticleEsService {
@Autowired
private RestHighLevelClient esClient;
private static final String INDEX_NAME = "article_index";
public void saveArticle(Article article) throws IOException {
IndexRequest request = new IndexRequest(INDEX_NAME);
request.id(article.getId().toString());
Map<String, Object> jsonMap = new HashMap<>();
jsonMap.put("id", article.getId());
jsonMap.put("title", article.getTitle());
jsonMap.put("content", article.getContent());
jsonMap.put("author", article.getAuthor());
jsonMap.put("createTime", article.getCreateTime().toString());
request.source(jsonMap);
esClient.index(request, RequestOptions.DEFAULT);
}
}
搜索功能实现:
public SearchResult searchArticles(String keyword, int pageNo, int pageSize) throws IOException {
SearchRequest searchRequest = new SearchRequest(INDEX_NAME);
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
// 分页配置
sourceBuilder.from((pageNo - 1) * pageSize);
sourceBuilder.size(pageSize);
// 时间倒序排序
sourceBuilder.sort("createTime", SortOrder.DESC);
// 多字段匹配查询
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
boolQuery.should(QueryBuilders.matchQuery("title", keyword).boost(2.0f));
boolQuery.should(QueryBuilders.matchQuery("content", keyword));
sourceBuilder.query(boolQuery);
// 高亮显示配置
HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder.field("title");
highlightBuilder.field("content");
highlightBuilder.preTags("<em>").postTags("</em>");
sourceBuilder.highlighter(highlightBuilder);
searchRequest.source(sourceBuilder);
SearchResponse response = esClient.search(searchRequest, RequestOptions.DEFAULT);
// 结果解析
List<ArticleDoc> articleList = new ArrayList<>();
for (SearchHit hit : response.getHits().getHits()) {
ArticleDoc doc = new ArticleDoc();
doc.setId(Long.parseLong(hit.getId()));
doc.setTitle(hit.getSourceAsMap().get("title").toString());
doc.setContent(hit.getSourceAsMap().get("content").toString());
doc.setAuthor(hit.getSourceAsMap().get("author").toString());
// 高亮字段处理
Map<String, HighlightField> highlightFields = hit.getHighlightFields();
if (highlightFields.containsKey("title")) {
doc.setTitle(highlightFields.get("title").getFragments()[0].string());
}
if (highlightFields.containsKey("content")) {
doc.setContent(highlightFields.get("content").getFragments()[0].string());
}
articleList.add(doc);
}
long total = response.getHits().getTotalHits().value;
return new SearchResult(articleList, total, pageNo, pageSize);
}
查询DSL语法解析
Elasticsearch 的查询DSL可以通过与SQL对比来理解:
SQL语句示例:
SELECT * FROM article
WHERE (title LIKE '%ES%' OR content LIKE '%ES%')
AND author = '张三'
ORDER BY create_time DESC
对应的DSL查询:
{
"query": {
"bool": {
"must": [
{
"bool": {
"should": [
{ "match": { "title": "ES" } },
{ "match": { "content": "ES" } }
]
}
},
{ "term": { "author": "张三" } }
]
}
},
"sort": [{ "createTime": "desc" }]
}
语法对应关系:
must = SQL中的AND条件
should = SQL中的OR条件
must_not = SQL中的NOT条件
term = 精确匹配查询
match = 全文检索查询
中文分词优化
Elasticsearch 默认对中文支持有限,需要安装IK分词器:
- 下载对应版本:https://github.com/medcl/elasticsearch-analysis-ik
- 解压到 plugins/ik 目录
- 重启Elasticsearch服务
测试分词效果:
POST /_analyze
{
"analyzer": "ik_max_word",
"text": "搜索引擎技术"
}
IK分词器能够识别"搜索引擎"作为整体,同时支持"搜索"、"引擎"等细分词汇,显著提升中文搜索准确率。
实践经验总结
基于实际项目应用,得出以下关键经验:
-
适用场景选择:Elasticsearch 适合全文搜索和日志分析场景,但不适用于事务性操作。建议将搜索功能独立部署,核心业务数据仍使用关系型数据库。
-
渐进式学习:初期重点关注基础功能实现,无需过度深入分片、副本等高级特性。先确保单节点稳定运行,再逐步优化性能。
-
开发调试技巧:使用 Kibana Dev Tools 进行查询语句测试和调试,能够大幅提升开发效率。
对于大多数应用场景,掌握基础的数据建模、查询语法和分词配置即可满足需求,后续根据实际性能要求再深入研究底层原理和优化策略。