目录
- 一、文档冲突
- 1.1、文档冲突的概述
- 1.2、在数据库领域中如何确保并发更新时变更不会丢失
- 1.2.1、悲观并发控制
- 1.2.2、乐观并发控制
- 二、乐观并发控制
- 2.1、Elasticsearch 的概述
- 2.2、Elasticsearch 如何确保文档的旧版本不会覆盖新的版本
- 2.3、Elasticsearch 如何解决相互冲突的变更不会导致数据丢失
- 三、外部系统版本控制
一、文档冲突
1.1、文档冲突的概述
-
当使用 index API 更新文档 ,可以一次性读取原始文档,做我们的修改,然后重新索引整个文档 。如果其他人同时更改这个文档,他们的更改将丢失。
-
试想我们使用 Elasticsearch 存储我们网上商城商品库存的数量, 每次我们卖一个商品的时候,我们在 Elasticsearch 中将库存数量减少。
-
突然地,一秒要卖好几个商品。 假设有两个 web程序并行运行,每一个都同时处理所有商品的销售。
-
web_1 对 stock_count 所做的更改已经丢失,因为 web_2 不知道它的 stock_count 的拷贝已经过期。结果我们会认为有超过商品的实际数量的库存,因为卖给顾客的库存商品并不存在,我们将让他们非常失望。
-
变更越频繁,读数据和更新数据的间隙越长,也就越可能丢失变更。
1.2、在数据库领域中如何确保并发更新时变更不会丢失
1.2.1、悲观并发控制
- 这种方法被关系型数据库广泛使用,它假定有变更冲突可能发生,因此阻塞访问资源以防止冲突。 一个典型的例子是读取一行数据之前先将其锁住,确保只有放置锁的线程能够对这行数据进行修改。
1.2.2、乐观并发控制
- Elasticsearch 中使用的这种方法假定冲突是不可能发生的,并且不会阻塞正在尝试的操作。 然而,如果源数据在读写当中被修改,更新将会失败。应用程序接下来将决定该如何解决冲突。 例如,可以重试更新、使用新的数据、或者将相关情况报告给用户。
二、乐观并发控制
2.1、Elasticsearch 的概述
- Elasticsearch 是分布式的:当文档创建、更新或删除时, 新版本的文档必须复制到集群中的其他节点。
- Elasticsearch 也是异步和并发的:这些复制请求被并行发送,并且到达目的地时也许 顺序是乱的 。
2.2、Elasticsearch 如何确保文档的旧版本不会覆盖新的版本
- 之前讨论 index , GET 和 delete 请求时,我们指出每个文档都有一个 _version(版本)号,当文档被修改时版本号递增。
- Elasticsearch 使用这个 version 号来确保变更以正确顺序得到执行。如果旧版本的文档在新版本之后到达,它可以被简单的忽略。
2.3、Elasticsearch 如何解决相互冲突的变更不会导致数据丢失
-
利用 version 号来确保 应用中相互冲突的变更不会导致数据丢失。
-
通过指定想要修改文档的 version 号来达到这个目的。 如果该版本不是当前版本号,我们的请求将会失败。
-
老的版本 es 使用 version,但是新版本不支持了,会报下面的错误,提示我们用 if_seq_no和 if_primary_term
{ "error": { "root_cause": [ { "type": "action_request_validation_exception", "reason": "Validation Failed: 1: internal versioning can not be used for optimistic concurrency control. Please use `if_seq_no` and `if_primary_term` instead;" } ], "type": "action_request_validation_exception", "reason": "Validation Failed: 1: internal versioning can not be used for optimistic concurrency control. Please use `if_seq_no` and `if_primary_term` instead;" }, "status": 400 }
三、外部系统版本控制
-
一个常见的设置是使用其它数据库作为主要的数据存储,使用 Elasticsearch 做数据检索, 这意味着主数据库的所有更改发生时都需要被复制到 Elasticsearch ,如果多个进程负责这一数据同步,你可能遇到类似于之前描述的并发问题。
-
如果你的主数据库已经有了版本号 或者一个能作为版本号的字段值比如 timestamp —那么你就可以在 Elasticsearch 中通过增加 version_type=external 到查询字符串的方式重用这些相同的版本号, 版本号必须是大于零的整数, 且小于 9.2E+18(一个 Java 中 long类型的正值)。
-
外部版本号的处理方式和我们之前讨论的内部版本号的处理方式有些不同,Elasticsearch 不是检查当前 _version 和请求中指定的版本号是否相同, 而是检查当前_version 是否 小于 指定的版本号。 如果请求成功,外部的版本号作为文档的新 _version进行存储。
-
通过 Postman 发送 POST请求创建文档,在消息体里,指定创建的文档数据,创建后版本号为1。
# POST http://localhost:9200/my_index/_doc/03 { "name":"zhangsan", "age":25, "sex":"男" }
-
其次,通过 Postman 发送 POST请求修改文档,在消息体里,指定修改的文档数据,修改后版本号为2。
# POST http://localhost:9200/my_index/_doc/03 { "name":"zhangsan", "age":20, "sex":"男" }
-
最后,通过 Postman 发送 GET请求查看文档,在消息体里,指定查看的文档数据的版本号为1小于当前版本号2。
# GET http://localhost:9200/my_index/_doc/03?version=1&version_type=external
-
由上图输出结果可知:当前_version 是否 大于 指定的版本号。 请求失败。
注:外部版本号不仅在索引和删除请求是可以指定,而且在 创建 新文档时也可以指定。