美文网首页个人学习
elasticsearch之五document详解

elasticsearch之五document详解

作者: Java及SpringBoot | 来源:发表于2020-02-17 16:56 被阅读0次

    个人专题目录


    1. 文档document详解

    1.1 document默认自带字段解析

    {
      "_index" : "book",
      "_type" : "_doc",
      "_id" : "1",
      "_version" : 1,
      "_seq_no" : 10,
      "_primary_term" : 1,
      "found" : true,
      "_source" : {
        "name" : "Bootstrap开发教程1",
        "description" : "Bootstrap是由Twitter推出的一个前台页面开发css框架,是一个非常流行的开发框架,此框架集成了多种页面效果。此开发框架包含了大量的CSS、JS程序代码,可以帮助开发者(尤其是不擅长css页面开发的程序人员)轻松的实现一个css,不受浏览器限制的精美界面css效果。",
        "studymodel" : "201002",
        "price" : 38.6,
        "timestamp" : "2019-08-25 19:11:35",
        "pic" : "group1/M00/00/00/wKhlQFs6RCeAY0pHAAJx5ZjNDEM428.jpg",
        "tags" : [
          "bootstrap",
          "开发"
        ]
      }
    }
    

    _index

    • 含义:此文档属于哪个索引
    • 原则:类似数据放在一个索引中。数据库中表的定义规则。如图书信息放在book索引中,员工信息放在employee索引中。各个索引存储和搜索时互不影响。
    • 定义规则:英文小写。尽量不要使用特殊字符。order user

    _type

    • 含义:类别。book java node
    • 注意:以后的es9将彻底删除此字段,所以当前版本在不断弱化type。不需要关注。见到_type都为doc。

    _id

    含义:文档的唯一标识。就像表的id主键。结合索引可以标识和定义一个文档。

    生成:手动(put /index/_doc/id)、自动

    创建索引时,不同数据放到不同索引中

    生成文档id的方式

    手动生成id

    场景:数据从其他系统导入时,本身有唯一主键。如数据库中的图书、员工信息等。使用这种方式,需要考虑是否满足手动指定id的条件。如果数据是从其他数据源中读取并新增到ElasticSearch中的时候,使用手动指定id。如:数据是从Database中读取并新增到ElasticSearch中的,那么使用Database中的PK作为ElasticSearch中的id比较合适。建议,不要把不同表的数据新增到同一个index中,可能有id冲突。

    用法:put /index/_doc/id

    PUT /test_index/_doc/1
    {
      "test_field": "test"
    }
    

    自动生成id

    自动生成的ID特点:长度为20的字符串;URL安全(经过base64编码的);GUID生成策略,支持分布式高并发(在分布式系统中,并发生成ID也不会有重复可能,参考https://baike.baidu.com/item/GUID/3352285?fr=aladdin)。适合用于手工录入的数据。数据没有一个数据源,且未经过任何的管理和存储。这种数据,是没有唯一标识,如果使用手工指定id的方式,容易出现id冲突,导致数据丢失。

    用法:POST /index/_doc

    POST /test_index/_doc
    {
      "test_field": "test1"
    }
    

    返回:

    {
      "_index" : "test_index",
      "_type" : "_doc",
      "_id" : "x29LOm0BPsY0gSJFYZAl",
      "_version" : 1,
      "result" : "created",
      "_shards" : {
        "total" : 2,
        "successful" : 1,
        "failed" : 0
      },
      "_seq_no" : 0,
      "_primary_term" : 1
    }
    

    自动id特点:

    长度为20个字符,URL安全,base64编码,GUID,分布式生成不冲突

    _source 字段

    含义:就是查询的document中的field值。也就是document的json字符串。此元数据可以定义显示结果(field)。

    GET /book/_doc/1

    定制返回字段

    就像sql不要select *,而要select name,price from book …一样。

    GET /book/_doc/1?__source_includes=name,price

    {
      "_index" : "book",
      "_type" : "_doc",
      "_id" : "1",
      "_version" : 1,
      "_seq_no" : 10,
      "_primary_term" : 1,
      "found" : true,
      "_source" : {
        "price" : 38.6,
        "name" : "Bootstrap开发教程1"
      }
    }
    

    _version元数据

    代表的是document的版本。在ElasticSearch中,为document定义了版本信息,document数据每次变化,代表一次版本的变更。版本变更可以避免数据并发冲突,同时提高ElasticSearch的搜索效率。

    第一次创建Document时,_version版本号为1,默认情况下,后续每次对Document执行修改或删除操作都会对_version数据自增1。

    删除Document也会_version自增1。

    当使用PUT命令再次增加同id的Document,_version会继续之前的版本继续自增。

    1.2 使用脚本更新

    es可以内置脚本执行复杂操作。例如painless脚本。

    注意:groovy脚本在es6以后就不支持了。原因是耗内存,不安全远程注入漏洞。

    内置脚本

    需求1:修改文档6的num字段,+1。

    插入数据

    PUT /test_index/_doc/6
    {
      "num": 0,
      "tags": []
    }
    

    执行脚本操作

    POST /test_index/_doc/6/_update
    {
       "script" : "ctx._source.num+=1"
    }
    

    查询数据

    GET /test_index/_doc/6
    

    返回

    {
      "_index" : "test_index",
      "_type" : "_doc",
      "_id" : "6",
      "_version" : 2,
      "_seq_no" : 23,
      "_primary_term" : 1,
      "found" : true,
      "_source" : {
        "num" : 1,
        "tags" : [ ]
      }
    }
    

    需求2:搜索所有文档,将num字段乘以2输出

    插入数据

    PUT /test_index/_doc/7
    {
      "num": 5
    }
    

    查询

    GET /test_index/_search
    {
      "script_fields": {
        "my_doubled_field": {
          "script": {
           "lang": "expression",
            "source": "doc['num'] * multiplier",
            "params": {
              "multiplier": 2
            }
          }
        }
      }
    }
    

    返回

    {
            "_index" : "test_index",
            "_type" : "_doc",
            "_id" : "7",
            "_score" : 1.0,
            "fields" : {
              "my_doubled_field" : [
                10.0
              ]
            }
          }
    

    外部脚本

    Painless是内置支持的。脚本内容可以通过多种途径传给 es,包括 rest 接口,或者放到 config/scripts目录等,默认开启。

    注意:脚本性能低下,且容易发生注入。

    官方文档:https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-scripting-using.html

    1.3 es内部基于_version乐观锁控制

    如同秒杀,多线程情况下,es同样会出现并发冲突问题。

    为控制并发问题,我们通常采用锁机制。分为悲观锁和乐观锁两种机制。

    悲观锁:很悲观,所有情况都上锁。此时只有一个线程可以操作数据。具体例子为数据库中的行级锁、表级锁、读锁、写锁等。

    特点:优点是方便,直接加锁,对程序透明。缺点是效率低。

    乐观锁:很乐观,对数据本身不加锁。提交数据时,通过一种机制验证是否存在冲突,如es中通过版本号验证。

    特点:优点是并发能力高。缺点是操作繁琐,在提交数据时,可能反复重试多次。

    基于_version的版本控制

    es对于文档的增删改都是基于版本号。

    1新增多次文档:

    PUT /test_index/_doc/3
    {
      "test_field": "test"
    }
    

    返回版本号递增

    2删除此文档

    DELETE /test_index/_doc/3
    

    返回

    DELETE /test_index/_doc/3
    {
      "_index" : "test_index",
      "_type" : "_doc",
      "_id" : "2",
      "_version" : 6,
      "result" : "deleted",
      "_shards" : {
        "total" : 2,
        "successful" : 1,
        "failed" : 0
      },
      "_seq_no" : 7,
      "_primary_term" : 1
    }
    

    3再新增

    PUT /test_index/_doc/3
    {
      "test_field": "test"
    }
    

    可以看到版本号依然递增,验证延迟删除策略。

    如果删除一条数据立马删除的话,所有分片和副本都要立马删除,对es集群压力太大。

    es内部并发控制

    es内部主从同步时,是多线程异步。乐观锁机制。

    1.4 客户端程序基于_version并发操作流程

    java python客户端更新的机制。

    新建文档

    PUT /test_index/_doc/5
    {
      "test_field": "itcast"
    }
    

    返回:

    {
      "_index" : "test_index",
      "_type" : "_doc",
      "_id" : "3",
      "_version" : 1,
      "result" : "created",
      "_shards" : {
        "total" : 2,
        "successful" : 1,
        "failed" : 0
      },
      "_seq_no" : 8,
      "_primary_term" : 1
    }
    

    客户端1修改。带版本号1。

    首先获取数据的当前版本号

    GET /test_index/_doc/5
    

    更新文档

    PUT /test_index/_doc/5?version=1
    {
      "test_field": "itcast1"
    }
    PUT /test_index/_doc/5?if_seq_no=21&if_primary_term=1
    {
      "test_field": "itcast1"
    }
    

    客户端2并发修改。带版本号1。

    PUT /test_index/_doc/5?version=1
    {
      "test_field": "itcast2"
    }
    PUT /test_index/_doc/5?if_seq_no=21&if_primary_term=1
    {
      "test_field": "itcast1"
    }
    

    报错。

    客户端2重新查询。得到最新版本为2。seq_no=22

    GET /test_index/_doc/4
    

    客户端2并发修改。带版本号2。

    PUT /test_index/_doc/4?version=2
    {
      "test_field": "itcast2"
    }
    es7
    PUT /test_index/_doc/5?if_seq_no=22&if_primary_term=1
    {
      "test_field": "itcast2"
    }
    

    修改成功。

    1.5 手动控制版本号 external version

    背景:已有数据是在数据库中,有自己手动维护的版本号的情况下,可以使用external version控制。hbase。

    要求:修改时external version要大于当前文档的_version

    对比:基于_version时,修改的文档version等于当前文档的版本号。

    使用?version=1&version_type=external

    新建文档

    PUT /test_index/_doc/4
    {
      "test_field": "itcast"
    }
    

    更新文档:

    客户端1修改文档

    PUT /test_index/_doc/4?version=2&version_type=external
    {
      "test_field": "itcast1"
    }
    

    客户端2同时修改

    PUT /test_index/_doc/4?version=2&version_type=external
    {
      "test_field": "itcast2"
    }
    

    返回:

    {
      "error": {
        "root_cause": [
          {
            "type": "version_conflict_engine_exception",
            "reason": "[4]: version conflict, current version [2] is higher or equal to the one provided [2]",
            "index_uuid": "-rqYZ2EcSPqL6pu8Gi35jw",
            "shard": "1",
            "index": "test_index"
          }
        ],
        "type": "version_conflict_engine_exception",
        "reason": "[4]: version conflict, current version [2] is higher or equal to the one provided [2]",
        "index_uuid": "-rqYZ2EcSPqL6pu8Gi35jw",
        "shard": "1",
        "index": "test_index"
      },
      "status": 409
    }
    

    客户端2重新查询数据

    GET /test_index/_doc/4
    

    客户端2重新修改数据

    PUT /test_index/_doc/4?version=3&version_type=external
    {
      "test_field": "itcast2"
    }
    

    1.6 更新时 retry_on_conflict 参数

    retry_on_conflict

    指定重试次数

    POST /test_index/_doc/5/_update?retry_on_conflict=3
    {
      "doc": {
        "test_field": "itcast1"
      }
    }
    

    与 _version结合使用

    POST /test_index/_doc/5/_update?retry_on_conflict=3&version=22&version_type=external
    {
      "doc": {
        "test_field": "itcast1"
      }
    }
    

    1.7 批量查询 mget

    单条查询 GET /test_index/_doc/1,如果查询多个id的文档一条一条查询,网络开销太大。

    mget 批量查询:

    GET /_mget
    {
       "docs" : [
          {
             "_index" : "test_index",
             "_type" :  "_doc",
             "_id" :    1
          },
          {
             "_index" : "test_index",
             "_type" :  "_doc",
             "_id" :    7
          }
       ]
    }
    

    返回:

    {
      "docs" : [
        {
          "_index" : "test_index",
          "_type" : "_doc",
          "_id" : "2",
          "_version" : 6,
          "_seq_no" : 12,
          "_primary_term" : 1,
          "found" : true,
          "_source" : {
            "test_field" : "test12333123321321"
          }
        },
        {
          "_index" : "test_index",
          "_type" : "_doc",
          "_id" : "3",
          "_version" : 6,
          "_seq_no" : 18,
          "_primary_term" : 1,
          "found" : true,
          "_source" : {
            "test_field" : "test3213"
          }
        }
      ]
    }
    

    提示去掉type

    GET /_mget
    {
       "docs" : [
          {
             "_index" : "test_index",
             "_id" :    2
          },
          {
             "_index" : "test_index",
             "_id" :    3
          }
       ]
    }
    

    同一索引下批量查询:

    GET /test_index/_mget
    {
       "docs" : [
          {
             "_id" :    2
          },
          {
             "_id" :    3
          }
       ]
    }
    

    第三种写法:搜索写法

    post /test_index/_doc/_search
    {
        "query": {
            "ids" : {
                "values" : ["1", "7"]
            }
        }
    }
    

    1.8 批量增删改 bulk

    Bulk 操作解释将文档的增删改查一些列操作,通过一次请求全都做完。减少网络传输次数。

    语法:

    POST /_bulk
    {"action": {"metadata"}}
    {"data"}
    

    如下操作,删除5,新增14,修改2。

    POST /_bulk
    { "delete": { "_index": "test_index",  "_id": "5" }} 
    { "create": { "_index": "test_index",  "_id": "14" }}
    { "test_field": "test14" }
    { "update": { "_index": "test_index",  "_id": "2"} }
    { "doc" : {"test_field" : "bulk test"} }
    

    总结:

    1. 功能:
    • delete:删除一个文档,只要1个json串就可以了
    • create:相当于强制创建 PUT /index/type/id/_create
    • index:普通的put操作,可以是创建文档,也可以是全量替换文档
    • update:执行的是局部更新partial update操作
    1. 格式:每个json不能换行。相邻json必须换行。

    2. 隔离:每个操作互不影响。操作失败的行会返回其失败信息。

    3. 实际用法:bulk请求一次不要太大,否则一下积压到内存中,性能会下降。所以,一次请求几千个操作、大小在几M正好。

    4. bulk size最佳大小

    bulk request会加载到内存里,如果太大的话,性能反而会下降,因此需要反复尝试一个最佳的bulk size。一般从10005000条数据开始,尝试逐渐增加。另外,如果看大小的话,最好是在515MB之间。

    相关文章

      网友评论

        本文标题:elasticsearch之五document详解

        本文链接:https://www.haomeiwen.com/subject/hwxifhtx.html