1.基本概念

image.png

  • Index索引

    • Document 文档
    • Type 类型
  • Node节点

    • Shard分片

1.1 文档(Document)

1.1.1 文档

  • Elasticsearch是面向文档的,文档是所有可搜索数据的最小单位。
  • 文档会被序列化成JSON格式,保存在Elasticsearch中。

    • JSON对象由字段构成。
    • 每个字段都有对应的字段类型(字符串、数值、布尔、日期、二进制、范围)
  • 每个文档都有一个Unique ID

    • 可以自己指定ID
    • 也可以由Elasticsearch自动生成。

1.1.2 JSON文档

  • 一篇文档类似于数据库表中的一条记录。
  • JSON文档格式灵活,不需要预先定义格式。

    • 字段的类型可以指定或者通过Elasticsearch自动推算。
    • 支持数组、嵌套。

1.1.3 文档元数据

{
  "_index" : "movies",
  "_type" : "_doc",
  "_id" : "8609",
  "_score" : 1.0,
  "_source" : {
    "year" : 1923,
    "title" : "Our Hospitality",
    "@version" : "1",
    "id" : "8609",
    "genre" : [
      "Comedy"
    ]
  }
}
  • _index:文档所属的索引名
  • _type:文档所属的类型名
  • _id: 文档唯一id
  • _score:文档相关性打分
  • _source:文档的原始JSON数据
  • _version:文档的版本信息

1.2 索引(Index)

1.2.1 索引

  • 索引是文档的容器,是一类文档的集合。

    • Index:体现了逻辑空间的概念。每个索引都有自己的Mapping定义,用于定义包含的文档的字段名和字段类型。
    • Shard:体现了物理空间的概念,索引中的数据分布在 Shard 上。
  • 索引的 MappingSetting

    • Mapping:定义文档字段的类型。
    • Setting:定义不同的数据分布。

1.2.2 索引的不同语义

  • 名词:一个Elasticsearch集群中,可以创建很多个不同的索引。
  • 动词:保存一个文档到Elasticsearh的过程也叫索引(indexing)

    • Elasticsearch创建倒排索引。

1.3 类型(Type)

  • 7.0之前,一个Index可以设置多个Types
  • 7.0开始,一个Index只能创建一个Type:_doc

    • 6.0开始,Type被Deprated。

1.4 REST API

image.png

  • GET /_cat/indices?v: 查看索引
  • GET /_cat/indices?v&health=green:查看状态为绿的索引
  • GET /_cat/indices?v&s=docs.count:desc:按照文档个数对索引进行排序

1.5 集群(Cluster)

1.5.1 分布式特性

  • 高可用性

    • 服务可用性:允许有节点停止服务
    • 数据可用性:部分节点丢失,不会丢失数据。
  • 可扩展性

    • 请求量提升/数据的不断增长(将数据分布到所有节点上)

1.5.2 集群

  • 不同的集群通过不同的名字来区分,默认为elasticsearch
  • 通过配置文件修改,或者在命令行指定-E cluster.name=demo进行设置
  • 一个集群可以有一个或者多个节点。

1.6 节点(Node)

1.6.1 节点

  • 节点是一个Elasticsearch实例

    • 本质上是一个java进程。
    • 一台机器上可以运行多个实例,但是生产环境建议一台机器只运行一个Elasticsearch实例。
  • 每个节点都有名字,通过配置文件可以设置。或者启动实例的时候通过参数 -E node.name=node1 指定。
  • 每一个节点启动之后,会分配一个UID,保存在 data 目录下

1.6.2 Master-eligible Node 和 Master Node

  • 每个节点启动后,默认就是一个Master-eligible节点。

    • 可以设置 node.master:false 禁止
  • Master-eligible可以参加选注流程,成为Master节点
  • 当第一个节点启动时候,它会将自己选举成Master节点。
  • 每个节点上都保存了集群的状态,只有Master节点才能修改集群的状态信息

    • 任意节点都能修改信息会导致数据的不一致性。
    • 集群状态,维护了一个集群中必要的信息

      • 所有的节点信息
      • 所有的索及和其相关 MappingSetting 信息
      • 分片的路由信息

