文档

  • Elasticsearch 是 面向文档 的,意味着它存储整个对象或 文档,文档是ElasticSearch 可搜索数据的最小单位。
    - 文档类似数据库的一条数据记录,有键值对
    - 文档可以是一条日志,一个电影的描述信息等
  • Elasticsearch 使用 JSON 作为文档的序列化格式,也就是说,文档数据会被 ElasticSearch 以 JSON 的方式存储。
    - JSON 对象由字段以及属性组成
    - JSON 对象的每个字段都有对应的字段类型(字符串 / 数值 / 布尔 / 日起 / 范围类型)
  • Elasticsearch 不仅存储文档,而且 索引 每个文档的内容,使之可以被检索。在 Elasticsearch 中,是对文档进行索引、检索、排序和过滤,而不是对行列数据,这也是 Elasticsearch 能支持复杂全文检索的原因。每个文档都有一个 Unique ID
    - Id 可以是你自己指定的,必须唯一
    - id也可以通过 ELasticSearch 自动生成
{
        "_index" : "movies",
        "_type" : "_doc",
        "_id" : "1468",
        "_score" : 1.0,
        "_source" : {
          "title" : "Booty Call",
          "@version" : "1",
          "id" : "1468",
          "year" : 1997,
          "genre" : [
            "Comedy",
            "Romance"
          ]
        }
      }

这就是 elastic的一个文档的格式,可以看到是按照JSON来存储的。

文档的元数据

文档是由一些元数据来描述的,元数据,用于描述的文档的一些基本信息,我们根据上边的这个文档结构来分析每个字段的元数据意义。

  • _index - - 文档所属的索引名
  • _type - - 文档所属的类型名,在7之前这个类型是可设置的,7之后这个类型默认都是_doc 并且不可以设置
  • _id - - 文档的唯一Id,可以通过这个去查找文档
  • _source - - 文档的原始数据,也就是我们存储的真正数据
  • version - - 文档的版本,通过这个做mvvc控制,解决并发的版本冲突
  • _score - - 相关性打分,可以用这个做排序
CRUD API

一下的所有Api 命令都是通过kibana的开发工具操作的,如果是 http 请求,那么将 es 的地址 拼在里边就可以

curl -XPOST "http://localhost:9210/users/_doc/1"
create 一个文档
  • 请求格式是 请求方法 + 索引 + 文档类型(7默认为 _doc) + 数据,一般这个请求会去自动创建索引如果索引不存在的话,并且会自动去生成mapping,这个可以通过参数来控制不允许自动创建索引
    - 通过 HTTP POST 创建文档并且自动生成 document ID
#create document. 自动生成 _id
POST users/_doc
{
	"user" : "Mike",
    "post_date" : "2019-04-15T14:12:12",
    "message" : "trying out Kibana"
}
result:
{
  "_index" : "users",
  "_type" : "_doc",
  "_id" : "cXg1JnQBubpWmoaWw7Pk",
  "_version" : 1,
  "result" : "created",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 0,
  "_primary_term" : 1
}
-  通过HTTP PUT 创建文档,需要制定当前操作是创建 _create , 必须指定 document ID,如果 ID 重复则报错
#create document. 指定 ID 如果已经存在,就报错
PUT users/_create/1
{
     "user" : "Jack",
    "post_date" : "2019-05-15T14:12:12",
    "message" : "trying out Elasticsearch"
}
重复创建
{
  "error": {
    "root_cause": [
      {
        "type": "version_conflict_engine_exception",
        "reason": "[1]: version conflict, document already exists (current version [1])",
        "index_uuid": "pHCTrajVRj-TfgxVTJ9oiA",
        "shard": "0",
        "index": "users"
      }
    ]
  },
  "status": 409
}
GET 获取一个文档
#Get the document by ID
GET users/_doc/1

result
{
  "_index" : "users",
  "_type" : "_doc",
  "_id" : "1",
  "_version" : 1,
  "_seq_no" : 1,
  "_primary_term" : 1,
  "found" : true,
  "_source" : {
    "user" : "Jack",
    "post_date" : "2019-05-15T14:12:12",
    "message" : "trying out Elasticsearch"
  }
}
index 一个文档
#Update 指定 ID  (先删除,在写入)
PUT users/_doc/1
{
	"user" : "Mike"
}

result
{
  "_index" : "users",
  "_type" : "_doc",
  "_id" : "1",
  "_version" : 2,
  "result" : "updated",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 2,
  "_primary_term" : 1
}
update 一个文档
  • 请求格式是 请求方法 + 索引 + 操作类型(_update) + 文档Id + 修改的数据
    update 并不会与 index 一样先删除再新增,update是直接在原来的数据上进行更新操作,索引 如果是新增字段则可以考虑 使用 update Api,index在这种场景下不可用,因为他会把原先的数据给删除掉,所以之前的数据就会丢失
