Elasticsearch是一款开源、近实时、高性能的分布式搜索引擎。
Elasticsearch底层基于Lucene开发,针对Lucene的局限性,ES提供了RESTful API风格的接口、支持分布式、可水平扩展。
前言—生活中的数据
搜索引擎是对数据的检索,所以我们要从生活中的数据说起,总体分为两种:
结构化数据
非结构化数据
结构化数据:也称作行数据,是由二维表结构来逻辑表达和实现的数据,严格地遵循数据格式与长度规范,主要通过关系型数据库进行存储和管理。指具有固定格式或有限长度的数据,如数据库,元数据等
非结构化数据:也可称为全文数据,不定长或无固定格式,不适于由数据库二维表来表现,包括所有格式的文档,如xml、html、word、邮件、各类图表、图片和音频、视频信息等
对于结构化数据,因为它们具有特定的结构,所以我们一般都是通过关系型数据库(MySQL等)的二维表的方式存储和搜索,也可建立索引
对于非结构化数据,也即对全文数据的搜索主要有两种方法:
①:顺序扫描
按照顺序扫描的方式查询特定的关键字
②:全文检索
将非结构化数据中的一部分信息提取出来,重新组织,使其变得有一定结构,然后对此有一定结构的数据进行搜索,从而达到搜索相对较快的目的
这种方式就构成了全文检索的基本思路。这部分从非结构化数据中提取出的然后重新组织的信息,我们称之为索引
ES7版本的变化:
TransportClient被废弃
废除单个索引下多Type的支持,使用默认的_doc 作为Type,同时在8.x版本将彻底废除type
一、基本概念
index,索引,在ES中,索引有两个含义:
1)名词:一个索引相当于关系型数据库中的一张表
2)动词:将一份document保存在一个index中,这个过程称为索引
type :
在6.x之前,index可以被理解为关系型数据库中的【数据库】,而type则可以被认为是【数据库中的表】。
使用type允许我们在一个index里存储多种类型的数据,数据筛选时可以指定type。type的存在从某种程度上可以减少index的数量,但是type存在以下限制:
①:不同type里的字段需要保持一致。例如,一个 index 下的不同 type 里有两个名字相同的字段,他们的类型(string, date 等等)和配置也必须相同
②:只在某个 type 里存在的字段,在其他没有该字段的 type 中也会消耗资源
③:得分是由 index 内的统计数据来决定的。也就是说,一个 type 中的文档会影响另一个 type 中的文档的得分
以上限制要求我们,只有同一个 index 的中的 type 都有类似的映射 (mapping) 时,才勉强适用 type 。否则,使用多个 type 可能比使用多个 index 消耗的资源更多。
这大概也是为什么 ES 决定废弃 type 这个概念,个人感觉 type 的存在,就像是一个语法糖,但是并未带来太大的收益,反而增加了复杂度
document,文档
index中的单条记录称为document,可以理解为表中的一行数据,多条document组成了一个index
field,字段
一个document会由一个或多个field组成,field是ES中数据索引的最小单位
在ES中,没有数组类型,任何字段都可以变成数组
ES中常用的类型:
string | text | 1、索引全文值的字段,如电子邮件正文或产品描述
2、出于不同目的,我们期望以不同方式索引同一字段,这就是milti-fileds(多字段)。
如可以将string字段映射为用于全文搜索的text字段
并映射为用于排序或聚合的keyword字段
注意:
纯text字段默认无法进行排序或聚合
使用text字段一定要使用合理的分词器
|
keyword |
存储的是原始内容 用于索引结构化内容的字段,例如ID、电子邮件地址,主机名、状态代码、邮政编码或标签 通常用于过滤、排序或聚合,keyword字段只能精准匹配 |
|
numeric | 整数类型 |
byte、short、integer、long 我们应该选择足以满足用例的最小类型 |
浮点类型 |
使用缩放因子(scaled_float)将浮点数据存储到整数中通常更有效 如果scaled_float无法满足精度要求,可以使用double、float、half_float |
|
⚠️ 不是所有的字段都适合存储为numberic,numberic类型更擅长range类查询,精确查询可以尝试使用keyword |
mapping,映射
mapping是一个定义document结构的过程,mapping中定义了一个文档所包含的所有field信息
定义字段索引过多会导致爆炸的映射,这可能会导致内存不足错误和难以恢复的情况, mapping 提供了一些配置对 field 进行限制,下面列举几个可能会比较常见的:
index.mapping.total_fields.limit 限制 field 的最大数量,默认值是 1000(field 和 object 内的所有字段,都会加入计数)。
index.mapping.depth.limit 限制 object 的最大深度,默认值是 20。
index.mapping.field_name_length.limit 限制中字段名的长度,默认是没有限制
dynamic mapping
动态映射,在索引document时,ES的动态映射会将新增内容中不存在的字段,自动的加入到映射关系中。ES会自动检测新增字段的逻辑,并赋予其默认值
⚠️ 在ES中,删除/变更 field定义,需要进行reindex,所以在构建mapping结构时记得评估好字段的用途,以使用最合适的字段类型。
检索
match:用于执行全文查询的标准查询,包括模糊匹配和短语或接近查询。
重要参数,控制token之间的布尔关系:or/and
match_phrase:与match查询类似,但用于匹配确切的短语或单词接近匹配。
重要参数,token之间的位置距离,slop,默认为0
term:精确查找的关键,在lucene中,term是索引和搜索的最小单位。一个field会由一个或多个term组成,term是由field经过analyzer(分词)产生。
term dictionary即term词典,是根据条件查找term的基本索引
⚠️ 注意:
避免对text字段使用term查询。默认情况下,ES会在分析过程中更改文本字段的值,这会使查找text字段值的精确匹配变得困难。要搜索text字段值,强烈建议使用match查询
默认分词情况下,无论是term还是match,都无法判断text类型字段是否为空字符串
因为,text字段存储的是分词结果,如果字段值为空,分词结果将不会存储 term 信息,keyword字段存储的是原始内容。
similarity 相似性得分
1、TF/IDF(v7版本已禁止使用,v8彻底废除)
TF-IDF=词频(TF)* 逆文档频率(IDF)
TF/IDF 使用逆文档频率作为权重,降低常见词汇带来的相似性得分。从公式中可以看出,这个相似性算法仅与文档词频相关,覆盖不够全面。
例如:缺少文档长度带来的权重,当其他条件相同,“王者荣耀”这个查询关键字同时出现在短篇文档和长篇文档中时,短篇文档的相似性其实更高
在 ESV5 之前,ES 使用的是 Lucene 基于 TF/IDF 自实现的一套相关性得分算法,如下所示:
score(q,d) = queryNorm(q) · coord(q,d) · ∑ ( tf(t in d) · idf(t)² · t.getBoost() · norm(t,d) ) (t in q)
queryNorm | query normalization factor 查询标准化因子,旨在让不同查询之间的相关性结果可以进行比较(实际上 ES 的 tips 中提到,并不推荐大家这样做,不同查询之间的决定性因素是不一样的) |
coord | coordination factor 协调因子,query 经过分析得到的 terms 在文章中命中的数量越多,coord 值越高。例如:查询“王者荣耀五周年”,terms:“王者”、“荣耀”、“五周年”,同时包含这几个 term 的文档 coord 值越高 |
tf | 词频 |
idf | 文档逆频率 |
boost | boost字面意思是增长推动,这里可以理解为一个支持可配的加权参数 |
norm | 文档长度标准化,内容越长,值越小 |
Lucene 已经针对 TF/IDF 做了尽可能的优化,但是有一个问题仍然无法避免:词频饱和度问题,TF/IDF 算法的相似性得分会随着词频不断上升。在 Lucene 现有的算法中,如果一个词出现的频率过高,会直接忽略掉文档长度带来的权重影响
2)BM25,默认
BM25相比TF-IDF的优点
①:词频饱和不同于 TF/IDF,BM25 的实现基于一个重要发现:“词频和相关性之间的关系是非线性的”。当词频到达一定阈值后,对相关性得分的影响是相同的,此时应该由其他因素的权重决定得分高低,例如之前提到的文档长度
②:将文档长度加入算法中,相同条件下,短篇文档的权重值会高于长篇文档
③:提供了可调整的参数
除了进行全文搜索,Elasticsearch也支持聚合/排序
sort排序
在执行ES查询时,默认的排序规则是根据相关性得分倒序排序,针对非全文索引字段,可以指定排序方式,使用也很简单,如:
//查询时先根据tab_id降序排列,若tab_id相同,则根究status升序排列 GET /[your index]/_search { "sort": [ {"tab_id": {"order": "desc"}}, {"status": {"order": "asc"}} ] }
注意,针对缺失数值类字段的默认值并不是 0,ES 默认会保证排序字段没有 value 的文档被放在最后,默认情况下:
①:降序排列,缺失字段默认值为该字段类型的最小值
②:升序排列,缺失字段默认值为该字段类型的最大值
ES为我们提供了missing参数,我们可以指定缺失值填充,其默认值为_last。
// with missing GET /[your index]/_search { "sort": [ { "num": { "order": "asc", "missing": "0" } } ] }
使用function score 实现自定义排序
当我们期望查询结果按照某个类型进行排序,或者查询结果顺序由多个字段的权重组合决定的场景时,可以使用function score自定义排序,部分参数介绍:
weight | 权重值 |
boost | 加权值 |
boost_mode | 加权值计算方式,默认为multiple |
score_mode | 得分计算方式,默认为multiple |
视频类型查询,电视剧tv的优先级高于电影movie的优先级:
GET /my_index/_search
{
"explain": true,
"query": {
"function_score": {
"functions": [
{
"filter": {"term": {"type": "movie"}},
"weight": 1
},
{
"filter": {"term": {"type": "tv"}},
"weight": 2
}
],
"boost": 1,
"score_mode": "sum"
}
}
}
聚合aggs
聚合操作可以帮助我们将查询数据按照指定的方式进行归类。常见的聚合方式,诸如:
max、min、avg、range、根据term聚合等
索引别名
Aliases 索引别名 索引别名,顾名思义,定义了别名之后,可以通过别名对 index 进行查询:PUT /[your index]/_alias/[your alias name]
索引生命周期策略
Index Lifecycle Policies 索引生命周期策略 索引生命周期策略支持我们根据天、存储量级等信息去自动管理我们的索引。创建方式可以通过 RESTful API,也可以直接在 kibana 上创建,可视化界面看起来比较清晰~ 支持配置满足一定规则后索引自动变化:
自动滚动索引(hot)
保留索引仅供检索(warm)
保留索引仅供检索同时减少磁盘存储(cold)
删除索引
索引模版
Template 索引模板 通过 index_patterns 参数设置索引名正则匹配规则,向一个不存在的索引 POST 数据,命中索引名规则后即会根据索引模版创建索引,不会进行动态映射
索引模式
END.