1.6.3 Data Node & Coordinating Node

  • Data Node

    • 可以保存数据的节点,叫做Data Node。
    • 负责保存分片数据。
    • 在数据扩展上起到重要作用。
  • Coordinating Node

    • 负责接受Client的请求,将请求分发到合适的节点,最终把结果汇集到一起。
    • 每个节点 默认 都起到了Coordinating Node的作用。

1.6.4 配置节点类型

  • 开发环境,一个节点可以承担多种角色。
  • 生产环境,应该设置单一角色的节点。

    节点类型 配置参数 默认值
    master eligible node.master true
    data node.data true
    ingest node.ingest true
    coordinating only / 每个节点默认都是Coordinating Node
    machine learning node.ml true, 需要enable x-pack

1.7 分片(Shard)

1.7.1 Primary Shard & Replica Shard

  • 主分片(Primary Shard):

    • 用以解决数据水平扩展的问题。通过主分片可以将数据分布到集群内的所有节点上。
    • 一个主分片是一个运行的Lucene实例,是一个索引。
    • 主分片数在索引创建时指定,后续不允许修改,除非Reindex。
  • 副本分片 (Replica Shard):

    • 用以解决数据高可用的问题。
    • 副本分片是主分片的副本。
    • 副本分片数可以动态调整。

1.7.2 分片的设置

生产环境中分片的设置,需要提前做好容量规划。

  • 分片数设置过小

    • 导致无法增加节点实现水平扩展。
    • 单个分片的数据量太大,导致数据重新分配耗时
  • 分片数设置过大

    • 7.0开始,默认主分片设置成1,解决了over-sharding的问题。
    • 影响搜索结果的相关性打分,影响统计结果的准确性。
    • 单个节点上过多的分片,导致资源浪费,同时也会影响性能。

1.7.3 查看集群的健康状况

GET /_cluster/health

{
  "cluster_name" : "learn_es",
  "status" : "green",
  "timed_out" : false,
  "number_of_nodes" : 3,
  "number_of_data_nodes" : 3,
  "active_primary_shards" : 7,
  "active_shards" : 14,
  "relocating_shards" : 0,
  "initializing_shards" : 0,
  "unassigned_shards" : 0,
  "delayed_unassigned_shards" : 0,
  "number_of_pending_tasks" : 0,
  "number_of_in_flight_fetch" : 0,
  "task_max_waiting_in_queue_millis" : 0,
  "active_shards_percent_as_number" : 100.0
}
  • green : 主分片与副本分片都正常分配。
  • yellow : 主分片正常分配,有副本分片未能正常分配。
  • red : 有主分片未能分配。

2. 文档的CRUD

2.1 基本API

2.1.1 说明

四种基本操作:

  • Index:添加文档

    • POST <index>/_doc: 添加的文档id为系统自动生成。
    • PUT <index>/_doc/<_id>:如果该id的文档不存在则添加,存在则更新同时增加版本号(version字段)。
    • POST <index>/_create/<_id>:如果该id的文档已存在,则报错。
    • PUT <index>/_create/<_id>:如果该id的文档已存在,则报错。
  • Get:读取文档

    • GET <index>/_doc/<_id>:获取该id文档的元信息
    • GET <index>/_source/<_id>:获取该id文档元信息中的 _source 字段
    • HEAD <index>/_doc/<_id>:判断该id文档是否存在,存在返回200,不存在返回404
    • HEAD <index>/_source/<_id>:判断该id文档中的_source字段是否存在,存在返回200,不存在返回404
  • Update:更新文档

    • POST <index>/_update/<_id>:更新部分文档,body体中使用doc字段。
  • Delete:删除文档

    • DELETE /<index>/_doc/<_id>:删除该id的文档,如果文档不存在 什么都不做

