ElasticSearch进阶篇(一)--版本控制
一、前言
ElasticSearch(以下简称ES)的数据写入支持高并发,高并发就会带来很普遍的数据一致性问题。常见的解决方法就是加锁。同样,ES为了保证高并发写的数据一致性问题,加入了类似于锁的实现方法--版本控制。锁从其中的一个角度可分为乐观锁和悲观锁。
对于同一个数据的并发操作,悲观锁认为自己在使用数据的时候一定会有别的线程过来修改数据,因此在获取数据的时候会先加锁,确保数据不会被别的线程修改。而乐观锁则认为自己在使用数据时不会有别的线程来修改数据,所以不会添加锁,只是在更新或者提交数据的时候去判断之前有没有别的线程更新了这个数据。那么ES属于那种锁呢?下面大狮兄就和大家一起探讨官方的具体做法来回答这个问题。
二、版本控制实现及验证
1. ES6.7 Before
# 新建测试索引
PUT test
{
"settings" : {
"number_of_shards" : "3",
"number_of_replicas" : "0"
}
}
## 插入文档
PUT test/_doc/1
{"user": "zhangsan", "age": 12}
## 响应结果
{
"_index" : "test",
"_type" : "_doc",
"_id" : "1",
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 1,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 0,
"_primary_term" : 1
}
更新文档(version版本大于已写入文档版本),更新年龄为10,版本号为200
## 更新文档
PUT test/_doc/1?version=200&version_type=external
{"user": "zhangsan", "age": 10}
## 返回结果
{
"_index" : "test",
"_type" : "_doc",
"_id" : "1",
"_version" : 200,
"result" : "updated",
"_shards" : {
"total" : 1,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 1,
"_primary_term" : 1
}
## 查询文档
GET test/_doc/1
## 返回结果
{
"_index" : "test",
"_type" : "_doc",
"_id" : "1",
"_version" : 200,
"_seq_no" : 1,
"_primary_term" : 1,
"found" : true,
"_source" : {
"user" : "zhangsan",
"age" : 10
}
}
更新成功,年龄更新为10且版本号更新为200
更新文档(version版本小于或等于已写入文档版本),更新年龄为22,版本号为180
## 更新文档
PUT test/_doc/1?version=180&version_type=external
{"user": "zhangsan", "age": 22}
## 返回结果
{
"error" : {
"root_cause" : [
{
"type" : "version_conflict_engine_exception",
"reason" : "[1]: version conflict, current version [200] is higher or equal to the one provided [180]",
"index_uuid" : "fCv7Q1dkTl6e9E1Z0dNE1g",
"shard" : "2",
"index" : "test"
}
],
"type" : "version_conflict_engine_exception",
"reason" : "[1]: version conflict, current version [200] is higher or equal to the one provided [180]",
"index_uuid" : "fCv7Q1dkTl6e9E1Z0dNE1g",
"shard" : "2",
"index" : "test"
},
"status" : 409
}
## 查询文档
GET test/_doc/1
## 返回结果
{
"_index" : "test",
"_type" : "_doc",
"_id" : "1",
"_version" : 200,
"_seq_no" : 1,
"_primary_term" : 1,
"found" : true,
"_source" : {
"user" : "zhangsan",
"age" : 10
}
}
更新失败,数据没有变化,提示版本冲突,现有的版本号大于要插入的版本号。
- vertion_type=external 或者 vertion_type=external_gt :目标版本号大于已有的版本号才会更新成功。
- vertion_type=external_gte :目标版本号大于或等于已有的版本号才会更新成功。
2. ES6.7 OR Later
# 新建测试索引
PUT testccc
{
"settings" : {
"number_of_shards" : "1",
"number_of_replicas" : "0"
}
}
## 插入文档
PUT testccc/_doc/1
{"user": "lisi", "age": 12}
## 返回结果
{
"_index" : "testccc",
"_type" : "_doc",
"_id" : "1",
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 1,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 0,
"_primary_term" : 1
}
返回结果注意最后的两个字段,_seq_no表示序列号是自增的,_primary_term表是文档位于哪个shard。
更新数据(seq_no大于已写入文档序列号),更新年龄为10,序列号为20
## 更新文档
PUT testccc/_doc/1?if_seq_no=20&if_primary_term=1
{"user": "lisi", "age": 10}
## 返回结果
{
"error" : {
"root_cause" : [
{
"type" : "version_conflict_engine_exception",
"reason" : "[1]: version conflict, required seqNo [20], primary term [1]. current document has seqNo [0] and primary term [1]",
"index_uuid" : "N6LzBNj9S5yqVWFubt3x4Q",
"shard" : "0",
"index" : "testccc"
}
],
"type" : "version_conflict_engine_exception",
"reason" : "[1]: version conflict, required seqNo [20], primary term [1]. current document has seqNo [0] and primary term [1]",
"index_uuid" : "N6LzBNj9S5yqVWFubt3x4Q",
"shard" : "0",
"index" : "testccc"
},
"status" : 409
}
## 查询文档
GET testccc/_doc/1
## 返回结果
{
"_index" : "testccc",
"_type" : "_doc",
"_id" : "1",
"_version" : 1,
"_seq_no" : 0,
"_primary_term" : 1,
"found" : true,
"_source" : {
"user" : "lisi",
"age" : 12
}
}
更新失败,数据无变化,提示版本冲突,最近文档的序列号为0,要更新的序列号为20。
更新数据(seq_no等于已写入文档序列号),更新年龄为10
## 更新文档
PUT testccc/_doc/1?if_seq_no=0&if_primary_term=1
{"user": "lisi", "age": 10}
## 返回结果
{
"_index" : "testccc",
"_type" : "_doc",
"_id" : "1",
"_version" : 2,
"result" : "updated",
"_shards" : {
"total" : 1,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 1,
"_primary_term" : 1
}
## 查询文档
GET testccc/_doc/1
## 返回结果
{
"_index" : "testccc",
"_type" : "_doc",
"_id" : "1",
"_version" : 2,
"_seq_no" : 1,
"_primary_term" : 1,
"found" : true,
"_source" : {
"user" : "lisi",
"age" : 10
}
}
更新成功,且seq_no自增为1。
## 插入新文档
PUT testccc/_doc/2
{"user": "wangwu", "age": 40}
## 返回结果
{
"_index" : "testccc",
"_type" : "_doc",
"_id" : "2",
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 1,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 2,
"_primary_term" : 1
}
## 更新原文档
PUT testccc/_doc/1?if_seq_no=1&if_primary_term=1
{"user": "lisi", "age": 50}
## 返回结果
{
"_index" : "testccc",
"_type" : "_doc",
"_id" : "1",
"_version" : 3,
"result" : "updated",
"_shards" : {
"total" : 1,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 3,
"_primary_term" : 1
}
## 更新新写入文档
PUT testccc/_doc/2?if_seq_no=2&if_primary_term=1
{"user": "wangwu", "age": 80}
## 返回结果
{
"_index" : "testccc",
"_type" : "_doc",
"_id" : "2",
"_version" : 2,
"result" : "updated",
"_shards" : {
"total" : 1,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 4,
"_primary_term" : 1
}
可以观察到对于不同的文档,seq_no总是自增1的。
三、总结
- ES版本控制类似于Java中的乐观锁,尤其对版本号字段的巧妙使用与解决乐观锁ABA问题的CAS算法有异曲同工之妙。
- ES6.7之后添加的if_seq_no与if_primary_term版本控制是针对于整个索引的,而_version和version_type版本控制是针对于单条记录(即单个文档)的,不同的应用场景可使用不同的版本控制策略。
- if_seq_no配置的值必须等于存在于现有文档中才能更新成功,而_version配置的值根据不同的version_type,必须大于或者大于等于文档最近更改过的_version值才能更新成功。
ElasticSearch进阶篇(一)--版本控制的更多相关文章
- Elasticsearch进阶篇(一)~head插件的安装与配置
1.安装node.js 1.1.通过官网下载二进制安装包 https://nodejs.org/en/download/ 选择对应的版本,右键复制下载链接,进入linux目录,切换到要安装目录的磁盘. ...
- SQL Server调优系列进阶篇(如何维护数据库索引)
前言 上一篇我们研究了如何利用索引在数据库里面调优,简要的介绍了索引的原理,更重要的分析了如何选择索引以及索引的利弊项,有兴趣的可以点击查看. 本篇延续上一篇的内容,继续分析索引这块,侧重索引项的日常 ...
- Membership三步曲之进阶篇 - 深入剖析Provider Model
Membership 三步曲之进阶篇 - 深入剖析Provider Model 本文的目标是让每一个人都知道Provider Model 是什么,并且能灵活的在自己的项目中使用它. Membershi ...
- idea 插件的使用 进阶篇
CSDN 2016博客之星评选结果公布 [系列直播]零基础学习微信小程序! "我的2016"主题征文活动 博客的神秘功能 idea 插件的使用 进阶篇(个人收集 ...
- 2. web前端开发分享-css,js进阶篇
一,css进阶篇: 等css哪些事儿看了两三遍之后,需要对看过的知识综合应用,这时候需要大量的实践经验, 简单的想法:把qq首页全屏另存为jpg然后通过ps工具切图结合css转换成html,有无从下手 ...
- windows系统快捷操作の进阶篇
上次介绍了windows系统上一些自带的常用快捷键,有些确实很方便,也满足了我们的一部分需求.但是我们追求效率的步伐怎会止步于此?这一次我将会进一步介绍windows上提升效率的方法. 一:运行 打开 ...
- python 面向对象(进阶篇)
上一篇<Python 面向对象(初级篇)>文章介绍了面向对象基本知识: 面向对象是一种编程方式,此编程方式的实现是基于对 类 和 对象 的使用 类 是一个模板,模板中包装了多个“函数”供使 ...
- 最快让你上手ReactiveCocoa之进阶篇
前言 由于时间的问题,暂且只更新这么多了,后续还会持续更新本文<最快让你上手ReactiveCocoa之进阶篇>,目前只是简短的介绍了些RAC核心的一些方法,后续还需要加上MVVM+Rea ...
- SQL Server调优系列进阶篇(查询优化器的运行方式)
前言 前面我们的几篇文章介绍了一系列关于运算符的基础介绍,以及各个运算符的优化方式和技巧.其中涵盖:查看执行计划的方式.几种数据集常用的连接方式.联合运算符方式.并行运算符等一系列的我们常见的运算符. ...
随机推荐
- 使用IDEA配置Maven
IDEA中配置Maven File --> settings 推荐配置:设置maven在不联网的情况下使用本地插件 一般使用maven为我们提供好的骨架时,是需要联网的,配置这个,可以在没有网络 ...
- 资源:zookeeper下载地址
提供zookeeper下载地址:https://archive.apache.org/dist/zookeeper/zookeeper-3.4.6/
- docker安装应用整理
nginx安装: docker run \ --name nginx \ --volume /var/data/nginx/nginx.conf:/etc/nginx/nginx.conf \ --v ...
- 使用 Java 和 Maven (JBake) 生成静态网站
使用 JBake("mvn generate-resources")构建您的静态网站或博客.使用布局.宏和数据文件. 我们迁移了整个www.optaplanner.org网站(13 ...
- XDFZOI 月赛 201905 Sliver
组题人自己组完过后,才发现自己还是太弱了... T1 简单模拟. 按照游戏规则直接模拟显然是不明智的,所以我们可以像石头剪刀布一样,将判断改变为检验. 同时,我们发现,一共只有48种牌,所以我们可以直 ...
- libzip开发笔记(二):libzip库介绍、ubuntu平台编译和工程模板
前言 Qt使用一些压缩解压功能,选择libzip库,libzip库比较原始,也是很多其他库的基础支撑库,编译过了windows版本,有需求编译一个ubuntu版本的,交叉编译需求的同样可参照本文章 ...
- 懂得分享 Linux 配置NFS共享服务
部署YUM仓库及NFS共享服务一.YUM概述 YUM (Yellow dog Updater Modified)二.准备安装源 ① 软件仓库的提供方式 ② RPM软件包的来源 ...
- python 交换变量的值 不需要借助第三个变量
>>> a,b,c,d=1,2,3,4>>> a,b,c,d=d,c,b,a>>> print(a,b,c,d)4 3 2 1>>&g ...
- SpringMVC中@Controller和@RequestMapping用法
一.简介 在SpringMVC 中,控制器Controller 负责处理由DispatcherServlet 分发的请求,它把用户请求的数据经过业务处理层处理之后封装成一个Model ,然后再把该Mo ...
- 计算机毕业设计选题大合集,含ssm,springboot,小程序,php,python
1基于springboot医院急诊系统 2基于springboot校园闲置物品租售系统 3基于springboot校园闲置物品交易网站 4基于springboot图书网站 5基于springboot外 ...