Elasticsearch学习笔记(四)版本控制[并发安全]

Elasticsearch系列文章目录

乐观锁

我们知道锁有悲观锁和乐观锁之分,数据库中的事务就是悲观锁,CAS就是属于乐观锁,关于CAS乐观锁的概念请参考本人另一篇文章: java高并发: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"]
    }
}

注意,这里_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"
  ]
}

结果:

{
    "_index": "megacorp",
    "_type": "employee",
    "_id": "2",
    "_version": 3,
    "_shards": {
        "total": 2,
        "successful": 2,
        "failed": 0
    },
    "created": false
}

修改成功,此时_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
}

结果和我们预想的一样,错误原因写的很明确了,当前版本是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..."
}

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

{
  "_index":   "website",
  "_type":    "blog",
  "_id":      "2",
  "_version": 5,
  "created":  true
}

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

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

请求成功的设置了当前_version10

{
  "_index":   "website",
  "_type":    "blog",
  "_id":      "2",
  "_version": 10,
  "created":  false
}

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

参考资料:Elasticsearch 权威指南(中文版)

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: Age of Ai 设计师:meimeiellie 返回首页
实付 9.90元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值