2.1.2 Index API

  • 支持 自动生成 文档id和 指定 文档id。
  • 自动生成文档id。

    • 语法: POST <index>/_doc
    • demo:

      POST users/_doc
      {
        "user" : "Mike",
        "phone" : "15512345678"
      }
      
      -----------
      
      {
        "_index" : "users",
        "_type" : "_doc",
        "_id" : "RfXT_28B5V-KMglJX8bm",
        "_version" : 1,
        "result" : "created",
        "_shards" : {
          "total" : 2,
          "successful" : 2,
          "failed" : 0
        },
        "_seq_no" : 3,
        "_primary_term" : 1
      }
      
  • 指定文档id

    • 语法:PUT <index>/_doc/<_id> 或者 POST | PUT <index>/_create/<_id>
    • demo 1:PUT <index>/_doc/<_id>

      PUT users/_doc/1
      {
        "user" : "John",
        "phone" : "15812345678"
      }
      
      --------
      # 不存在该id的文档时,直接新增
      {
        "_index" : "users",
        "_type" : "_doc",
        "_id" : "1",
        "_version" : 1,
        "result" : "created",
        "_shards" : {
          "total" : 2,
          "successful" : 2,
          "failed" : 0
        },
        "_seq_no" : 4,
        "_primary_term" : 1
      }
      
      # 存在该id的文档时,替换文档(删除现有的,创建新的,version +1)
      {
        "_index" : "users",
        "_type" : "_doc",
        "_id" : "1",
        "_version" : 23,
        "result" : "updated",
        "_shards" : {
          "total" : 2,
          "successful" : 2,
          "failed" : 0
        },
        "_seq_no" : 26,
        "_primary_term" : 1
      }
    • demo 2: POST | PUT <index>/_create/<_id>

      POST users/_create/2
      {
        "user" : "Dave",
        "phone" : "15912345678"
      }
      
      ---------
      # 不存在该id的文档时,直接新增
      {
        "_index" : "users",
        "_type" : "_doc",
        "_id" : "2",
        "_version" : 1,
        "result" : "created",
        "_shards" : {
          "total" : 2,
          "successful" : 2,
          "failed" : 0
        },
        "_seq_no" : 27,
        "_primary_term" : 1
      }
      
      # 存在该id的文档时,version冲突,报错。
      {
        "error": {
          "root_cause": [
            {
              "type": "version_conflict_engine_exception",
              "reason": "[2]: version conflict, document already exists (current version [1])",
              "index_uuid": "mjgjxIROT72xLMHnYNiUxw",
              "shard": "0",
              "index": "users"
            }
          ],
          "type": "version_conflict_engine_exception",
          "reason": "[2]: version conflict, document already exists (current version [1])",
          "index_uuid": "mjgjxIROT72xLMHnYNiUxw",
          "shard": "0",
          "index": "users"
        },
        "status": 409
      }

2.1.3 Get API

根据id查找文档

  • 语法:GET <index>/_doc/<_id>
  • demo:

    GET users/_doc/2
    
    --------
    
    # 该id的文档存在,返回文档元信息
    {
      "_index" : "users",
      "_type" : "_doc",
      "_id" : "2",
      "_version" : 1,
      "_seq_no" : 27,
      "_primary_term" : 1,
      "found" : true,
      "_source" : {
        "user" : "Dave",
        "phone" : "15912345678"
      }
    }
    
    # 该id的文档不存在,返回找不到
    {
      "_index" : "users",
      "_type" : "_doc",
      "_id" : "2",
      "found" : false
    }

2.1.4 Update API

