ElasticSearch 6.0 乐观锁

2018-03-20 15:32:37
866次阅读
0个评论
并发处理

比如某件商品存货100件,用户1下单买走1件,剩余99件;与此同时用户2也下单买走1件,但是用户2不知道用户1已经下单,看到剩余商品仍然是99件。这样造成系统中显示商品总数比实际数量要多,这种情况在商业系统中肯定是不能容忍的。 


乐观并发控制

执行 index , GET 和 delete 请求时,我们指出每个文档都有一个 _version (版本)号,当文档被修改时版本号递增。 Elasticsearch 使用这个 _version 号来确保变更以正确顺序得到执行。如果旧版本的文档在新版本之后到达,它可以被简单的忽略。

我们可以利用 _version 号来确保 应用中相互冲突的变更不会导致数据丢失。我们通过指定想要修改文档的 version 号来达到这个目的。 如果该版本不是当前版本号,我们的请求将会失败。

PUT website
{
  "acknowledged": true,
  "shards_acknowledged": true,
  "index": "website"
}

添加一个文档

PUT website/blog/1/_create
{
  "title": "My first blog entry",
  "text":  "Just trying this out..."
}
{
  "_index": "website",
  "_type": "blog",
  "_id": "1",
  "_version": 1,
  "result": "created",
  "_shards": {
    "total": 2,
    "successful": 2,
    "failed": 0
  },
  "created": true
}

响应体告诉我们,这个新创建的文档 _version 版本号是 1 。 
现在假设我们想编辑这个文档:我们加载其数据到 web 表单中, 做一些修改,然后保存新的版本。

GET website/blog/1
{
  "_index": "website",
  "_type": "blog",
  "_id": "1",
  "_version": 1,
  "found": true,
  "_source": {
    "title": "My first blog entry",
    "text": "Just trying this out..."
  }
}

现在,当我们尝试通过重建文档的索引来保存修改,我们指定 version 为我们的修改会被应用的版本


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

此请求成功,并且响应体告诉我们 _version 已经递增到 2 :


{
  "_index": "website",
  "_type": "blog",
  "_id": "1",
  "_version": 2,
  "result": "updated",
  "_shards": {
    "total": 2,
    "successful": 2,
    "failed": 0
  },
  "created": false
}
然而,如果我们重新运行相同的索引请求,仍然指定 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": "ekHPTnDgRH63lHUpvxqQBA",
        "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": "ekHPTnDgRH63lHUpvxqQBA",
    "shard": "3",
    "index": "website"
  },
  "status": 409
}

响应体告诉我们在 Elasticsearch 中这个文档的当前 _version 号是 2 ,但我们指定的更新版本号为 1

我们现在怎么做取决于我们的应用需求。我们可以告诉用户说其他人已经修改了文档,并且在再次保存之前检查这些修改内容。 或者,在之前的商品 stock_count 场景,我们可以获取到最新的文档并尝试重新应用这些修改。

所有文档的更新或删除 API,都可以接受 version 参数,这允许你在代码中使用乐观的并发控制,这是一种明智的做法。

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

一个常见的设置是使用其它数据库作为主要的数据存储,使用 Elasticsearch 做数据检索, 这意味着主数据库的所有更改发生时都需要被复制到 Elasticsearch

Elasticsearch 中通过增加 version_type=external 方式指定外部版本号,如果外部版本号是否大于当前文档版本,则可以执行更新操作。

例如,要更新一个新的具有外部版本号 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
}
当我们再次执行上面的外部版本号更新时,报错。因为外部版本号不大于当前版本号5.


{
  "error": {
    "root_cause": [
      {
        "type": "version_conflict_engine_exception",
        "reason": "[blog][2]: version conflict, current version [5] is higher or equal to the one provided [5]",
        "index_uuid": "ekHPTnDgRH63lHUpvxqQBA",
        "shard": "2",
        "index": "website"
      }
    ],
    "type": "version_conflict_engine_exception",
    "reason": "[blog][2]: version conflict, current version [5] is higher or equal to the one provided [5]",
    "index_uuid": "ekHPTnDgRH63lHUpvxqQBA",
    "shard": "2",
    "index": "website"
  },
  "status": 409
}


收藏00

登录 后评论。没有帐号? 注册 一个。