Elasticsearch版本控制[并发安全]

来源:互联网 发布:编程都用mac 编辑:程序博客网 时间:2024/05/16 08:07

原文地址:http://blog.csdn.net/liubenlong007/article/details/54729129

 乐观锁

我们知道锁有悲观锁和乐观锁之分,数据库中的事务就是悲观锁,CAS就是属于乐观锁。

一个CAS方法包含三个参数CAS(V,E,N)。V表示要更新的变量,E表示预期的值,N表示新值。只有当V的值等于E时,才会将V的值修改为N。如果V的值不等于E,说明已经被其他线程修改了,当前线程可以放弃此操作,也可以再次尝试次操作直至修改成功。基于这样的算法,CAS操作即使没有锁,也可以发现其他线程对当前线程的干扰(临界区值的修改),并进行恰当的处理。

 ES中的乐观锁

 使用ES自带的版本控制

我们get请求id=2的员工信息megacorp/employee/2

{    "_index": "megacorp",    "_type": "employee",    "_id": "2",    "_version": 2,    "found": true,    "_source": {        "first_name": "Jane",        "last_name": "Smith",        "age": 33,        "about": "I like to collect rock albums",        "interests": ["music"]    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

注意,这里_version是2,我们从ES中查询到一条记录,修改完成以后重新PUT建立索引,这时候我们要把_version作为一个参数传递过去,ES会用该参数和实际的数据进行比对,如果相同则进行重建索引,如果不同则返回错误信息,具体如何处理这个错误就看业务需要了,比如你可以借鉴atomicInteger循环重试等等。

我们将年龄改为80: 
请求URL:megacorp/employee/2?version=2 
请求参数:

{  "first_name": "Jane",  "last_name": "Smith",  "age": 80,  "about": "I like to collect rock albums",  "interests": [    "music"  ]}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

结果:

{    "_index": "megacorp",    "_type": "employee",    "_id": "2",    "_version": 3,    "_shards": {        "total": 2,        "successful": 2,        "failed": 0    },    "created": false}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

修改成功,此时_version变为了3。

我们在重新执行一遍上面的程序,_version依旧传2,按照CAS理论应该修改不成功。实验结果:

{    "error": {        "root_cause": [{            "type": "version_conflict_engine_exception",            "reason": "[employee][2]: version conflict, current [3], provided [2]",            "index": "megacorp",            "shard": "2"        }],        "type": "version_conflict_engine_exception",        "reason": "[employee][2]: version conflict, current [3], provided [2]",        "index": "megacorp",        "shard": "2"    },    "status": 409}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

结果和我们预想的一样,错误原因写的很明确了,当前版本是3,但是传递过来的版本是2,与current不一致。

 使用外部版本控制系统

一种常见的结构是使用一些其他的数据库做为主数据库,然后使用Elasticsearch搜索数据,这意味着所有主数据库发生变化,就要将其拷贝到Elasticsearch中。如果有多个进程负责这些数据的同步,就会遇到上面提到的并发问题。

如果主数据库有版本字段——或一些类似于timestamp等可以用于版本控制的字段——是你就可以在Elasticsearch的查询字符串后面添加version_type=external来使用这些版本号。版本号必须是整数,大于零小于9.2e+18——Java中的正的long

外部版本号与之前说的内部版本号在处理的时候有些不同。它不再检查_version是否与请求中指定的一致,而是检查是否小于指定的版本。如果请求成功,外部版本号就会被存储到_version

外部版本号不仅在索引和删除请求中指定,也可以在创建(create)新文档中指定。

例如,创建一个包含外部版本号5的新博客,我们可以这样做:

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

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

{  "_index":   "website",  "_type":    "blog",  "_id":      "2",  "_version": 5,  "created":  true}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

现在我们更新这个文档,指定一个新version号码为10

PUT /website/blog/2?version=10&version_type=external{  "title""My first external blog entry",  "text":  "This is a piece of cake..."}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

请求成功的设置了当前_version10

{  "_index":   "website",  "_type":    "blog",  "_id":      "2",  "_version": 10,  "created":  false}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

如果你重新运行这个请求,就会返回一个像之前一样的冲突错误,因为指定的外部版本号不大于当前在Elasticsearch中的版本。


0 0
原创粉丝点击