ES 26 - 通过partial update局部更新索引文档 (partial update增量修改原理)
1 什么是partial update
1.1 全量修改文档的原理
全量修改文档的语法: PUT index/type/1
, 如果id=1的文档不存在, 则创建, 如果存在, 将发生替换原有文档的操作.
全量替换文档的性能比较低, 为了避免替换操作的发生, 引入partial update: 只修改指定的field, 不用全量修改数据.
1.2 修改指定field的思路
(1) 根据用户请求, 获得要修改的文档;
(2) 在内存中封装用户提交的新文档, 发送PUT请求到ES内部;
(3) 将要替换的旧文档标记为deleted;
(4) 最后将封装好的新文档存入索引中.
1.3 partial update的优势
(1) 所有的查询、修改和写回操作, 都在同一个shard中进行, 避免了网络传输的开销.
不需要: 从特定shard查询文档 -> 返回到内存 -> 内存中修改 -> 将修改的文档发送到原来的shard -> 写索引 —— 这个复杂的操作, 显著提升了性能.
(2) 减少了查询和修改的时间间隔, 可以有效减少并发冲突.
1.4 partial update的使用
使用方法: 通过_update
关键字实现增量更新:
// 添加测试数据:
PUT employee/developer/1
{
"name": "shou feng",
"sex": "male",
"age": 20
}
// partial update修改指定field:
POST employee/developer/1/_update
{
"doc": {
"age": 21
}
}
// 响应结果:
{
"_index": "employee",
"_type": "developer",
"_id": "1",
"_version": 2,
"result": "updated",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
}
}
// 查看文档, 发现age已经从20变为21了.
GET employee/developer/1
如果不使用_update
, 则会直接覆盖掉源文档, 导致原文档丢失部分数据:
// 不使用_update:
POST employee/developer/1
{
"doc": {
"age": 22
}
}
// 再次查看, 发现id=1的该文档就只剩一个age字段了:
GET employee/developer/1
2 通过脚本进行partial update操作
ES提供了脚本支持 —— 可以通过Groovy外置脚本(已过时)、内置painless
脚本实现各种复杂操作.
2.1 内置painless脚本修改文档
插入文档:
PUT employee/developer/1
{
"name": "shou feng",
"age": 20,
"salary": 10000
}
执行脚本: —— 这里使用的是更轻快简短的painless脚本, 就是直接由字符串表示的脚本:
POST employee/developer/1/_update // 发送POST请求, 执行partial update
{
"script": "ctx._source.salary+=500" // 为salary自增500
}
查看修改结果:
GET employee/developer/1 // 结果如下:
{
"_index": "employee",
"_type": "developer",
"_id": "1",
"_version": 5,
"found": true,
"_source": {
"name": "shou feng",
"age": 20,
"salary": 10500 // 自增500成功
}
}
2.2 外置Groovy脚本修改文档
说明: 在ES 6.x版本之后, groovy脚本不再支持, 这里演示所用的是ES 5.6.10版本, 如果在6.x版本中使用, 将会抛出如下异常:
"type": "illegal_argument_exception",
"reason": "script_lang not supported [groovy]"
将脚本文件存放在
${ES_HOME}/config/scripts
下, 文件名为xxx.groovy
, 内容为:ctx._source.salary+=bonus
—— 增加值为将近bonus的值, 脚本信息示例如下:[root@localhost scripts]# pwd
/data/elk-5.6.10/es-node/config/scripts
[root@localhost scripts]# cat change_salary.groovy
ctx._source.salary+=bonus
[root@localhost scripts]#
修改文档:
POST employee/developer/1/_update
{
"script": {
"lang": "groovy",
"file": "change_salary",
"params": {
"bonus": 500
}
}
} // 响应结果为:
#! Deprecation: [groovy] scripts are deprecated, use [painless] scripts instead
{
"_index": "employee",
"_type": "developer",
"_id": "1",
"_version": 6,
"result": "updated",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
}
}
查看修改结果:
GET employee/developer/1
// 结果如下:
{
"_index": "employee",
"_type": "developer",
"_id": "1",
"_version": 6,
"found": true,
"_source": {
"name": "shou feng",
"age": 20,
"salary": 9000
}
}
说明:
在执行外置Groovy脚本时, ES提示Groovy脚本已经过时, 建议我们使用painless
—— 更轻快的表达方式, 即类似于ctx._source.salary+=bonus
的简短表达方式.
Elasticsearch 5.6开始, 默认脚本使用的方式就已经是painless
了. 关于脚本的详细使用, 请查看博文: ES 27 - Elasticsearch的painless脚本使用实践.
2.3 内置painless脚本upsert文档
(先删除id=1的文档:
DELETE employee/developer/1
) 假设我们并不知道id=1的文档已经被删除了, 现在为其添加"level": 1
的内容:POST employee/developer/1/_update
{
"doc": {
"level": 1
}
}
抛出 [404 - 文档丢失] 的错误:
{
"error": {
"root_cause": [
{
"type": "document_missing_exception",
"reason": "[developer][1]: document missing",
"index_uuid": "rT6tChP2QISaVd2OzdCEMA",
"shard": "3",
"index": "employee"
}
],
"type": "document_missing_exception",
"reason": "[developer][1]: document missing",
"index_uuid": "rT6tChP2QISaVd2OzdCEMA",
"shard": "3",
"index": "employee"
},
"status": 404
}
修改upsert策略: 如果指定的文档不存在, 就执行
upsert
中的初始化操作; 如果存在, 就执行doc
或script
中的partial update操作:POST employee/developer/1/_update
{
"script": "ctx.source.level+=1",
"upsert": {
"name": "heal",
"age": 20
}
}
此时发现
"result" : "created"
—— 新建了文档.
2.4 外置Groovy脚本delete文档
说明: 这里演示所用的是ES 5.6.10版本.
脚本路径:${ES_HOME}/config/scripts/delete_doc.groovy
脚本内容:ctx.op = ctx._source.age == age ? 'delete': 'none' ctx.op = ctx._source.age == param ? 'delete' : 'none'
使用示例:
POST employee/developer/1/_update
{
"script": {
"lang": "groovy",
"file": "delete_doc",
"params": {
"age": 20 // 如果年龄是20, 则删除之
}
}
}
响应结果:
#! Deprecation: [groovy] scripts are deprecated, use [painless] scripts instead
{
"_index": "employee",
"_type": "developer",
"_id": "1",
"_version": 13,
"result": "deleted",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
}
}
查看文档是否被删除:
GET employee/developer/1
// 响应结果 - 成功删除:
{
"_index": "employee",
"_type": "developer",
"_id": "1",
"found": false
}
3 partial update的并发控制策略
partial update内部也是通过乐观锁进行并发控制的.
关于并发控制, 请参见博文: Elasticsearch的并发控制策略.
3.1 控制方式
POST index/type/id/_update?retry_on_conflict=5
POST index/type/id/_update?retry_on_conflict=5&version=5
3.2 retry原理
retry_on_conflict
: 发生冲突后的重试次数.
(1) 客户端A、B几乎同时获取同一个文档, 一并获得_version
版本信息, 假设此时_version=1
;
(2) 客户端A修改文档中的部分内容, 将修改写入索引;
(3) Elasticsearch在写入索引时, 检查客户端A提交的文档的版本信息(这里仍然是1) 和 现存的文档的版本信息(这里也是1), 发现相同后, 执行写入操作, 并修改版本号_version=2
;
(4) 客户端B也修改文档中的部分内容, 其操作写回索引的速度稍慢. 此时同样执行过程(3): ES发现客户端B提交的文档的版本为1, 而现存文档的版本为2 ===> 发生冲突, 此次partial update将失败;
(5) partial update操作失败后, 将重复(1) - (3) 过程, 重复的次数, 就是retry_on_conflict
参数的值.
版权声明
出处: 博客园 马瘦风的博客(https://www.cnblogs.com/shoufeng)
感谢阅读, 如果文章有帮助或启发到你, 点个[好文要顶
ES 26 - 通过partial update局部更新索引文档 (partial update增量修改原理)的更多相关文章
- word文档的生成、修改、渲染、打印,使用Aspose.Words
无需MS Word也可执行各种文档处理任务,包括文档的生成.修改.渲染.打印,文档格式转换和邮件合并等文档处理.
- jQuery---jq操作标签文本(html(),text()),jq操作文档标签(插入,删除,修改),克隆,,jq操作属性,jq操作class属性,jq操作表单value,jq操作css,jq操作盒子(重要),jq操作滚动条
jQuery---jq操作标签文本(html(),text()),jq操作文档标签(插入,删除,修改),克隆,,jq操作属性,jq操作class属性,jq操作表单value,jq操作css,jq操作盒 ...
- MongoDB小结12 - update【多文档更新】
当一次更新一个文档无法满足我们的脚步时,我们可以选择一次更新多个文档,及在update的第四个参数的位置添上true,及做多文档更新,建议就算不做多文档更新也显式的在第四个参数上置false,这样明确 ...
- 4: ES内执行Groovy脚本,做文档部分更新、执行判断改变操作类型
ES有内置的Groovy脚本执行内核,可以在命令的Json内嵌入Groovy脚本语句 前提数据:
- es的相关知识二(检索文档)
一.es的使用 1.检索文档: 想要从Elasticsearch中获取文档,我们使用同样的 _index . _type . _id ,但是HTTP方法改为 GET : GET /{index ...
- hisql orm update表数据更新文档
更新 HiSql数据更新 HiSql 提供了好几种数据更新的方式下面一一介绍一下 如果你的表中增加了这四个字段 字段 描述 类型 CreateTime 创建时间 DateTime CreateName ...
- __x__(26)0907第四天__文档流_网页最底层
文档流 处在网页的最底层,表示的是一个页面中的位置. 创建的元素,默认都处于文档流中. 元素在文档流中的特点 块元素 在文档流中独占一行. 自上而下排列. 宽度默认占父元素的 100%,width=& ...
- 26 JavaScript HTML DOM简介&方法&文档
HTML DOM: Document Object Model 文档对象模型.是HTML的标准对象模型和编程接口.(JavaScript只是可以操作HTML DOM的语言之一) 定义了HTML元素 ...
- 使用NPOI读取Word文档内容并进行修改
前言 网上使用NPOI读取Word文件的例子现在也不少,本文就是参考网上大神们的例子进行修改以适应自己需求的. 参考博文 http://www.cnblogs.com/mahongbiao/p/376 ...
随机推荐
- 站在巨人的肩膀上看Servlet——原来如此(更适合初学者认识Servlet)
前言: 有段时间没更新博客了,这段时间因为要准备考试,考完试后又忙了一阵别的事,一直没能静下心来写博客.大学考试真是越来越恶心了,各种心酸,那酸爽,够味.不过还好,马上就要大三了,听大三学长学姐说大三 ...
- kuangbin专题 专题一 简单搜索 Pots POJ - 3414
题目链接:https://vjudge.net/problem/POJ-3414 题意:给你两个杯子,分别容量为A(1),B(2)和一个C,C是需要经过下列操作,得到的一个升数.(1) FILL(i) ...
- C++学习书籍推荐《Effective C++ 第三版(英文)》下载
百度云及其他网盘下载地址:点我 作者简介 Scott Meyers is one of the world's foremost authorities on C++, providing train ...
- Spring Boot微服务电商项目开发实战 --- 基础配置及搭建
根据SpringBoot实现分布式微服务项目近两年的开发经验,今天决定开始做SpringBoot实现分布式微服务项目的系列文章,帮助其他正在使用或计划使用SringBoot开发的小伙伴们.本次系列文章 ...
- nu.xom:Serializer
Serializer: 机翻 /* 使用用于控制空格,规范化,缩进,换行和基本URI的各种选项以特定编码输出Document对象 */ Serializer(OutputStream out) :创建 ...
- c++小游戏——三国杀
#include<iostream> #include<time.h> #include<stdio.h> #include <stdlib.h> us ...
- Spark-windows安装
Spark 目的:达到能在pycharm中测试 1.安装必要的文件: JDK AnaConda spark hadoop jdk测试:java -version Anaconda测试: 打开Anaco ...
- C/C++中指向结构体变量的指针,调用指向的那个结构体中的成员
设p是指向结构体变量的指针,则可以通过以下的方式,调用指向的那个结构体中的成员: (1)结构体变量.成员名.如,stu.num. (2)(*p).成员名.如,(*p).num. (3)p->成员 ...
- Codeforces1144B(B题)Parity Alternated Deletions
B. Parity Alternated Deletions Polycarp has an array aa consisting of nn integers. He wants to play ...
- 没事别想不开做Halcon视觉工程师 halcon机器视觉如何学习?
今天我们来听听看来自一个机器视觉工程师的唠叨和吐槽,在这之后,你还想学人工智能,还想学机器视觉?恭喜你,你对人工智能机器视觉是真爱了! 既然自己选择了这条路,那么无论前进路上有多坎坷,跪着也要走完. ...