Elasticsearch核心技术与实战
2025-06-17 14:42:54 0 举报
AI智能生成
全面解析Elasticsearch原理与应用的权威指南。深入探讨了Elasticsearch的核心功能,如搜索、索引、分词器、映射和聚合,同时也包括数据建模、性能优化和分布式特性等高级主题。文件类型方面,便于读者在各种设备上阅读学习。修饰语上,该书可被视为详尽、实用以及指导性极强,适用于不同水平的技术人员,无论是新手入门还是经验丰富的开发者都能从中获益。
作者其他创作
大纲/内容
发展史
发展健康
1. Elastic Inc 开源软件、上市公司
2. Elasticsearch软件下载量巨大,超过3.5亿次
3. 10W+ 社区用户
4. 7200+ 的订阅用户, 分布在100+ 个国家
5. 云服务-Elastic, Amazon, Alibaba, 腾讯
起源
Lucene 基于Java语言开发的搜索引擎
创建于1999年, 2005年成为Apache顶级开源项目
Lucene具有高性能,易扩展的有点
Lucene 有一定的局限性, 只能基于Java语言, 学习复杂, 不支持水平扩展
2004年基于Lucene开发了Compass
2010年重写了Compass, 取名Elasticsearch, 支持RESTful API
分布式
集群规模可以从单个节点扩展到数百个节点
高可用、水平扩展: 服务和数据两个维度
支持不同的几点类型: 支持Hot & Warm架构
主要功能
海量数据的分布式存储和集群
近实时搜索,性能高: 架构化、全文、地理位置、自动完成
海量数据的聚合功能
Elastic家族
Logstash: 数据抓取工具,从不能数据源采集数据进行转化,并发数据发送到不同的存储中
Kibana: 可视化分析利器
BEATS: 轻量的数据采集器
X-Pack: 商业化套件, 智能化监控
安装上手
依赖于Java
先安装Java,设置环境变量
elasticsearch
安装方式
下载安装:https://www.elastic.co/downloads/elasticsearch
Docker安装(推荐)
安装插件: bin/elasticsearch-plugin install ...
分布式集群
执行多次启动命令,并且指定不同的名字,和相同的集群名即可
kibana
安装方式
下载安装:https://www.elastic.co/downloads/kibana
Docker安装(推荐)
Logstash
安装方式
下载安装: https://www.elastic.co/downloads/logstash
Docker安装(推荐)
ES入门
文档(Document)
可搜索数据的最小单位,类似数据库的一行数据
文档被序列化成json放在es服务器
每个文档都有一个唯一ID,可以主动指定这个ID,也可es自动生成
元数据(MetaData)
用于标记文档的相关信息
索引(Index)
文档的容器, 是一类文档的结合。Index体现逻辑空间,Shard体现了物理空间概念
索引的Mapping 定义文档字段的类型;Setting定义不同的数据分布
类型(Type)
7.0之前,一个Index可以设置多个Types,7.0以及以后,一个索引只能创建一个Type,被规定为: _doc
节点(Node)
一个ES实例,本质上就是一个Java进程,每个节点都是有自己的名字,每个节点都会分配一个UID,保存在data目录下
节点类型
Master-eligible/Master
Master-eligible 可以参加选主流程,成为Master节点,每个节点启动默认就是这种节点。
当第一个Master-eligible节点启动的时候,会把自己选举为Master节点
每个节点都保存了集群的状态,只有Master节点才能修改集群的状态信息
Master Node 的职责
处理创建,删除索引等请求、决定分片被分配到哪个节点上
维护并更新 Cluster State
Data Node
可以保存数据的节点,负责保存分片数据, 一个节点启动默认是数据节点,可以设置node.data: false禁止
Master Node 决定如何把分片分发到数据节点上。可以通过数据节点水平扩展
Coordinating Node
负责接受Client的请求,将请求分发到合适的节点,最终把结果汇集到一起
每个节点默认都起到了Coordinating Node 的职责
Hot & Warm Node
不同硬件配置的Data Node, 用来实现 Hot & Warm 架构,降低集群部署成本
Machine Learning Node
负责跑机器学习的Job, 用来异常检测
分片(Shard)
分类
主分片(Primary Shard)
用以解决数据水平扩展的问题,通过主分片可以将数据分布到集群内的所有节点上,一个分片上运行一个Lucene的实例, 在索引创建的时候指定,后续不能修改, 除非Reindex
副本分片(Replica Shard)
用以解决数据高可用的问题,是主分片的拷贝, 副本分片的数量可以动态调整,增加副本数量可以在一定程度上提高服务的可用性(读取的吞吐)
设定
分片数量设置过小,导致后续无法增加节点而实现水平扩容,单个分片数据量太大,导致数据重新分配耗时
分片数量设置过大,影响搜索结果的相关性打分, 影响统计结果的准确性。 7.0 默认主分片为1, 解决over-sharding的问题
文档操作
Create: POST index-name/_doc/id {data}
GET: GET index-name/_doc/id
Update: POST index-name/_doc/id {"doc":{data}}
Bulk API
支持在一次API调用中,对不同的索引进行操作,以及一个索引的多个操作
正排倒排索引
从ID到文档就表示正排索引; 倒排索引其实就是从文档的搜索到ID的过程
倒排索引的核心组成
单词词典: 记录所有的文档的单词, 记录倒排列表和单词之间的关联关系
倒排列表:记录单词对应的文档结合,由倒排索引项组成
ES的倒排索引
ES的JSON文档中的每个字段,都有自己的倒排索引,可以指定对某些字段不做索引
倒排索引采用Immutable Design, 一旦生成,不可更改,无需考虑并发写的问题,避免锁机制带来的性能问题
Analysis分词
把全文本转化为一系列单词、词组的过程
分词器
analyzer: standard
标准分词
组成
Character Filters 针对原始文本加工处理,例如去除html
Tokenizer 按照规则切分为单词
Token Filter 把切分的单词进行加工, 小写, 删除 stopwords, 增加同义词
Search API
URI Search
在URL中使用查询参数
q: 指定查询语句
df: 默认字段,不指定则查询所有字段
sort: 排序
from、size 用于分页
profile 可以查看查询是如何被执行的
Request Body Search
使用ES的提供的json格式的搜索方法
Match中的terms 之间是or的关系; Match Phrase 的 terms之间是and的关系,并且term之间的位置关系也影响搜索结果
Mapping
定义索引的字段名称,和数量类型, 倒排索引相关联信息
Mapping会把JSON文档映射成Lucene所需要的扁平格式
一个Mapping属于一个索引的Type, 每个文档都属于一个Type,一个Type有一个Mapping定义
字段类型
简单类型
Text, Date, Integer, Boolean, IPv4
复杂类型
对象的嵌套
特殊类型
geo_point / geo_shape / percolator
Dynamic Mapping
在写入文档的时候,如果索引不存在则自动创建
通过这个机制可以 自动推算出字段的数据类型
如果类型设置不正确的时候, 会导致一些功能无法正常使用
文档的dynamic 设置的值含义
true
文档,字段可以被索引;Mapping可以更新
false
文档可以被索引,字段不可被搜索; Mapping不能更新
strict
文档,字段不可被索引;Mapping不可更新
定义个Mapping
建议步骤
创建一个临时Index, 写入一些样本数据
通过方为Mapping API的方法获得给临时Index的动态Mapping的定义
修改后使用该配置创建自己的索引
删除临时Index
属性作用
index
当前字段是否可以被索引, 设置false时,该字段不会被索引,这样可以减少磁盘的存储开销
copy_to
_all 在7中被copy_to所替代,将字段的数据拷贝到目标字段, 目标字段不会出现在 _source中,在查询的时候使用
null_value
当字段为空值时,设置一个被搜索需指定的定值
多字段特性
增加一个keyword字段
不同语言使用不同的analyzer
pinyin字段的搜索
精确匹配 vs 全文搜索
精确匹配
数字、日期,具体的字符串,es中的keyword
全文
非结构化文本数据,需要进行分词
自定义分词
Character Filters
对文本进行处理, 例如增加替换字符, 职责替换等,可以配置多个Character Filters
Tokenizer
把原始文本按照一段的规则进行切分为词, 可以开发Java插件,实现自己的Tokenizer
Token Filters
将Tokenizer分词后的单词进行加工, 比如转化小写,添加一些近义词等
Template
Index Template
设定Mappings 和 Settings, 并按照一定的规则,自动匹配到新建的index上,可以设定多个索引模板,可以被merge在一起, 可以指定order值,控制merging的过程
Dynamic Template
根据ES识别的数据类型,结合字段名称,动态设定字段类型,所有字符串都设定吃keyword, 或者关闭keyword字段, is开头的都设置成boolean, long_开头都设置成long类型
聚合功能
针对ES的数据进行统计分析
集合分类
Bucket Aggregation
一些满足特定条件的文档集合
Metric Aggregation
一些数据运算,可以对文档字段进行统计分析
Pipeline Aggregation
对其他的聚合结果进行二次聚合
Matrix Aggregration
支持多个字段的操作并提供一个结果矩阵
常用的kibana命令
索引文档总数:GET index_name/_count
查看前10条文档: POST index_name/_search
查看Index: GET /_cat/indices/kibana*?v&s=index
查看状态为绿色的索引: GET /_cat/indices?v&health=green
集群健康状况: GET _cluster/health
查node信息
GET _cat/nodes
查看Mapping文件
GET index-name/_mapping
创建index Templates
PUT _template/temple_name
深入搜索
基于词项查询
Term
表达语义的最小单位,输入的内容不做分词,作为一个整体在倒排索引中精确查找词项
Term Query, Range Query, Exists Query, Prefix Query, Wildcard Query
基于全文的查询
Match Query, Match Phrase Query , Query String Query
索引和搜索时都会进行分词,查询字符串先传递到一个合适的分词器,然后生成一个供查询的词项列表
查询的时候,先对输入的内容进行分词,然后对每个词项逐个进行底层的查询,最终把查询的结果合并,并为每个文档生成一个算分
Text类型直接搜索会进行分词处理,使用 keyword 子字段查询不会进行分词
复合查询 Constant Score, 即便是对Keyword进行Term查询, 同样会进行算分, 可以将查询转化为Filtering, 取消相关性算分的环节, 以提升性能
结构化搜索
布尔,时间,日期,数字: 有精确的格式。可以进行逻辑操作,包括比较数字或是时间的范围,判断两个值得大小
结构化的文本可以做精确匹配或部分匹配。Term查询、Prefix前缀查询
结构化结果只有 是、否 两个值
相关性算分
概念
描述的就是一个文档和查询语句的匹配程度
打分的本质就是排序,需把最符合用户需求的文档排在前面
词频
Term Frequency
检索词在一片文档中出现的频率。检索词出现的次数除以文档的总字数
简单度量一个查询和结果的相关性: 可以把搜索的每个词的TF相加
Stop Word
类似 “的”会在文档中出现多次,但是对于相关性没有作用,不应考虑他们的TF
逆文档频率 IDF
检索词在所有文档中出现的频率,简单说: log(全部文档数/检索词在出现过的文档总数)
TF-IDF 本质上就是将TF求和变成了加权求和
BM25
ES5开始,默认算法修改成了BM25, 和 TF-IDF相比, 当TF无限增加时,BM25算分会趋于一个数值
Boosting Relevance
控制相关度的一种手段。 索引,字段或查询子条件
boost > 1时, 打分的相关度相对提升; 0<boost<1时,打分的权重相对性降低; boost < 0时, 贡献负分
Query Context & filter Context
支持多个文本的输入,对多个字段进行搜索
Query Context: 相关性算分; Filter Context: 不需要算分,可以利用Cache, 获得更好的性能
bool查询
一个或者多个查询字句的组合。总共有4中子句,其中两个影响算分
单字符串查询
Disjunction Max Query
多字段相互竞争,不应该简单的将分数叠加,而是找到单个最佳匹配的字段的评分
将任何与任一查询匹配的文档作为结果返回,采用字段上的最匹配的评分最终评分返回
tie_breaker: 获取最佳评分_score + (其他匹配语句的评分* tie_breaker) = 最终算分
Multi Match
场景
最佳字段(Best Fields)
字段之间有竞争,又相互关联。评分来自最匹配字段,默认类型
多数字段(Most Fields)
例如处理英文内容时:常见操作,抽取词干,加入同义词,以匹配更多的文档
混合字段(Cross Fields)
多个字段中确定信息,单个字段只能作为整体的一部分
多语言和中文分词检索
优化措施
归一化词元, 抽取词根, 包含同义词, 拼写错误
中文分词器
hanLp
词法分析,句法分析。面向生产环境的自然语言处理工具包
IK
支持字典热更新
Pinyin
中文的拼音,拼音首字母
Search Template
定义个Contract,通过特定的模板语言对查询的参数进行参数化
Index Alias
实现零停机运维,指定alias, 定义索引的别名,可以通过别名访问该索引
Function Score Query
可以在查询结束后,对每一个匹配的文档进行重新算分,然后进行排序
Weight
为每个文档设置一个简单而不被规范化的权重
Field Value Factor
使用该数值来修改_score
Random Score
为每个用户使用一个不同的随机算分结果
衰减函数
以某个字段的值为标准,距离某个值越近得分越高
Script Score
自定义脚本控制逻辑
自动补全
The Completion Suggester ,用户输入一个字符就需要即时发送请求查找匹配项
对性能要求高,采用不同的数据结构,并非倒排索引来完成, 把Analyze的数据编码成FST和索引一起存放, FST会被ES整个加载到内存中,速度比较快
FST只能用于前缀查找
ES的分布式
分布式特性
存储的水平扩容,支持PB级别的数据
提高系统的可用性,部分节点停止服务,整个集群的服务不受影响
不同的集群可以使用参数 cluster.name设定,默认是 elasticsearch
集群状态
集群状态信息
所有节点的信息
所有的索引和其相关的Mapping和Setting的信息
分配的路由信息
每个节点上都保存了集群的状态信息
只有Master节点才能修改集群状态信息,并负责同步给其他节点
选主过程
互相ping对方, Node Id 较低的会成为被选举的节点
其他节点会加入集群,一旦发现master丢失,则会重新选出新的master节点
脑裂问题
当出现网络问题,一个节点无法和其他节点建立连接,则最终可能会选出两个master节点,导致一个集群中维护了两个不同的cluster state, 网络回复语的时候,无法正确的恢复集群
避免脑裂问题
限定一个选举条件,设置 quorum(仲裁), 只有在Master eligible 节点数大于quorum 时, 才能进行选举, quorum = (master 节点数 / 2) + 1。 例如: 当有3个master eligible 时, 设置discovery.zen.minimum_master_nodes = 2, 可避免脑裂
7.0 开始,移除了上诉参数,让ES自己选择可以形成仲裁的节点
故障转移
当某个节点出现问题, 如果挂掉的是master,则先选出新的master, 把挂掉的节点上的分片分散到其他数据节点上
文档存储
文档到分片的映射算法
确保文档能均匀的分布在分片上, 充分利用硬件资源, 避免出现负载倾斜
潜在的算法
随机;维护文档到分片的映射关; 实时计算,通过文档的id取模运算
分片的生命周期
Lucene Index
在Lucene中,单个倒排索引文件被称为Segment, 是自包含的,不可变的, 多个Segment汇总在一起,称为Lucene的Index, 对应的就是ES中的Shard(分片)
当有新文档写入时, 会生成新的Segment, 查询时会查询所有的Segments,并对结果汇总, Lucene 中有个文件, 用来记录所有的Segments信息, 叫做 Commit Ponit
删除的文档信息, 被保存在“.del”文件中
Refresh
文档写入的时候,先把文档的内容先写入到Index Buffer中
把Index buffer 写入到Segment的过程叫做 Refresh, 不执行 fsync 操作
Refresh频率: 默认1s发生一次, 可以通过 index.refresh_interval设置。 Refresh后数据就可以被搜索到
如果系统有大量的数据写入, 那就会产生很多的Segment
Index Buffer被占满时, 会触发Refresh, 默认值是JVM的10%
Transaction Log
Segment写入磁盘的过程相对耗时, 借助于系统的缓存,Refresh时,先把Segment写入到缓存中以开放查询
为了保证数据不会丢失, 在Index文档的时候,同时写入Transaction Log, 默认写盘, 每个分片都有个 Transaction Log
在ES Refresh时, Index Buffer被清空, Transaction Log 不会被清空
Flush
调用 Refresh, Index Buffer 清空
调用 fsync, 将缓存中的Segments 写入磁盘
清空 Transaction Log
默认 30 分钟调用一次
Transaction Log 满(默认: 512M)
Merge
Segment很多,需要定期进行合并, 以减少Segments、删除已经被删除的文档
ES 和 Lucene会自动进行Merge操作;可以通过 POST index-name/_forcemerge 强制执行一次merge
分布式查询过程
Query
ES节点接受到查询请求之后,会以Coordinating节点的身份,随机在主副分片中选择查询分片进行查询, 被选择的节点执行查询操作并排序, 然后把From+Size排序后的文档Id和排序值给Coordinating节点
Fetch
Coordinating Node会把从其他节点返回的Id和排序值进行排序选取 From 到 From + Size 个文档 Id, 以multi get请求的方式 到相应的分片获取文档数据
潜在问题
深度分页性能问题,相关性算分不准确
排序
Doc Values
索引的时候创建, 和倒排索引一起创建, 保存在磁盘中, 会占用而外的磁盘空间, 降低索引的速度。 ES 2.x 之后的默认方式
Field Data
在搜索的时候指定, 保存在JVM Heap中, 不影响索引的速度, 不占用额外的磁盘空间, 文档过多的时候 ,动态创建的开销比较大, 占用过多的JVM Heap
分页查询
默认, 查询在Coordinating Node选择的分片上按照相关性算分排序,查询前10条的 id 和排序值; 然后通过Coordinating Node 按照排序聚合, 在把id分发到被选择的分片上就行查询,最终返回对应的Size数. 所以当页数越深,在分片上的查询文档的Id和排序值的数量就越多.
Search After
第一步需要指定搜索的sort, 并且需要保证唯一性(可考虑加入_id)
然后使用上一次查询结果返回的文档中的sort值进行查询
限制: 不能指定form, 只能往下翻页
Scroll API
第一次调用的时候指定scroll的存活时间, 基于这个请求创建一个快照,有新的数据写入以后, 无法被查到
每次查询之后, 可以私用上次返回结果中的中的scroll_id进行查询
处理并发读写
多个clinet同时更新某个文档,如果缺乏有效的并发控制,可能会出现数据缺失
ES的乐观并发控制
ES中的文档是不可变更的, 当更新一个文档时, 先把文档标记为删除, 同时增加一个全新的文档, 同时文档 version + 1, 早期的版本就可以使用 version进行版本的并发控制
内部版本控制
if_seq_no + if_primary_term, 目前ES的并发的方案
深入聚合分析
Bucket
按照一定的规则, 把文档分配到不同的桶中, 从而达到分类的目的
允许通过添加子聚合分析来进一步分析
Aggregation
属于search的一部分, 一般情况下, 建议把Size指定为0, 这样可以减少网络开销
单值分析, 只输出一个分析结果 min, max, avg, sum, cardinality (count distinct)
多值分析, 输出多个分析结果 stats, extended stats, pipeline
Pipeline
对聚合分析的结果再次聚合分析
聚合的作用范围
ES聚合分析的默认作为范围是query的查询结果集
同时ES还支持一下方式改变聚合的作用范围: Fiter, PostFilter, Blobal
数据建模
数据建模
创建数据模型的过程: 对真实世界进行抽象描述的一种工具和方法, 实现对现实世界的映射
三个过程: 概念模型-> 逻辑模型 -> 数据模型
功能需要 + 性能需求
数据关联关系
对象类型
嵌套对象
Nested数据类型: 允许对象数组中对象独立索引
使用nested和properties关键字, 将所有的actors索引到多个分割文档中
在内部, Nested文档会被保存在两个Lucene文档中, 在查询时做Join处理
父子关联关系
应用端关联
文档的父子关系
对象和Nested对下的具有一定的局限性: 每次更新, 需要重新索引整个对象(包括嵌套对象和根对象)
ES提供了类似关系型数据库中的Join的实现, 可以通过维护Parent/ Child 的关系, 从而分离出两个对象
重建索引
使用场景
索引的Mappings发生变更: 字段类型更改, 分词器和字典更新
索引的Settings发生变更: 索引的主分片数发生变化
集群内, 集群间做数据迁移
ES内置重建索引API
Update By Query: 在现有的索引上重建
Reindex: 在其他索引上重建索引
支持把一个文档拷贝到另一个索引, 只会创建不存在的索引, 文档如果存在会出现版本冲突
场景
修改索引的主分片数量
改变字段的Mapping中的字段类型
集群内数据迁移/ 跨技巧的数据迁移
Ingest Node
ES5.0 之后, 默认所有的节点都是Ingest Node, 具有预处理数据的能力, 可拦截Index/ Bulk API 的请求
对数据进行转换, 并重新返回给Index 或 Bulk API
Pipeline
对文档进行加工, 按照一定的顺序处理文档
Split Processor: 将给字段值分成一个数组
Remove/ Rename Processor : 移除一个重命名字段
Append: 为商品增加一个新的标签
Convert: 把商品的价格从字符串转换为float类型
Date/Json : 日期格式转换
Date Index Name Processor: 通过该处理器的文档, 分配到指定时间格式的索引中
Painless
对文档的字段加工处理
更新/删除字段, 处理数据聚合操作
Script Field: 对返回的字段提前进行计算
Function Score: 对文档的算分进行处理
在Ingest Pipeline中执行脚本
在Reindex API , Update By Query 时, 对数据进行处理
最佳实践
处理好关联关系
利用Denormalization , Nested, Child/Parent 设计不同场景都关联
避免过多的字段
过多字段不容易维护
Mapping信息保存在Cluster State中, 数据类型过大, 对集群性能会有影响
删除或者修改数据需要reindex
默认最大字段数: 1000, 可以设置
避免正则查询
性能比较差, 更应该避免的是开头的匹配符
避免空置影响查询聚合不准确
为索引的Mapping加入Meta信息
保护你的数据
数据安全的基本需求
身份认证: 鉴定用户是否合法
用户鉴权: 指定那个用户可以访问哪个索引
传输加密
日志审计
RBAC
定义个角色, 并分配一组权限, 包括索引级别的, 字段级别的, 集群级别的操作, 不同的权限分配给不同的用户
ES的内置用户和角色
elastic : super user
kibana: 帮助kibana连接ES
...
集群内部的安全通讯
通过TLS证书, 对集群的信息进行加密
集群外部的安全通讯
配置xpack下面的ssl证书相关的参数配置上
0 条评论
下一页