专栏首页Jed的技术阶梯016.Elasticsearch文档管理操作

016.Elasticsearch文档管理操作

1.1 创建测试用index

curl -X PUT "node01:9200/nba"

curl -X PUT "node01:9200/nba" -H 'Content-Type:application/json' -d'
{
    "mappings": {
        "_doc": {
            "properties": {
                "jerse_no": {
                    "type": "keyword"
                },
                "name": {
                    "type": "text"
                },
                "play_year": {
                    "type": "keyword"
                },
                "position": {
                    "type": "keyword"
                },
                "team_name": {
                    "type": "text"
                },
                "country": {
                    "type": "keyword"
                }
            }
        }
    }
}
'

1.2 自动创建索引

当向一个不存在的index中添加document时,可以自动创建索引,也可以根据传入的数据自动创建mapping,ES也会自动对这些文档进行倒排索引

# 查看ES集群配置
curl -X GET "node01:9200/_cluster/settings"

{
    "persistent":{},
    "transient":{}
}

# 设置自动创建index功能
curl -X PUT "node01:9200/_cluster/settings" -H 'Content-Type:application/json' -d'
{
    "persistent": {
        "action.auto_create_index": "false"
    }
}
'

{   "acknowledged":true,
    "persistent":{
        "action":{
            "auto_create_index":"false"
        }
    },
    "transient":{}
}

当把action.auto_create_index设置成false后,就不可以向不存在的index插入数据了

1.2 添加document

# 手动指定id
curl -X PUT "node01:9200/nba/_doc/1" -H 'Content-Type:application/json' -d'
{
    "name":"哈登",
    "team_name":"火箭",
    "position":"得分后卫",
    "play_year":"10",
    "jerse_no":"13",
    "country": "美国"
}
'
# 也可以让ES自动生成id,不指定id要使用POST请求
curl -X POST "node01:9200/nba/_doc" -H 'Content-Type:application/json' -d'
{
    "name":"库里",
    "team_name":"勇士",
    "position":"控球后卫",
    "play_year":"10",
    "jerse_no":"13",
    "country": "美国"
}
'

# 可以手动指定操作类型
# 指定操作类型时,必须手动设置id
# 当不指定操作时,假如原来已经有id=2的document,那么执行以下操作就会把原来的覆盖掉
# 而指定操作为"create",那么当id=2的document已经存在时,就会报错
curl -X POST "node01:9200/nba/_doc/2?op_type=create" -H 'Content-Type:application/json' -d'
{
    "name":"姚明",
    "team_name":"火箭",
    "position":"中锋",
    "play_year":"9",
    "jerse_no":"11",
    "country": "中国"
}
'

自动设置id与手动设置id的比较:

  • 手动设置id

一般来说,从其他外部系统导入数据到es时,会采取这种方式,使用外部系统中已有数据的唯一标识,作为document的id,例如数据从MySQL导入到ES中,就可以直接使用MySQL表中自己的id

  • 自动生成id

自动生成的id,长度为20个字符,URL安全,base64编码,使用GUID算法可以保证在并行生成id时也不会发生冲突,在直接往ES中写数据的时候,可以使用这种方式

1.3 查看文档

查看单个文档

curl -X GET "node01:9200/nba/_doc/1"

{
    "_index":"nba",
    "_type":"_doc",
    "_id":"1",
    "_version":1,
    "_seq_no":0,
    "_primary_term":1,
    "found":true,
    "_source": {
        "name":"哈登",
        "team_name":"火箭",
        "position":"得分后卫",
        "play_year":"10",
        "jerse_no":"13",
        "country": "美国"
    }
}

文档元数据解析:

  • _index:此文档属于哪个index
  • _type:此文档属于哪个type
  • _id:此文档的id
  • _version:此文档的版本号,ES基于此版本进行并发控制
  • _source:此文档的数据内容

指定返回结果的字段:

curl -X GET "node01:9200/nba/_doc/1?_source=name,country"

{
    "_index":"nba",
    "_type":"_doc",
    "_id":"1",
    "_version":1,
    "_seq_no":0,
    "_primary_term":1,
    "found":true,
    "_source": {
        "name":"哈登",
        "country": "美国"
    }
}

查看多个文档

