Elasticsearch内容汇总[持续更新]

一、Elasticsearch技术简介

Elastic本身也是一个分布式存储系统,如同其他分布式系统一样,我们经常关注的一些特性如下。

  • 数据可靠性:通过分片副本和事务日志机制保障数据安全
  • 服务可用性:在可用性和一致性的取舍方面,默认情况下Elastic更倾向于可用性,只要主分片可用即可执行写入操作
  • 一致性:弱一致性。只要主分片写成功,数据就可能被读取。因此读取操作在主分片和副本分片上可能会得到不同的结果
  • 原子性:索引的读写、别名更新是原子操作,不会出现中间状态。但Bulk不是原子操作。不能用来实现事务
  • 扩展性:主副本分片都可以承担读请求,分担系统负载

1.1 Elasticsearch与MySQL的关系

RDBMS Elasticsearch Table Index(Type) Row Document Column Field Schema Mapping SQL DSL

1.1.1 Mapping

索引结构

1.1.2 DSL

查询语句

1.1.3 倒排索引

正排索引: 文档ID -> 文档内容
倒排索引:文档内容 -> 文档ID

文档ID 文档内容 1 Mastering Elasticsearch 2 Elasticsearch Server 3 Elasticsearch Essentials Term Count DocumentId:Position Elasticsearch 3 1:1,2:0,3:0 Mastering 1 1:0 Server 1 2:1 Essentials 1 3:1

倒排索引的核心组成

  • 倒排索引包含两个部分
  • 单词词典(Term Dictionary),记录所有文档的单词,记录单词到倒排列表的关联关系
    • 单词词典一般比较大,可以通过B+树或哈希拉链法实现,以满足高性能的插入与查询
  • 倒排列表(Posting List)记录了单词对应的文档结合,由倒排索引项组成
    • 倒排索引项(Posting)
    • 文档ID
    • 词频TF – 该单词在文档中出现的次数,用于相关性评分
    • 位置(Position)- 单词在文档中分词的位置。用于语句搜索(phrase query)
    • 便宜(Offset)- 记录单词的开始结束位置,实现高亮显示

文档ID 文档内容 1 Mastering Elasticsearch 2 Elasticsearch Server 3 Elasticsearch Essentials

Posting List

DocId TF Position Offset 1 1 1

  • Elasticsearch的JSON文档中的每个字段,都有自己的倒排索引
  • 可以指定对某些字段不做索引
  • 优点:节省存储空间
  • 缺点:字段无法被搜索

1.1.4 Lucene字典数据结构FST

常见的词典数据结构:

名称 特点 排序列表Array/List 使用二分法查找,不平衡 HashMap/TreeMap 性能高,内存消耗大,几乎是原始数组的三倍 Skip List 跳跃表,可快速查找词语,在Lucene、Redis、HBase等均有实现。相对于TreeMap等结构,特别适合高并发场景 Trie 适合英文词典,如果系统中存在大量字符串且这些字符串基本没有公共前缀,则相应的trie树将非常消耗内存 Double Array Trie 适合做中文词典,内存占用小,很多分词工具均采用此算法 Ternary Search Tree 三叉树,每一个node有3个节点,兼具省空间和查询快的优点 Finite State Transducers(FST) 一种有限状态机,Luncene 4有开源实现,并大量使用

FST数据结构:
插入单词”cat”、”deep”、”do”、”dog”、”dogs”

Elasticsearch内容汇总[持续更新]

1.2 Elasticsearch基本概念

高可用高扩展的分布式搜索引擎——Elasticsearch

1.2.1 节点

节点即一个Elasticsearch的实例,本质上就是一个Java进程,一台机器上可以运行多个Elasticsearch进程,但是生产环境一般建议一台机器上只运行一个Elasticsearch实例

Master-eligible节点和Master节点

  • 每个节点启动后默认就是一个Master-eligible节点,该类型节点可以参加选主流程,成为Master节点。
  • 当第一个节点启动的时候,它会将自己选举成Master节点
  • 每个节点都保存了集群的状态,但是只有Master节点可以修改集群的状态信息(如所有节点的信息、所有的索引以及其相关的Mapping、Setting信息、分片路由信息等)

