elasticsearch作为一个开源的检索引擎产品,继续分析其关键概念、原理和应用场景。
一、简介
ELK 技术栈,包括Elasticsearch、Kibana和Logstash,能够安全可靠的获取任何来源、任何格式的数据,然后实时地对数据进行搜索、分析和可视化。
1、Elasticsearch
一个开源的、分布式、实时分析搜索引擎,建立在全文搜索引擎库 Apache Lucene 基础上,同时隐藏了 Apache Lucene 的复杂性。Elasticsearch 将所有的功能打包成一个独立的服务,并提供了一个简单的 RESTful API 接口。
2、Kibana
一个开源和免费的工具,Kibana 可以为 Logstash 和 Elasticsearch 提供图形化的日志分析 Web 界面,可以汇总、分析和搜索重要数据日志。
3、Logstach
一个完全开源的工具,主要用于日志收集,同时可以对数据处理,并输出给 Elasticsearch。 Logstash 只做三件事情:数据输入、数据加工(如过滤,改写等) 以及数据输出。
4、Lucene
一个Apache软件基金会Jakarta项目组的一个子项目,提供了简单却强大的应用程序接口,能够全文索引和搜索。Java开发环境中Lucene是成熟的免费开源工具,但Lucene只是一个提供全文搜索功能类库的核心工具包。
二、关键技术
Elasticsearch是面向文档型数据库,一条数据就是一个文档,和关系型数据库mysql的类比如下
其中Types概念已经弱化了,在ES6中,一个index下已经只能包含一个type,ES7中Types的概念已经被删除。
- 索引:索引就类似新华字段中的目录,可以通过偏旁部首快速找到汉字。Elasticsearch的精髓就是为了提高检索效率。
- 文档:文档就是一条数据,可以被索引的基础信息单元,一个索引中可以存储N个文档,每个文档由多个字段组成,通过序列号json格式保存在ES中。文档元数据是用于标准文档的相关信息,_index是文档所属的索引名,_type是文档所属的类型名,_id是文档唯一Id,_source是文档原始json数据,_version是文档版本信息,_score是相关性打分。
- 字段:对文档数据的不同数据进行分类标识。
- 映射:对处理数据的方式和规则做一些限制,如某个字段的数据类型、分析器、是否被索引等。
- 分片:Elasticsearch提供了将索引划分成多份的能力,每一份称之为分片。可以在分片之上进行分布式、并行操作,进而提高性能/吞吐量。
- 副本:Elasticsearch允许创建分片的一份或多份拷贝,这些拷贝叫做副本。
- 分配:将分片分配给某个节点的过程,包括分配主分片或副本。
1、倒排索引
Elasticsearch 使用一种称为倒排索引的结构,它适用于快速的全文搜索。
正向索引,就是搜索引擎会将待搜索的文件都对应一个文件 ID,搜索时将这个ID 和搜索关键字进行对应,形成 K-V 对,然后对关键字进行统计计数排名 ,类似如下
倒排索引, 搜索引擎会将正向索引重新构建为倒排索引,即把文件ID对应到关键词的映射转换为关键词到文件ID的映射,每个关键词都对应着一系列的文件,这些文件中都出现这个关键词,类似如下
示例:
对文件中内容创建倒排索引
2、分词器
将一块文本分成适合于倒排索引的独立的词条,将这些词条统一化为标准格式以提高它们的“可搜索性”。
- 字符过滤器:首先,字符串按顺序通过每个字符过滤器。他们的任务是在分词前整理字符串。一个字符过滤器可以用来去掉 HTML,或者将 & 转化成 and;
- 分词器:其次,字符串被分词器分为单个的词条。一个简单的分词器遇到空格和标点的时候,可能会将文本拆分成词条;
- Token 过滤器:最后,词条按顺序通过每个 token 过滤器 。这个过程可能会改变词条(例如,小写化Quick ),删除词条(例如, 像 a, and, the 等无用词),或者增加词条(例如,像 jump 和 leap 这种同义词);
常见分词器有标准分词器、简单分析器、空格分析器等,对中文支持较好的为IK分词器。
三、关键流程
Logstach收集日志,并存放到Elasticsearch 集群中,而 Kibana 则 从 ES 群集中查询数据生成图表,再返回给 Browser。
1、收集日志
2、将日志通过Logstach转存到Elasticsearch
3、利用Elasticsearch 对格式化后的日志数据进行创建索引和存储
4、 前端数据的展示(Kibana)
ES中的操作流程:
1、通过ES提供的API创建索引
2、通过ES提供的API对上游处理后的Document集合存入ES
3、通过ES提供的API实现全文检索
4、ES返回命中的Document对象集合,包含命中次数、命中的Document
ES提供了对索引、文档的维护操作,包括新增、修改、删除、查询。在利用ES检索引擎的检索能力的前提是对指定的日志文件创建索引并对格式化数据后存入ES。其中,Document就好比日志文件中的一条日志记录。
四、springboot+elasticsearch实例
1、新建springboot工程,添加elasticsearch相关组件,pom如下
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
2、applicaton.properties配置elasticsearch
elasticsearch.host=127.0.0.1
elasticsearch.port=9200
3、elasticsearch配置类
ElasticsearchRestTemplate和spring中其他template类似,是elasticsearch包中封装好的一个类。在最新的版本中,ElasticsearchRestTemplate替代了原来的ElasticsearchTemplate,原因是ElasticsearchTemplate基于TransportClient,而TransportClient在8.x以上版本中移除,所以推荐直接使用ElasticsearchRestTemplate。ElasticsearchRestTemplate是基于RestHighLevelClient客户端的,需要自定义配置类,继承AbstractElasticsearchConfiguration并实现elasticsearchClient抽象方法,创建返回RestHighLevelClient对象。
@Configuration
@Data
public class ElasticsearchConfig extends AbstractElasticsearchConfiguration {
@Value("${elasticsearch.host}")
private String host ;
@Value("${elasticsearch.port}")
private Integer port ;
@Override
public RestHighLevelClient elasticsearchClient() {
RestClientBuilder builder = RestClient.builder(new HttpHost(host, port));
RestHighLevelClient restHighLevelClient = new RestHighLevelClient(builder);
return restHighLevelClient;
}
}
4、实体类,映射索引和文档
@Data
@NoArgsConstructor
@AllArgsConstructor
@Document(indexName = "log_index", type = "log_type", shards = 2, replicas = 1)//映射到ES,表示此类是product索引的映射,主分片3个,副本1个,每个主片,都有一个副本
public class EsLog {
/**
* id 是全局唯一的标识,等同于 es 中的"_id"
*/
@Id
private Long id;
/**
* 是否索引: 看该域是否能被搜索, index = true(默认为true)
* 是否分词: 表示搜索的时候是整体匹配还是单词匹配,Keyword不进行分词,Text进行分词
*/
@Field(type = FieldType.Keyword)
private String time;
/**
* 是否索引: 看该域是否能被搜索, index = true(默认为true)
* 是否分词: 表示搜索的时候是整体匹配还是单词匹配,Keyword不进行分词,Text进行分词
*/
@Field(type = FieldType.Text, analyzer = "ik_smart", searchAnalyzer = "ik_smart")
private String content;
}
5、ES持久层接口
@Repository
public interface EsLogRepository extends ElasticsearchRepository<EsLog, Long> {
}
6、控制层接口
@Autowired
private ElasticsearchRestTemplate elasticsearchRestTemplate;
@Autowired
private EsLogRepository esLogRepository;
@GetMapping("createIndex")
public Object createIndex() {
// 系统初始化会自动创建索引
return "ok";
}
@GetMapping("deleteIndex")
public Object deleteIndex() {
boolean flag = elasticsearchRestTemplate.deleteIndex(EsLog.class);
return flag;
}
@GetMapping("insertDocument")
public Object insertDocument() {
EsLog esLog = new EsLog(11L,"20220601", "Root WebApplicationContext: initialization completed in 1097 ms");
EsLog save = esLogRepository.save(esLog);
return save;
}
@GetMapping("updateDocument")
public Object updateDocument() {
EsLog esLog = new EsLog(1L,"20220601", "Root WebApplicationContext: initialization completed in 1097 ms. This is es world");
EsLog save = esLogRepository.save(esLog);
return save;
}
@GetMapping("getDocument")
public Object getDocument() {
EsLog esLog = esLogRepository.findById(1L).get();
return esLog;
}
@GetMapping("deleteDocument")
public Object deleteDocument() {
esLogRepository.deleteById(1L);
return "ok";
}
@GetMapping("insertBatch")
public Object insertBatch() {
List<EsLog> esLogList = new ArrayList<>();
for (int i = 0; i < 10; i++) {
EsLog esLog = new EsLog((long) i,"20220601", "content" + i);
esLogList.add(esLog);
}
Iterable<EsLog> esLogs = esLogRepository.saveAll(esLogList);
return esLogs;
}
@GetMapping("getAllDocument")
public Object getAllDocument() {
Iterable<EsLog> esLogs = esLogRepository.findAll();
return esLogs;
}
@PostMapping("search")
public Object search(@RequestBody Map<String, Object> params) {
SearchQuery searchQuery= getQueryBuilder(params);
List<EsLog> esLogList = elasticsearchRestTemplate.queryForList(searchQuery, EsLog.class);
return esLogList;
}
public SearchQuery getQueryBuilder(Map<String, Object> params) {
MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("content", params.get("content").toString());
// 分页
PageRequest pageable = PageRequest.of(0, 10);
// 排序
SortBuilder sort = SortBuilders.fieldSort("time").order(SortOrder.DESC);
//设置高亮效果
String preTag = "<font color='#dd4b39'>";
String postTag = "</font>";
HighlightBuilder.Field highlightFields = new HighlightBuilder.Field("content").preTags(preTag).postTags(postTag);
SearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(matchQueryBuilder)
.withPageable(pageable)
.withSort(sort)
.withHighlightFields(highlightFields)
.build();
return searchQuery;
}
7、测试结果
启动服务后自动创建索引
添加文档,如下
在Elasticsearch-head查看,如下
修改文档,如下
查看文档,如下
批量插入,如下
查看所有文档,如下
在Elasticsearch-head查看,如下
检索,如下