curl -X POST "node01:9200/_mget" -H 'Content-Type:application/json' -d'
{
    "docs": [
        {
            "_index": "nba",
            "_type": "_doc",
            "_id": "1"
        },
        {
            "_index": "nba",
            "_type": "_doc",
            "_id": "2"
        }
    ]
}
'

curl -X POST "node01:9200/nba/_mget" -H 'Content-Type:application/json' -d'
{
    "docs": [
        {
            "_type": "_doc",
            "_id": "1"
        },
        {
            "_type": "_doc",
            "_id": "2"
        }
    ]
}
'

curl -X POST "node01:9200/nba/_doc/_mget" -H 'Content-Type:application/json' -d'
{
    "docs": [
        {
            "_id": "1"
        },
        {
            "_id": "2"
        }
    ]
}
'

curl -X POST "node01:9200/nba/_doc/_mget" -H 'Content-Type:application/json' -d'
{
    "ids": ["1", "2"]
}
'

说明:

mget是很重要的,查询的时候,如果一次性要查询多条数据的话,那么一定要用mget,尽可能减少网络开销次数,这样可以大幅提升查询性能

1.4 修改文档

# 方法一:直接覆盖原文档
# 但是要列出所有的field,即使这个field不修改
# 否则新的文档就不包含你没有写的那个field了
# 当然,如果就是要删除这个字段,就不用写了
curl -X PUT "node01:9200/nba/_doc/1" -H 'Content-Type:application/json' -d'
{
    "name":"大胡子",
    "team_name":"火箭",
    "position":"得分后卫",
    "play_year":"9",
    "jerse_no":"13",
    "country": "美国"
}
'

# 方法二:修改指定的字段
curl -X POST "node01:9200/nba/_doc/1/_update" -H 'Content-Type:application/json' -d'
{
    "doc": {
        "name":"登哥"
    }
}
'
# ES7.x
curl -X POST "node01:9200/nba/_update/1" -H 'Content-Type:application/json' -d'
{
    "doc": {
        "name":"登哥"
    }
}
'

# 增加一个字段
curl -X POST "node01:9200/nba/_doc/1/_update" -H 'Content-Type:application/json' -d'
{
    "script": "ctx._source.age=18"
}
'
# ES7.x
curl -X POST "node01:9200/nba/_update/1" -H 'Content-Type:application/json' -d'
{
    "script": "ctx._source.age=18"
}
'

# 删除一个字段
curl -X POST "node01:9200/nba/_doc/1/_update" -H 'Content-Type:application/json' -d'
{
    "script": "ctx._source.remove(\"age\")"
}
'
# ES7.x
curl -X POST "node01:9200/nba/_update/1" -H 'Content-Type:application/json' -d'
{
    "script": "ctx._source.remove(\"age\")"
}
'

# 根据参数值,更新字段,要求文档存在并且修改的field存在
curl -X POST "node01:9200/nba/_doc/1/_update" -H 'Content-Type:application/json' -d'
{
    "script": "ctx._source.age+=params.age",
    "params": {
        "age": 4
    }
}
'
# 根据参数值,更新字段,如果文档不存在,新创建一个文档,并且将upsert中的数据插入到该文档中
curl -X POST "node01:9200/nba/_doc/3/_update" -H 'Content-Type:application/json' -d'
{
    "script": {
        "source": "ctx._source.age+=params.age",
        "params": {
            "age": 4
        }
    },
    "upsert": {
        "age": 10
    }
}
'

说明:

修改一个文档的某个字段,在ES的底层,其实也是全量替换,将原来的文档标记为delete状态,新插入一条数据,根据客户端传入的字段加上原数据的其他字段组成了一条新的文档,只不过,这些操作都在shard内部去做了,相比于让用户执行全量替换的操作,优化了网络传输开销,减少了查询和修改的时间开销,提升了性能。

1.5 删除文档

curl -X DELETE "node01:9200/nba/_doc/3"

1.6 批量增删改查

语法:

POST /_bulk
{"delete": {"_index": "index_name", "_type": "type_name", "_id": 1}}
{"create": {"_index": "index_name", "_type": "type_name", "_id": 4}}
{"doc": {"field": "value"}}
{"create": {"_index": "index_name", "_type": "type_name", "_id": 5}}
{"doc": {"field1": "value1", "field2": "value2"}}
{"update": {"_index": "index_name", "_type": "type_name", "_id": 1, "some_properties": "properties_value"}}
{"doc": {"field": "value"}}

