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中的初始化操作; 如果存在, 就执行docscript中的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://healchow.com)

出处: 博客园 马瘦风的博客(https://www.cnblogs.com/shoufeng)

感谢阅读, 如果文章有帮助或启发到你, 点个[好文要顶

ES 26 - 通过partial update局部更新索引文档 (partial update增量修改原理)的更多相关文章

  1. word文档的生成、修改、渲染、打印,使用Aspose.Words

    无需MS Word也可执行各种文档处理任务,包括文档的生成.修改.渲染.打印,文档格式转换和邮件合并等文档处理.

  2. 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操作盒 ...

  3. MongoDB小结12 - update【多文档更新】

    当一次更新一个文档无法满足我们的脚步时,我们可以选择一次更新多个文档,及在update的第四个参数的位置添上true,及做多文档更新,建议就算不做多文档更新也显式的在第四个参数上置false,这样明确 ...

  4. 4: ES内执行Groovy脚本,做文档部分更新、执行判断改变操作类型

    ES有内置的Groovy脚本执行内核,可以在命令的Json内嵌入Groovy脚本语句   前提数据:           

  5. es的相关知识二(检索文档)

    一.es的使用 1.检索文档: 想要从Elasticsearch中获取文档,我们使用同样的 _index  . _type  . _id  ,但是HTTP方法改为 GET  : GET /{index ...

  6. hisql orm update表数据更新文档

    更新 HiSql数据更新 HiSql 提供了好几种数据更新的方式下面一一介绍一下 如果你的表中增加了这四个字段 字段 描述 类型 CreateTime 创建时间 DateTime CreateName ...

  7. __x__(26)0907第四天__文档流_网页最底层

    文档流 处在网页的最底层,表示的是一个页面中的位置. 创建的元素,默认都处于文档流中. 元素在文档流中的特点 块元素 在文档流中独占一行. 自上而下排列. 宽度默认占父元素的 100%,width=& ...

  8. 26 JavaScript HTML DOM简介&方法&文档

    HTML DOM: Document  Object  Model 文档对象模型.是HTML的标准对象模型和编程接口.(JavaScript只是可以操作HTML DOM的语言之一) 定义了HTML元素 ...

  9. 使用NPOI读取Word文档内容并进行修改

    前言 网上使用NPOI读取Word文件的例子现在也不少,本文就是参考网上大神们的例子进行修改以适应自己需求的. 参考博文 http://www.cnblogs.com/mahongbiao/p/376 ...

随机推荐

  1. NOIP2015斗地主题解 7.30考试

    问题 B: NOIP2015 斗地主 时间限制: 3 Sec  内存限制: 1024 MB 题目描述 牛牛最近迷上了一种叫斗地主的扑克游戏.斗地主是一种使用黑桃.红心.梅花.方片的A到K加上大小王的共 ...

  2. 双剑合璧——掌握 cURL 和 Dig 走天涯

    如今随着大量的应用转移到网络,作为开发者,会经常做一些通讯测试,例如从网站获取信息.模拟用户向网站提交或者上传数据,查看应用通讯情况等等,现在变成了非常重要的任务. 一起来认识 cURL cURL 是 ...

  3. textarea 绕过jq验证的方法,提交空值

    <textarea placeholder=" aria-required="true" aria-describedby="OtherNotes-err ...

  4. Spark第一周

    Why Scala 在数据集不是很大的时候,开发人员可以使用python.R.MATLAB等语言在单机上处理数据集.但是在大数据时代,数据集少说都是TB.PB级别,此时便需要分布式地处理.相较于上述语 ...

  5. 从无到有构建vue实战项目(六)

    十.徒手撸一个vue下拉左侧二级导航 先附上最终效果图: vue代码: <div class="dropdown-menu-explore" v-on:mouseover=& ...

  6. C#3.0新增功能09 LINQ 基础06 LINQ 查询操作中的类型关系

    连载目录    [已更新最新开发文章,点击查看详细] 若要有效编写查询,应了解完整的查询操作中的变量类型是如何全部彼此关联的. 如果了解这些关系,就能够更容易地理解文档中的 LINQ 示例和代码示例. ...

  7. 阿里云服务器连接以及centos 搭建 web java环境(linux java部署 tomcat部署)

    版权声明:本文为博主原创文章,未经博主允许不得转载. 最近弄了个试用阿里云服务器倒腾了半天终于部署好,分享一下. 1.登入阿里云打开你申请的是云服务器的实例: 点击重置密码---重置密码后重启服务器才 ...

  8. Python中文本文件读写操作的编码问题

    Python中文本文件读写的编码问题 编码(encode): 我们输入的任何字符想要以文件(如.txt)的形式保存在计算机的硬盘上, 必须先经按照一定的规则编成计算机认识的二进制后,才能存在电脑硬盘上 ...

  9. 將Python打包成 exe可执行文件

    利用Python寫了一個小腳本想要傳給使用Windows但沒有裝Python的朋友執行,這時候就可以利用將檔案包裝成exe檔案,讓沒有Python的朋友也可以執行.本篇將介紹利用套件「PyInstal ...

  10. RabbitMQ搭建单机及集群

    1,基本环境配置 hosts 文件 免密登录 2,访问官网 https://www.rabbitmq.com/download.html 3, 4,安装依赖 yum -y install make g ...