更新指定id的文档:

  • 语法:POST <index>/_update/<_id>
  • demo:更新部分文档

    POST users/_update/1
    {
      "doc": {
        "age":28
      }
    }
    
    --------
    # 该id的文档存在,且字段值有变动 则更新文档;如果文档存在,且字段值无变动,result为noop
    {
      "_index" : "users",
      "_type" : "_doc",
      "_id" : "1",
      "_version" : 27,
      "result" : "updated",
      "_shards" : {
        "total" : 2,
        "successful" : 2,
        "failed" : 0
      },
      "_seq_no" : 34,
      "_primary_term" : 1
    }
  • demo2:按照脚本更新文档

    # index the doc
    PUT users/_doc/2
    {
      "name" : "John",
      "counter" : 1
    }
    
    {
      "_index" : "users",
      "_type" : "_doc",
      "_id" : "2",
      "_version" : 6,
      "result" : "updated",
      "_shards" : {
        "total" : 2,
        "successful" : 2,
        "failed" : 0
      },
      "_seq_no" : 53,
      "_primary_term" : 2
    }
    
    --------
    
    # update the doc
    POST users/_update/2
    {
      "script": {
        "source": "ctx._source.counter += params.count",
        "params": {
          "count":2
        }
      }
    }
    
    {
      "_index" : "users",
      "_type" : "_doc",
      "_id" : "2",
      "_version" : 7,
      "result" : "updated",
      "_shards" : {
        "total" : 2,
        "successful" : 2,
        "failed" : 0
      },
      "_seq_no" : 54,
      "_primary_term" : 2
    }

2.1.5 Delete API

根据id删除文档

  • 语法:Delete <index>/_doc/<_id>
  • demo:

    DELETE users/_doc/2
    
    --------
    
    # 该id的文档存在,直接删除
    {
      "_index" : "users",
      "_type" : "_doc",
      "_id" : "2",
      "_version" : 2,
      "result" : "deleted",
      "_shards" : {
        "total" : 2,
        "successful" : 2,
        "failed" : 0
      },
      "_seq_no" : 31,
      "_primary_term" : 1
    }
    
    # 该id的文档不存在,什么都不做
    {
      "_index" : "users",
      "_type" : "_doc",
      "_id" : "2",
      "_version" : 3,
      "result" : "not_found",
      "_shards" : {
        "total" : 2,
        "successful" : 2,
        "failed" : 0
      },
      "_seq_no" : 32,
      "_primary_term" : 1
    }

2.2 其他API

