# 并发处理
悲观并发控制
这种方法被关系型数据库广泛使用,它假定每个操作都会有冲突发生,因此阻塞访问资源以防止冲突。 一个典型的例子是读取一行数据之前先将其锁住,确保只有放置锁的线程能够对这行数据进行修改。
乐观并发控制
Elasticsearch 中使用的这种方法是假定冲突不可能发生,并且不会阻塞正在尝试的操作。 然而,如果源数据在读写当中被修改,更新将会失败。应用程序接下来将决定该如何解决冲突。 例如,可以重试更新、使用新的数据、或者将相关情况报告给用户。
Elasticsearch 默认使用乐观并发控制,每个文档都有一个 _version
(版本)号,当文档被修改时版本号递增。 Elasticsearch 使用这个 _version
号来确保变更以正确顺序得到执行。如果旧版本的文档在新版本之后到达,它可以被简单的忽略。
利用 _version
号来确保 应用中相互冲突的变更不会导致数据丢失。我们通过指定想要修改文档的 version
号来达到这个目的。 如果该版本不是当前版本号,我们的请求将会失败。
新建一个文档
PUT /website/blog/1/_create
{
"title": "My first blog entry",
"text": "Just trying this out..."
}
我们检索文档:
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
"created": false
}
然而,如果我们重新运行相同的索引请求,仍然指定 version=1
, Elasticsearch 返回 409 Conflict
HTTP 响应码,和一个如下所示的响应体:
{
"error": {
"root_cause": [
{
"type": "version_conflict_engine_exception",
"reason": "[blog][1]: version conflict, current [2], provided [1]",
"index": "website",
"shard": "3"
}
],
"type": "version_conflict_engine_exception",
"reason": "[blog][1]: version conflict, current [2], provided [1]",
"index": "website",
"shard": "3"
},
"status": 409
}
这告诉我们在 Elasticsearch 中这个文档的当前 _version
号是 2
,但我们指定的更新版本号为 1
。
我们现在怎么做取决于我们的应用需求。我们可以告诉用户说其他人已经修改了文档,并且在再次保存之前检查这些修改内容。 或者,在之前的商品 stock_count
场景,我们可以获取到最新的文档并尝试重新应用这些修改。
所有文档的更新或删除 API,都可以接受 version
参数,这允许你在代码中使用乐观的并发控制,这是一种明智的做法。