#在原文档上增加字段
POST users/_update/2/
{
    "doc":{
        "post_date" : "2019-05-15T14:12:12",
        "message" : "trying out Elasticsearch"
    }
}
delete 一个文档
# 删除文档
DELETE users/_doc/1
批量操作 BULK API
  • 上边的请求都是一个个的请求,如果我们有批量的需求,那么会很费事,ElasticSearch 提供了批量的接口,也就是 BULK API ,支持在一次请求中 对不同的索引进行操作。
    请求格式: POST + _bulk + 执行语句,如果是创建语句,那么在这条语句后边跟上你要修改的值
    - 支持的操作类型 create index update delete
    - 一次请求中单条语句失败并不会影响其他的语句执行
    - 返回结果会将每条数据的执行结果返回
POST _bulk
{ "index" : { "_index" : "test", "_id" : "1" } }
{ "field1" : "value1" }  # 这个是前边的index 索要执行的值
{ "delete" : { "_index" : "test", "_id" : "2" } } # delete 不需要修改值
{ "create" : { "_index" : "test2", "_id" : "3" } }
{ "field1" : "value3" }
{ "update" : {"_id" : "1", "_index" : "test"} }
{ "doc" : {"field2" : "value2"} }
批量获取 mget
#URI中指定index
GET /test/_mget
{
    "docs" : [
        {
            "_id" : "1"
        },
        {
            "_id" : "2"
        }
    ]
}

索引

  • index 索引是文档的容器,是一类文档的集合,功能类似于传统关系型数据库中的一个数据库。
    - index 体现了逻辑空间的概念,是一个逻辑意义上的聚合。
    - shard 分片体现了物理空间的观念,索引中的数据具体存放的地方。
  • 索引在 ElasticSearch 中有名词与动词的区分,语义是不一样的。
    - 名词,一个 索引 类似于传统关系数据库中的一个 数据库 ,是一个存储关系型文档的地方。
    - 动词,索引一个文档 就是存储一个文档到一个 索引 (名词)中以便被检索和查询。这非常类似于 SQL 语句中的 INSERT 关键词。
  • Mapping 与 Setting
    - Mapping 定义文档字段的类型
    - Setting 定义不同数据的分布,指定多少的分片,要怎么存储
{
  "movies" : {
    "aliases" : { },
    "mappings" : {
      "properties" : {
        "@version" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "id" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        }
      }
    },
    "settings" : {
      "index" : {
        "creation_date" : "1597680584404",
        "number_of_shards" : "1",
        "number_of_replicas" : "1",
        "uuid" : "28eYBLMIRPqf2t52acSobA",
        "version" : {
          "created" : "7030299"
        },
        "provided_name" : "movies"
      }
    }
  }
}

这就是 ElasticSearch 的一个索引结构,可以看到是由两块的,一个是mappings,一个是 settings,mappings 中维护了每个字段的一些元数据信息,settings中更多的则是分片的一些信息,针对存储。

一些基本Api
# get 索引名 查看索引的元数据信息
GET movies

# 查看文档的统计信息
GET movies/_count
{
  "count" : 9743,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  }
}

# 查看所有的索引
GET _cat/indices
yellow open movies                       28eYBLMIRPqf2t52acSobA 1 1  9743 5  1.4mb  1.4mb
green  open .kibana_task_manager         xK3JGCoQTPiSjDWEBowOlg 1 0     2 0 31.7kb 31.7kb
green  open kibana_sample_data_ecommerce 6jy0_oyhSmiqxLWlAnLqKw 1 0  4675 0  4.9mb  4.9mb

#查看索引并且模糊查找
GET /_cat/indices/kibana*?v&s=index
health status index                        uuid                   pri rep docs.count docs.deleted store.size pri.store.size
green  open   kibana_sample_data_ecommerce 6jy0_oyhSmiqxLWlAnLqKw   1   0       4675            0      4.9mb          4.9mb
green  open   kibana_sample_data_flights   QIh5rLomSkqaaIVYSKzCKA   1   0      13059            0      6.5mb          6.5mb

#查看状态为绿的索引
GET /_cat/indices?v&health=green
health status index                        uuid                   pri rep docs.count docs.deleted store.size pri.store.size
green  open   .kibana_task_manager         xK3JGCoQTPiSjDWEBowOlg   1   0          2            0     31.7kb         31.7kb
green  open   kibana_sample_data_ecommerce 6jy0_oyhSmiqxLWlAnLqKw   1   0       4675            0      4.9mb          4.9mb

#查看索引具体的字段并且状态为绿色的
GET /_cat/indices/kibana*?pri&v&h=health,index,pri,rep,docs.count,mt
health index                        pri rep docs.count mt pri.mt
green  kibana_sample_data_ecommerce   1   0       4675  0      0
green  kibana_sample_data_logs        1   0      14074  0      0
green  kibana_sample_data_flights     1   0      13059  0      0