Data节点和Coordinating节点

  • Data节点
  • 可以保存数据的节点,叫做Data Node。负责保存分片数据。在数据扩展上起到了至关重要的作用。
  • Coordinating节点
  • 负责接收Client的请求,将请求分发到合适的节点,最终把结果汇集到一起
  • 每个节点默认都起到了Coordinating Node的职责
  • Ingest 节点
  • 数据前置处理转换节点,支持pipeline管道设置
  • 可以使用ingest节点对数据进行过滤、转换等操作
  • 每个节点默认都起到了该职责,即在文档进入索引前做预处理

Hot节点和Warm节点

不同硬件配置的Data Node,用来实现Hot & Warm 架构,降低集群部署成本

分片

又称为主分片,用以解决数据水平扩展问题。通过主分片,可以将数据分布到集群内的所有节点之上。

  • 一个分片是一个运行Lucene的实例
  • 主分片数在索引创建时指定,后续不允许修改,除非Reindex

副本

用以解决数据高可用问题,是主分片的拷贝。

  • 副本分片数可以动态调整
  • 增加副本数,可以在一定程度上提高服务的可用性(读取的吞吐)

Elasticsearch内容汇总[持续更新]

1.2.2 水平扩展

Elasticsearch内容汇总[持续更新]

1.2.3 写入流程

Elasticsearch内容汇总[持续更新]
(1)客户端向NODE1发送写请求。
(2)NODE1使用文档ID来确定文档属于分片0,通过集群状态中的内容路由表信息获知分片0的主分片位于NODE3,因此请求被转发到NODE3上。
(3)NODE3上的主分片执行写操作。如果写入成功,则它将请求并行转发到 NODE1和NODE2的副分片上,等待返回结果。当所有的副分片都报告成功,NODE3将向协调节点报告成功,协调节点再向客户端报告成功。

在客户端收到成功响应时,意味着写操作已经在主分片和所有副分片都执行完成。

Elasticsearch内容汇总[持续更新]
Elasticsearch内容汇总[持续更新]

1.2.4 查询流程

Elasticsearch内容汇总[持续更新]

(1)客户端向NODE1发送读请求。
(2)NODE1使用文档ID来确定文档属于分片0,通过集群状态中的内容路由表信息获知分片0有三个副本数据,位于所有的三个节点中,此时它可以将请求发送到任意节点,这里它将请求转发到NODE2。
(3)NODE2将文档返回给 NODE1,NODE1将文档返回给客户端。

1.2.5 搜索流程

Elasticsearch内容汇总[持续更新]
(1)客户端发送search请求到NODE 3。
(2)NODE 3将查询请求转发到索引的每个主分片或副分片中。
(3)每个分片在本地执行查询,并使用本地的Term/Document Frequency信息进行打分,添加结果到大小为from + size的本地有序优先队列中。
(4)每个分片返回各自优先队列中所有文档的ID和排序值给协调节点,协调节点合并这些值到自己的优先队列中,产生一个全局排序后的列表。

1.2.6 动态索引

Elasticsearch内容汇总[持续更新]
  • 新增加字段
  • Dynamic 设置为true时,一旦有新增字段的文档写入,Mapping也同时被更新
  • Dynamic 设置为false,Mapping不会被更新,新增字段的数据无法被索引,但是信息会出现在_source中
  • Dynamic 设置为Strict,文档写入失败
  • 对已有字段,一旦已经有数据写入,就不再支持修改字段定义
  • Lucene实现的倒排索引,一旦生成后,就不允许修改
  • 如果希望改变字段类型,使用Reindex API,重建索引
  • 因为如果修改了字段的数据类型,会导致已被索引的数据无法被搜索
  • 如果是增加新的字段,就不会有这样的影响

1.2.7 数据建模

1.2.7.1 Elasticsearch中处理关联关系

  • 对象类型
  • 嵌套对象(Nested Object)
  • 父子关联关系(Parent / Child)
  • 应用端关联
