Elasticsearch入门基本概念以及基本的数据操作教程版

  • 8

Elasticsearch入门基本概念以及基本的数据操作

本篇文章记录Elasticsearch一些基本概念与数据操作方式。

Elasticsearch概念比较多,从开发人员角度来说有文档(Document)、索引(Index)、类型(Type)等逻辑概念,从运维角度来说有节点、集群、分片、副本等物理概念。

es逻辑上有关概念

先来聊聊开发人员比较关注的逻辑性概念,文档、索引、类型等。

什么是Elasticsearch?

Elasticsearch 是一个分布式的开源搜索和分析引擎,适用于所有类型的数据,包括文本、数字、地理空间、结构化和非结构化数据。Elasticsearch 在 Apache Lucene 的基础上开发而成,由 Elasticsearch N.V.(即现在的 Elastic)于 2010 年首次发布。Elasticsearch 以其简单的 REST 风格 API、分布式特性、速度和可扩展性而闻名,是 Elastic Stack 的核心组件;Elastic Stack 是适用于数据采集、充实、存储、分析和可视化的一组开源工具。人们通常将 Elastic Stack 称为 ELK Stack(代指 Elasticsearch、Logstash 和 Kibana),目前 Elastic Stack 包括一系列丰富的轻量型数据采集代理,这些代理统称为 Beats,可用来向 Elasticsearch 发送数据。

文档(Document)

Elasticsearch搜索是面向文档的,官方给的定义是:在大多数应用中,多数实体或对象可以被序列化为包含键值对的 JSON 对象。键可以为一个字段名,值可以为一个布尔值或者一个数字或者字符串等,类似于我们平时写接口最后返回的一串JSON数据。相较于关系型数据库中的行概念。

如:
一本书籍的基本信息,包括书籍名称、书籍作者、书籍价格、书籍简介、书籍出版社等等。

{
    "bookName":"深入理解Java虚拟机",
    "bookAuthor":"周志明",
    "bookPrice":"20.00RMB",
    "bookJian":"深入理解Java虚拟机底层实现原理",
    "bookPress":[
        "中华书局",
        "电子工业出版社",
        "吉林工业出版社"
    ]
}

上面的JSON字符串就可以表示为一个文档。文档是所有可搜索数据的最小单元,文档会被序列化成为JSON格式保存在Elasticsearch中,每一个文档都有一个unique ID,可以自己指定或者es自动生成。

文档元数据

一个文档不仅仅只包含数据,它还包含 元数据 也就是有关文档的信息。需要特别关注的三个:_index、_type、_id

  • _index: 该文档在哪里存放,也就是说它属于哪个索引。
  • _type: 文档表示的对象类别,也就是说它属于索引中的哪个类型,一个索引中可能存在很多不同的产品类别,使用_type可以有效区别您所需要查找的类型。es6.0已移除_type。
  • _id: id是一个字符串,当它和_index、_type组合时可以唯一确定elasticsearch中的文档,id可以自己指定,也可以使用es自动生成的id。
  • _source: 文档的原始JSON数据,也就是我们定义的业务数据。比如上述书籍基本信息。
  • _all: 整合所有字段内容到该字段,7.0以后 已被移除。
  • _version: 文档的版本信息。
  • _score: 相关性打分,搜索时会有一个匹配打分,最高的分数会在前面。

索引(index)

简单来说索引就是文档的容器,一类相似文档的集合。

  1. index体现了逻辑空间的概念:每个索引都有自己的mapping定义。
  2. shard体现了物理空间的概念:每个索引中的数据分散在shard上
  3. 可以为索引设置mapping与settingsmapping:用于定义包含的文档的字段名和字段类型。

    settings: 定义数据的分布情况

通过例子来直观的感受一下索引(index)中的元素信息。

如下语句PUT是向es中增加一个文档,index->mengxi,type->_doc(6.0已将移除,所以这里设置es默认的_doc),2->id。json字符串表示文档的原数据。

PUT /mengxi/_doc/2
{
  "title":"elasticsearch",
  "oper":"true",
  "date":"2021-01-26"
}

通过GET 索引名称返回索引中所包含的信息。如下

