Elasticsearch 创建、更新、删除文档、处理冲突

 ----创建新文档----

1._index,_type和_id的组合可以唯一标识一个文档,所以确保一个新文档的最简单的办法就是,使用索引请求的POST形式让elsticsearch自动生成唯一_id:

POST /website/blog
{  ...  }

2.如果需要指定文档的_id,那就需要告诉elasticsearch在_index,_type和_id的组合不存在的时候进行新建操作,有两种方法实现

  • 使用op_type
PUT /website/blog/123?op_type=create
{ ... }
  • 在URL末端使用/_create
PUT /website/blog/123/_create
{ ... }

  注意: 请求执行成功会返回元数据(执行结果)和一个201 Created 的HTTP响应码。

     如果相同_index,_type和_id的组合的文档已经存在,elasticsearch将会返回409 Conflict 响应码以及如下错误信息。

{
  "error": {
    "root_cause": [
      {
        "type": "version_conflict_engine_exception",
        "reason": "[blog][124]: version conflict, document already exists (current version [1])",
        "index_uuid": "oCXgGtl9SfmXXD-edmYdfA",
        "shard": "3",
        "index": "website"
      }
    ],
    "type": "version_conflict_engine_exception",
    "reason": "[blog][124]: version conflict, document already exists (current version [1])",
    "index_uuid": "oCXgGtl9SfmXXD-edmYdfA",
    "shard": "3",
    "index": "website"
  },
  "status": 409
}

----删除文档----

删除文档,使用DELETE 方法:

DELETE /website/blog/123

一如果找到该文档,elasticsearch将返回一个200 ok 的HTTP响应码,和一个类似以下结构的响应体,字段_version 值会增加。

{
  "found": true,
  "_index": "website",
  "_type": "blog",
  "_id": "123",
  "_version": 6,
  "result": "deleted",
  "_shards": {
    "total": 2,
    "successful": 2,
    "failed": 0
  }
}

标如果文档没有找到,我们将会得到一个404 not Found 的响应码和一个类似以下结构的响应体,即便文档不存在,_version值也会增加,这是 Elasticsearch 内部记录本的一部分,用来确保这些改变在跨多节点时以正确的顺序执行。

{
  "found": false,
  "_index": "website",
  "_type": "blog",
  "_id": "123",
  "_version": 3,
  "result": "not_found",
  "_shards": {
    "total": 2,
    "successful": 2,
    "failed": 0
  }
}

注意:
  正如已经在更新整个文档中提到的,删除文档不会立即将文档从磁盘中删除,只是将文档标记为已删除状态。随着你不断的索引更多的数据,Elasticsearch 将会在后台清理标记为已删除的文档。

----处理冲突----

多个用户对一份数据进行修改操作时,变更越频繁,读数据和更新数据的间隙越长,也就越可能丢失变更。

在数据库领域中,有两种方法通常被用来确保并发更新时变更不会丢失:

1.悲观并发控制(悲观锁)

  这种方法被关系型数据库广泛使用,它假定有变更冲突可能发生,因此阻塞访问资源以防止冲突。 一个典型的例子是读取一行数据之前先将其锁住,确保只有放置锁的线程能够对这行数据进行修改。

2.乐观并发控制(乐观锁)

  Elasticsearch 中使用的这种方法假定冲突是不可能发生的,并且不会阻塞正在尝试的操作。 然而,如果源数据在读写当中被修改,更新将会失败。应用程序接下来将决定该如何解决冲突。 例如,可以重试更新、使用新的数据、或者将相关情况报告给用户。

  Elasticsearch 是分布式的,当文档创建,更新或者删除时,新版本的文档必须复制到集群中的其他节点。

  Elasticsearch 也是异步和并发的,这意味着这些复制请求被并行发送,并且到达目的地时顺序也许是乱的。Elasticsearch 需要一种方法确保文档的旧版本不会覆盖新的版本。

  可以通过_version的递增来确保变更正确的执行顺序,简单的忽略掉旧版本的文档在新版本之后到达的操作。

