logstash的elasticsearch output plugin:不同action的区别(index/create/update)

elasticsearch plugin的action

logstash提供了多达40多种的output plugin用于将处理后的数据输出到下游系统。其中最为常见的输出端自然是elasticsearch plugin。而elasticsearch plugin提供了3种操作类型,分别是indexcreateupdate

对于这三种action,官方文档上的解释如下:

Value type is string
Default value is “index”
Protocol agnostic (i.e. non-http, non-java specific) configs go here Protocol agnostic methods The Elasticsearch action to perform. Valid actions are:

  • index: indexes a document (an event from Logstash).
  • delete: deletes a document by id (An id is required for this action)
  • create: indexes a document, fails if a document by that id already exists in the index.
  • update: updates a document by id. Update has a special case where you can upsert — update a document if not already present. See the upsert option. NOTE: This does not work and is not supported in Elasticsearch 1.x. Please upgrade to ES 2.x or greater to use this feature with Logstash!
    A sprintf style string to change the action based on the content of the event. The value %{[foo]} would use the foo field for the action

For more details on actions, check out the Elasticsearch bulk API documentation

这段解释没有针对不同的情况给予详细的说明,因此,以下问题让人费解:

  • indexdocument_id 的关系
  • indexcreateupdate的异同

而让人不知道该选择何种类型的操作。在这里,我们进行深入的解释

create

相当于:

POST _bulk
{ "create" : { "_index" : "<index>", "_id" : "<id>" } }
{ "<field>" : "<value>" }

其具体行为是:

  • 必须指定document_id ,若不指定,直接返回失败
  • 如果 document_id 在elasticsearch中不存在,创建一条新的文档,使用document_id 作为该文档的_id
  • 如果 document_id 在elasticsearch中存在,直接返回失败

只适合用在像是用户创建之后就不能再更新的场景。

index

index是action的默认值。
相当于:

POST _bulk
{ "index" : { "_index" : "<index>", "_id" : "<id>" } }
{ "<field>" : "<value>" }

POST _bulk
{ "index" : { "_index" : "<index>" } }
{ "<field>" : "<value>" }

其具体行为是:

  • 没有document_id 的情况下,会将数据索引到elasticsearch当中,并且由elasticsearch生成该文档的_id。因为没有指定document_id ,重复的数据会生成多条内容相同但_id不同的文档。
  • document_id 的情况下:
    • 如果 document_id 在elasticsearch中不存在,创建一条新的文档,使用document_id 作为该文档的_id
    • 如果 document_id 在elasticsearch中存在,直接更新该文档。

注意:如果指定了document_id ,会造成写入的效率降低,因为额外增加了查询该document_id 是否存在的过程。因此,在日志分析等只写入不更新的场景,就不要尝试自己去指定id。

update

相当于:

POST _bulk
{ "update" : { "_index" : "<index>", "_id" : "<id>" } }
{ "<field>" : "<value>" }

其具体行为是:

  • 必须指定document_id ,若不指定,直接无法启动pipeline
  • document_id 的情况下:
    • 如果 document_id 在elasticsearch中不存在,创建一条新的文档,使用document_id 作为该文档的_id
      • doc_as_upsert为true,使用event的值做为文档的值
      • scripted_upsert为true,使用script作为文档的值
      • 其余情况,使用upsert作为文档的值
    • 如果 document_id 在elasticsearch中存在,直接更新该文档。

doc_as_upsert

input {
  stdin {
    add_field => {
      id => "my_id"
    }
  }
}

output {
  elasticsearch {
    hosts => ["http://localhost:9200"]
    index => "logstash-%{+YYYY.MM.dd}"
    document_id => "%{id}"
    action => "update"
    doc_as_upsert => true
    # 不能同时设置 doc_as_upsert => ture和 upsert
    # upsert => '{"message":"hello"}'
  }
  stdout {}
}

在标准输入输入“aaaaa”,文档内容为:

{
  "_index" : "logstash-2019.09.24-000001",
  "_type" : "_doc",
  "_id" : "my_id",
  "_version" : 1,
  "_seq_no" : 0,
  "_primary_term" : 1,
  "found" : true,
  "_source" : {
    "host" : "lexlideMacBook-Pro.local",
    "id" : "my_id",
    "@timestamp" : "2019-09-24T09:52:41.661Z",
    "@version" : "1",
    "message" : "aaaaa"
  }
}

可以看到,这样的逻辑其实是和index一样的

upsert

input {
  stdin {
    add_field => {
      id => "my_id"
    }
  }
}

output {
  elasticsearch {
    hosts => ["http://localhost:9200"]
    index => "logstash-%{+YYYY.MM.dd}"
    document_id => "%{id}"
    action => "update"
    # 不能同时设置 doc_as_upsert => ture和 upsert
    #doc_as_upsert => true
    upsert => '{"message":"hello"}'
  }
  stdout {}
}

在标准输入输入“aaaaa”,文档内容为:

{
  "_index" : "logstash-2019.09.24-000001",
  "_type" : "_doc",
  "_id" : "my_id",
  "_version" : 1,
  "_seq_no" : 0,
  "_primary_term" : 1,
  "found" : true,
  "_source" : {
    "message" : "hello"
  }
}

这种情况下,可以为id不存在的文档指定自定义的数据内容

异常

如果都不设置,则会抛出异常:

[2019-09-24T17:59:26,671][WARN ][logstash.outputs.elasticsearch] Could not index event to Elasticsearch. {:status=>404, :action=>["update", {:_id=>"my_id", :_index=>"logstash", :_type=>"_doc", :routing=>nil, :retry_on_conflict=>1}, #<LogStash::Event:0x61b3501a>], :response=>{"update"=>{"_index"=>"logstash-2019.09.24-000001", "_type"=>"_doc", "_id"=>"my_id", "status"=>404, "error"=>{"type"=>"document_missing_exception", "reason"=>"[_doc][my_id]: document missing", "index_uuid"=>"XWAUuIwqTtmZMnnhnVnMkA", "shard"=>"0", "index"=>"logstash-2019.09.24-000001"}}}}

总结

总的来说,大部分情况下,我们直接使用默认的index操作就可以了,只在一些特殊的情况下使用别的action:

  • 创建指定id的文档,并且只能创建一次的场景下使用create
  • 对指定id的文档进行更新,并且在文档不存在的情况下,需要使用自定义的数据内容而非原始数据内容的,使用update
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章