GET /mengxi
//索引mengxi相对应的信息
{
  "mengxi" : {
    "aliases" : { },
    "mappings" : {
      "properties" : {
        "date" : {
          "type" : "date"
        },
        "oper" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "title" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        }
      }
    },
    "settings" : {
      "index" : {
        "creation_date" : "1611630544025",
        "number_of_shards" : "1",
        "number_of_replicas" : "1",
        "uuid" : "aJiuHLLcRyWSBZ3tb9FQjA",
        "version" : {
          "created" : "7060299"
        },
        "provided_name" : "mengxi"
      }
    }
  }
}

通过上面的index信息,可以看出mappings所对应的内容就是在PUT时指定的文档内容(json),不过将文档的具体信息展示出来了,包括字段的名称、字段的类型等。settings所对应的内容就是我们上面讲到的物理空间信息,包括创建日期、shard分片号、空间号、uuid、版本号等。

类型(Type)

类型上面已经提到了,每个类型下面会包含一系列的文档,es7.0之前一个索引(index)下可以设置多个Types,6.0开始Type已经被废弃掉。7.0开始,一个索引(index)只能创建一个type,就是_doc。

关系型数据库与Elasticsearch的对比

我们对关系型数据库都比较熟悉,在这里把关系型数据库与elasticsearch中的概念做一个不那么严谨的对比,有助于我们加深对elasticsearch相关概念的理解。

关系型数据库 Elasticsearch
Table Index(Type)
Row Document
Column Filed
Schema Mapping
SQL DSL

es物理上有关概念

说完逻辑概念,接下来我们聊聊有关运维物理上的概念,节点、集群、分片、副本等。

节点

elasticsearch是一个分布式存储引擎,可以实现高可用、可扩展等特性,必然会涉及到集群以及多节点实例存储相关内容。

  • 节点是一个elasticsearch的实例,本质上就是一个Java进程。
  • 每一个节点都是有名字的,通过配置文件配置或在启动的时候指定 -E node.name=node1
  • 每一个节点在启动后,都会分配一个UID,保存在data目录下

Master eligible 与 Master 节点

  • 每个节点启动后,默认就是一个Master eligible节点
  • Master eligible节点可以参加选主节点流程,成为Master节点
  • 当只有一个节点启动时,它将会把自己选为Master节点
  • 每个节点上都有集群的状态,只有master节点才能修改集群状态信息

Data Node 与 Coordinating Node

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

Coordinating Node

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

分片

主分片,解决数据水平扩展的问题,通过主分片,可以将数据分配到集群内的所有节点上去。

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

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

  • 副本分片数,可以动态调整
  • 增加副本的分片数,可以提高es的吞吐量
//查看集群健康状态
GET _cluster/health

集群健康状态信息

{
  "cluster_name" : "elasticsearch",
  "status" : "yellow",
  "timed_out" : false,
  "number_of_nodes" : 1,
  "number_of_data_nodes" : 1,
  "active_primary_shards" : 7,
  "active_shards" : 7,
  "relocating_shards" : 0,
  "initializing_shards" : 0,
  "unassigned_shards" : 1,
  "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" : 87.5
}

Elasticsearch数据基本操作

新增

提供三种API

  1. PUT /index/type/id 如果id不存在会创建新的文档,如果存在,先删除现有文档,再创建新的文档,然后追加版本号。注意:这里type统一设置为 _doc
//新增文档
PUT /mxblog/_doc/1
{
  "title":"mxblog",
  "context":"es study record",
  "date":"2021-01-27"
}
//返回结果
{
  "_index" : "mxblog",
  "_type" : "_doc",
  "_id" : "1",
  "_version" : 1,
  "result" : "created",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 0,
  "_primary_term" : 1
}

如上,新增mxblog索引以及一个id为1的文档,最终返回的result为 created

//再次执行PUT操作
PUT /mxblog/_doc/1
{
  "title":"mxblog",
  "context":"es study record",
  "date":"2021-01-27"
}
//返回结果
{
  "_index" : "mxblog",
  "_type" : "_doc",
  "_id" : "1",
  "_version" : 2,
  "result" : "updated",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 1,
  "_primary_term" : 1
}

如上,再次执行PUT操作,返回的结果为updated,并且版本号_version 追加为2

  1. PUT /mxblog/_create/1 使用create新增是,如果ID存在则会出错。