对象类型

因为Elasticsearch会把JSON打平(扁平式键值对结构),所以能够搜索到名称为”John”,年龄为31的文档,因为这些数据都能够被搜索到。(对象之间没有界限)

Elasticsearch内容汇总[持续更新]
Nested Data Type
  • Nested数据类型:允许对象数组中的对象被独立索引
  • 使用nested和properties关键字,将所有actors索引到多个分隔的文档
  • 在内部,Nested文档会被保存在两个Lucene文档中,在查询时做Join处理

如下对对象设置了”nested”类型,则不再能够搜索到”不正确的数据”了。

Elasticsearch内容汇总[持续更新]
父子关联关系
  • 对象和Nested对象的局限性
  • 每次更新,需要重新索引整个对象(包括根对象和嵌套对象)
  • ES提供了类似关系型数据库中Join的实现。使用Join数据类型实现,可以通过维护Parent/Child的关系,从而分离两个对象
  • 父文档和子文档是两个独立的文档
  • 更新父文档无需重新索引子文档。子文档被添加,更新或者删除也不会影响到父文档和其他的子文档
  • 父文档和子文档必须在相同的分片上 -> 确保查询join的性能
  • 当指定子文档的时候,必须指定它的父文档Id -> 使用route参数保证分配到相同分片上

Elasticsearch内容汇总[持续更新]

Nested Object Parent / Child 优点 文档存储在一起,读取性能高 父子文档可以独立更新 缺点 更新嵌套的子文档时,需要更新整个文档 需要额外的内存维护关系。读取性能相对差 适用场景 子文档偶尔更新,以查询为主 子文档更新频繁

2.4 优化手段

2.4.1 深度分页

2.4.1.1 FROM+SIZE

这种分页方式,当FROM+SIZE > 10000的时候,Elasticsearch会报错,因为这里它有个默认分页窗口设置(当然也可以修改,一般不建议修改)。

  • ES天生就是分布式的。查询信息的时候需要从多个分片(多台机器)上拉取数据,并且ES天生就需要满足排序的需要(按照相关性算分)
  • 当一个查询: From = 990, Size = 10
  • 会在每个分片上都获取1000个文档。然后,通过Coordinating Node聚合所有结果。最后再通过排序选取前1000个文档
  • 页数越深,占用内存越多。为了避免深度分页带来的内存开销。ES有一个设定,默认限定到10000个文档。
    • Index.max.result.window

特别注意:如果你的查询没有指定from,size的话ES默认会限制为from,size=0,10。
提示:from是指偏移量,不是第几页,与MySQL的limit后的两个参数一样。(我就脑瓜子疼了很久,刚开始一直把from当页码。。)

2.4.1.2 SearchAfter

  • 避免深度分页的性能问题,可以实时获取下一页文档信息
  • 不支持指定偏移量
  • 只能继续向后偏移翻页
  • 第一步搜索需要指定sort,并且保证值是唯一的(可以通过加入_id保证唯一性)
  • 然后使用上一次查询的结果集中,最后一个文档的sort值继续进行查询
    Elasticsearch内容汇总[持续更新]
    关键点:根据提供的排序属性排序后的sort值为依据,向后继续翻页。类似于MySQL中,LIMIT 10000,30。我拿到了第9999条数据的id值,然后SELECT * FROM a WHERE id > 9999 LIMIT 30。
    特别注意:SearchAfter这种特性,很显然不支持跳页,但是它也是能够实时向后翻页的,而接下来介绍的Scoll翻页方式就不支持实时。

2.4.1.3 ScollAPI

  • 创建一个快照,有新的数据写入以后,无法被查到
  • 每次查询后,输入上一次的Scoll Id

Elasticsearch内容汇总[持续更新]
我理解和SearchAfter类似,一个是通过传递上一次的排序值,一个是通过传递上一次的Scoll值。不同的是,SearchAfter是实时的,而Scoll方式对翻页过程中有数据变更是无感知的。

分页总结