# 创建一个测试的文档
PUT /website/blog/1/_create
{
  "title": "My first blog entry",
  "text":  "Just trying this out..."
}
# 得到该文档的version版本号
GET /website/blog/1
# 尝试通过重建文档的索引来保存修改,我们指定 version 为我们的修改会被应用的版本,这个操作只有在version=1的时,更新才能成功
PUT /website/blog/1?version=1 
{
  "title": "My first blog entry",
  "text":  "Starting to get the hang of this..."
}
# 更新成功后,_vetsion递增
# 重新运行相同的索引请求,仍然指定 version=1 , Elasticsearch 返回 409 Conflict HTTP 响应码
{
  "error": {
    "root_cause": [
      {
        "type": "version_conflict_engine_exception",
        "reason": "[blog][1]: version conflict, current version [2] is different than the one provided [1]",
        "index_uuid": "oCXgGtl9SfmXXD-edmYdfA",
        "shard": "3",
        "index": "website"
      }
    ],
    "type": "version_conflict_engine_exception",
    "reason": "[blog][1]: version conflict, current version [2] is different than the one provided [1]",
    "index_uuid": "oCXgGtl9SfmXXD-edmYdfA",
    "shard": "3",
    "index": "website"
  },
  "status": 409
}

  注:所有文档的更新或删除 API,都可以接受 version 参数,这允许你在代码中使用乐观的并发控制

3.通过外部系统使用版本控制

  一个常见的设置是使用其它数据库作为主要的数据存储,使用 Elasticsearch 做数据检索, 这意味着主数据库的所有更改发生时都需要被复制到 Elasticsearch ,如果多个进程负责这一数据同步,你可能遇到类似于之前描述的并发问题。

  如果你的主数据库已经有了版本号 — 或一个能作为版本号的字段值比如 timestamp — 那么你就可以在 Elasticsearch 中通过增加 version_type=external 到查询字符串的方式重用这些相同的版本号, 版本号必须是大于零的整数, 且小于 9.2E+18 — 一个 Java 中 long 类型的正值。

  外部版本号的处理方式和我们之前讨论的内部版本号的处理方式有些不同, Elasticsearch 不是检查当前 _version 和请求中指定的版本号是否相同, 而是检查当前 _version 是否 小于 指定的版本号。 如果请求成功,外部的版本号作为文档的新 _version 进行存储。

  外部版本号不仅在索引和删除请求是可以指定,而且在 创建 新文档时也可以指定。

要创建一个新的具有外部版本号 5 的博客文章,我们可以按以下方法进行:

PUT /website/blog/2?version=5&version_type=external
{
  "title": "My first external blog entry",
  "text":  "Starting to get the hang of this..."
}

在响应中,我们能看到当前的 _version 版本号是 5 :

{
  "_index": "website",
  "_type": "blog",
  "_id": "2",
  "_version": 5,
  "result": "created",
  "_shards": {
    "total": 2,
    "successful": 2,
    "failed": 0
  },
  "created": true
}

现在我们更新这个文档,指定一个新的 version 号是 10 :

PUT /website/blog/2?version=10&version_type=external
{
  "title": "My first external blog entry",
  "text":  "Starting"
}

请求成功并将当前 _version 设为 10 :

{
  "_index": "website",
  "_type": "blog",
  "_id": "2",
  "_version": 10,
  "result": "updated",
  "_shards": {
    "total": 2,
    "successful": 2,
    "failed": 0
  },
  "created": false
}

  注:如果你要重新运行此请求时,它将会失败,并返回像我们之前看到的同样的冲突错误, 因为指定的外部版本号不大于 Elasticsearch 的当前版本号。

posted @ 2017-12-25 20:11  40块钱抓娃娃  阅读(8070)  评论(0编辑  收藏  举报