#查看索引使用了多少内存
GET /_cat/indices?v&h=i,tm&s=tm:desc
i                                 tm
kibana_sample_data_ecommerce 203.4kb
kibana_sample_data_logs       81.1kb
kibana_sample_data_flights    72.7kb
movies                        13.1kb

#清除索引
DELETE users

节点

  • ElasticSearch 节点的概念,其实就是一个运行中的实例,每启动一个 ElasticSearch 就是一个节点
  • 每个节点都有名字,可以通过配置文件配置,也可以在启动命令中添加参数 -E node.name=node1
  • 每个节点启动都会生成一个 UID 放在data 目录下,所以启动时最好保证 data 目录唯一

节点的类型

Master-eligible node 和 master node

master 与 Master-eligible的概念,其实就是主从的概念,每个集群内部,一定是有一个Master的,然后集群内的其他节点为 Master-eligible 从节点,然后主节点挂掉的时候,从节点选举上位。

  • 每个节点启动之后,默认就是一个 Master-eligible 节点
  • Master-eligible 节点可以参与选举成为 master 节点,当集群中第一个节点启动时,他会将自己选举成为 master 节点
  • 每个节点都保存了集群的状态,不过只有 master 节点才能修改集群的状态,master 节点负责管理集群范围内的所有变更,例如增加、删除索引,或者增加、删除节点等。
Data node 和 Coordinating node
  • Data 节点顾名思义,就是保存数据的节点,负责保存分片数据。
  • Coordinating node 有些类似网关,负责接收客户端的请求,并且将请求转发到合适的节点,最终再将结果聚合,每个节点都是一个 Coordinating node ,都可以负责路由转发
Hot node 和 Warm node

冷热节点,冷热节点的概念是根据机器的性能来区分的,比如 hot 热节点,如果机器的性能好的话,他可以作为 hot node,机器的性能差可以作为 warm node,有冷热节点的概念,其实就是为了节省费用,在日志场景下还是比较好用的,如果一些区域的日志吞吐量大可以使用 hot node,如果一些区域都是一系诶老的日志并且要慢慢处理掉的,可以使用 warm node。

配置节点类型

一个节点可以配置多个节点类型

node.master=true  # 设置 Master-eligible,代表当前节点可以参与选举
node.data=true # 设置数据节点
node.ingest # 设置
node.ml=true # 设置这个节点为异常节点

分片

  • 主分片
    一个主分片其实就是一个 lucene 实力,主分片主要是用来解决水平扩容的问题,通过主分片,可以将数据分散到所有的节点上,文档这些数据就存放在分片中。
    - 主分片在创建索引时就已经指定好了,不允许修改(真要修改也可以,Reindex Api)
  • 副本分片
    是朱分片的拷贝,用于解决高可用的问题,并且副分片一般不与主分片在一个node上,这样这个节点挂掉,副分片依然存在,数据不会丢失
    副本分片的数量是可以设置的

路由文档到分片

当索引一个文档的时候,文档会被存储到一个主分片中。 Elasticsearch 如何知道一个文档应该存放到哪个分片中呢?当我们创建文档时,它如何决定这个文档应当被存储在分片 1 还是分片 2 中呢?

首先这肯定不会是随机的,否则将来要获取文档的时候我们就不知道从何处寻找了。实际上,这个过程是根据下面这个公式决定的:

shard = hash(routing) % number_of_primary_shards

routing 是一个可变值,默认是文档的 _id ,也可以设置成一个自定义的值。 routing 通过 hash 函数生成一个数字,然后这个数字再除以 number_of_primary_shards (主分片的数量)后得到 余数 。这个分布在 0 到 number_of_primary_shards-1 之间的余数,就是我们所寻求的文档所在分片的位置。

分片的集群中部署

主副本分片的出现是为了保证数据的高可用的,那么这些分片在 ElasticSearch 中时怎样分布的才能保证数据的高可用。 我们 假设有一个集群由三个节点组成。 有两个主分片 P0 P1,每个主分片有两个副本分 片R0 R1。在分配副本分片的时候,相同分片的副本不会放在同一节点,所以有些类似下图,这样当 node 1 挂掉的时候, node2 以及 node3 中都有R0 的数据,保证高可用。

es设置索引分片数量 es索引分片大小_es设置索引分片数量

与关系型数据库的对比

ElasticSearch 在 7 之后默认已经不支持 type的创建,都是 _doc 类型

mysql

ElasticSearch

database 数据库

index 索引

table 数据表

type 文档类型

data 数据

document 文档

schemal DDL表格元数据信息

mapping 索引字段元数据描述信息

Column 列

field 字段