一.ElasticSearch教程-全文检索&ES概述&ES安装&Kibana安装
1 全文检索
1.1 为什么需要全文检索
大型分布式的商城项目,例如京东,淘宝的关键字搜索框如何高效地检索数据?
如果使用数据库的模糊查询,像是like,缺点如下:
速度非常慢,因为LIKE是将数据从头到尾匹配,在大数据的情况下,匹配速度会非常久
需要匹配的数据库表、数据库数据众多
使用全文检索引擎:可以通过提前将数据库中要检索的数据,放入到全文搜索工具中,将所有数据按照一定的规则进行排序,再进搜索
优点:
速度快:按照一定的规则排序之后,无论用户搜索什么,我们都可以快速找到对应数据
可以实现搜索相似度高的数据排在前面
关键字的高亮,仔细观察我们的百度搜索、商城搜索,可以发现我们搜索的关键字是会有颜色标注的
只处理文本不处理语义:意思就是把相关搜索结果文章告诉你,而不是直接得到答案
全文检索引擎的优点:搜索速度快,可根据需求进行定制化配置
1.2 什么是全文搜索引擎
- 数据的分类
- 结构化数据:类似于MySql存储的数据,就是结构化数据,结构化数据搜索可以按照一定的规则去找,所以结构化数据搜索是最快的
- 半结构化数据:类似于XML、JSON格式的数据,有一定结构的数据,搜索效率没有结构化数据高
- 非结构化数据:类似于Word、PDF格式的数据,就是纯粹的文本数据,完全没有结构
- 全文检索引擎:其实就是将无结构的数据,通过全文检索引擎变成有结构的数据,从而实现高效率搜索
- 全文:针对所有文本,也就是我们存储到搜索服务器中的文本内容
- 检索:就是搜索的意思
- 引擎:可以认为是一个服务器,一个工具,我们可以使用这个工具达到我们想要的效果
1.3 常见的全文搜索引擎
- Lucene:是搜索引擎的底层,常用的搜索引擎都是封装了Lucene,它是一个工具包
- ElasticSearch:全文搜索服务器 ,封装了lucene并扩展,适用于中大型项目
- Solr:全文检索服务器,仅次于ElasticSearch,封装了lucene并扩展,使用于中型项目(社区活跃度不及ElasticSearch,不推荐)
2 ES概述
2.1 为什么使用ElasticSearch(ES)
全文搜索领域,Lucene被认为是迄今为止最先进、性能最好的、功能最全的搜索引擎库
但是Lucene只是一个库,想要使用它,你必须使用Java来作为开发语言并将其直接集成到你的应用中,而且Lucene的配置及使用非常复杂,你需要深入了解检索的相关知识来理解它是如何工作的,所以我们需要使用Lucene的包装技术ElasticSearch
2.2 什么是ES
-
ES是一个分布式的全文搜索引擎,为了解决原生Lucene使用的不足,优化Lucene的调用方式,并实现了高可用的分布式集群的搜索方案,ES的索引库管理支持依然是基于Apache Lucene™的开源搜索引擎
-
ES使用Java开发,并使用Lucene作为其核心来实现索引创建和索引搜索的功能,但是它的目的是通过简单的 RESTful API来替换掉Lucene的复杂性,从而让全文搜索变得简单
-
总的来说ElasticSearch简化了全文检索lucene的使用,同时增加了分布式的特性,使得构建大规模分布式全文检索变得非常容易 ,底层基于Luence,上手快。
添加一个文档:
PUT /shop/user/1
{
"id":1,
"name":"张三"
}
根据索引获取文档:
GET /shop/user/1
2.3 ES特点
- 分布式的近实时文件存储:存入ES的数据,可以很快被搜索到,所以叫近实时,mysql就是实时文件存储,存入立马可以被查询到
- 能在分布式项目/集群中使用,是分布式全文搜索引擎,每个字段都被索引并可被搜索,ES的数据在集群中的每个服务器上都可被搜索到
- 处理PB级结构化或非结构化数据:可以处理的数据量非常大,因为1024TB=1PB
- 简单的 RESTful API通信方式:可以通过API进行调用使用
- 支持各种语言的客户端:只要是支持发送HTTP请求的语言ES都支持
- 基于Lucene封装,操作简单
2.4 ES和Lucene的区别
- Lucene只支持Java,ES支持多种语言
- Lucene非分布式,ES支持分布式
- Lucene非分布式的,索引目录只能在项目本地 , ES的索引库可以跨多个服务分片存储
- Lucene使用非常复杂 , ES屏蔽了Lucene的使用细节,操作更方便
- 单体/小项目使用Lucene/solr,大项目,分布式项目使用ES
2.5 ES使用案例
- Github使用Elasticsearch搜索20TB的数据,包括13亿的文件和1300亿行的代码
- Foursquare实时搜索5千万地点信息,Foursquare每天都用Elasticsearch做这样的事
- Sony公司使用Elasticsearch 作为信息搜索引擎
2.6 其他搜索引擎
- Solr-重量级对手
- Apache Lucene项目的开源企业搜索平台。其主要功能包括全文检索、命中标示、分面搜索、动态聚类、数据库集成,以及富文本(如Word、PDF)的处理。Solr是高度可扩展的,并提供了分布式搜索和索引复制。Solr是最流行的企业级搜索引擎,Solr4 还增加了NoSQL支持
- Solr和ES比较
- Solr 利用 Zookeeper 进行分布式管理,而 Elasticsearch 自身带有分布式协调管理功能
- Solr 支持更多格式的数据,而 Elasticsearch 仅支持json文件格式
- Solr 官方提供的功能更多,而 Elasticsearch 本身更注重于核心功能,高级功能多有 第三方插件提供
- Solr 在传统的搜索应用中表现好于 Elasticsearch,但在处理实时搜索应用时效率明显低于 Elasticsearch
- Solr 是传统搜索应用的有力解决方案,但Elasticsearch更适用于新兴的实时搜索应用
- Katta
- 基于 Lucene 的,支持分布式,可扩展,具有容错功能,准实时的搜索方案
- 优点:开箱即用,可以与 Hadoop (大数据)配合实现分布式。具备扩展和容错机制
- 缺点:只是搜索方案,建索引部分还是需要自己实现。在搜索功能上,只实现了最基本的需求。成功案例较少,项目的成熟度稍微差一些
- HadoopContrib
- 大数据相关的东西
- Map/Reduce 模式(云计算)的,分布式建索引方案,可以跟 Katta 配合使用
- 优点:分布式建索引,具备可扩展性
- 缺点:只是建索引方案,不包括搜索实现。工作在批处理模式,对实时搜索的支持不佳
2.7 ES的优点
-
分布式架构:ES是一种基于Lucene的分布式全文搜索引擎,在数据存储和查询方面具有较强的并发处理能力和水平扩展能力。例如,在日志分析领域,使用ELK Stack(Elasticsearch、Logstash和Kibana)将日志收集、过滤、存储和可视化展现的整个流程,其中的Elasticsearch作为强大的搜索引擎支持海量日志的快速检索和分析。
-
实时搜索:ES支持实时查询,并且可以在毫秒级别内返回搜索结果,有效地支持监控应用程序、搜索引擎、电商应用、金融交易等需要快速反馈的场景。例如,在电商应用领域,使用ES来搜索商品可以实现实时展示用户感兴趣的商品信息,提高用户购物体验。
-
强大的搜索功能:ES拥有先进的搜索能力,可以支持全文检索、多字段匹配、模糊匹配、拼音转换、近似搜索、词项匹配等多种搜索方式,同时还支持聚合、分组、分页、排序等高级特性。例如,在社交网络应用中,可以使用ES来搜索用户、话题和帖子,实现高效的匹配和聚合。
-
易于使用:ES提供了简单易用的API和UI界面,支持多种编程语言的客户端,方便开发人员进行搜索和管理。同时ES还有许多插件和第三方工具,如Kibana、Beats等,能够进一步扩展ES的功能。例如,在安全分析领域,使用Elasticsearch Security插件可以扩展ES的用户认证和授权功能,提高系统的安全性。
-
大数据处理能力:ES支持海量数据的存储和处理,并且能够与Hadoop、Spark等大数据技术结合使用,处理大规模数据。例如,在广告推荐系统中,使用ES作为数据存储和查询的引擎,可以处理千万级别的用户和广告数据,从而实现更准确的推荐服务。
-
开源免费:ES是一款开源软件,采用Apache 2.0许可证,可以免费使用和修改,对于独立开发者和小型企业来说成本较低。例如,国内知名的拼音搜索引擎Pinyinjoe就采用了Elasticsearch,提供了开放的搜索接口。
综上所述,ES具有分布式、实时、强大的搜索功能、易于使用、大数据处理能力和开源免费等多种优点,成为了企业级全文搜索引擎的首选方案,被广泛应用于多个领域。
3 ES下载安装
- ES的安装比较简单,只需要官方下载ES的运行包,然后启动ES服务即可。ES的使用主要是通过能够发起HTTP请求的终端来接入,比如Poster插件、CURL、kibana5等
- ES服务只依赖于JDK,推荐使用JDK1.8+,本课程在window环境下,以ES6.8.6版本为例,下载对应的ZIP文件
- 官网: https://www.elastic.co/downloads/elasticsearch,最新版本是8.X,不推荐下载使用
- 安装:ES是绿色安装,解压即安装
3.1 启动
3.2 ES内存配置
- ES默认占用内存是1G,我们可以调小一点,防止ES占用内存过大
4 Kibana:ES的可视化平台
- Kibana:是为ES设计的开源分析和可视化平台,你可以使用 Kibana 来搜索、查看存储在ES索引中的数据并与之交互,你可以很容易实现高级的数据分析和可视化,以图表的形式展现出来
- 总的来说就是帮助我们更加便捷的操作ES、展示ES的数据
4.1 Kibana安装
- 官网: https://www.elastic.co/downloads/kibana
- Kibana也是绿色安装,本次我们使用的是和ES一样的版本6.X,一般情况下都和使用的ES版本一致即可
- 默认情况下双击Kibana.bat会连接本地ES: http://localhost:9200,进行使用,但是我们可以通过配置文件进行修改
4.2 Kibana测试
- 浏览器访问: http://localhost:5601,Kibana的默认访问地址
4.3修改Kibana为中文界面
- Kibana默认是英文界面,我们可以在配置中进行修改,修改后重启即可
4.4 ES基本概念
- 查询上下文
- 在ES查询后会返回一些信息,比如分页信息、结果信息、相关度分数等,这些都是通过查询上下文进行返回
- 相关度分数
- 默认情况下,ES根据相关性分数对结果进行排序,相关性分数是一个浮点型数字,通过过滤元数据字段_score返回,分数越高,代表搜索结果的相关性越大
- 元数据
- 从索引中查询出的结果可以称之为元数据,也就是我们搜索ES后得到的结果称为元数据
# 创建索引,可以先删除索引,再重新添加文档类型映射
DELETE shop
PUT /shop
PUT /shop/_doc/_mapping
{
"_doc":{
"properties": {
"id":{
"type": "long"
},
"name":{
"type": "text",
"analyzer": "ik_smart",
"search_analyzer": "ik_smart"
},
"age":{
"type": "integer"
},
"sex":{
"type": "integer"
}
}
}
}
GET /shop/_doc/_search
GET /shop/_doc/_search
{
"query":{
"bool":{
"must": [{
"match": {
"name":"我在成都"
}
}
],
"filter": [{
"term": {
"sex": {
"value": "1"
}
}
},{
"range": {
"age": {
"gte": 1,
"lte": 12
}
}
}
]
}
},
"from": 2,
"size":2,
"sort": [
{
"age": "desc"
}
],
"_source": ["name","age","sex"]
}
package com.alibaba.es;
import com.alibaba.utils.ESClientUtil;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequestBuilder;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.sort.SortOrder;
import org.junit.Test;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;
public class EsTest {
/*
* ES的crud
* */
@Test
public void testAdd() {
TransportClient client = ESClientUtil.getClient();// 获取连接,这里的client就是java连接ES的客户端
// 添加数据
// client.prepareIndex("索引名","类型名","id").setSource("json格式的数据").get();
IndexRequestBuilder indexRequestBuilder = client.prepareIndex("shop", "_doc");
Map<String, Object> map = new HashMap<>();
map.put("id", 2);
map.put("name", "李思思");
map.put("age", 16);
map.put("sex", 0);
IndexResponse indexResponse = indexRequestBuilder.setSource(map).get();
System.out.println(indexResponse);
}
@Test
public void testGet() {
TransportClient client = ESClientUtil.getClient();// 获取连接,这里的client就是java连接ES的客户端
// 查询数据
// client.prepareGet("索引名","类型名","id").get();
// client.prepareGet("索引名","类型名","id").setOperationThreaded(false).get();
GetResponse documentFields = client.prepareGet("shop", "_doc", "9g4_vogBBfk9FP3Z9OLv").get();
System.out.println(documentFields.getSourceAsString());
}
@Test
public void testUpdate() {
TransportClient client = ESClientUtil.getClient();// 获取连接,这里的client就是java连接ES的客户端
// 修改数据
// client.prepareUpdate("索引名","类型名","id").setDoc("json格式的数据").get();
IndexRequestBuilder indexRequestBuilder = client.prepareIndex("shop", "_doc", "9g4_vogBBfk9FP3Z9OLv");
Map<String, Object> map = new HashMap<>();
map.put("name", "李二麻子");
IndexResponse indexResponse = indexRequestBuilder.setSource(map).get();
System.out.println(indexResponse);
}
@Test
public void testDelete() {
TransportClient client = ESClientUtil.getClient();// 获取连接,这里的client就是java连接ES的客户端
// 删除数据
// client.prepareDelete("索引名","类型名","id").get();
DeleteResponse deleteResponse = client.prepareDelete("shop", "_doc", "9g4_vogBBfk9FP3Z9OLv").get();
System.out.println(deleteResponse);
}
/*
复杂查询
条件
- 查询_doc表
- name包含:我在成都
- age在1~12之间
- sex=1
- 每页大小2
- 从2页开始查
- 按照age倒序
*/
@Test
public void testQuery() {
TransportClient client = ESClientUtil.getClient();// 获取连接,这里的client就是java连接ES的客户端
// 复杂查询
// client.prepareSearch("索引名").setTypes("类型名").setQuery(QueryBuilders.boolQuery().must(QueryBuilders.matchQuery("字段名","字段值"))).get();
SearchRequestBuilder searchRequestBuilder = client.prepareSearch("shop").setTypes("_doc"); // 查询_doc表
// 查询条件
// 查询_doc表
// - name包含:我在成都
// - age在1~12之间
// - sex=1
// - 每页大小2
// - 从2页开始查
// 按照age倒序
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
// 分页查询
searchRequestBuilder.setFrom(2).setSize(2);
// 排序, 按照age倒序
searchRequestBuilder.addSort("age", SortOrder.DESC);
// name包含:我在成都
boolQueryBuilder.must(QueryBuilders.matchQuery("name", "我在成都"));
// sex = 1, age在1~12之间
boolQueryBuilder.filter(QueryBuilders.termQuery("sex", 1)).filter(QueryBuilders.rangeQuery("age").gte(1).lte(12));
// 查询
SearchRequestBuilder searchRequestBuilder1 = searchRequestBuilder.setQuery(boolQueryBuilder);
// 将结果转换为map输出
Stream<SearchHit> stream = Arrays.stream(searchRequestBuilder1.get().getHits().getHits());
// 流转换为数组
SearchHit[] searchHits = stream.toArray(SearchHit[]::new);
System.out.println(Arrays.toString(searchHits));
// System.out.println(searchRequestBuilder1.get().getHits().getHits());
}
@Test
public void testGenData(){
TransportClient client = ESClientUtil.getClient();
// for循环生成数据
IndexRequestBuilder indexRequestBuilder = client.prepareIndex("shop", "_doc");
Map<String, Object> map = new HashMap<>();
for (int i = 0; i < 100; i++) {
map.put("id", i);
map.put("name", i%2==0?"李思思"+i:"我在成都");
map.put("age", i);
map.put("sex", i%2==0?0:1);
indexRequestBuilder.setSource(map).get();
// 清空map数据
map.clear();
}
}
}
{
"took" : 20,
"timed_out" : false,
"_shards" : {
"total" : 5,
"successful" : 5,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : 6,
"max_score" : null,
"hits" : [
{
"_index" : "shop",
"_type" : "_doc",
"_id" : "_g5KvogBBfk9FP3Zo-JB",
"_score" : null,
"_source" : {
"sex" : 1,
"name" : "我在成都",
"age" : 7
},
"sort" : [
7
]
},
{
"_index" : "shop",
"_type" : "_doc",
"_id" : "_A5KvogBBfk9FP3Zo-I2",
"_score" : null,
"_source" : {
"sex" : 1,
"name" : "我在成都",
"age" : 5
},
"sort" : [
5
]
}
]
}
}
这个信息是Elasticsearch查询操作的返回结果,主要包括以下几个部分:
took:该查询操作耗时,单位为毫秒。
timed_out:查询操作是否超时。
_shards:查询操作在集群中涉及到的分片信息,包括:
a) total:涉及到的总分片数。
b) successful:查询成功的分片数。
c) skipped:跳过的分片数。
d) failed:查询失败的分片数。
hits:查询结果,包括:
a) total:符合查询条件的文档数量。
b) max_score:最高得分。
c) hits:命中的文档详细信息,包括该文档所在的索引、类型、ID等信息。
- _index:文档所在的索引名称。
- _type:文档类型。
- _id:文档ID。
- _score:文档得分。
- _source:文档内容。
- sort:排序方式和依据。
例如,上述结果中返回的6个命中文档,其中两个文档的相关信息包含有 “sex”、“name”、“age”三个字段。同时使用对“age”字段进行排序,第一个文档的“age”字段值为7,第二个文档的“age”字段值为5。
- DSL:数据查询规范
- 在MySql中查询语句叫做查询SQL,例如我们的SELECT,在Elasticsearch中查询语句叫做DSL,Elasticsearch基于JSON提供完整的查询DSL来定义查询
- NearRealtime【NRT】
- 近实时:表示从写入数据到数据可以被搜索到有一个小延迟,大概1秒,基于ES执行搜索和分析可以达到秒级
- Index:索引
- 在ES中索引就相当于是一个数据库,我们可以类比一下MySql,我们使用MySql第一步就是要创建数据库,那么在ES中第一步就是要创建索引
- 创建一个索引:PUT http://localhost:9200/索引名称
- 删除一个索引:DELETE http://localhost:9200/索引名称
- 查询一个索引:GET http://localhost:9200/索引名称
- Type:类型
- 在ES中类型就相当于是一张数据库表,我们可以类比一下MySql,我们在MySql中创建好了数据库,那么必定要创建数据库表,在ES中叫做类型,但是在ES7之后会慢慢弃用类型,也就是不需要创建类型,我们目前在ES6.X中还是需要的
- Document:文档,ES其实就是面向文档的
- 在ES中文档就是最小的存储数据单位了,我们可以类比一下MySql,以前是在数据库/表/下存储一条数据,那么在ES中我们存储一条数据,叫做存储一个文档,在Index/Type/下存储一个Document
- field:字段
- 表示Document中的某个字段,叫做field
- 文档ID:代表一个Document数据的唯一ID
- 类似于MySql中一条数据的主键ID
Relational DB | ElasticSearch |
---|---|
数据库 | 索引-Index |
表 | 类型-Type |
行 | 文档-Document |
字段 | 字段-fieId |
SQL语句 | DSL语句 |
主键ID | 文档ID |
喜欢的话请给我点个赞再走吧,你的鼓励是我最大的动力!