elasticsearch 学习笔记02
Match Query
对字段作全文检索、常使用Api
image.png
查询流程
对查询语句分词、将其分为alfred 和 way 然后分别根据username的倒排索引进行匹配算分、然后再进行汇总得分、然后根据得分进行排序返回文档给客户端
相关性算分
相关性算分是指文档与查询语句间的相关度、英文relevance
通过倒排索引可以获取与查询语句相匹配的文档列表、那么如何将最符合用户查询需求的文档放在前列
相关性算分有几个重要的概念如下:
- Term Frequency TF词频、单词在该文档中出现的次数、词频越高、相关度越高
- Document Frequency DF 文档频率、即单词出现的文档数
- Inverse Document Frequency IDF 逆向文档频率、与文档频率相反、简单理解为1/DF、即单词出项的文档数越少、相关度越高
- Field-length norm 文档越短、相关性越高
相关性算分模型
主要有两个
- TF/IDF 模型
- BM25 模型 beast match 25(25指迭代了25次才计算方法)
Cluster Status
es集群相关的数据称为cluster status、主要记录如下信息:
- 节点信息、比如说节点的名称、连接地址
- 索引信息、比如说索引名称、配置等
- cluster status 存储在每一个节点上、master维护最新版本并同步给其他节点
Master 节点
- 可以修改cluster status 的节点称为master节点、一个集群只能有一个
- master节点是通过集群中所有节点选举产生的、可以被选举的节点成为master-eligible,通过配置node.master 默认为true 表示参与master选举
Coordinating 节点
- 处理请求的节点即为coordinating 节点、该节点为所有节点的默认角色、不能取消、路由请求到正确的节点处理、比如创建索引的请求到master节点
数据节点
存储数据的节点称为data 节点、默认节点都是data类型、相关配置node.data:true
分片
- 分片存储了部分数据、可以分布于任意节点上
- 分片在索引创建时指定且后续不允许再更改、默认为5个
- 分片有主分片和副本分片之分、以实现数据的高可用
- 副本分片的数据由主分片同步、可以有多个、从而提高读取的吞吐量
分片数的设定很重要、需要提前规划好
- 过小会导致后续无法通过增加节点实现水平扩容
- 过大会导致一个节点分布过多分片、造成资源浪费、同时也会影响查询性能
cluster health
- green 健康状态、指所有主副分片都正常分配
- yellow 指所有主分片都正常分配、但是有副本分片未正常分配
- red 有主分片未分配
分布式特性
es 集群由多个es实例组成
- 不同集群通过集群名字来区分、可以通过cluster.name进行修改、默认为elasticsearch
- 每个es实例本质上是一个jvm进程、且有自己的名字、通过node.name进行修改
故障转移
image.pngimage.png
image.png
image.png
文档分布式存储
文档最终会存储在分片上、那么document是如何存储到某一个分片上、选择这个分片的原因是啥
这个就涉及到文档到分片的映射算法
需要这么做的目的就是使文档均匀的分布在所有的分片上、以充分利用资源
es通过如下公式计算文档对应的分片
shard = hash(routing) % number_of_primary_shards
hash 算法可以保证可以将数据均匀地分散在分片中
routing是一个关键参数、默认是文档id、也可以自行指定
number_of_primary_shards 是主分片数
该算法与主分片数相关、这也是分片数一旦确定后便不能改变的原因
文档创建和读取流程
image.pngimage.png
脑裂问题
split-brain 是分布式系统中经典的网络问题
解决方案就是 仅在可选举master-eligible 节点数大于等于quorum 时才可以进行master选举
quorum = master-eligible 节点数/2 + 1 例如三个master-eligible 节点时、quorum 为2
通过设定discovery.zen.minimun_master_nodes 为quorum 避免脑裂
倒排索引的不可变更
倒排索引一旦生成、不能更改
好处如下
- 不用考虑并发写文件的问题、杜绝了锁机制带来的性能问题
- 由于文件不再更改、可以充分利用文件系统的缓存、只需载入一次、只要内存足够、对该文件的读取都会从内存读取、性能高
- 利于生成缓存数据
- 利于对文件进行压缩存储、节省磁盘和内存存储空间
文档搜索的实时性
但是倒排索引不可变带来的坏处就是写入新文档时、必须重新构建倒排索引文件、替换老文件后、新文档才能被检索、导致文档的实时性差
而这个的解决方法就是 新文档直接生成新的倒排索引文件、查询的时候同事查询所有的倒排文件、然后做结果汇总计算即可
image.pngLucene 便是采用这种解决方案、它构建的单个倒排索引称为 segment 合在一起称为Index 与ES的Index概念不同、ES中的一个Shard 对应一个Lucene
Lucene 会有一个专门的文件记录所有的segment 信息、称为commit point
segment 写入磁盘的过程依然很耗时、可以借助文件系统缓存的特性、先将segment在缓存中创建并开放查询进一步来提升实时性、该过程在es中称为refresh
在refresh之前文档会先存储在一个buffer中、refresh时将buffer中的所有文档清空并生成segment
es默认每一秒执行一次refresh、因此文档的实时性被提高到一秒、这也是es被称为近实时的原因
translog
如果在内存中的segment还没有写入到磁盘前发生了宕机、那么其中的文档就无法恢复了、如何解决这个问题
es引入translog 机制、写入文档到buffer时、同时将该操作写入到translog。
translog 文件会即时写入到磁盘(fsync)、6.x默认每个请求都会写入请求、就是每个请求都会马上写入到磁盘中持久化、 这个值可以通过 index.translog.*去修改 比如设置为5秒写一次、那么这样子就会有丢失 5 秒内的数据
es 启动时会检查translog 文件、并从中恢复数据
每个分片都有自己的translog文件
flush
flush 负责将内存中的segment写入磁盘、主要做如下工作
- 将还在内存中的translog写入到磁盘中(index.translog.* 这个参数是否是实时落盘)
- 将index buffer 清空、其中的文档生成一个新的segment、相当于一个refresh操作
- 执行fsync操作、将内存中的segment写入磁盘中、
- 更新commit point 并写入磁盘
- 删除translog 文件
refresh触发时机
- 间隔时间达到、通过index.settings.refresh_interval来设定、默认是 1秒
- index.bffer占满时、其大小通过indices.memory.index_buffer_size设置、默认为jvm heap的10% 所有shard共享
- flush 发生时也会发生refresh
flush发生的时机
- 间隔时间达到时、默认是30分钟、5.x之前可以通过index.transog.flush_threshold_period修改、之后就无法修改
- translog占满时、其大小可以通过index.translog.flush_threshold_size控制、默认是512mb 每个分片都有自己的translog
- segment合并的时候
删除和更新文档
segment一旦生成就不能更改、那么如果你要删除文档该如何操作
- Lucene专门维护一个.del的文件、记录所有已经删除的文档、注意.del上记录的是文档在Lucene内部的id
- 查询结果返回前会过滤掉.del中的所有文档
更新文档如何进行
- 首先删除文档、然后再创建新的文档
segment合并
随着segment的增多、由于一次性查询的segment数量增多、查询速度会变慢
es会定时在后台进行segment merge操作、减少segment的数量、小段合并成大、再合成更大的段、尚未flush的segment可以与已经flush的segment合并
合并的时候会触发 flush操作、也会删除对应的.del 文件