//执行 PUT /mxblog/_create/1
PUT /mxblog/_create/1
{
  "title":"mxblog",
  "context":"es study record",
  "date":"2021-01-27"
}
//返回结果
{
  "error" : {
    "root_cause" : [
      {
        "type" : "version_conflict_engine_exception",
        "reason" : "[1]: version conflict, document already exists (current version [2])",
        "index_uuid" : "j14hgRe5Tra7eOroQobBbw",
        "shard" : "0",
        "index" : "mxblog"
      }
    ],
    "type" : "version_conflict_engine_exception",
    "reason" : "[1]: version conflict, document already exists (current version [2])",
    "index_uuid" : "j14hgRe5Tra7eOroQobBbw",
    "shard" : "0",
    "index" : "mxblog"
  },
  "status" : 409
}

如上,使用_create新增时,id为1的记录已经存在,返回的结果状态为409,出错原因为 文档已存在,当前版本号为2。

//执行 PUT /mxblog/_create/2
PUT /mxblog/_create/2
{
  "title":"mxblog",
  "context":"es study record one day",
  "date":"2021-01-27"
}
//返回结果
{
  "_index" : "mxblog",
  "_type" : "_doc",
  "_id" : "2",
  "_version" : 1,
  "result" : "created",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 2,
  "_primary_term" : 1
}

使用_create新增文档,成功返回。

  1. POST /mxblog/_doc/ 新增不指定ID,es会自动生成ID。
//执行 POST /mxblog/_doc/
POST /mxblog/_doc/
{
  "title":"mxblog",
  "context":"es study record two day",
  "date":"2021-01-27"
}
//返回结果
{
  "_index" : "mxblog",
  "_type" : "_doc",
  "_id" : "mKNWQ3cBVRucdjq3I4VM",
  "_version" : 1,
  "result" : "created",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 3,
  "_primary_term" : 1
}

如上执行 POST新增不指定ID时,es返回结果会随机指定一个ID字符串。

查询

使用 GET /index/type/id 可以查询对应的文档

//查询
GET /mxblog/_doc/1
//返回结果
{
  "_index" : "mxblog",
  "_type" : "_doc",
  "_id" : "1",
  "_version" : 2,
  "_seq_no" : 1,
  "_primary_term" : 1,
  "found" : true,
  "_source" : {
    "title" : "mxblog",
    "context" : "es study record",
    "date" : "2021-01-27"
  }
}

如上,查看found属性值为true,表示该文档成功读取。_source为文档原数据。

更新

使用 POST /index/_update/id { "doc":{"filed":"value"}} 对文档进行修改。

//执行更新操作
POST mxblog/_update/1/
{
      "doc":{
        "title":"liu meng xi"
      }
}
//返回结果
{
  "_index" : "mxblog",
  "_type" : "_doc",
  "_id" : "1",
  "_version" : 3,
  "_seq_no" : 4,
  "_primary_term" : 1,
  "found" : true,
  "_source" : {
    "title" : "liu meng xi",
    "context" : "es study record",
    "date" : "2021-01-27"
  }
}

如上执行update操作,将title修改为liu meng xi,最终修改完毕后,执行GET操作查看修改成功。

删除

使用 DELETE /index/type/id 将当前文档删除

//执行删除
DELETE /mxblog/_doc/1
//返回结果
{
  "_index" : "mxblog",
  "_type" : "_doc",
  "_id" : "1",
  "_version" : 5,
  "result" : "deleted",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 6,
  "_primary_term" : 1
}
//再次查看被删除的文档
GET /mxblog/_doc/1
//返回结果
{
  "_index" : "mxblog",
  "_type" : "_doc",
  "_id" : "1",
  "found" : false
}

如上执行DELETE,可以看到返回的resultdeleted,表示已删除。查看ID为1的文档,发现found字段为false

Bulk API

这里再介绍一个Bulk API操作,它支持在一次API调用中,对不同的索引进行操作,支持index、create、update、delete四种操作。操作中单条操作失败,并不影响其他操作,并且返回结果包含每一次操作。

