ElasticSearch索引数据版本(_version)控制(5.4版本)

1. 用于处理冲突

通常,当数据库因为请求冲突发生幻读问题时,有两种解决策略:

  • 悲观并发控制如:行级锁;

    这种方法被关系型数据库广泛使用,它假定有变更冲突可能发生,因此阻塞访问资源以防止冲突。一个典型的例子是读取一行数据之前先将其锁住,确保只有放置锁的线程能够对这行数据进行修改。

  • 乐观并发控制如:写操作时发现数据发生变化,操作失败。

    它假定冲突是不可能发生的,所以不会阻塞正在尝试的操作。 然而,如果源数据在读写当中被修改,更新将会失败。应用程序接下来将决定该如何解决冲突。 例如,可以获取新的数据,重试更新、或者将相关情况报告给用户。

ElasticSearch采用乐观并发控制。Elasticsearch是分布式的,当文档被创建、更新或删除,文档的新版本会被复制到集群的其它节点。Elasticsearch即是同步的又是异步的,意思是这些复制请求都是平行发送的,并无序(out of sequence)的到达目的地。这就需要一种方法确保老版本的文档永远不会覆盖新的版本。

这时就用到了元数据“_version”,每个文档都有一个_version号码,这个号码在文档被改变时加1,Elasticsearch使用这个_version保证所有修改都被正确排序。当一个旧版本出现在新版本之后,它会被简单的忽略。

2. _version的运用

我们利用_version的这一优点确保数据不会因为修改冲突而丢失。我们可以指定文档的version来做想要的更改。如果那个版本号不是现在的,我们的请求就失败了。

如:先插入一条数据

PUT test_index_01/default/1
{
	"name":"lzz"
}

响应体告诉我们这是一个新建的文档,它的_version是1。

{
	“_index”: "test_index_01",
	"_type":"default",
	"_id":"1",
	"_version":1,
	"result":"created",
	"_shards": {
		"total": 2,
		"successful": 2,
		"failed": 0
	},
	"created": true	
}

现在假设我们要编辑这个文档:

PUT test_index_01/default/1?version=1
{
	"name":"lcc"
}

请求成功,响应体告诉我们_version已经增加到2。

{
	“_index”: "test_index_01",
	"_type":"default",
	"_id":"1",
	"_version":2,
	"result":"updated",
	"_shards": {
		"total": 2,
		"successful": 2,
		"failed": 0
	},
	"created": false
}

现在,如果我们重新运行相同的索引请求,依旧指定version=1,Elasticsearch将返回409 版本冲突version_conflict_engine_exception异常的HTTP响应。这告诉我们当前提供的_version1,但是我们指定想要更新的数据版本是2

{
	"error": {
		"root_cause": [
			{
				"type": "version_conflict_engine_exception",
				"reason": "[default][1]: version conflict, current version [2] is different than the one provided [1]",
				"index_uuid": "avGv62ylTJCaTdsUBa96oQ",
				"shard": "3",
				"index": "test_index_01"
			}
		],
		"type": "version_conflict_engine_exception",
		"reason": "[default][1]: version conflict, current version [2] is different than the one provided [1]",
		"index_uuid": "avGv62ylTJCaTdsUBa96oQ",
		"shard": "3",
		"index": "test_index_01"
	},
	"status": 409
}

3. 使用外部版本

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

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

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

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

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

PUT test_index_02/default/1?version=5&version_type=external
{
	"name":"lpp"
}

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

{
	“_index”: "test_index_02",
	"_type":"default",
	"_id":"1",
	"_version":5,
	"result":"created",
	"_shards": {
		"total": 2,
		"successful": 2,
		"failed": 0
	},
	"created": true	
}

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

PUT test_index_02/default/1?version=10&version_type=external
{
	"name":"loo"
}

请求成功,该条数据当前_version变为10

{
	“_index”: "test_index_02",
	"_type":"default",
	"_id":"1",
	"_version":10,
	"result":"updated",
	"_shards": {
		"total": 2,
		"successful": 2,
		"failed": 0
	},
	"created": false
}

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

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值