1. 概述
1.1 ElasticStack特点
- 使用门槛低,开发周期短,上线快
- 性能好,查询快,实时展示结果
- 扩容方便,快速支撑增长迅猛的数据
1.2 ElasticStack各组件作用
- **
Beats
**:数据采集 LogStash
: 数据处理ElasticSearch
(核心引擎): 数据存储、查询和分析Kibana
: 数据探索与可视化分析
1.3 ElasticStack使用场景
- 搜索引擎、日志分析、指标分析
1.4 ElasticStack安装启动
ElasticSearch
启动:解压到安装目录,启动bin/elasticsearch
(默认端口:http:\\localhost:9200
, 加参数-d
后台启动)ElasticSearch
集群:
1 | bin/elasticsearch -d |
- Kibana启动:解压到安装目录,启动
bin/kibana
(默认端口:http:\\localhost:5601
)
1.5 ElasticSearch常见术语
Document
(文档):用户存储在ES中的数据文档Index
(索引):由具有相同字段的文档列表组成field
(字段):包含具体数据Node
(节点):一个ES的实例,构成clister的单元Cluster
(集群):对外服务的一个/多个节点
1.6 Document介绍
- 常用数据类型:字符串、数值型、布尔型、日期型、二进制、范围类型
- 每个文档都有一个唯一
ID
标识。(可以自行指定,也可由ES自动生成) - 元数据,用于标注文档的相关信息:
_index
:文档所在的索引名_type
:文档所在的类型名(后续的版本中type这个概念将会被移除,也不允许一个索引中有多个类型)_id
:文档唯一标识_source
:文档的原始JSON数据,可从这获取每个字段的内容_all
:整合所有字段内容到该字段。(默认禁用)_version
:文档字段版本号,标识被操作了几次
Index
介绍:- 索引中存储相同结构的文档,且每个index都有自己的Mapping定义,用于定义字段名和类型;
- 一个集群中可以有多个inex,类似于可以有多个table。
RESTful API
两种交互方式:- CURL命令行:curl -XPUT xxx
- Kibana DevTools————PUT xxx{ }
Index API
: 用户创建、删除、获取索引配置等。- 创建索引:
PUT /test_index
#创建一个名为test_index
的索引
- 查看索引:
GET _cat/indices
#查看所有的索引
- 删除索引:
DELETE /test_index
#删除名为test_index
的索引
- 创建索引:
1.7 CRUD操作(交互基于Kibana DevTools)
- 创建文档
1 | # 创建ID为1的Document |
- 查询文档:
1 | # 查看名为test_index的索引中id为1的文档 |
- 批量操作文档:
1 | # 批量创建文档,用到endpoint:_bulk |
- 删除文档:
1 | # 根据搜索内容删除文档,使用endpoint:_delete_by_query |
2. ElasticSearch倒排索引与分词
2.1 倒排索引
- 正排索引和倒排索引
- 正排索引:文档ID —> 文档内容
- 倒排索引:单词—> 文档ID列表
- 倒排索引组成:(单词词典,倒排列表)
- 单词词典(
Term Dictionary
)- 记录所有文档的单词,记录了单词到倒排列表的关联信息,一般使用
B+Tree
实现。
- 记录所有文档的单词,记录了单词到倒排列表的关联信息,一般使用
- 倒排列表(
Posting List
)- 记录单词对应的文档集合,由倒排索引项
Posting List
组成。 - 倒排索引项:
- 文档
ID
:用于获取原始信息。 - 词频
TF
:记录该单词在该文档中的出现次数,用于计算相关性得分。 - 位置
Position
:记录单词在文档中的分词位置(多个),用于词语搜索。 - 偏移
Offset
:记录单词在文档的开始和结束位置,用于高亮显示。
- 文档
- 记录单词对应的文档集合,由倒排索引项
- 单词词典(
2.2 分词Analysis
分词:将文本转换成一系列单词Term/Token
的过程,也可称作文本分析,ES中叫作:Analysis。
- 一些概念:
Token
(词元):全文搜索引擎会用某种算法对要建索引的文档进行分析, 从文档中提取出若干Token(词元)。Tokenizer
(分词器):这些算法叫做Tokenizer(分词器)Token Filter
(词元处理器):这些Token会被进一步处理, 比如转成小写等, 这些处理算法被称为TokenFilter(词元处理器)Term
(词):被处理后的结果被称为Term(词)Character Filter
(字符过滤器):文本被Tokenizer处理前可能要做一些预处理, 比如去掉里面的HTML标记, 这些处理的算法被称为Character Filter(字符过滤器)Analyzer
(分析器):这整个的分析算法被称为Analyzer(分析器),由Tokenizer(分词器)和Filter(过滤器)组成
- ES有很多内置
Analyzer
,比如:standard
:按单词边界划分、支持多语言、小写处理、移除大部分标点符号,支持停用词whitespace
:空格为分隔符simple
:按非字母划分、小写处理stop
:类似简单分词器,同时支持移除停用词(the、an、的、这等)keyword
:不分词pattern
:通过正则表达式自定义分隔符,默认\w+,即:非字词的符号作为分隔符
- 第三方analyzer插件:常用的中文分词器有:
- IK:实现中英文分词,支持多模式,可自定义词库,支持热更新分词词典。
- jieba。python中流行,支持繁体分词、并行分词,可自定义词典、词性标记等。
- ES提供了一个测试分词的API接口,使用
endpoint:_analyze
,不指定分词时,会使用默认的standard
1 | # 指定分词器进行分词测试 |
3. ElasticSearch的Mapping
3.1 Mapping简介
Mapping:类似于数据库中的表结构
- 主要作用如下:
- 定义
Index
下的Field Name
; - 定义
Field
的类型,如:数值型、字符串型、布尔型等; - 定义倒排索引的相关配置,如:是否有索引,记录position等。
- 定义
- 获取一个
mapping
,使用endpoint:_mapping
,例如:GET /test_index/_mapping
3.2 自定义Mapping
- 使用
mappings
进行自定义mapping
。 Mapping
中的字段类型一旦设定之后,禁止直接修改。- 因为
Luence
事先的倒排索引生成后不能修改。 - 如果一定要改,可以重新建立新的索引,然后对应修改
mapping
,之后将之前的数据进行reindex
操作,导入新的文档。
- 因为
- 自定义
mapping
时允许新增字段。通过dynamic
参数进行控制字段的新增,dynamic
有三种配置:true
:默认配置,允许自动新增字段;false
:不允许自动新增字段,文档可以正常写入,但不能进行查询等操作;strict
:严格模式。文档不能写入,写入会报错。
1 | # 创建名为my_index的索引,并自定义mapping |
3.3 copy_to的使用
将该字段的值复制到目标字段,类似于6.0版本之前的_all
的作用。且不会出现在_source
,一般只用来进行搜索。
1 | # copy_to的使用 |
3.4 index参数的使用
控制当前字段是否为索引,默认true
,当设置为false
的时候,不进行记录,此时该字段不能被搜索
1 | # index参数的使用 |
此时在进行数据写入和查询,不能进行该字段搜索。一般用来进行不想被查询的私密信息设置,如身份证号,电话号码等:
1 | # 向使用了index参数的字段写入信息 |
3.5 index_options参数的使用:
控制倒排索引记录的内容,有如下四种配置:
docs
:只记录文档IDfreqs
:记录文档ID和词频TFpositions
:记录文档ID、词频TF和分词位置offsets
:记录文档ID、词频TF、分词位置和偏移其中:text类型默认的配置是positions,其他的比如integer等类型默认为docs,目的是为了节省空间。
1 | # index_options参数的使用 |
3.6 null_value参数的使用:
当字段遇到空值null
时的处理策略。默认为null
,即跳过。此时ES会忽略该值,可通过修改进行默认值的修改:
1 | # 使用null_value修改ES遇到null值时的默认返回值 |
3.7 Field字段的数据类型:
- 核心数据类型
- 字符串型:
text
(分词),keyword
(不分词) - 数值型:
long,integer,short,byte,double,float,half_float,scaled_float
- 日期类型:
date
- 布尔类型:
boolean
- 二进制类型:binary
- 范围类型:
integer_range,float_range,long_range,double_range,date_range
- 字符串型:
- 复杂数据类型
- 数组类型:
array
- 对象类型:
object
- 嵌套类型:
nested object
- 数组类型:
- 地理位置数据类型
- 点:
geo-point
- 形状:
geo-shape
- 点:
- 专用类型
- 记录ip地址:
ip
- 实现自动补全:
completion
- 记录分词数:
token_count
- 记录字符串hash值:
murmur3
perclator
join
- 记录ip地址:
- 多字段特性:
- ES允许对同一个字段采用不同的配置,如:分词。举例:对一个人名实现拼音搜索,只需要在人名字段中新增一个子字段pinyin即可。
3.8 ES的自动类型识别:
- Dynamic Mapping:
- ES可以自动识别文档字段类型,从而降低用户使用成本。
1 | # ES的自动类型识别 |
- ES依靠JSON文档的字段类型实现自动识别字段类型:
JSON类型 | ElasticSearch类型 |
---|---|
null | 忽略 |
boolean | boolean |
浮点类型 | float |
整数类型 | long |
object | object |
array | 由第一个非null的值的类型决定 |
String | 匹配为日期,则为date类型(默认开启) 匹配为数字,则为long类型/float类型(默认关闭) 都未匹配,则设为text类型,并附带keyword子字段 |
- 验证ES的字段类型自动识别:
1 | # 验证ES的字段类型自动识别 |
3.9 ES中日期类型和数字的自动识别:
ES中可自行配置日期的格式,默认:[“strict_date_optional_time
“,”yyyy/MM/dd HH:mm:ss Z
|| yyyy/MM/dd z
“]
1 | # 1. 使用dynamic_date_formats自定义日期格式 |
3.10 ES中根据自动识别的数据类型,动态生成字符类型
例:
- 字符串类型都设为keyword类型(不分词)
- 以message开头的字段都设为text类型(分词)
- 以long_开头的字段都设为long类型
- 自动匹配为double的类型都设为float类型。(为了节省空间)
1 | # ES根据自动识别的数据类型、字段名等动态设定字符类型 |
匹配规则的参数:
match_mapping_type
:匹配ES自动识别的字段类型,如boolean、long、string等match
、unmatch
:匹配字段名,比如”match”:”message*” ===>以message开头的数据path_match
、path_unmatch
:匹配路径
3.11 自定义mapping的操作步骤
- 写入一条文档到ES的临时索引中,获取(复制)ES自动生成的mapping
- 修改获得的mapping,并在其中自定义相关配置
- 使用修改后的mapping创建实际所需索引。
4. ElasticSearch的Search API
在ES中,为了实现对存储的数据进行查询分析,使用endpoint
:**_search
**。
- 实现对所有索引的泛查询:
GET /_search
- 实现对一个索引的单独查询:
GET /my_index/_search
- 实现对多个索引的指定查询:
GET /my_index1,my_index2/_search
- 实现对符合指定要求的索引进行查询:
GET /my_*/_search
在进行查询的时候,主要有两种方式:(URI Search
,Request Body Search
)
- **
URI Search
**:操作简单,直接通过命令行方便测试,但仅包含部分查询语法;- 如:
GET /my_index/_search?q=username:alfred
- 如:
- **
Request Body Search
**:ES提供的完备查询语法,使用Query DSL(Domain Specific Language)
进行查询
1 | # 如:Request Body Search方式进行查询 |
4.1 URI Search
- 通过
url query
参数实现搜索,常用参数有:- **
q
**:指定查询的语句,使用query string syntax语法 - **
df
**:q中不指定字段时默认查询的字段(在不指定的时候默认查询所有字段) - **
sort
**:排序 - **
timeout
**:指定超时时间,默认不超时 - **
from,size
**:用于分页
- 举例:
GET my_index/_search?q=alfred&df=username&sort=age:asc&from=4&size=10&timeout=1s
- 解释:查询索引
my_index
中username
字段中包含alfred
的文档,结果按age
字段升序排列
,返回第5-14
个文档,若超过1s
未结束,则以超时结束。
- **
query string syntax
语法- 前置内容:
term:单词
,phrase:词语
。 - 单词与词语语法:
- 单词:
alfred way
等价于alfred
ORway
- 词语:
"alfred way"
语句查询,要求先后顺序 - 泛查询:不指定字段,会在所有字段中去匹配其单词
- 指定字段查询:指定字段,在指定字段中匹配单词
- 单词:
- Group分组设定,使用括号指定匹配的规则
- 举例:
GET my_index/_search?q=username:(alfred OR way)AND lee
- 举例:
- 前置内容:
4.1.1 URI Search API
- 泛查询:
1 | GET my_index/_search?q=alfred |
- 指定字段查询:
1 | # a.查询字段username中包含alfred的文档 |
- 布尔操作符AND(&&)、OR(||)、NOT(!)、+(must)、-(must_not)
1 | # 查询索引my_index中username包含alfred但是不包含way的文档 |
- 范围查询(支持数值和日期)
- 区间写法:闭区间使用
[]
,开区间使用{}
age:[1 TO 10]
# 1<= age <=10age:[1 TO 10}
# 1<= age <10age:[1 TO ]
# age >=1age:[* TO 10]
# age <=10
- 算数符号写法:
age:>=1
age:(>=1 && <= 10) / age:(+ >= 1 + <= 10)
- 还可以对日期进行范围查询,注意:年/月是从1月1号/1号开始算的:
- 区间写法:闭区间使用
1 | # a.查询索引my_index中username字段包含alfred_或_年龄大于20的文档 |
通配符查询
?
代表一个字符,*
代表0个或多个字符,如:name:a?lfred
或name:a*d
或name:alfred*
- 注意:通配符匹配的执行效率较低,且占用内存较多,不建议使用,如果没有特殊要求,也不要将?或者*放在最前面,因为意味着要匹配所有文档,可能会造成OOM。
正则表达式/模糊匹配/近似度查询
- 正则表达式:举例:
/[a]?l.*/
- 模糊匹配:
fuzzy query
- 近似度查询:
proximity search
- 正则表达式:举例:
1 | # 模糊匹配。匹配与alfred差一个字符的词,比如:alfreds、alfret等 |
使用场景常见于用户输入词的纠错中。
4.2 Request Body Search
ES自带的完备查询语句,将查询语句通过http request body
发送到ES,主要参数有:
query
:符合Query DSL
语法的查询条件from,size
timeout
sort
Query DSL
语法:- 基于
JSON
定义的查询语言,主要包含两个类型:- 字段类查询————如:
term
,match
,range
等。只针对一个字段进行查询 - 复合查询————如:
bool
查询等。包含一个/多个字段类查询/符合查询语句
- 字段类查询————如:
- 基于
4.2.1 字段类查询-全文匹配
针对text
类型的字段进行全文检索,会对查询语句进行“先分词再查询”处理,如:match
、match_phrase
等
4.2.1.1 match query
- 对字段进行全文检索(最基本和最常用的查询类型),举例:
1 | GET my_index/_search |
从结果,可以返回匹配文件总数,返回文档列表,_score相关性得分等。
一般的执行流程为: 1.对查询语句分词==>2.根据字段的倒排索引列表,进行匹配算分==>3.汇总得分==>4.根据得分排序,返回匹配文档
- 使用
operator
参数,可以控制单词间关系,有and/or
:
1 | # 使用operator参数控制单词间关系 |
- 使用
minimum_should_match
参数控制需匹配的单词数
1 | # 使用minimum_should_match参数控制需匹配的单词数 |
4.2.1.2 相关性算分,其本质就是一个排序问题
- 计算文档与待查询语句之间的相关度,一般有四个重要概念:
Term Frequency
词频(正相关)Document Frequency
文档频率(负相关)Inverse Term Frequency
逆文本频率(正相关)Field-length Norm
文档长度(负相关)
- 目前ES有两个相关性算分的模型:
TF/IDF
模型:经典模型。BM25
模型:5.x版本后的默认模型,是对TF/IDF的优化模型。
TF/IDF
模型:在使用kibana进行查询时,使用explain参数,可以查看具体的计算方法。
1 | # 使用explain参数,可以查看具体的相关性的得分是如何计算的 |
注意:ES计算相关性得分是根据
shard
进行的,即分片的分数计算相互独立,所以在使用的时候要注意分片数,可以通过设定分片数为1来避免这个问题,主要是为了观察,不代表之后所有的分片全都设为1。一般放在创建索引后,未加数据之前。
1 | # 设定shards数量为1 |
- BM25模型。5.x版本后的默认模型,是对TF/IDF的优化模型。
best match,25
指:迭代了25次才计算。BM25的使用,降低了TF/IDF中因为TF过大导致的负面影响,在BM25中,一个单词的TF一直增长,到一定程度就趋于0变化。
4.2.1.3 match phrase query
对字段做全文检索,有顺序要求。
- 使用
match——phrase
查询词语
1 | GET my_index/_search |
- 通过使用
slop
参数,可以控制单词间间隔:
1 | GET my_index/_search |
4.2.1.4 query string query
类似于URI Search
中的q参数查询,举例:
- 使用
query_string
查询
1 | GET my_index/_search |
4.2.1.5 simple query string query
类似于query string
,但会忽略错误的查询语法,且仅支持部分查询语句。使用+,|,-
分别代替AND,OR,NOT
。
- 使用simple query string query
1
2
3
4
5
6
7
8
9GET my_index/_search
{
"query":{
"simple_query_string":{
"fields":[username],
"query":"alfred +way" #等价于 "query":"alfred AND way"
}
}
}
4.2.2 字段类查询-单词匹配
4.2.2.1 term/terms query
将待查询语句作为整个单词进行查询,不做分词处理,举例:
- 使用term进行单查询
1 | GET my_index/_search |
- 使用terms进行多查询
1 | GET my_index/_search |
此时如果直接使用
alfred way
作为username
查询条件,是不会返回任何文档的。因为在username
的倒排索引列表中,存在"alfred"
和"way"
的索引,但是不存在"alfred way"
的索引。
4.2.2.2 range query
- 范围查询,主要针对数值类型和日期类型。
gt
: greater than 大于gte
: greate than or equal to 大于等于lt
: less than 小于lte
: less than or equal to 小于等于
- 对数值的查询
1 | # range query对数值的查询 |
- 对日期的查询
1 | # range query对日期的查询 |
Date Math
类型:针对日期提供的一种更友好的计算方式。
当前时间用now
代替,具体时间的引用,需要使用||
间隔。年、月、日、时、分、秒跟date
一致:y、M、w、d、h、m、s
。
举例:
1 | # 假设当前时间为2019-01-02 12:00:00 |
4.2.3 复合查询
包含一个/多个字段类查询/符合查询语句
4.2.3.1 constant_score query
constant_score query
: 将内部的查询结果文档得分全部设定为1或boost的值。返回的相关性得分全部为1或boost
1 | # 使用constant_score query |
4.2.3.2 bool query
bool query
: 由一个/多个布尔子句组成,主要包含以下四个:
filter
: 只过滤符合条件的文档,不计算相关性得分,返回的相关性得分全部为0;ES
会对filter
进行智能缓存,因此执行效率较高,在做简单匹配查询且不考虑得分的时候没推荐使用filter
代替query
1 | # 使用filter查询 |
must
: 文档必须符合must
中的所有条件,影响相关性得分;
1 | # 使用must进行查询 |
must_not
: 文档必须排除must_not中的所有条件;
1 | # 使用must_not进行查询 |
should
: 文档可以符合should
中的条件,影响相关性得分,分为两种情况:同时配合minimum_should_match
控制满足调价你的个数/百分比。bool
查询中只有should
,不包含must
的情况- bool查询中既有should,又包含must的情况,文档不必满足should中的条件,但是如果满足的话则会增加相关性得分。
1 | # bool查询中只有should的情况 |
当一个查询语句位于query或filter上下文的时候,ES的执行结果也不同。
- | - | - |
---|---|---|
query | 查找和查询语句最匹配的文档, 并对所有文档计算相关性得分 |
query bool中的:must/should |
filter | 查找和查询语句最匹配的文档 | bool中的:filter/must_not constant_score中的:filter |
1 | # query和filter上下文 |
4.2.3.3 count API
count API
: 获取符合条件的文档书,使用endpoint:_count
。
1 | # 使用_count获取符合条件的文档数 |
4.2.3.4 Source Filtering
Source Filtering
: 过滤返回结果中的_source
中的字段,主要由以下两种方式:
- GET my_index/_search?_source=username #url参数
- 使用Request Body Search:
1 | # 不返回_source |