一般不建议深度分页,尽可能让业务增加时间范围,减少搜索范围,或者说直接使用另外两种,滚动分页即可。需要注意的是后两者对数据变化的感知是不一样的,具体需要根据场景来选择分页方式。
思考:Scoll是快照,即那一瞬间的快照,所以翻页是在快照中自己玩,对数据的变化无感知了,那么如果数据量很大,会不会把内存玩脱。

代码片段

PUT nested_index
{
  "mappings": {
    "properties": {
      "actors" : {
        "type" : "nested",
        "properties": {
          "first_name" : {"type" : "keyword"},
          "last_name" : {"type" : "keyword"}
        }
      },
      "title": {
        "type" : "text",
        "fields" : {"keyword": {"type" : "keyword","ignore_above":256}}
      }
    }
  }
}
PUT nested_index/_doc/1
{
  "title": "Speed",
  "actors": [
    {"first_name": "Keanu","last_name": "Reeves"},
    {"first_name": "Dennis","last_name": "Hopper"}
  ]
}
POST nested_index/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "title": "Speed"
          }
        },
        {
          "nested": {
            "path": "actors",
            "query": {
              "bool": {
                "must": [
                  {"match": {"actors.first_name": "Keanu"}},
                  {"match": {"actors.last_name": "Hopper"}}
                ]
              }
            }
          }
        }
      ]
    }
  }
}

#指定父子关系,父亲为博客“blog”,子为评论“comment”
PUT parent_child_index
{
  "mappings": {
    "properties": {
      "blog_comments_relation": {
        "type" : "join",
        "relations": { "blog": "comment"}
      },
      "content": {"type":"text"},
      "title":{"type": "keyword"}
    }
  }
}
#索引父文档,确认自身身份为“blog”->父文档
PUT parent_child_index/_doc/blog1
{
  "title" : "Learning Elasticsearch",
  "content": "hello Elasticsearch",
  "blog_comments_relation": {"name":"blog"}
}
#索引子文档,引用父文档。
PUT parent_child_index/_doc/comment1?routing=blog1
{
  "comment": "I am learning ELK",
  "username": "Jack",
  "blog_comments_relation": {"name":"comment","parent":"blog1"}
}

PUT parent_child_index/_doc/blog2
{
  "title" : "Learning Elasticsearch",
  "content": "hello Elasticsearch",
  "blog_comments_relation": {"name":"blog"}
}
PUT parent_child_index/_doc/comment2?routing=blog2
{
  "comment": "I am learning ELK too",
  "username": "Bob",
  "blog_comments_relation": {"name":"comment","parent":"blog2"}
}

#查询所有文档
POST parent_child_index/_search
{}

#根据Parent Id查询
POST parent_child_index/_search
{
  "query": {
    "parent_id":{
      "type": "comment",
      "id": "blog2"
    }
  }
}
Has Child查询,返回父文档
POST parent_child_index/_search
{
  "query": {
    "has_child": {
      "type": "comment",
      "query": {
        "match": {
          "username": "Jack"
        }
      }
    }
  }
}
Has Parent查询,返回相关子文档
POST parent_child_index/_search
{
  "query": {
    "has_parent": {
      "parent_type": "blog",
      "query": {
        "match": {
          "title": "Learning Elasticsearch"
        }
      }
    }
  }
}