# 例如:
POST /_bulk
{"delete": {"_index": "user", "_type": "_doc", "_id": 5}}
{"create": {"_index": "user", "_type": "_doc", "_id": 6}}
{"doc": {"uid": "1006", "uname": "Nancy"}}
{"index": {"_index": "shop", "_type": "product", "_id": 5}}
{"doc": {"name": "Gaolujie Toothpaste"}}
{"update": {"_index": "user", "_type": "_doc", "_id": 1, "retry_on_conflict": 3}}
{"doc": {"name": "Nancy"}}

bulk api对json的语法有严格的要求,每个json串不能换行,只能放一行,同时一个json串和一个json串之间,必须换行,除"delete"操作外,每个操作要两个json串,语法如下:

{"action": {"metadata"}}
{"data"}

# action包括:
delete:删除一个文档,只要1个json串就可以了
create:PUT /index/type/id/_create,强制创建
index:普通的put操作,可以是创建文档,也可以是全量替换文档
update:更新操作

任意一个操作失败,不会影响其他操作,但是在返回结果里,会告诉你哪个操作失败了及其错误信息

批量操作会将所有请求加载到内存中,一次请求过多的话,性能反而会下降,一般从5000-1W条数据开始测试,以得到一个最佳性能,同时,一般来说,将一次请求的大小在5-15MB之间

为什么批量操作对请求json字符串的要求这么严格呢?

bulk中的每个操作都可能要转发到不同的node的shard去执行,如果采用比较良好的json数组格式,允许任意的换行,整个可读性非常棒,读起来很爽,es拿到那种标准格式的json串以后,要按照下述流程去进行处理:

  • 将json数组解析为JSONArray对象,整个数据在内存中出现两份,一份数据是json文本,一份数据是JSONArray对象
  • 解析json数组里的每个json,对每个请求中的document进行路由
  • 为路由到同一个shard上的多个请求,创建一个请求数组
  • 将这个请求数组序列化
  • 将序列化后的请求数组发送到对应的节点上去

这样就耗费更多内存,造成更多的jvm gc开销,导致性能下降,而使用这种严格的json格式之后:

  • 不用解析json字符串,不用将其转换为json对象,不会出现内存中的相同数据的拷贝,直接按照换行符切割json
  • 对每两个一组的json,读取其请求信息,进行document路由
  • 直接将json发送到对应的node上去

这样,减少了内存开销,减少了解析成本,提高了性能

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 001.快速理解Elasticsearch是什么

    Lucene实现了建立倒排索引、搜索等功能以及各种算法,对于开发人员来说,只要引入lucene的jar包,基于lucene的API进行开发即可。

    CoderJed
  • 017.Elasticsearch搜索操作入门篇

    在插入这条数据后,ES会自动将多个field的值,全部用字符串的方式串联起来,组成一个长字符串,作为_all field的值,同时建立索引,本例中,"_all ...

    CoderJed
  • 图解 Java 数组与内存控制

    Java的数组变量是一种引用类型的变量,数组变量并不是数组本身,它只是指向堆内存中的数组对象,改变一个数组变量所引用的数组,可以造成数组长度可变的假象。

    CoderJed
  • 前端day21-JS正则表达式

    1.字符类: /[abc]/ 含义:只要字符串中有 a 或者有 b 或者有 c 就满足条件

    帅的一麻皮
  • python面向对象小练习

    用户1539362
  • String - 151. Reverse Words in a String

    Given an input string, reverse the string word by word.

    用户5705150
  • Nginx 负载均衡配置+使用方法

    双面人
  • TypeScript手记(六)

    TypeScript 里,在有些没有明确指出类型的地方,类型推断会帮助提供类型。如下面的例子:

    用户7572539
  • 给你讲讲五年前工作遇到的,海量数据分页场景(limit,offset)为什么会慢

    原文链接:http://t.cn/AidABz08

    业余草
  • SNMP学习笔记之SNMP4J介绍(Java)

      SNMP4J是一个用Java来实现SNMP(简单网络管理协议)协议的开源项目.它支持以命令行的形式进行管理与响应。SNMP4J是纯面向对象设计与SNMP++...

    Jetpropelledsnake21

扫码关注云+社区

领取腾讯云代金券