//批量操作es api
POST _bulk
{"index":{"_index":"mxblog","_id":"1"}}
{"filed":"value"}
{"delete":{"_index":"mxblog","_id":"1"}}
{"create":{"_index":"mxblog","_id":"1"}}
{"filed1":"value1"}
{"update":{"_index":"mxblog","_id":"1"}}
{"doc":{"filed2":"value2"}}
//返回结果
{
  "took" : 1282,
  "errors" : false,
  "items" : [
    {
      "index" : {
        "_index" : "mxblog",
        "_type" : "_doc",
        "_id" : "1",
        "_version" : 1,
        "result" : "created",
        "_shards" : {
          "total" : 2,
          "successful" : 2,
          "failed" : 0
        },
        "_seq_no" : 0,
        "_primary_term" : 1,
        "status" : 201
      }
    },
    {
      "delete" : {
        "_index" : "mxblog",
        "_type" : "_doc",
        "_id" : "1",
        "_version" : 2,
        "result" : "deleted",
        "_shards" : {
          "total" : 2,
          "successful" : 2,
          "failed" : 0
        },
        "_seq_no" : 1,
        "_primary_term" : 1,
        "status" : 200
      }
    },
    {
      "create" : {
        "_index" : "mxblog",
        "_type" : "_doc",
        "_id" : "1",
        "_version" : 3,
        "result" : "created",
        "_shards" : {
          "total" : 2,
          "successful" : 2,
          "failed" : 0
        },
        "_seq_no" : 2,
        "_primary_term" : 1,
        "status" : 201
      }
    },
    {
      "update" : {
        "_index" : "mxblog",
        "_type" : "_doc",
        "_id" : "1",
        "_version" : 4,
        "result" : "updated",
        "_shards" : {
          "total" : 2,
          "successful" : 2,
          "failed" : 0
        },
        "_seq_no" : 3,
        "_primary_term" : 1,
        "status" : 200
      }
    }
  ]
}

如上结果,可以看出每一个api的调用都会返回对应的结果集。

批量获取数据

使用 GET _mget,POST /index/_msearch进行批量查询,减少网络开销,提高性能

//执行批量操作
GET /_mget
{
  "docs":[
    {"_index":"mengxi","_id":"1"},
    {"_index":"mengxi","_id":"2"}
    ]
}
//返回结果
{
  "docs" : [
    {
      "_index" : "mengxi",
      "_type" : "_doc",
      "_id" : "1",
      "_version" : 1,
      "_seq_no" : 0,
      "_primary_term" : 1,
      "found" : true,
      "_source" : {
        "name" : "mengxi",
        "domain" : "www.mxblog.com.cn"
      }
    },
    {
      "_index" : "mengxi",
      "_type" : "_doc",
      "_id" : "2",
      "_version" : 1,
      "_seq_no" : 1,
      "_primary_term" : 1,
      "found" : true,
      "_source" : {
        "name" : "mengxi",
        "domain" : "https://www.mxblog.com.cn"
      }
    }
  ]
}
//执行_msearch
POST /mengxi/_msearch
{}
{"query":{"match_all":{}},"size":2}
//返回结果
{
  "took" : 20,
  "responses" : [
    {
      "took" : 20,
      "timed_out" : false,
      "_shards" : {
        "total" : 1,
        "successful" : 1,
        "skipped" : 0,
        "failed" : 0
      },
      "hits" : {
        "total" : {
          "value" : 2,
          "relation" : "eq"
        },
        "max_score" : 1.0,
        "hits" : [
          {
            "_index" : "mengxi",
            "_type" : "_doc",
            "_id" : "1",
            "_score" : 1.0,
            "_source" : {
              "name" : "mengxi",
              "domain" : "www.mxblog.com.cn"
            }
          },
          {
            "_index" : "mengxi",
            "_type" : "_doc",
            "_id" : "2",
            "_score" : 1.0,
            "_source" : {
              "name" : "mengxi",
              "domain" : "https://www.mxblog.com.cn"
            }
          }
        ]
      },
      "status" : 200
    }
  ]
}

elasticserach api 操作返回码说明

结果 原因
无法连接 网络故障或集群挂了
连接无法关闭 网络故障或节点出错
429 集群繁忙
4xx 请求格式错误
500 集群内部错误