概述
功能
- 分布式搜索引擎
- 大数据近实时分析引擎
学习
- 开发: 产品基本功能,底层原理,数据建模
- 运维: 容量规划; 性能优化; 问题诊断; 滚动升级
- 方案: 搜索与如何解决搜索; 大数据分析实践,理论知识实际场景
考试: elastic 官网
简介
1 | restful 接口, 供各种语言使用 |
elastic 生态
- logstash 开源服务端数据处理管道, 从不同源采集数据,转换数据,发送到不同存储库
1 | 实时解析和转换数据 |
- kibana : 可视化分析利器
1 | 数据可视化公司 |
- BEATS : 轻量数据采集器
1 | 有多种不同的beat |
场景
安装上手
安装
- 二进制文件
- docker
- helm chart for k8s
- puppet module
文件目录结构
- bin : 脚本执行文件 包括: 启动,安装插件,运行统计数据等
- config : 集群配置文件
- JDK : java运行环境
- data : 数据文件
- lib : java类库
- logs : 日志文件
- modules : 包含所有ES模块
- plugins : 所有已安装插件
启动
- 物理机
1 | /bin/elasticsearch 启动 |
- docker
1 |
入门
索引,文档,RESTAPI
文档
1 | ES是面向文档的, 文档是可搜索数据的最小单位 |
文档的元数据
1 | ES用于标注文档的相关信息 |
索引
1 | 文档的容器, 一类文档的集合 |
Shard 则是体现 物理空间的概念: 索引中的数据分散在 shard 上
1 | 索引的不同语意: |
类型 type
1
27.0 版本之前, 每个索引可以建立多个type, 每个type下就是同类文档
7.0开始废弃不用,每个索引只能建立一个type (_doc)
REST API
1 | 集成各种语言 |
节点,集群,分片,副本
1 | 分布式系统的可用性和扩展性 |
ES的分布式架构
1 | 不同的集群 通过不同名字区分 (默认名字 elasticsearch) |
节点
1 | 节点: 一个 ES 的实例 |
master-eligible nodes
1
2
3
4可以参加 选主流程,成为master 节点
每个节点启动后: 默认就是一个 master eligible 节点
可设置 node.master: false 禁止master node
1
2当第一个节点启动时, 会将自己选举成 master 节点
每个节点都保存了集群的状态, 只有 master 节点才能修改集群的状态信息 (任何节点都能修改会导致数据不一致)集群状态: 维护集群中的必要信息
所有节点的信息
所有索引 和 其相关的 Mapping 与 Setting 信息
分片的路由信息
Data Node
- 保存数据的节点,叫做Data Node, 负责保存分片数据 (在数据扩展 起重要作用)
Coordinating Node
- 负责接受client的请求,将请求分发到合适节点,最终把结果汇聚到一起。
- 每个节点默认起到 coordinating node 职责
Hot & Warm Node
- 冷热节点, Hot节点配置高, warm配置低(可存储比较旧的数据)
- 不同硬件配置的 Data Node, 用来实现 Hot & Warm 架构,降低集群部署的成本
Machine Learning Node
- 负责跑 机器学习的 Job, 用来做异常检测
Tribe Node
- 连接到不同的 ES集群,支持将这些集群当成一个单独的集群处理
- 淘汰, 5.3开始使用 cross cluster search(跨集群搜索)
节点类型的配置: 生成环境中,应该设置单一角色的节点
节点类型 | 配置参数 | 默认值 |
---|---|---|
master eligible | node.master | true |
data | Node.data | true |
ingest | node.ingest | True |
coordinating only | 无 | 每个节点默认都是coordinating节点。设置其他类型则为false |
machine learning | node.ml | true (需 enable x-pack ) |
分片
主分片 ( primary shard )
1
2
3解决数据水平扩展的问题。 主分片可以将数据分布到集群内的所有节点之上
一个分片 是一个运行的 lucene 的实例
主分片数 在索引创建时指定, 后续不允许修改, 除非 Reindex
副本 ( replica shard )
1
2
3用以解决数据高可用的问题。 副本分片是主分片的拷贝
副本分片数 可以动态调整
增加副本数,还可在一定程度上提高服务的可用性 ( 读取的吞吐 )
分片的设置: 生成环境分片的设定,提前做好容量规划
1 | 分片数设置过小: |
1 | 查看 集群健康状况 |
文档的基本 CRUD
操作 | 命令 (type约定用_dec) | 说明 |
---|---|---|
Index | PUT my_index/_doc/1 | 如果ID不存在,创建信息的。 已经存在则删除旧的创建新的,版本号增加 |
Create | PUT my_index/_create/1 | 如果id已经存在,会失败 |
Read | GET my_index/_doc/1 | |
Update | POST my_index/_update/1 | 文档必须已经存在, 更新只会对相应字段做增量修改 |
Delete | DELETE my_index/_doc/1 |
创建说明
1 | 支持 自动生成文档ID 和 指定文档ID 两种方式 |
注意: create是创建新的, index 是 删除旧的创建新的, update是在旧的上面做增量修改
批量操作
1 | 批量操作, 减少 网络连接产生的开销,提高性能 |
Bulk API
1
2
3
4
5
6
7
8
9POST _bulk
支持一次API调用中, 对不同索引进行不同操作
支持四种操作:
Index, Creat, Update, Delete
可以在 URI 中指定Index, 也可以再请求 payload 中进行
操作中 单条操作失败, 并不会影响其他操作
返回结果包括 每一条操作执行的结果mget 批量读取
1
GET /_mget
msearch 批量查询
1
POST /_msearch
常见错误返回
1 | 无法连接: 网络故障或集群挂了 |
注意: 批量操作一次请求不宜过多
倒排索引
1 | 数据引擎中的 重要数据结构 |
书本对比理解
1 | 书本开始: 有目录,书本内容的索引 - 正排索引 |
搜索引擎中
1 | 正排索引: 文档ID 到 文档内容和单词 的关联 |
一个例子:
1 | 左边正排索引, 三篇 es书 的文档 |
倒排索引的核心组成
- 单词词典( term dictionary ), 记录所有文档的单词, 记录单词 到 倒排列表 的关联关系
- 单词词典很大, 通过 B+树 或 哈希拉链法实现, 满足高性能的插入和查询
- 倒排列表 ( posting list ) , 记录了 单词对应的文档 的结合, 由倒排索引项组成
- 倒排索引项(posting)
- 文档ID
- 词频 TF - 该单词在文档中出现的次数, 用于相关性评分
- 位置(position) - 单词在文档中分词的位置。 用于 语句搜索
- 偏移(offset)-记录单词的开始结束位置, 实现高亮显示
- 倒排索引项(posting)
上面例子在 ES中的实现
- ES中 JSON文档每个字段都有自己的 倒排索引
- 可以指定 对某些字段不做索引
- 优点: 节省存储空间
- 缺点: 字段无法被搜索
通过 analyzer 进行分词
1 | analysis 文本分析: 把全文本转换为 一系列单词 (term / token)的过程, 也叫 分词 |
- 分词器是专门 处理分词的组件, analyzer 由三部分组成 (按顺序执行)
- character filters ( 针对原始文本处理, 例如去除html )
- Tokenizer ( 按照规则切分为 单词 )
- Token Filter ( 将切分的单词进行加工, 小写,删除 stopwords,增加同义词 )
ES内置分词器
- standard analyzer - 默认分词器,按词切分,小写处理
- simple - 按 非字母切分 (符号被过滤), 小写处理
- stop - 小写处理, 停止词过滤(the,a,is)
- whitespace - 按空格切分,不转小写
- keyword - 不分词,直接将输入当做输出
- patter - 正则表达式, 默认 \W+ (非字符分割)
- language - 提供 30多种常见语言的分词器
- customer - 自定义分词器
使用 _analyzer API
1 | GET /_analyze // 直接指定 analyzer 进行测试 对文本如何分词 |
1 | POST my_index/_analyze // 指定索引字段, 查看 该字段如何进行分词 |
1 | POST /_analyze // 自定义分词测试, 定义一个tokenizer,定义fileter, 组合后 是如何进行分词的 |
中文分词 的难点
1 | 中文句子,切分成一个个词 (而不是字) |
ICU Analyzer
1
2安装plugin: elasticsearch-plugin install analysis-icu
提供unicode支持,更好支持亚洲语言IK
1
支持自定义词库, 支持热更新分词字典
THULAC
1 | 清华大学自然语言处理 和 社会人文计算实验室的一套中文分词器 |
Search API
- URI search
- 在URL中使用查询参数
- Request Body search
- 使用 ES 提供的, 基于JSON格式的更加完备的 Query Domain Specific Language (DSL)
1 | 指定查询的索引 |
搜索的相关性
1 | 搜索是: 用户 和 搜索引擎 的对话 |
- Web 搜索
- page rank 算法 (不仅仅是内容,更重要的是内容的可信度)
- 电商搜索
- 扮演 销售的角色
- 提高用户购物体验,提升网站销售业绩,去库存
衡量相关性
学科 information Retrieval
1 | precision (查准率) - 尽可能返回较少的无关文档 |
URI search 详解
1 | 通过 URI query 实现搜索 |
指定字段 , 泛查询(会查询所有字段)
- q=title:2021 / q=2021
Term, Phrase ( 查询 beautiful mind )
- Term : 等效 OR
- Phrase:等效 AND, 还要求 前后顺序一致
分组 与 引号
- title:(beautiful AND mind)
- title=”beautiful mind”
布尔操作
- AND / OR / NOT 或者 && / || / ! (必须大写)
分组
+ 表示 must
- 表示 must_not
- title:(+matrix -reloaded)
范围查询
- 区间表示: []闭区间, {}开区间
- year:{2019 TO 2018}
算数符号
- year:>2010
通配符查询 ( 效率低,占用内存大,不建议使用)
- ? 代表一个字符, * 代表 0 或 多个字符
正则表达
- title:[bt]oy
模糊匹配 与 近似查询
Request Body 与 Query DSL 介绍
- 将查询语句通过 HTTP Request Body 发送给 ES
- Query DSL
1 | ``` |
类似于 数据库中的 schema 的定义, 作用:
定义索引中 字段的名称
定义字段的数据类型
字段,倒排索引的相关配置 ( Analyzed or Not Analyzed, Analyzer )
Mapping 会把 JSON文档 映射成 Lucene 所需要的扁平格式
一个Mapping属于一个索引的Type ( 7.0 开始一个索引一个type )
每个文档都属于 一个索引的type
一个type 有一个 Mapping的定义
7.0开始, 不需要在mapping定义中指定type信息
1 |
|
写入文档时: 如果索引不存在,自动创建索引
ES自动根据文档信息推算出字段的类型
有时候会推算不对,如地理信息 (类型不对,会导致一些功能出错)
1
2
3
4
5
- 能否更改mapping的字段类型
- 新增字段
dynamic 设为true, 一旦有新增字段的文档写入, mapping也同时被更新
dynamic 设为 false, mapping不会更新,新增字段的数据无法被索引,但信息会出现在 _source 中
dynamic 设置为 strict, 文档写入失败
1
2
3
- 已有字段, 不在支持修改字段定义
lucene 实现的倒排索引, 一旦生成,不允许修改
改变字段类型, 必须 Reindex API, 重建索引
1
2
3
4
5
6
7
- 原因
- 修改字段的数据类型, 导致已被索引的数据无法被搜索
- 增加新的字段不会有影响
## 显示Mapping的设置与常见参数介绍
定义一个Mapping
PUT movies
{
“mappings”:{
// 定义mapping
}
}
建议:
参考API手册, 纯手写
步骤:
创建一个临时的index, 写入一些样本数据
通过访问 mapping API 获得临时文件的动态 Mapping 定义
进行修改后,使用该配置创建真正的索引
删除临时索引
1 |
控制当前字段是否被索引: 默认为true, 如果要设置为不可被搜索,字段添加设置 “index”:false
null_value, 需要对null值实现搜索: 只有keyword类型支持设定 “Null_value”:”NULL”
_all 在版本7中被 copy_to 所替代: copy_to 将字段的数值拷贝到目标字段(如 firstName,lastName 两个字段拷贝到 fullName,实现全名称搜索), copy_to 目标字段不出现在_source 中
数组类型: ES不提供专门的数组类型。 但任何字段,都可以包含多个相同类型的数值
1 |
|
Excat values : 包括 数字 / 日期 / 具体一个字符串 (ES中的 keyword)(在索引时,不分词)
Full Text : 全文本,非结构化的文本树 (ES中的 text)
1 |
|
当ES自带的分词器 无法满足,可以自定义分词器。 通过自组合不同组件实现:
Character Filter :
在Tokenizer之前,对文本进行第一步处理。
可配置多个,会影响Tokenizer的 position 和 offset 信息
1. HTML strip - 去除 html 标签
2. Mapping - 字符串替换
3. Pattern replace - 正则匹配替换
Tokenizer :
将原始文本安装一定规则,切分为词 ( term or token )
ES内置的: whitspace / standard / uax_url_email / pattern / keyword / path hierarchy
可使用Java开发插件,实现自己的Tokenizer
Token Filter :
将Tokenizer 输出的单词( term ), 进行增加,修改,删除
ES自带的 Token Filters: lowercase(小写) / stop(停止词) / synonym(添加近义词)
1 |
创建索引时, 在 settings 中 自定义 analysis
1 |
|
当一个索引 被新创建时
应用ES默认的 settings 和 mappngs
应用 order 数值低的 Index Template 中的设定
应用 order 数值高的 Index Template 中的设定,之前设定被覆盖
应用 用户指定的 Settings 和 Mappings,并覆盖之前模板中的设定
1 |
|
Dynamic Template 定义在 某个索引的 Mapping 中
Template有一个 名称
匹配规则是 一个数组
为匹配到字段设置 Mapping
1 |
|
ES 除搜索外,提供 针对ES数据进行统计分析的功能
实时性高
Hadoop (T+1)
通过聚合,得到数据的概览,分析和总结全套的数据
高性能, 只需要一条语句,就可以从ES中得到分析结果 (无需客户端自己去实现分析逻辑)
1 |
kibana 可视化报表 - ES的聚合分析
1 |
|
搜索的相关性算分: 描述 文档 和 查询语句 匹配的程度。 ES会对每个匹配查询条件的结果进行算分 _score
打分的本质是 排序, 把最符合用户需求的文档排在前面。 ES5之前的默认算分采用 TF-IDF, 现在采用 BM25
1 |
|
TF(区块链)*IDF(区块链) + TF(的)*IDF(的) + TF(应用)*IDF(应用)
1
2
3
### BM25
和经典的TF-IDF, 当TF无限增加时, BM25算分会趋于一个数值
1 |
|
PUT /my_index
{
“settings”:{
“similarity”:{
“custom_similarity”:{
“type”:”BM25”,
“b”:0,
“k1”:2
}
}
}
}
K 默认值是 1.2, 数值越小, 饱和度越高
b 默认值是 0.75(取值范围0-1), 0代表禁止 Normalization
1 |
|
两字段竞争,不应 将分数简单叠加,应该找到单个最佳匹配的字段的评分
1 |
|
三种场景:
最佳字段(Best Fields): 当字段之间相互竞争又相互关联。 评分来自 最匹配字段
多数字段(Most Fields):
处理英文内容时,常见手段:
在主字段(采用户English Analyzer),抽取词干,加入同义词,以匹配更多的文档
相同的文本,加入子字段(采用Standard Analyzer), 以提供更加精确的匹配
其他字段作为匹配文档提高相关度的信号。匹配字段越多则越好
混合字段(cross Field)
对某些实体, 例如人名,地址等, 需要在多个字段中确定信息,单个字段只能作为整体的一部分。 希望在任何这些字段中找到尽可能多的词
1 |
POST my_index/_search
{
“query”: {
“multi_match”: {
“type”: “best_fields”,
“query”: “Quick pets”,
“fields”: [“title”,”body”],
“tie_breaker”: 0.2,
“minimum_should_match”: “20%”
}
}
}
1 |
跨字段搜索
无法使用 Operator
可以用 copy_to 解决,但是需要额外的存储空间
cross_fields 类型的 multi_match
支持 Operator
与 copy_to 相比, 优势: 在搜索时为单个字段提升权重
1 |
|
不同索引使用不同语言
同一索引中,不同字段使用不同语言
一个文档的一个字段内混合不同的语言
挑战:
词干提取
不正确的文档频率 - 英文为主的文字,德文算分高(稀有)
需要判断用户搜索时使用的语言,语言识别( compact language detector) - 根据语言查询不同索引
技巧:
归一化词元 / 单词词根抽取 / 同义词 / 拼写错误
1 |
|
- 查字典, (有的词就标示,复合词就找最长的。 不认识就分割成单字)
- 最小词数的分词理论 ( 一句话分词数量最少的词串, 二义性分割不行)
- 统计语言模型 ( 解决二义性问题, 将中文分词错误率降低。 概率问题: 动态规划+利用维特比算法 )
- 基于统计的机器学习算法 ( HMM,CRF,SVM,深度学习等算法, 基本思路是对汉字进行标注训练。。。)ES的查询语句 对 相关性算分 / 查询性能 都至关重要
1
2
3
4
5
## Search Template 和 Index Alias 查询
### search Template - 解耦程序 & 搜索DSL
在开发初期, 可以明确查询参数,但往往还不能最终定义查询的DSL的具体结构
通过 search Template 定义一个 Contract
各司其职,解耦
开发人员 / 搜索工程师 / 性能工程师
1 |
创建一个 search Template
POST _scripts/tmdb
{
“script”: {
“lang”: “mustache”,
“source”: {
“_source”: [
“title”,”overview”
],
“size”: 20,
“query”: {
“multi_match”: {
“query”: ““,
“fields”: [“title”,”overview”]
}
}
}
}
}
1 |
搜索工程师 维护 search Template
开发工程师 使用 Template 查询, 不用感知具体的查询语句
1 |
|
可以在查询结束后, 对每一个匹配的文档进行 一系列的重新算分, 根据新生成的分数进行排序
默认计算分值的函数:
Weight: 为每个文档设置一个简单而不被规范化的权重
Field Value Factor: 使用该数值修改 _score, 例如将 "热度","点赞数"作为算分的参考因素。
新的算分=老的算分*热度 (问题,热度数值差异很大 0和10000,结果分数差异过大)
使用 modifier 平滑曲线(使用各种 公式计算):如log1p, 则 新的算分 = 老的算分 * log(1+热度)
Random Score: 为每一个用户使用一个不同的 随机算分结果
(网站广告提高展现率, 让每个用户看到不同的随机排名,但是也希望同一个用户访问时,结果的相对顺序保持一致)
衰减函数: 以某个字段的值为标准,距离某个值越近,得分越高
Script Score: 自定义脚本完全控制所需逻辑
1 |
|