Elastic Stack 介绍
if you没有听说过ElasticSearch,那你一定听说过ELK(Elastic Stack),实际上ELK是三款软件的简称,分别是Elasticsearch、Logstash、Kibana组成,在发展的过程中,又有新成员Beats的加入,所以就形成了Elastic Stack。所以说,ELK是旧的称呼,Elastic Stack是新的名字。它能够安全可靠地获取任何来源、任何格式的数据,然后实时地对数据进行搜索、分析和可视化
Elastic Stack的组成:
本篇文章主要介绍Elasticsearch。
一、Elasticsearch
Elasticsearch 基于java,简称为 ES,是个开源分布式全文搜索引擎,是整个 Elastic
Stack 技术栈的核心。它的特点有:分布式,零配置,自动发现,索引自动分片,索引副本机制,restful风格接口,多数据源,自动搜索负载等。
什么是全文搜索引擎?
Google,百度类的网站搜索,它们都是根据网页中的关键字生成索引,我们在搜索的时
候输入关键字,它们会将该关键字即索引匹配到的所有网页返回;还有常见的项目中应用日
志的搜索等等。
就像这种,我们搜索了springboot,以springboot为索引,将匹配到的所有网页返回,并将关键字高亮显示。
然而对于这些非结构化的数据文本,关系型数据库(mysql等)搜索不是能很好的支持。
一般传统数据库,全文检索都实现的很鸡肋,因为一般也没人用数据库存文本字段。进
行全文检索需要扫描整个表,如果数据量大的话即使对 SQL 的语法优化,也收效甚微。建
立了索引,但是维护起来也很麻烦,对于 insert 和 update 操作都会重新构建索引。
基于以上原因可以分析得出,在一些生产环境中,使用常规的搜索方式,性能是非常差
的:
- 搜索的数据对象是大量的非结构化的文本数据。
- 文件记录量达到数十万或数百万个甚至更多。
- 支持大量基于交互式文本的查询。
- 需求非常灵活的全文搜索查询。
- 对高度相关的搜索结果的有特殊需求,但是没有可用的关系数据库可以满足。
- 对不同记录类型、非文本数据操作或安全事务处理的需求相对较少的情况。
为了解决结构化数据搜索和非结构化数据搜索性能问题,我们就需要专业,健壮,强大的全
文搜索引擎
这里说到的全文搜索引擎指的是目前广泛应用的主流搜索引擎。它的工作原理是计算机
索引程序通过扫描文章中的每一个词,对每一个词建立一个索引,指明该词在文章中出现的
次数和位置,当用户查询时,检索程序就根据事先建立的索引进行查找,并将查找的结果反
馈给用户的检索方式。这个过程类似于通过字典中的检索字表查字的过程。
目前市面上流行的搜索引擎软件,主流的就两款:Elasticsearch 和 Solr。二者的区别这里就不做过多的解释,大家百度搜一下。
Elasticsearch 应用案例
- GitHub: 2013 年初,抛弃了 Solr,采取 Elasticsearch 来做 PB 级的搜索。“GitHub 使用
Elasticsearch 搜索 20TB 的数据,包括 13 亿文件和 1300 亿行代码”。 - 维基百科:启动以 Elasticsearch 为基础的核心搜索架构
- SoundCloud:“SoundCloud 使用 Elasticsearch 为 1.8 亿用户提供即时而精准的音乐搜索
服务”。 - 百度:目前广泛使用 Elasticsearch 作为文本数据分析,采集百度所有服务器上的各类指标数据及用户自定义数据,通过对各种数据进行多维分析展示,辅助定位分析实例异常或业务层面异常。目前覆盖百度内部 20 多个业务线(包括云分析、网盟、预测、文库、直达号、钱包、风控等),单集群最大 100 台机器,200 个 ES 节点,每天导入 30TB+数据。
- 新浪:使用 Elasticsearch 分析处理 32 亿条实时日志。
- 阿里:使用 Elasticsearch 构建日志采集和分析体系。
- Stack Overflow:解决 Bug 问题的网站,全英文,编程人员交流的网站。
Elasticsearch安装
Docker安装Elasticsearch
Postman 安装
Postman 是一款强大的网页调试工具,提供功能强大的 Web API 和 HTTP 请求调试。
软件功能强大,界面简洁明晰、操作方便快捷,设计得很人性化。Postman 中文版能够发送任何类型的 HTTP 请求 (GET, HEAD, POST, PUT…),不仅能够表单提交,且可以附带任意类型请求体。
Postman 官网:https://www.getpostman.com
Postman 下载:https://www.getpostman.com/apps
二、Elasticsearch的基本操作
Elasticsearch 是一个分布式、RESTful风格的搜索和数据分析引擎,能够解决不断涌现出的各种用例。如果不知道什么是RESTful风格的小伙伴推荐去看一下阮一峰大佬的这篇博客!
RESTful API 设计指南
2.1 基本概念
Elasticsearch 是面向文档型数据库,一条数据在这里就是一个文档。为了方便大家理解,我们将 Elasticsearch 里存储文档数据和关系型数据库 MySQL 存储数据的概念进行一个类比,如下
Elasticsearch | mysql |
---|---|
Index (索引) | Database(数据库) |
Types(类型) | Table(表) |
Documents(文档) | Row(行) |
Fileds(字段) | Column(列) |
ES 里的 Index 可以看做一个库,而 Types 相当于表,Documents 则相当于表的行。
这里 Types 的概念已经被逐渐弱化,Elasticsearch 6.X 中,一个 index 下已经只能包含一个
type,Elasticsearch 7.X 中, Type 的概念已经被删除了。
索引
- 索引(index)是Elasticsearch对逻辑数据的逻辑存储,所以它可以分为更小的部分。
- 可以把索引看成关系型数据库的表,索引的结构是为快速有效的全文索引准备的,特别是它不存储原始值。
- Elasticsearch可以把索引存放在一台机器或者分散在多台服务器上,每个索引有一或多个分片(shard),每个分片可以有多个副本(replica)。
文档
- 存储在Elasticsearch中的主要实体叫文档(document)。用关系型数据库来类比的话,一个文档相当于数据库表中的一行记录。
- Elasticsearch和MongoDB中的文档类似,都可以有不同的结构,但Elasticsearch的文档中,相同字段必须有相同类型。
- 文档由多个字段组成,每个字段可能多次出现在一个文档里,这样的字段叫多值字段(multivalued)。
每个字段的类型,可以是文本、数值、日期等。字段类型也可以是复杂类型,一个字段包含其他子文档或者数
组。
映射
所有文档写进索引之前都会先进行分析,如何将输入的文本分割为词条、哪些词条又会被过滤,这种行为叫做
映射(mapping)。一般由用户自己定义规则。
文档类型
- 在Elasticsearch中,一个索引对象可以存储很多不同用途的对象。例如,一个博客应用程序可以保存文章和评
论。 - 每个文档可以有不同的结构。
- 不同的文档类型不能为相同的属性设置不同的类型。例如,在同一索引中的所有文档类型中,一个叫title的字段必须具有相同的类型。
2.2 索引操作
创建索引
对比关系型数据库,创建索引就等同于创建数据库
在 Postman 中,向 ES 服务器发 PUT 请求 :http://127.0.0.1:9200/shopping
我们可以在创建索引时设置分片数和副本数,也可以使用默认设置
{
"settings": {
"index": {
"number_of_shards": "2", #分片数
"number_of_replicas": "0" #副本数
}
}
}
注意:如果重复添加索引,会返回错误信息
查看所有索引
查看所有索引
在 Postman 中,向 ES 服务器发 GET 请求 :http://127.0.0.1:9200/_cat/indices?v
GET /_cat/indices?v
_cat 表示查看的意思,indices 表示索引
所以整体含义就是查看当前 ES服务器中的所有索引,就好像 MySQL 中的 show tables 的感觉,服务器响应结果如下
查看单个索引
GET /shopping
查看索引向 ES 服务器发送的请求路径和创建索引是一致的。但是 HTTP 方法不一致。这里
可以体会一下 RESTful 的意义,
{
"shopping"【索引名】: {
"aliases"【别名】: {},
"mappings"【映射】: {},
"settings"【设置】: {
"index"【设置 - 索引】: {
"creation_date"【设置 - 索引 - 创建时间】: "1630377432837",
"number_of_shards"【设置 - 索引 - 主分片数量】: "2",
"number_of_replicas"【设置 - 索引 - 副分片数量】: "0",
"uuid"【设置 - 索引 - 唯一标识】: "hkn1Qqn4T4Kn0EeCwXE_gA",
"version"【设置 - 索引 - 版本】: {
"created": "7020099"
},
"provided_name"【设置 - 索引 - 名称】: "shopping"
}
}
}
}
删除索引
#删除索引
DELETE /shopping
{
"acknowledged": true
}
2.3 文档操作
创建文档
索引已经创建好了,接下来我们来创建文档,并添加数据。这里的文档可以类比为关系型数据库中的表数据,添加的数据格式为 JSON 格式
在 Postman 中,向 ES 服务器发 POST 请求 :http://127.0.0.1:9200/shopping/_doc
请求体内容为:
{
"title":"小米手机",
"category":"小米",
"price":3999.00
}
此处发送请求的方式必须为 POST,不能是 PUT,否则会发生错误
{
"_index"【索引】: "shopping",
"_type"【类型-文档】: "_doc",
"_id"【唯一标识】: "9XgjmnsB_GTUWLpZY8A7", #可以类比为 MySQL 中的主键,随机生成
"_version"【版本】: 1,
"result"【结果】: "created", #这里的 create 表示创建成功
"_shards"【分片】: {
"total"【分片 - 总数】: 1,
"successful"【分片 - 成功】: 1,
"failed"【分片 - 失败】: 0
},
"_seq_no": 0,
"_primary_term": 1
}
上面的数据创建后,由于没有指定数据唯一性标识(ID),默认情况下,ES 服务器会随机
生成一个。
如果想要自定义唯一性标识,需要在创建时指定:
http://127.0.0.1:9200/shopping/_doc/1
此处需要注意:如果增加数据时明确数据主键,那么请求方式也可以为 PUT
查看文档
查看文档时,需要指明文档的唯一性标识,类似于 MySQL 中数据的主键查询
在 Postman 中,向 ES 服务器发 GET 请求 :
GET shopping/_doc/1
{
"_index"【索引】: "shopping",
"_type"【文档类型】: "_doc",
"_id": "9XgjmnsB_GTUWLpZY8A7",
"_version": 1,
"_seq_no": 0,
"_primary_term": 1,
"found"【查询结果】: true, # true 表示查找到,false 表示未查找到
"_source"【文档源信息】: {
"title": "小米手机",
"category": "小米",
"price": 3999.00
}
}
修改文档
和新增文档一样,输入相同的 URL 地址请求,如果请求体变化,会将原有的数据内容覆盖
在 Postman 中,向 ES 服务器发 POST 请求 :
POST /shopping/_doc/1
{
"title":"华为手机",
"category":"华为",
"price":4999.00
}
{
"_index": "shopping",
"_type": "_doc",
"_id": "1",
"_version"【版本】: 2,
"result"【结果】: "updated", # updated 表示数据被更新
"_shards": {
"total": 1,
"successful": 1,
"failed": 0
},
"_seq_no": 1,
"_primary_term": 1
}
修改字段
修改数据时,也可以只修改某一给条数据的局部信息
POST /shopping/_update/1
{
"doc": {
"price":3000.00
}
}
删除文档
删除一个文档不会立即从磁盘上移除,它只是被标记成已删除(逻辑删除)。
在 Postman 中,向 ES 服务器发 DELETE 请求 :
DELETE /shopping/_doc/1
{
"_index": "shopping",
"_type": "_doc",
"_id": "1",
"_version"【版本】: 4, #对数据的操作,都会更新版本
"result"【结果】: "deleted", # deleted 表示数据被标记为删除
"_shards": {
"total": 1,
"successful": 1,
"failed": 0
},
"_seq_no": 2,
"_primary_term": 1
}
条件删除文档
POST /shopping/_delete_by_query
{
"query":{
"match":{
"price":4000.00
}
}
}
2.4 高级查询
查询所有文档
GET /shopping/_search
{
"query": {
"match_all": {}
}
}
# "query":这里的 query 代表一个查询对象,里面可以有不同的查询属性
# "match_all":查询类型,例如:match_all(代表查询所有), match,term , range 等等
# {查询条件}:查询条件会根据类型的不同,写法也有差异
{
"took【查询花费时间,单位毫秒】" : 7,
"timed_out【是否超时】" : false,
"_shards【分片信息】" : {
"total【总数】" : 2,
"successful【成功】" : 2,
"skipped【忽略】" : 0,
"failed【失败】" : 0
},
"hits【搜索命中结果】" : {
"total"【搜索条件匹配的文档总数】: {
"value"【总命中计数的值】: 1,
"relation"【计数规则】: "eq" # eq 表示计数准确, gte 表示计数不准确
},
"max_score【匹配度分值】" : 1.0,
"hits【命中结果集合】" : [
。。。
]
}
}
匹配查询
match 匹配类型查询,会把查询条件进行分词,然后进行查询,多个词条之间是 or 的关系
在 Postman 中,向 ES 服务器发 GET 请求 :
GET /shopping/_search
{
"query": {
"match": {
"title": "小米手机"
}
}
}
字段匹配查询
multi_match 与 match 类似,不同的是它可以在多个字段中查询。
在 Postman 中,向 ES 服务器发 GET 请求 :
GET /shopping/_search
{
"query": {
"multi_match": {
"query": "小米",
"fields": ["title", "category"]
}
}
}
关键字精确查询(term查询)
term 主要用于精确匹配哪些值,比如数字,日期,布尔值或 not_analyzed 的字符串(未经分析的文本数据类型):
在 Postman 中,向 ES 服务器发 GET 请求 :
GET /shopping/_search
{
"query": {
"term": {
"price": 5999.00
}
}
}
多关键字精确查询(terms查询)
terms 跟 term 有点类似,但 terms 允许指定多个匹配条件。 如果某个字段指定了多个值,那么文档需要一起去做匹配:
GET /shopping/_search
{
"query": {
"terms": {
"price": [
5999.00,
3999.00
]
}
}
}
指定查询字段
指定查询字段
默认情况下,Elasticsearch 在搜索的结果中,会把文档中保存在_source 的所有字段都返回。
如果我们只想获取其中的部分字段,我们可以添加_source 的过滤
在 Postman 中,向 ES 服务器发 GET 请求 :```
GET /shopping/_search
{
"_source": ["title", "category"],
"query": {
"terms": {
"price": [3999.00]
}
}
}
过滤查询
POST /shopping/_search
{
"query":{
"bool":{
"filter":{
"term":{
"price":3999.00
}
}
}
}
}
过滤字段
- includes:来指定想要显示的字段
- excludes:来指定不想要显示的字段
在 Postman 中,向 ES 服务器发 GET 请求 :
GET /shopping/_search
{
"_source": {
"includes": ["title", "category"]
},
"query": {
"terms": {
"nickname": ["zhangsan"]
}
}
}
组合查询
bool
把各种其它查询通过must
(必须 )、must_not
(必须不)、should
(应该)的方式进行组合。
- must : 多个查询条件的完全匹配,相当于 and 。
- must_not : 多个查询条件的相反匹配,相当于 not 。
- should : 至少有一个查询条件匹配, 相当于 or 。
这些参数可以分别继承一个查询条件或者一个查询条件的数组:
在 Postman 中,向 ES 服务器发 GET 请求 :
GET /shopping/_search
{
"query": {
"bool": {
"must": [{
"match": {
"title": "小米手机"
}
}],
"must_not": [{
"match": {
"category": "华为"
}
}],
"should": [{
"match": {
"price": 3999.00
}
}]
}
}
}
范围查询
range 查询找出那些落在指定区间内的数字或者时间。range 查询允许以下字符
GET /shopping/_search
{
"query": {
"range": {
"price": {
"gte": 4000,
"lte": 6000
}
}
}
}
字段排序
单字段排序
sort 可以让我们按照不同的字段进行排序,并且通过 order 指定排序的方式。desc 降序,asc升序。
GET /shopping/_search
{
"query": {
"match": {
"title": "小米手机"
}
},
"sort": [{
"price": {
"order": "desc"
}
}]
}
多字段排序
并且匹配的结果首先按照价格排序,然后按照相关性得分排序,
注意如果使用文本字段排序会报错。
GET /shopping/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"price": {
"order": "desc"
},
"_score": {
"order": "desc"
}
}
]
}
高亮查询
在进行关键字搜索时,搜索出的内容中的关键字会显示不同的颜色,称之为高亮。
Elasticsearch 可以对查询内容中的关键字部分,进行标签和样式(高亮)的设置。
在使用 match 查询的同时,加上一个 highlight 属性:
- pre_tags:前置标签
- post_tags:后置标签
- fields:需要高亮的字段
- title:这里声明 title 字段需要高亮,后面可以为这个字段设置特有配置,也可以空
在 Postman 中,向 ES 服务器发 GET 请求 :
GET /shopping/_search
{
"query": {
"match": {
"category": "华为"
}
},
"highlight": {
"pre_tags": "<font color='red'>",
"post_tags": "</font>",
"fields": {
"category": {}
}
}
}
分页查询
- from:当前页的起始索引,默认从 0 开始。 from = (pageNum - 1) * size
- size:每页显示多少条
GET /shopping/_search
{
"query": {
"match_all": {}
},
"sort": [{
"price": {
"order": "desc"
}
}],
"from": 0,
"size": 2
}
聚合查询
聚合允许使用者对 es 文档进行统计分析,类似与关系型数据库中的 group by,当然还有很多其他的聚合,例如取最大值、平均值等等。
GET /shopping/_search
{
"aggs": {
"max_price": {
"max": {
"field": "price"
}
}
}
}