2.2.1 Bulk API

  • 支持在一次API调用中,对不同的索引进行操作。
  • 操作中单条操作失败,并不会影响其他操作。
  • 返回结果包含了每一条操作的结果。
  • 支持四种类型操作。

    • Index
    • Create
    • Update
    • Delete
  • 语法;POST _bulk 或者 POST <index>/_bulk`

    • newline delimited JSON (NDJSON)结构

      action_and_meta_data\n
      optional_source\n
      action_and_meta\_data\n
      optional_source\n 
      .... 
      action_and_meta_data\n
      optional_source\n
  • demo:

    POST _bulk
    # index、create:下一行需要跟着source
    { "index" : { "_index" : "test", "_id" : "1" } }
    { "field1" : "value1" }
    { "create" : { "_index" : "test", "_id" : "2" } }
    { "field2" : "value2" }
    # update下一行需要跟着doc或者script
    { "update" : {"_id" : "1", "_index" : "test"} }
    { "doc" : {"field3" : "value3"} }
    # delete与标准delete API语法一样
    { "delete" : { "_index" : "test", "_id" : "2" } }

2.2.2 mget API

  • 批量读取文档
  • 语法:GET _mget 或者 GET <index>/_mget
  • demo:

    GET /_mget
    {
        "docs" : [
            {
                "_index" : "users",
                "_id" : "1"
            },
            {
                "_index" : "twitter",
                "_id" : "2"
            }
        ]
    }
    
    --------
    
    {
      "docs" : [
        {
          "_index" : "users",
          "_type" : "_doc",
          "_id" : "1",
          "_version" : 31,
          "_seq_no" : 38,
          "_primary_term" : 2,
          "found" : true,
          "_source" : {
            "user" : "abc",
            "class" : 8,
            "age" : 28,
            "gender" : "male",
            "field1" : "value1"
          }
        },
        {
          "_index" : "twitter",
          "_type" : null,
          "_id" : "2",
          "error" : {
            "root_cause" : [
              {
                "type" : "index_not_found_exception",
                "reason" : "no such index [twitter]",
                "resource.type" : "index_expression",
                "resource.id" : "twitter",
                "index_uuid" : "_na_",
                "index" : "twitter"
              }
            ],
            "type" : "index_not_found_exception",
            "reason" : "no such index [twitter]",
            "resource.type" : "index_expression",
            "resource.id" : "twitter",
            "index_uuid" : "_na_",
            "index" : "twitter"
          }
        }
      ]
    }
  • demo2:

    GET users/_mget
    {
      "docs": [
        {
          "_id" : "2"
        },
        {
          "_id" : "3"
        }
      ]
    }
    
    GET users/_mget
    {
      "ids" : ["2", "3"]
    }
    
    --------
    
    {
      "docs" : [
        {
          "_index" : "users",
          "_type" : "_doc",
          "_id" : "2",
          "_version" : 7,
          "_seq_no" : 54,
          "_primary_term" : 2,
          "found" : true,
          "_source" : {
            "name" : "John",
            "counter" : 3
          }
        },
        {
          "_index" : "users",
          "_type" : "_doc",
          "_id" : "3",
          "found" : false
        }
      ]
    }

3. 倒排索引

3.1 组成

倒排索引包含两个部分:

  • 单词词典(Term Dictionary):

    • 记录所有文档的单词,记录单词到倒排列表的关联关系
    • 一般比较大,通过B+树或者哈希拉链法实现,以满足高性能的插入与查询。
  • 倒排列表(Posting List):

    • 记录单词对应的文档结合,由倒排索引项组成。
    • 倒排索引项:

      • 文档Id
      • 词频TF:该单词在文档中出现的次数,用于相关性评分。
      • 位置(Position):单词在文档中分词的位置。用于语句搜索(phrase query)
      • 偏移(Offset):记录单词的开始结束位置,实现高亮显示。

3.2 示例

在以下文档中搜索Elasticsearch

  • 文档内容

    文档Id 文档内容
    1 Mastering Elasticsearch
    2 Elasticsearch Server
    3 Elasticsearch Essentials
  • 倒排列表

    文档Id 词频TF 位置 偏移
    1 1 1 <10,23>
    2 1 0 <0,13>
    3 1 0 <0,13>

3.3 Elasticsearch的倒排索引

  • Elasticsearch的JSON文档的每个字段,都有自己的倒排索引。
  • 可以指定对某些字段不做索引。

    • 优点:节省存储空间
    • 缺点:字段无法被搜索

4. 通过Analyzer进行分词

4.1 Analysis 与 Analyzer

  • Analysis:

    • 文本分析,是把全文本转换为一系列单词( term/ token )的过程,也叫分词。
    • 是通过Analyzer实现的,可以是内置分词器,也可以是定制分词器。
    • 除了在数据写入时转换词条,匹配Query语句时也需要用相同的分析器对查询语句进行分析。
  • Analyzer:

    • 分析器:一个分析器包括一个可选的字符过滤器、一个单个分词器、0个或多个分词过滤器。
    • Analyzer由三部分组成。

      • Character Filters:字符过滤器,使用字符过滤器转变字符。
      • Tokenizer:分词器,将文本切分为单个或者多个分词。
      • Token Filter:分词过滤器,使用分词过滤器转变每个分词(小写、停用词、同义词)

4.2 Elasticsearch内置分词器

  • Standard Analayzer:默认 分词器,按词切分,小写处理。
  • Simple Analayzer:按照非字母切分(符号被过滤),小写处理。
  • Stop Analayzer:小写处理,停用词过滤(the,a,is)
  • Whitespace Analyzer:按照空格切分,不转小写。
  • Keyword Analyzer:不分词,直接将输入当作输出。
  • Pattern Analayzer:正则表达式,默认W+(非字符分隔)
  • Langugae:提供30多种常见语言的分词器。
  • Customer Analyzer;自定义分词器

4.2.1 _analyze API

  • GET /_analyze
  • POST /_analyze
  • GET /<index>/_analyze
  • POST /<index>/_analyze
  • demo:

    POST _analyze
      {
        "analyzer": "standard",
        "text": ["share your experience with NoSql & big data technologies"]
      }

John
10 声望2 粉丝

好记性不如烂笔头。