Original: https://www.cnblogs.com/deepSleeping/p/14984771.html
Author: DeepSleeping丶
Title: Elasticsearch内容汇总[持续更新]

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/574535/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • Web前端面试题整合,持续更新【可以收藏】

    万能居中 1.margin: 0 auto;水平 2.text-align: center;水平 3.行高,垂直 4.表格,center,middle;水平垂直 5.display…

    Java 2023年6月5日
    085
  • 并发编程AQS源码分析

    AQS的全称为(AbstractQueuedSynchronizer),这个类在java.util.concurrent.locks包下面。它是一个Java提高的底层同步工具类,比…

    Java 2023年6月16日
    092
  • kafka从入门到了解

    kafka从入门到了解 一、什么是kafka Apache Kafka是Apache软件基金会的开源的流处理平台,该平台提供了消息的订阅与发布的消息队列,一般用作系统间解耦、异步通…

    Java 2023年6月13日
    097
  • FastAPI+pydantic+SQLAlchemy返回多个数据

    问题 比如在ORM中查询 all, 那么使用 pydantic.from_orm获取对应的数据呢? 解决思路 只需要, 将一个字段对应为 List, 元素为对应 SQLAlchem…

    Java 2023年6月7日
    071
  • JS 模块化-03 AMD 规范与 Require JS

    1 AMD 规范介绍 AMD 规范,全称 Asynchronous Module Definition,异步模块定义,模块之间的依赖可以被异步加载。 AMD 规范由 Common …

    Java 2023年6月16日
    092
  • 经典实验–飞机大战小游戏

    ·一、需求设计 1.为检测C语言的学习成果,根据所学的C语言知识,设计程序:飞机大战小游戏; 2.自行定义变量,函数或结构体,编写源代码并进行编译运行测试; 3.根据编写的代码,自…

    Java 2023年6月15日
    060
  • Docker 完整版教程

    Docker 安装 一、安装前必读 在安装 Docker 之前,先说一下配置,我这里是Centos7 Linux 内核:官方建议 3.10 以上,3.8以上貌似也可。 注意:本文的…

    Java 2023年6月16日
    075
  • 1_day01_java入门

    java入门 学习目标: 1.熟悉计算机编程语言 2.熟练掌握java特点 3.熟练配置java开发环境 4.熟练编写入门程序 5.熟练编写注释信息 一、计算机语言 1.1 什么是…

    Java 2023年6月8日
    068
  • springboot中@Configuration的用法

    一、背景在spring框架中,会有大量 的xml配置文件,或者需要做很多繁琐的配置。 从spring3开始,spring就支持了两种bean的配置方式, 一种是基于xml文件方式、…

    Java 2023年5月30日
    082
  • Spring Security定义多个过滤器链(10)

    在Spring Security中可以同时存在多个过滤器链,一个WebSecurityConfigurerAdapter的实例就可以配置一条过滤器链。 我们来看如下一个案例: @C…

    Java 2023年6月13日
    083
  • SpringCloudAlibaba项目之Nacos搭建及服务注册

    SpringCloudAlibaba随笔目录 一、SpringCloudAlibaba项目之父工程搭建 二、 SpringCloudAlibaba项目之Nacos搭建及服务注册 三…

    Java 2023年6月5日
    073
  • CPU密集型和IO密集型(判断最大核心线程的最大线程数)

    CPU密集型和IO密集型(判断最大核心线程的最大线程数) CPU密集型 1.CPU密集型获取电脑CPU的最大核数,几核,最大线程数就是几 Runtime.getRuntime()….

    Java 2023年6月5日
    081
  • Maven基础

    核心思想:约定大于配置其实约定大于配置这一思想在开发中经常出现当程序中的某一个或者某一些参数没有配置的时候,程序会自动设定一个默认值。这个默认值的设定,就称之为约定。意义在于:减少…

    Java 2023年6月7日
    093
  • CocosCreator中worldMatrix到底是什么(中)

    Cocos Creator 中 _worldMatrix 到底是什么(中) 1. 中篇摘要 在上篇中主要做了三件事 简单表述了矩阵的基本知识,以及需要涉及到的三角函数知识 推导了图…

    Java 2023年6月13日
    084
  • 设计模式(单例模式)

    单例模式:保证类在内存中只有一个对象。 如何保证类在内存中只有一个对象呢? A:把构造方法私有 B:在成员位置自己创建一个对象—-私有化并静态化 C:通过一个公共的方法…

    Java 2023年6月5日
    070
  • CentOS7下安装ELK(nginx 、elasticsearch-5.1.1、logstash-5.1.1、kibana-5.1.1)

    nginx: csharp;gutter:true;</p> <h1>直接yum安装:</h1> <p>[root@elk-node…

    Java 2023年5月30日
    065
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球