Term Vectors

https://www.elastic.co/guide/en/elasticsearch/reference/5.5/docs-termvectors.html


edit

Returns information and statistics on terms in the fields of a particular document. The document could be stored in the index or artificially provided by the user. Term vectors are realtime by default, not near realtime. This can be changed by setting realtime parameter to false.

GET /twitter/tweet/1/_termvectors

Optionally, you can specify the fields for which the information is retrieved either with a parameter in the url

GET /twitter/tweet/1/_termvectors?fields=message

or by adding the requested fields in the request body (see example below). Fields can also be specified with wildcards in similar way to the multi match query

Warning

Note that the usage of /_termvector is deprecated in 2.0, and replaced by /_termvectors.

Return valuesedit

Three types of values can be requested: term informationterm statistics and field statistics. By default, all term information and field statistics are returned for all fields but no term statistics.

Term informationedit

  • term frequency in the field (always returned)
  • term positions (positions : true)
  • start and end offsets (offsets : true)
  • term payloads (payloads : true), as base64 encoded bytes

If the requested information wasn’t stored in the index, it will be computed on the fly if possible. Additionally, term vectors could be computed for documents not even existing in the index, but instead provided by the user.

Warning

Start and end offsets assume UTF-16 encoding is being used. If you want to use these offsets in order to get the original text that produced this token, you should make sure that the string you are taking a sub-string of is also encoded using UTF-16.

Term statisticsedit

Setting term_statistics to true (default is false) will return

  • total term frequency (how often a term occurs in all documents)
  • document frequency (the number of documents containing the current term)

By default these values are not returned since term statistics can have a serious performance impact.

Field statisticsedit

Setting field_statistics to false (default is true) will omit :

  • document count (how many documents contain this field)
  • sum of document frequencies (the sum of document frequencies for all terms in this field)
  • sum of total term frequencies (the sum of total term frequencies of each term in this field)

Terms Filteringedit

With the parameter filter, the terms returned could also be filtered based on their tf-idf scores. This could be useful in order find out a good characteristic vector of a document. This feature works in a similar manner to the second phase of the More Like This Query. See example 5 for usage.

The following sub-parameters are supported:

max_num_terms

Maximum number of terms that must be returned per field. Defaults to 25.

min_term_freq

Ignore words with less than this frequency in the source doc. Defaults to 1.

max_term_freq

Ignore words with more than this frequency in the source doc. Defaults to unbounded.

min_doc_freq

Ignore terms which do not occur in at least this many docs. Defaults to 1.

max_doc_freq

Ignore words which occur in more than this many docs. Defaults to unbounded.

min_word_length

The minimum word length below which words will be ignored. Defaults to 0.

max_word_length

The maximum word length above which words will be ignored. Defaults to unbounded (0).

Behaviouredit

The term and field statistics are not accurate. Deleted documents are not taken into account. The information is only retrieved for the shard the requested document resides in. The term and field statistics are therefore only useful as relative measures whereas the absolute numbers have no meaning in this context. By default, when requesting term vectors of artificial documents, a shard to get the statistics from is randomly selected. Use routing only to hit a particular shard.

Example: Returning stored term vectorsedit

First, we create an index that stores term vectors, payloads etc. :

PUT /twitter/
{ "mappings": {
    "tweet": {
      "properties": {
        "text": {
          "type": "text",
          "term_vector": "with_positions_offsets_payloads",
          "store" : true,
          "analyzer" : "fulltext_analyzer"
         },
         "fullname": {
          "type": "text",
          "term_vector": "with_positions_offsets_payloads",
          "analyzer" : "fulltext_analyzer"
        }
      }
    }
  },
  "settings" : {
    "index" : {
      "number_of_shards" : 1,
      "number_of_replicas" : 0
    },
    "analysis": {
      "analyzer": {
        "fulltext_analyzer": {
          "type": "custom",
          "tokenizer": "whitespace",
          "filter": [
            "lowercase",
            "type_as_payload"
          ]
        }
      }
    }
  }
}

Second, we add some documents:

PUT /twitter/tweet/1
{
  "fullname" : "John Doe",
  "text" : "twitter test test test "
}

PUT /twitter/tweet/2
{
  "fullname" : "Jane Doe",
  "text" : "Another twitter test ..."
}

The following request returns all information and statistics for field text in document 1 (John Doe):

GET /twitter/tweet/1/_termvectors
{
  "fields" : ["text"],
  "offsets" : true,
  "payloads" : true,
  "positions" : true,
  "term_statistics" : true,
  "field_statistics" : true
}

Response:

{
    "_id": "1",
    "_index": "twitter",
    "_type": "tweet",
    "_version": 1,
    "found": true,
    "took": 6,
    "term_vectors": {
        "text": {
            "field_statistics": {
                "doc_count": 2,
                "sum_doc_freq": 6,
                "sum_ttf": 8
            },
            "terms": {
                "test": {
                    "doc_freq": 2,
                    "term_freq": 3,
                    "tokens": [
                        {
                            "end_offset": 12,
                            "payload": "d29yZA==",
                            "position": 1,
                            "start_offset": 8
                        },
                        {
                            "end_offset": 17,
                            "payload": "d29yZA==",
                            "position": 2,
                            "start_offset": 13
                        },
                        {
                            "end_offset": 22,
                            "payload": "d29yZA==",
                            "position": 3,
                            "start_offset": 18
                        }
                    ],
                    "ttf": 4
                },
                "twitter": {
                    "doc_freq": 2,
                    "term_freq": 1,
                    "tokens": [
                        {
                            "end_offset": 7,
                            "payload": "d29yZA==",
                            "position": 0,
                            "start_offset": 0
                        }
                    ],
                    "ttf": 2
                }
            }
        }
    }
}

Example: Generating term vectors on the flyedit

Term vectors which are not explicitly stored in the index are automatically computed on the fly. The following request returns all information and statistics for the fields in document 1, even though the terms haven’t been explicitly stored in the index. Note that for the field text, the terms are not re-generated.

GET /twitter/tweet/1/_termvectors
{
  "fields" : ["text", "some_field_without_term_vectors"],
  "offsets" : true,
  "positions" : true,
  "term_statistics" : true,
  "field_statistics" : true
}

Example: Artificial documentsedit

Term vectors can also be generated for artificial documents, that is for documents not present in the index. For example, the following request would return the same results as in example 1. The mapping used is determined by the index and type.

If dynamic mapping is turned on (default), the document fields not in the original mapping will be dynamically created.

GET /twitter/tweet/_termvectors
{
  "doc" : {
    "fullname" : "John Doe",
    "text" : "twitter test test test"
  }
}
Per-field analyzeredit

Additionally, a different analyzer than the one at the field may be provided by using the per_field_analyzer parameter. This is useful in order to generate term vectors in any fashion, especially when using artificial documents. When providing an analyzer for a field that already stores term vectors, the term vectors will be re-generated.

GET /twitter/tweet/_termvectors
{
  "doc" : {
    "fullname" : "John Doe",
    "text" : "twitter test test test"
  },
  "fields": ["fullname"],
  "per_field_analyzer" : {
    "fullname": "keyword"
  }
}

Response:

{
  "_index": "twitter",
  "_type": "tweet",
  "_version": 0,
  "found": true,
  "took": 6,
  "term_vectors": {
    "fullname": {
       "field_statistics": {
          "sum_doc_freq": 2,
          "doc_count": 4,
          "sum_ttf": 4
       },
       "terms": {
          "John Doe": {
             "term_freq": 1,
             "tokens": [
                {
                   "position": 0,
                   "start_offset": 0,
                   "end_offset": 8
                }
             ]
          }
       }
    }
  }
}

Example: Terms filteringedit

Finally, the terms returned could be filtered based on their tf-idf scores. In the example below we obtain the three most "interesting" keywords from the artificial document having the given "plot" field value. Notice that the keyword "Tony" or any stop words are not part of the response, as their tf-idf must be too low.

GET /imdb/movies/_termvectors
{
    "doc": {
      "plot": "When wealthy industrialist Tony Stark is forced to build an armored suit after a life-threatening incident, he ultimately decides to use its technology to fight against evil."
    },
    "term_statistics" : true,
    "field_statistics" : true,
    "positions": false,
    "offsets": false,
    "filter" : {
      "max_num_terms" : 3,
      "min_term_freq" : 1,
      "min_doc_freq" : 1
    }
}

Response:

{
   "_index": "imdb",
   "_type": "movies",
   "_version": 0,
   "found": true,
   "term_vectors": {
      "plot": {
         "field_statistics": {
            "sum_doc_freq": 3384269,
            "doc_count": 176214,
            "sum_ttf": 3753460
         },
         "terms": {
            "armored": {
               "doc_freq": 27,
               "ttf": 27,
               "term_freq": 1,
               "score": 9.74725
            },
            "industrialist": {
               "doc_freq": 88,
               "ttf": 88,
               "term_freq": 1,
               "score": 8.590818
            },
            "stark": {
               "doc_freq": 44,
               "ttf": 47,
               "term_freq": 1,
               "score": 9.272792
            }
         }
      }
   }
}


Reindex API

https://www.elastic.co/guide/en/elasticsearch/reference/5.5/docs-reindex.html


edit

Important

Reindex does not attempt to set up the destination index. It does not copy the settings of the source index. You should set up the destination index prior to running a _reindex action, including setting up mappings, shard counts, replicas, etc.

The most basic form of _reindex just copies documents from one index to another. This will copy documents from the twitter index into the new_twitter index:

POST _reindex
{
  "source": {
    "index": "twitter"
  },
  "dest": {
    "index": "new_twitter"
  }
}

That will return something like this:

{
  "took" : 147,
  "timed_out": false,
  "created": 120,
  "updated": 0,
  "deleted": 0,
  "batches": 1,
  "version_conflicts": 0,
  "noops": 0,
  "retries": {
    "bulk": 0,
    "search": 0
  },
  "throttled_millis": 0,
  "requests_per_second": -1.0,
  "throttled_until_millis": 0,
  "total": 120,
  "failures" : [ ]
}

Just like _update_by_query_reindex gets a snapshot of the source index but its target must be a different index so version conflicts are unlikely. The destelement can be configured like the index API to control optimistic concurrency control. Just leaving out version_type (as above) or setting it to internal will cause Elasticsearch to blindly dump documents into the target, overwriting any that happen to have the same type and id:

POST _reindex
{
  "source": {
    "index": "twitter"
  },
  "dest": {
    "index": "new_twitter",
    "version_type": "internal"
  }
}

Setting version_type to external will cause Elasticsearch to preserve theversion from the source, create any documents that are missing, and update any documents that have an older version in the destination index than they do in the source index:

POST _reindex
{
  "source": {
    "index": "twitter"
  },
  "dest": {
    "index": "new_twitter",
    "version_type": "external"
  }
}

Settings op_type to create will cause _reindex to only create missing documents in the target index. All existing documents will cause a version conflict:

POST _reindex
{
  "source": {
    "index": "twitter"
  },
  "dest": {
    "index": "new_twitter",
    "op_type": "create"
  }
}

By default version conflicts abort the _reindex process but you can just count them by settings "conflicts": "proceed" in the request body:

POST _reindex
{
  "conflicts": "proceed",
  "source": {
    "index": "twitter"
  },
  "dest": {
    "index": "new_twitter",
    "op_type": "create"
  }
}

You can limit the documents by adding a type to the source or by adding a query. This will only copy tweet's made by kimchy into new_twitter:

POST _reindex
{
  "source": {
    "index": "twitter",
    "type": "tweet",
    "query": {
      "term": {
        "user": "kimchy"
      }
    }
  },
  "dest": {
    "index": "new_twitter"
  }
}

index and type in source can both be lists, allowing you to copy from lots of sources in one request. This will copy documents from the tweet and posttypes in the twitter and blog index. It’d include the post type in the twitterindex and the tweet type in the blog index. If you want to be more specific you’ll need to use the query. It also makes no effort to handle ID collisions. The target index will remain valid but it’s not easy to predict which document will survive because the iteration order isn’t well defined.

POST _reindex
{
  "source": {
    "index": ["twitter", "blog"],
    "type": ["tweet", "post"]
  },
  "dest": {
    "index": "all_together"
  }
}

It’s also possible to limit the number of processed documents by setting size. This will only copy a single document from twitter to new_twitter:

POST _reindex
{
  "size": 1,
  "source": {
    "index": "twitter"
  },
  "dest": {
    "index": "new_twitter"
  }
}

If you want a particular set of documents from the twitter index you’ll need to sort. Sorting makes the scroll less efficient but in some contexts it’s worth it. If possible, prefer a more selective query to size and sort. This will copy 10000 documents from twitter into new_twitter:

POST _reindex
{
  "size": 10000,
  "source": {
    "index": "twitter",
    "sort": { "date": "desc" }
  },
  "dest": {
    "index": "new_twitter"
  }
}

The source section supports all the elements that are supported in a search request. For instance only a subset of the fields from the original documents can be reindexed using source filtering as follows:

POST _reindex
{
  "source": {
    "index": "twitter",
    "_source": ["user", "tweet"]
  },
  "dest": {
    "index": "new_twitter"
  }
}

Like _update_by_query_reindex supports a script that modifies the document. Unlike _update_by_query, the script is allowed to modify the document’s metadata. This example bumps the version of the source document:

POST _reindex
{
  "source": {
    "index": "twitter"
  },
  "dest": {
    "index": "new_twitter",
    "version_type": "external"
  },
  "script": {
    "inline": "if (ctx._source.foo == 'bar') {ctx._version++; ctx._source.remove('foo')}",
    "lang": "painless"
  }
}

Just as in _update_by_query, you can set ctx.op to change the operation that is executed on the destination index:

noop
Set ctx.op = "noop" if your script decides that the document doesn’t have to be indexed in the destination index. This no operation will be reported in the noopcounter in the response body.
delete
Set ctx.op = "delete" if your script decides that the document must be deleted from the destination index. The deletion will be reported in the deleted counter in the response body.

Setting ctx.op to anything else is an error. Setting any other field in ctx is an error.

Think of the possibilities! Just be careful! With great power…. You can change:

  • _id
  • _type
  • _index
  • _version
  • _routing
  • _parent

Setting _version to null or clearing it from the ctx map is just like not sending the version in an indexing request. It will cause that document to be overwritten in the target index regardless of the version on the target or the version type you use in the _reindex request.

By default if _reindex sees a document with routing then the routing is preserved unless it’s changed by the script. You can set routing on the destrequest to change this:

keep
Sets the routing on the bulk request sent for each match to the routing on the match. The default.
discard
Sets the routing on the bulk request sent for each match to null.
=<some text>
Sets the routing on the bulk request sent for each match to all text after the =.

For example, you can use the following request to copy all documents from the source index with the company name cat into the dest index with routing set to cat.

POST _reindex
{
  "source": {
    "index": "source",
    "query": {
      "match": {
        "company": "cat"
      }
    }
  },
  "dest": {
    "index": "dest",
    "routing": "=cat"
  }
}

By default _reindex uses scroll batches of 1000. You can change the batch size with the size field in the source element:

POST _reindex
{
  "source": {
    "index": "source",
    "size": 100
  },
  "dest": {
    "index": "dest",
    "routing": "=cat"
  }
}

Reindex can also use the Ingest Node feature by specifying a pipeline like this:

POST _reindex
{
  "source": {
    "index": "source"
  },
  "dest": {
    "index": "dest",
    "pipeline": "some_ingest_pipeline"
  }
}

Reindex from Remoteedit

Reindex supports reindexing from a remote Elasticsearch cluster:

POST _reindex
{
  "source": {
    "remote": {
      "host": "http://otherhost:9200",
      "username": "user",
      "password": "pass"
    },
    "index": "source",
    "query": {
      "match": {
        "test": "data"
      }
    }
  },
  "dest": {
    "index": "dest"
  }
}

The host parameter must contain a scheme, host, and port (e.g.https://otherhost:9200). The username and password parameters are optional and when they are present reindex will connect to the remote Elasticsearch node using basic auth. Be sure to use https when using basic auth or the password will be sent in plain text.

Remote hosts have to be explicitly whitelisted in elasticsearch.yaml using thereindex.remote.whitelist property. It can be set to a comma delimited list of allowed remote host and port combinations (e.g. otherhost:9200, another:9200, 127.0.10.*:9200, localhost:*). Scheme is ignored by the whitelist - only host and port are used.

This feature should work with remote clusters of any version of Elasticsearch you are likely to find. This should allow you to upgrade from any version of Elasticsearch to the current version by reindexing from a cluster of the old version.

To enable queries sent to older versions of Elasticsearch the query parameter is sent directly to the remote host without validation or modification.

Note

Reindexing from remote clusters does not support manual orautomatic slicing.

Reindexing from a remote server uses an on-heap buffer that defaults to a maximum size of 100mb. If the remote index includes very large documents you’ll need to use a smaller batch size. The example below sets the batch size 10 which is very, very small.

POST _reindex
{
  "source": {
    "remote": {
      "host": "http://otherhost:9200"
    },
    "index": "source",
    "size": 10,
    "query": {
      "match": {
        "test": "data"
      }
    }
  },
  "dest": {
    "index": "dest"
  }
}

It is also possible to set the socket read timeout on the remote connection with the socket_timeout field and the connection timeout with theconnect_timeout field. Both default to thirty seconds. This example sets the socket read timeout to one minute and the connection timeout to ten seconds:

POST _reindex
{
  "source": {
    "remote": {
      "host": "http://otherhost:9200",
      "socket_timeout": "1m",
      "connect_timeout": "10s"
    },
    "index": "source",
    "query": {
      "match": {
        "test": "data"
      }
    }
  },
  "dest": {
    "index": "dest"
  }
}

URL Parametersedit

In addition to the standard parameters like pretty, the Reindex API also supports refreshwait_for_completionwait_for_active_shardstimeout, and requests_per_second.

Sending the refresh url parameter will cause all indexes to which the request wrote to be refreshed. This is different than the Index API’s refreshparameter which causes just the shard that received the new data to be refreshed.

If the request contains wait_for_completion=false then Elasticsearch will perform some preflight checks, launch the request, and then return a taskwhich can be used with Tasks APIs to cancel or get the status of the task. Elasticsearch will also create a record of this task as a document at .tasks/task/${taskId}. This is yours to keep or remove as you see fit. When you are done with it, delete it so Elasticsearch can reclaim the space it uses.

wait_for_active_shards controls how many copies of a shard must be active before proceeding with the reindexing. See here for details. timeout controls how long each write request waits for unavailable shards to become available. Both work exactly how they work in the Bulk API.

requests_per_second can be set to any positive decimal number (1.461000, etc) and throttles rate at which reindex issues batches of index operations by padding each batch with a wait time. The throttling can be disabled by setting requests_per_second to -1.

The throttling is done by waiting between batches so that scroll that reindex uses internally can be given a timeout that takes into account the padding. The padding time is the difference between the batch size divided by therequests_per_second and the time spent writing. By default the batch size is1000, so if the requests_per_second is set to 500:

target_time = 1000 / 500 per second = 2 seconds
wait_time = target_time - write_time = 2 seconds - .5 seconds = 1.5 seconds

Since the batch is issued as a single _bulk request large batch sizes will cause Elasticsearch to create many requests and then wait for a while before starting the next set. This is "bursty" instead of "smooth". The default is -1.

Response bodyedit

The JSON response looks like this:

{
  "took" : 639,
  "updated": 0,
  "created": 123,
  "batches": 1,
  "version_conflicts": 2,
  "retries": {
    "bulk": 0,
    "search": 0
  }
  "throttled_millis": 0,
  "failures" : [ ]
}
took
The number of milliseconds from start to end of the whole operation.
updated
The number of documents that were successfully updated.
created
The number of documents that were successfully created.
batches
The number of scroll responses pulled back by the the reindex.
version_conflicts
The number of version conflicts that reindex hit.
retries
The number of retries attempted by reindex. bulk is the number of bulk actions retried and search is the number of search actions retried.
throttled_millis
Number of milliseconds the request slept to conform to requests_per_second.
failures
Array of all indexing failures. If this is non-empty then the request aborted because of those failures. See conflicts for how to prevent version conflicts from aborting the operation.

Works with the Task APIedit

You can fetch the status of all running reindex requests with the Task API:

GET _tasks?detailed=true&actions=*reindex

The responses looks like:

{
  "nodes" : {
    "r1A2WoRbTwKZ516z6NEs5A" : {
      "name" : "r1A2WoR",
      "transport_address" : "127.0.0.1:9300",
      "host" : "127.0.0.1",
      "ip" : "127.0.0.1:9300",
      "attributes" : {
        "testattr" : "test",
        "portsfile" : "true"
      },
      "tasks" : {
        "r1A2WoRbTwKZ516z6NEs5A:36619" : {
          "node" : "r1A2WoRbTwKZ516z6NEs5A",
          "id" : 36619,
          "type" : "transport",
          "action" : "indices:data/write/reindex",
          "status" : {    
            "total" : 6154,
            "updated" : 3500,
            "created" : 0,
            "deleted" : 0,
            "batches" : 4,
            "version_conflicts" : 0,
            "noops" : 0,
            "retries": {
              "bulk": 0,
              "search": 0
            },
            "throttled_millis": 0
          },
          "description" : ""
        }
      }
    }
  }
}

this object contains the actual status. It is just like the response json with the important addition of the total field. total is the total number of operations that the reindex expects to perform. You can estimate the progress by adding the updatedcreated, and deleted fields. The request will finish when their sum is equal to the total field.

With the task id you can look up the task directly:

GET /_tasks/taskId:1

The advantage of this API is that it integrates with wait_for_completion=falseto transparently return the status of completed tasks. If the task is completed and wait_for_completion=false was set on it them it’ll come back with aresults or an error field. The cost of this feature is the document thatwait_for_completion=false creates at .tasks/task/${taskId}. It is up to you to delete that document.

Works with the Cancel Task APIedit

Any Reindex can be canceled using the Task Cancel API:

POST _tasks/task_id:1/_cancel

The task_id can be found using the tasks API above.

Cancelation should happen quickly but might take a few seconds. The task status API above will continue to list the task until it is wakes to cancel itself.

Rethrottlingedit

The value of requests_per_second can be changed on a running reindex using the _rethrottle API:

POST _reindex/task_id:1/_rethrottle?requests_per_second=-1

The task_id can be found using the tasks API above.

Just like when setting it on the _reindex API requests_per_second can be either -1 to disable throttling or any decimal number like 1.7 or 12 to throttle to that level. Rethrottling that speeds up the query takes effect immediately but rethrotting that slows down the query will take effect on after completing the current batch. This prevents scroll timeouts.

Reindex to change the name of a fieldedit

_reindex can be used to build a copy of an index with renamed fields. Say you create an index containing documents that look like this:

POST test/test/1?refresh
{
  "text": "words words",
  "flag": "foo"
}

But you don’t like the name flag and want to replace it with tag_reindexcan create the other index for you:

POST _reindex
{
  "source": {
    "index": "test"
  },
  "dest": {
    "index": "test2"
  },
  "script": {
    "inline": "ctx._source.tag = ctx._source.remove(\"flag\")"
  }
}

Now you can get the new document:

GET test2/test/1

and it’ll look like:

{
  "found": true,
  "_id": "1",
  "_index": "test2",
  "_type": "test",
  "_version": 1,
  "_source": {
    "text": "words words",
    "tag": "foo"
  }
}

Or you can search by tag or whatever you want.

Manual slicingedit

Reindex supports Sliced Scroll, allowing you to manually parallelize the process relatively easily:

POST _reindex
{
  "source": {
    "index": "twitter",
    "slice": {
      "id": 0,
      "max": 2
    }
  },
  "dest": {
    "index": "new_twitter"
  }
}
POST _reindex
{
  "source": {
    "index": "twitter",
    "slice": {
      "id": 1,
      "max": 2
    }
  },
  "dest": {
    "index": "new_twitter"
  }
}

Which you can verify works with:

GET _refresh
POST new_twitter/_search?size=0&filter_path=hits.total

Which results in a sensible total like this one:

{
  "hits": {
    "total": 120
  }
}

Automatic slicingedit

You can also let reindex automatically parallelize using Sliced Scroll to slice on _uid:

POST _reindex?slices=5&refresh
{
  "source": {
    "index": "twitter"
  },
  "dest": {
    "index": "new_twitter"
  }
}

Which you also can verify works with:

POST new_twitter/_search?size=0&filter_path=hits.total

Which results in a sensible total like this one:

{
  "hits": {
    "total": 120
  }
}

Adding slices to _reindex just automates the manual process used in the section above, creating sub-requests which means it has some quirks:

  • You can see these requests in the Tasks APIs. These sub-requests are "child" tasks of the task for the request with slices.
  • Fetching the status of the task for the request with slices only contains the status of completed slices.
  • These sub-requests are individually addressable for things like cancellation and rethrottling.
  • Rethrottling the request with slices will rethrottle the unfinished sub-request proportionally.
  • Canceling the request with slices will cancel each sub-request.
  • Due to the nature of slices each sub-request won’t get a perfectly even portion of the documents. All documents will be addressed, but some slices may be larger than others. Expect larger slices to have a more even distribution.
  • Parameters like requests_per_second and size on a request with slices are distributed proportionally to each sub-request. Combine that with the point above about distribution being uneven and you should conclude that the using size with slices might not result in exactly size documents being `_reindex`ed.
  • Each sub-requests gets a slightly different snapshot of the source index though these are all taken at approximately the same time.

Picking the number of slicesedit

At this point we have a few recommendations around the number of slicesto use (the max parameter in the slice API if manually parallelizing):

  • Don’t use large numbers. 500 creates fairly massive CPU thrash.
  • It is more efficient from a query performance standpoint to use some multiple of the number of shards in the source index.
  • Using exactly as many shards as are in the source index is the most efficient from a query performance standpoint.
  • Indexing performance should scale linearly across available resources with the number of slices.
  • Whether indexing or query performance dominates that process depends on lots of factors like the documents being reindexed and the cluster doing the reindexing.

Reindex daily indicesedit

You can use _reindex in combination with Painless to reindex daily indices to apply a new template to the existing documents.

Assuming you have indices consisting of documents as following:

PUT metricbeat-2016.05.30/beat/1?refresh
{"system.cpu.idle.pct": 0.908}
PUT metricbeat-2016.05.31/beat/1?refresh
{"system.cpu.idle.pct": 0.105}

The new template for the metricbeat-* indices is already loaded into elasticsearch but it applies only to the newly created indices. Painless can be used to reindex the existing documents and apply the new template.

The script below extracts the date from the index name and creates a new index with -1 appended. All data from metricbeat-2016.05.31 will be reindex into metricbeat-2016.05.31-1.

POST _reindex
{
  "source": {
    "index": "metricbeat-*"
  },
  "dest": {
    "index": "metricbeat"
  },
  "script": {
    "lang": "painless",
    "inline": "ctx._index = 'metricbeat-' + (ctx._index.substring('metricbeat-'.length(), ctx._index.length())) + '-1'"
  }
}

All documents from the previous metricbeat indices now can be found in the *-1 indices.

GET metricbeat-2016.05.30-1/beat/1
GET metricbeat-2016.05.31-1/beat/1

The previous method can also be used in combination with change the name of a field to only load the existing data into the new index, but also rename fields if needed.

Extracting a random subset of an indexedit

Reindex can be used to extract a random subset of an index for testing:

POST _reindex
{
  "size": 10,
  "source": {
    "index": "twitter",
    "query": {
      "function_score" : {
        "query" : { "match_all": {} },
        "random_score" : {}
      }
    },
    "sort": "_score"    
  },
  "dest": {
    "index": "random_twitter"
  }
}

Reindex defaults to sorting by _doc so random_score won’t have any effect unless you override the sort to _score.


Bulk API

https://www.elastic.co/guide/en/elasticsearch/reference/5.5/docs-bulk.html


edit

The bulk API makes it possible to perform many index/delete operations in a single API call. This can greatly increase the indexing speed.

The REST API endpoint is /_bulk, and it expects the following newline delimited JSON (NDJSON) structure:

action_and_meta_data\n
optional_source\n
action_and_meta_data\n
optional_source\n
....
action_and_meta_data\n
optional_source\n

NOTE: the final line of data must end with a newline character \n. Each newline character may be preceded by a carriage return \r. When sending requests to this endpoint the Content-Type header should be set to application/x-ndjson.

The possible actions are indexcreatedelete and updateindex and create expect a source on the next line, and have the same semantics as the op_type parameter to the standard index API (i.e. create will fail if a document with the same index and type exists already, whereas index will add or replace a document as necessary). delete does not expect a source on the following line, and has the same semantics as the standard delete API. update expects that the partial doc, upsert and script and its options are specified on the next line.

If you’re providing text file input to curl, you must use the --data-binary flag instead of plain -d. The latter doesn’t preserve newlines. Example:

$ cat requests
{ "index" : { "_index" : "test", "_type" : "type1", "_id" : "1" } }
{ "field1" : "value1" }
$ curl -s -H "Content-Type: application/x-ndjson" -XPOST localhost:9200/_bulk --data-binary "@requests"; echo
{"took":7, "errors": false, "items":[{"index":{"_index":"test","_type":"type1","_id":"1","_version":1,"result":"created","forced_refresh":false}}]}

Because this format uses literal \n's as delimiters, please be sure that the JSON actions and sources are not pretty printed. Here is an example of a correct sequence of bulk commands:

POST _bulk
{ "index" : { "_index" : "test", "_type" : "type1", "_id" : "1" } }
{ "field1" : "value1" }
{ "delete" : { "_index" : "test", "_type" : "type1", "_id" : "2" } }
{ "create" : { "_index" : "test", "_type" : "type1", "_id" : "3" } }
{ "field1" : "value3" }
{ "update" : {"_id" : "1", "_type" : "type1", "_index" : "test"} }
{ "doc" : {"field2" : "value2"} }

The result of this bulk operation is:

{
   "took": 30,
   "errors": false,
   "items": [
      {
         "index": {
            "_index": "test",
            "_type": "type1",
            "_id": "1",
            "_version": 1,
            "result": "created",
            "_shards": {
               "total": 2,
               "successful": 1,
               "failed": 0
            },
            "created": true,
            "status": 201
         }
      },
      {
         "delete": {
            "found": false,
            "_index": "test",
            "_type": "type1",
            "_id": "2",
            "_version": 1,
            "result": "not_found",
            "_shards": {
               "total": 2,
               "successful": 1,
               "failed": 0
            },
            "status": 404
         }
      },
      {
         "create": {
            "_index": "test",
            "_type": "type1",
            "_id": "3",
            "_version": 1,
            "result": "created",
            "_shards": {
               "total": 2,
               "successful": 1,
               "failed": 0
            },
            "created": true,
            "status": 201
         }
      },
      {
         "update": {
            "_index": "test",
            "_type": "type1",
            "_id": "1",
            "_version": 2,
            "result": "updated",
            "_shards": {
                "total": 2,
                "successful": 1,
                "failed": 0
            },
            "status": 200
         }
      }
   ]
}

The endpoints are /_bulk/{index}/_bulk, and {index}/{type}/_bulk. When the index or the index/type are provided, they will be used by default on bulk items that don’t provide them explicitly.

A note on the format. The idea here is to make processing of this as fast as possible. As some of the actions will be redirected to other shards on other nodes, only action_meta_data is parsed on the receiving node side.

Client libraries using this protocol should try and strive to do something similar on the client side, and reduce buffering as much as possible.

The response to a bulk action is a large JSON structure with the individual results of each action that was performed. The failure of a single action does not affect the remaining actions.

There is no "correct" number of actions to perform in a single bulk call. You should experiment with different settings to find the optimum size for your particular workload.

If using the HTTP API, make sure that the client does not send HTTP chunks, as this will slow things down.

Versioningedit

Each bulk item can include the version value using the _version/version field. It automatically follows the behavior of the index / delete operation based on the _version mapping. It also support the version_type/_version_type (see versioning)

Routingedit

Each bulk item can include the routing value using the _routing/routing field. It automatically follows the behavior of the index / delete operation based on the _routing mapping.

Parentedit

Each bulk item can include the parent value using the _parent/parent field. It automatically follows the behavior of the index / delete operation based on the _parent / _routing mapping.

Wait For Active Shardsedit

When making bulk calls, you can set the wait_for_active_shards parameter to require a minimum number of shard copies to be active before starting to process the bulk request. See here for further details and a usage example.

Refreshedit

Control when the changes made by this request are visible to search. Seerefresh.

Updateedit

When using update action _retry_on_conflict can be used as field in the action itself (not in the extra payload line), to specify how many times an update should be retried in the case of a version conflict.

The update action payload, supports the following options: doc (partial document), upsertdoc_as_upsertscript and _source. See updatedocumentation for details on the options. Example with update actions:

POST _bulk
{ "update" : {"_id" : "1", "_type" : "type1", "_index" : "index1", "_retry_on_conflict" : 3} }
{ "doc" : {"field" : "value"} }
{ "update" : { "_id" : "0", "_type" : "type1", "_index" : "index1", "_retry_on_conflict" : 3} }
{ "script" : { "inline": "ctx._source.counter += params.param1", "lang" : "painless", "params" : {"param1" : 1}}, "upsert" : {"counter" : 1}}
{ "update" : {"_id" : "2", "_type" : "type1", "_index" : "index1", "_retry_on_conflict" : 3} }
{ "doc" : {"field" : "value"}, "doc_as_upsert" : true }
{ "update" : {"_id" : "3", "_type" : "type1", "_index" : "index1", "_source" : true} }
{ "doc" : {"field" : "value"} }
{ "update" : {"_id" : "4", "_type" : "type1", "_index" : "index1"} }
{ "doc" : {"field" : "value"}, "_source": true}

Securityedit

See URL-based access control


Update By Query API

https://www.elastic.co/guide/en/elasticsearch/reference/5.5/docs-update-by-query.html#docs-update-by-query


edit

The simplest usage of _update_by_query just performs an update on every document in the index without changing the source. This is useful to pick up a new property or some other online mapping change. Here is the API:

POST twitter/_update_by_query?conflicts=proceed

That will return something like this:

{
  "took" : 147,
  "timed_out": false,
  "updated": 120,
  "deleted": 0,
  "batches": 1,
  "version_conflicts": 0,
  "noops": 0,
  "retries": {
    "bulk": 0,
    "search": 0
  },
  "throttled_millis": 0,
  "requests_per_second": -1.0,
  "throttled_until_millis": 0,
  "total": 120,
  "failures" : [ ]
}

_update_by_query gets a snapshot of the index when it starts and indexes what it finds using internal versioning. That means that you’ll get a version conflict if the document changes between the time when the snapshot was taken and when the index request is processed. When the versions match the document is updated and the version number is incremented.

Note

Since internal versioning does not support the value 0 as a valid version number, documents with version equal to zero cannot be updated using _update_by_query and will fail the request.

All update and query failures cause the _update_by_query to abort and are returned in the failures of the response. The updates that have been performed still stick. In other words, the process is not rolled back, only aborted. While the first failure causes the abort, all failures that are returned by the failing bulk request are returned in the failures element; therefore it’s possible for there to be quite a few failed entities.

If you want to simply count version conflicts not cause the _update_by_queryto abort you can set conflicts=proceed on the url or "conflicts": "proceed"in the request body. The first example does this because it is just trying to pick up an online mapping change and a version conflict simply means that the conflicting document was updated between the start of the _update_by_queryand the time when it attempted to update the document. This is fine because that update will have picked up the online mapping update.

Back to the API format, you can limit _update_by_query to a single type. This will only update tweet documents from the twitter index:

POST twitter/tweet/_update_by_query?conflicts=proceed

You can also limit _update_by_query using the Query DSL. This will update all documents from the twitter index for the user kimchy:

POST twitter/_update_by_query?conflicts=proceed
{
  "query": { 
    "term": {
      "user": "kimchy"
    }
  }
}

The query must be passed as a value to the query key, in the same way as the Search API. You can also use the q parameter in the same way as the search api.

So far we’ve only been updating documents without changing their source. That is genuinely useful for things like picking up new properties but it’s only half the fun. _update_by_query supports a script object to update the document. This will increment the likes field on all of kimchy’s tweets:

POST twitter/_update_by_query
{
  "script": {
    "inline": "ctx._source.likes++",
    "lang": "painless"
  },
  "query": {
    "term": {
      "user": "kimchy"
    }
  }
}

Just as in Update API you can set ctx.op to change the operation that is executed:

noop
Set ctx.op = "noop" if your script decides that it doesn’t have to make any changes. That will cause _update_by_query to omit that document from its updates. This no operation will be reported in the noop counter in the response body.
delete
Set ctx.op = "delete" if your script decides that the document must be deleted. The deletion will be reported in the deleted counter in the response body.

Setting ctx.op to anything else is an error. Setting any other field in ctx is an error.

Note that we stopped specifying conflicts=proceed. In this case we want a version conflict to abort the process so we can handle the failure.

This API doesn’t allow you to move the documents it touches, just modify their source. This is intentional! We’ve made no provisions for removing the document from its original location.

It’s also possible to do this whole thing on multiple indexes and multiple types at once, just like the search API:

POST twitter,blog/tweet,post/_update_by_query

If you provide routing then the routing is copied to the scroll query, limiting the process to the shards that match that routing value:

POST twitter/_update_by_query?routing=1

By default _update_by_query uses scroll batches of 1000. You can change the batch size with the scroll_size URL parameter:

POST twitter/_update_by_query?scroll_size=100

_update_by_query can also use the Ingest Node feature by specifying a pipeline like this:

PUT _ingest/pipeline/set-foo
{
  "description" : "sets foo",
  "processors" : [ {
      "set" : {
        "field": "foo",
        "value": "bar"
      }
  } ]
}
POST twitter/_update_by_query?pipeline=set-foo

URL Parametersedit

In addition to the standard parameters like pretty, the Update By Query API also supports refreshwait_for_completionwait_for_active_shards, and timeout.

Sending the refresh will update all shards in the index being updated when the request completes. This is different than the Index API’s refreshparameter which causes just the shard that received the new data to be indexed.

If the request contains wait_for_completion=false then Elasticsearch will perform some preflight checks, launch the request, and then return a taskwhich can be used with Tasks APIs to cancel or get the status of the task. Elasticsearch will also create a record of this task as a document at .tasks/task/${taskId}. This is yours to keep or remove as you see fit. When you are done with it, delete it so Elasticsearch can reclaim the space it uses.

wait_for_active_shards controls how many copies of a shard must be active before proceeding with the request. See here for details. timeout controls how long each write request waits for unavailable shards to become available. Both work exactly how they work in the Bulk API.

requests_per_second can be set to any positive decimal number (1.461000, etc) and throttles rate at which _update_by_query issues batches of index operations by padding each batch with a wait time. The throttling can be disabled by setting requests_per_second to -1.

The throttling is done by waiting between batches so that scroll that_update_by_query uses internally can be given a timeout that takes into account the padding. The padding time is the difference between the batch size divided by the requests_per_second and the time spent writing. By default the batch size is 1000, so if the requests_per_second is set to 500:

target_time = 1000 / 500 per second = 2 seconds
wait_time = target_time - delete_time = 2 seconds - .5 seconds = 1.5 seconds

Since the batch is issued as a single _bulk request large batch sizes will cause Elasticsearch to create many requests and then wait for a while before starting the next set. This is "bursty" instead of "smooth". The default is -1.

Response bodyedit

The JSON response looks like this:

{
  "took" : 639,
  "updated": 0,
  "batches": 1,
  "version_conflicts": 2,
  "retries": {
    "bulk": 0,
    "search": 0
  }
  "throttled_millis": 0,
  "failures" : [ ]
}
took
The number of milliseconds from start to end of the whole operation.
updated
The number of documents that were successfully updated.
batches
The number of scroll responses pulled back by the the update by query.
version_conflicts
The number of version conflicts that the update by query hit.
retries
The number of retries attempted by update-by-query. bulk is the number of bulk actions retried and search is the number of search actions retried.
throttled_millis
Number of milliseconds the request slept to conform to requests_per_second.
failures
Array of all indexing failures. If this is non-empty then the request aborted because of those failures. See conflicts for how to prevent version conflicts from aborting the operation.

Works with the Task APIedit

You can fetch the status of all running update-by-query requests with the Task API:

GET _tasks?detailed=true&actions=*byquery

The responses looks like:

{
  "nodes" : {
    "r1A2WoRbTwKZ516z6NEs5A" : {
      "name" : "r1A2WoR",
      "transport_address" : "127.0.0.1:9300",
      "host" : "127.0.0.1",
      "ip" : "127.0.0.1:9300",
      "attributes" : {
        "testattr" : "test",
        "portsfile" : "true"
      },
      "tasks" : {
        "r1A2WoRbTwKZ516z6NEs5A:36619" : {
          "node" : "r1A2WoRbTwKZ516z6NEs5A",
          "id" : 36619,
          "type" : "transport",
          "action" : "indices:data/write/update/byquery",
          "status" : {    
            "total" : 6154,
            "updated" : 3500,
            "created" : 0,
            "deleted" : 0,
            "batches" : 4,
            "version_conflicts" : 0,
            "noops" : 0,
            "retries": {
              "bulk": 0,
              "search": 0
            }
            "throttled_millis": 0
          },
          "description" : ""
        }
      }
    }
  }
}

this object contains the actual status. It is just like the response json with the important addition of the total field. total is the total number of operations that the reindex expects to perform. You can estimate the progress by adding the updatedcreated, and deleted fields. The request will finish when their sum is equal to the total field.

With the task id you can look up the task directly:

GET /_tasks/taskId:1

The advantage of this API is that it integrates with wait_for_completion=falseto transparently return the status of completed tasks. If the task is completed and wait_for_completion=false was set on it them it’ll come back with aresults or an error field. The cost of this feature is the document thatwait_for_completion=false creates at .tasks/task/${taskId}. It is up to you to delete that document.

Works with the Cancel Task APIedit

Any Update By Query can be canceled using the Task Cancel API:

POST _tasks/task_id:1/_cancel

The task_id can be found using the tasks API above.

Cancellation should happen quickly but might take a few seconds. The task status API above will continue to list the task until it is wakes to cancel itself.

Rethrottlingedit

The value of requests_per_second can be changed on a running update by query using the _rethrottle API:

POST _update_by_query/task_id:1/_rethrottle?requests_per_second=-1

The task_id can be found using the tasks API above.

Just like when setting it on the _update_by_query API requests_per_second can be either -1 to disable throttling or any decimal number like 1.7 or 12 to throttle to that level. Rethrottling that speeds up the query takes effect immediately but rethrotting that slows down the query will take effect on after completing the current batch. This prevents scroll timeouts.

Manual slicingedit

Update-by-query supports Sliced Scroll allowing you to manually parallelize the process relatively easily:

POST twitter/_update_by_query
{
  "slice": {
    "id": 0,
    "max": 2
  },
  "script": {
    "inline": "ctx._source['extra'] = 'test'"
  }
}
POST twitter/_update_by_query
{
  "slice": {
    "id": 1,
    "max": 2
  },
  "script": {
    "inline": "ctx._source['extra'] = 'test'"
  }
}

Which you can verify works with:

GET _refresh
POST twitter/_search?size=0&q=extra:test&filter_path=hits.total

Which results in a sensible total like this one:

{
  "hits": {
    "total": 120
  }
}

Automatic slicingedit

You can also let update-by-query automatically parallelize using Sliced Scrollto slice on _uid:

POST twitter/_update_by_query?refresh&slices=5
{
  "script": {
    "inline": "ctx._source['extra'] = 'test'"
  }
}

Which you also can verify works with:

POST twitter/_search?size=0&q=extra:test&filter_path=hits.total

Which results in a sensible total like this one:

{
  "hits": {
    "total": 120
  }
}

Adding slices to _update_by_query just automates the manual process used in the section above, creating sub-requests which means it has some quirks:

  • You can see these requests in the Tasks APIs. These sub-requests are "child" tasks of the task for the request with slices.
  • Fetching the status of the task for the request with slices only contains the status of completed slices.
  • These sub-requests are individually addressable for things like cancellation and rethrottling.
  • Rethrottling the request with slices will rethrottle the unfinished sub-request proportionally.
  • Canceling the request with slices will cancel each sub-request.
  • Due to the nature of slices each sub-request won’t get a perfectly even portion of the documents. All documents will be addressed, but some slices may be larger than others. Expect larger slices to have a more even distribution.
  • Parameters like requests_per_second and size on a request with slices are distributed proportionally to each sub-request. Combine that with the point above about distribution being uneven and you should conclude that the using size with slices might not result in exactly size documents being `_update_by_query`ed.
  • Each sub-requests gets a slightly different snapshot of the source index though these are all taken at approximately the same time.

Picking the number of slicesedit

At this point we have a few recommendations around the number of slicesto use (the max parameter in the slice API if manually parallelizing):

  • Don’t use large numbers. 500 creates fairly massive CPU thrash.
  • It is more efficient from a query performance standpoint to use some multiple of the number of shards in the source index.
  • Using exactly as many shards as are in the source index is the most efficient from a query performance standpoint.
  • Indexing performance should scale linearly across available resources with the number of slices.
  • Whether indexing or query performance dominates that process depends on lots of factors like the documents being reindexed and the cluster doing the reindexing.

Pick up a new propertyedit

Say you created an index without dynamic mapping, filled it with data, and then added a mapping value to pick up more fields from the data:

PUT test
{
  "mappings": {
    "test": {
      "dynamic": false,   
      "properties": {
        "text": {"type": "text"}
      }
    }
  }
}

POST test/test?refresh
{
  "text": "words words",
  "flag": "bar"
}
POST test/test?refresh
{
  "text": "words words",
  "flag": "foo"
}
PUT test/_mapping/test   
{
  "properties": {
    "text": {"type": "text"},
    "flag": {"type": "text", "analyzer": "keyword"}
  }
}

This means that new fields won’t be indexed, just stored in _source.

This updates the mapping to add the new flag field. To pick up the new field you have to reindex all documents with it.

Searching for the data won’t find anything:

POST test/_search?filter_path=hits.total
{
  "query": {
    "match": {
      "flag": "foo"
    }
  }
}
{
  "hits" : {
    "total" : 0
  }
}

But you can issue an _update_by_query request to pick up the new mapping:

POST test/_update_by_query?refresh&conflicts=proceed
POST test/_search?filter_path=hits.total
{
  "query": {
    "match": {
      "flag": "foo"
    }
  }
}
{
  "hits" : {
    "total" : 1
  }
}

You can do the exact same thing when adding a field to a multifield.

Delete API

https://www.elastic.co/guide/en/elasticsearch/reference/5.5/docs-delete.html


edit

The delete API allows to delete a typed JSON document from a specific index based on its id. The following example deletes the JSON document from an index called twitter, under a type called tweet, with id valued 1:

DELETE /twitter/tweet/1

The result of the above delete operation is:

{
    "_shards" : {
        "total" : 2,
        "failed" : 0,
        "successful" : 2
    },
    "found" : true,
    "_index" : "twitter",
    "_type" : "tweet",
    "_id" : "1",
    "_version" : 2,
    "result": "deleted"
}

Versioningedit

Each document indexed is versioned. When deleting a document, the versioncan be specified to make sure the relevant document we are trying to delete is actually being deleted and it has not changed in the meantime. Every write operation executed on a document, deletes included, causes its version to be incremented.

Routingedit

When indexing using the ability to control the routing, in order to delete a document, the routing value should also be provided. For example:

DELETE /twitter/tweet/1?routing=kimchy

The above will delete a tweet with id 1, but will be routed based on the user. Note, issuing a delete without the correct routing, will cause the document to not be deleted.

When the _routing mapping is set as required and no routing value is specified, the delete api will throw a RoutingMissingException and reject the request.

Parentedit

The parent parameter can be set, which will basically be the same as setting the routing parameter.

Note that deleting a parent document does not automatically delete its children. One way of deleting all child documents given a parent’s id is to use the Delete By Query API to perform a index with the automatically generated (and indexed) field _parent, which is in the format parent_type#parent_id.

When deleting a child document its parent id must be specified, otherwise the delete request will be rejected and a RoutingMissingException will be thrown instead.

Automatic index creationedit

The delete operation automatically creates an index if it has not been created before (check out the create index API for manually creating an index), and also automatically creates a dynamic type mapping for the specific type if it has not been created before (check out the put mapping API for manually creating type mapping).

Distributededit

The delete operation gets hashed into a specific shard id. It then gets redirected into the primary shard within that id group, and replicated (if needed) to shard replicas within that id group.

Wait For Active Shardsedit

When making delete requests, you can set the wait_for_active_shardsparameter to require a minimum number of shard copies to be active before starting to process the delete request. See here for further details and a usage example.

Refreshedit

Control when the changes made by this request are visible to search. See ?refresh.

Timeoutedit

The primary shard assigned to perform the delete operation might not be available when the delete operation is executed. Some reasons for this might be that the primary shard is currently recovering from a store or undergoing relocation. By default, the delete operation will wait on the primary shard to become available for up to 1 minute before failing and responding with an error. The timeout parameter can be used to explicitly specify how long it waits. Here is an example of setting it to 5 minutes:

DELETE /twitter/tweet/1?timeout=5m


Get API

The get API allows to get a typed JSON document from the index based on its id. The following example gets a JSON document from an index called twitter, under a type called tweet, with id valued 0:

GET twitter/tweet/

The result of the above get operation is:

{
    "_index" : "twitter",
    "_type" : "tweet",
    "_id" : "0",
    "_version" : 1,
    "found": true,
    "_source" : {
        "user" : "kimchy",
        "date" : "2009-11-15T14:12:12",
        "likes": 0,
        "message" : "trying out Elasticsearch"
    }
}

The above result includes the _index_type_id and _version of the document we wish to retrieve, including the actual _source of the document if it could be found (as indicated by the found field in the response).

The API also allows to check for the existence of a document using HEAD, for example:

HEAD twitter/tweet/0

Realtime

By default, the get API is realtime, and is not affected by the refresh rate of the index (when data will become visible for search). If a document has been updated but is not yet refreshed, the get API will issue a refresh call in-place to make the document visible. This will also make other documents changed since the last refresh visible. In order to disable realtime GET, one can set the realtime parameter to false.

Optional Type

The get API allows for _type to be optional. Set it to _all in order to fetch the first document matching the id across all types.

Source filtering

By default, the get operation returns the contents of the _source field unless you have used the stored_fields parameter or if the _source field is disabled. You can turn off _source retrieval by using the _source parameter:

GET twitter/tweet/0?_source=false

If you only need one or two fields from the complete _source, you can use the _source_include & _source_exclude parameters to include or filter out that parts you need. This can be especially helpful with large documents where partial retrieval can save on network overhead. Both parameters take a comma separated list of fields or wildcard expressions. Example:

GET twitter/tweet/0?_source_include=*.id&_source_exclude=entities

If you only want to specify includes, you can use a shorter notation:

GET twitter/tweet/0?_source=*.id,retweeted

Stored Fields

The get operation allows specifying a set of stored fields that will be returned by passing the stored_fields parameter. If the requested fields are not stored, they will be ignored. Consider for instance the following mapping:

PUT twitter
{
   "mappings": {
      "tweet": {
         "properties": {
            "counter": {
               "type": "integer",
               "store": false
            },
            "tags": {
               "type": "keyword",
               "store": true
            }
         }
      }
   }
}

Now we can add a document:

PUT twitter/tweet/1
{
    "counter" : 1,
    "tags" : ["red"]
}
  1. and try to retrieve it:
GET twitter/tweet/1?stored_fields=tags,counter

The result of the above get operation is:

{
   "_index": "twitter",
   "_type": "tweet",
   "_id": "1",
   "_version": 1,
   "found": true,
   "fields": {
      "tags": [
         "red"
      ]
   }
}

Field values fetched from the document it self are always returned as an array. Since the counter field is not stored the get request simply ignores it when trying to get the stored_fields.

It is also possible to retrieve metadata fields like _routing and _parent fields:

PUT twitter/tweet/2?routing=user1
{
    "counter" : 1,
    "tags" : ["white"]
}
GET twitter/tweet/2?routing=user1&stored_fields=tags,counter

The result of the above get operation is:

{
   "_index": "twitter",
   "_type": "tweet",
   "_id": "2",
   "_version": 1,
   "_routing": "user1",
   "found": true,
   "fields": {
      "tags": [
         "white"
      ]
   }
}

Also only leaf fields can be returned via the stored_field option. So object fields can’t be returned and such requests will fail.

Getting the _source directly

Use the /{index}/{type}/{id}/_source endpoint to get just the _source field of the document, without any additional content around it. For example:

GET twitter/tweet/1/_source

You can also use the same source filtering parameters to control which parts of the _source will be returned:

GET twitter/tweet/1/_source?_source_include=*.id&_source_exclude=entities'

Note, there is also a HEAD variant for the _source endpoint to efficiently test for document _source existence. An existing document will not have a _source if it is disabled in the mapping.

HEAD twitter/tweet/1/_source

Routing

When indexing using the ability to control the routing, in order to get a document, the routing value should also be provided. For example:

GET twitter/tweet/2?routing=user1

The above will get a tweet with id 2, but will be routed based on the user. Note, issuing a get without the correct routing, will cause the document not to be fetched.

Preference

Controls a preference of which shard replicas to execute the get request on. By default, the operation is randomized between the shard replicas.

The preference can be set to:

_primary
The operation will go and be executed only on the primary shards.
_local
The operation will prefer to be executed on a local allocated shard if possible.
Custom (string) value
A custom value will be used to guarantee that the same shards will be used for the same custom value. This can help with "jumping values" when hitting different shards in different refresh states. A sample value can be something like the web session id, or the user name.

Refresh

The refresh parameter can be set to true in order to refresh the relevant shard before the get operation and make it searchable. Setting it to trueshould be done after careful thought and verification that this does not cause a heavy load on the system (and slows down indexing).

Distributed

The get operation gets hashed into a specific shard id. It then gets redirected to one of the replicas within that shard id and returns the result. The replicas are the primary shard and its replicas within that shard id group. This means that the more replicas we will have, the better GET scaling we will have.

Versioning support

You can use the version parameter to retrieve the document only if its current version is equal to the specified one. This behavior is the same for all version types with the exception of version type FORCE which always retrieves the document. Note that FORCE version type is deprecated.

Internally, Elasticsearch has marked the old document as deleted and added an entirely new document. The old version of the document doesn’t disappear immediately, although you won’t be able to access it. Elasticsearch cleans up deleted documents in the background as you continue to index more data.


ELASTICSEARCH – 2. SHARD & REPLICA

Elasticsearch의 shard와 replica에 대해서 알아보기 전에, Elasticsearch에서의 노드 생성 및 동작원리에 대해서 간단히 알아보도록 하겠습니다.

사용자가 하나의 머신에서 Elasicsearch를 시작하게 되면, 하나의 Elasticsearch 노드가 생성되며, 이 노드는 동일한 네트워크 상에서 같은 클러스터명을 같는 클러스터가 존재하는 지를 찾게 됩니다 [Figure 1].  만약, 연결(join)될 수 있는 클러스터가 없다면 이 노드는 스스로 클러스터를 생성하게 되고, 만약 클러스터가 존재한다면 해당 클러스터에 연결됩니다. (Elasticsearch의 클러스터 구성 및 설정에 대해서는 추후에 포스팅 하도록 하겠습니다.)

Figure 1. 노드의 생성 및 동작원리

새로운 클러스터가 생성되었다면, 노드에는 아직 어떠한 인덱스도 존재하지 않는 상태이며, 새로운 인덱스를 생성할 때 인덱스를 몇 개의 shard로 나누어 저장할 것인지를 정의할 수 있습니다. Shard의 개수를 따로 지정하지 않는다면, Elasticsearch의 기본 shard 개수인 5개로 데이터가 나누어져 저장됩니다. 만약, 노드가 기존에 존재하던 클러스터에 연결되고 해당 클러스터에 이미 인덱스가 존재한다면 해당 인덱스 shard들은 추가된 노드에 균일하게 재분산 됩니다.

그렇다면 Shard란 무엇일까요?

Elasticsearch를 비롯한 많은 수의 분산 데이터베이스(분산 검색엔진)에서 shard란 일종의 파티션과 같은 의미이며, 데이터를 저장할 때 나누어진 하나의 조각에 대한 단위입니다. 여기에서 주의할 점은 각각의 shard는 데이터에 대한 복사본이 아니라, 데이터 그 자체라는 점입니다. shard가 노드에 저장되어 있는 상태에서 아래의 Figure 2.와 같이 하나의 노드가 추가 된다면, 기존에 존재하던 shards는 각 노드에 균일하게 재분산 됩니다. 이렇게 가장 먼저 1copy씩 존재하는 데이터 shard를 primary shard라고 합니다.

Figure 2. Shard relocating

그렇다면 이렇게 데이터를 나누어 shard 형태로 저장하는 이유는 무엇일까요?

Elasticsearch는 분산 데이터베이스(분산 검색엔진)이기 때문에, 이렇게 데이터를 나누어 저장하는 방식으로 대용량 데이터에 대한 분산처리를 가능하게 합니다. Primary shard는 각 인덱스 별로 최소 1개 이상 존재해야만 합니다. Shard가 많아지면 그만큼 데이터양의 증가에 대한 Elasticsearch 노드의 확장으로 대응이 가능하다는 장점이 있습니다. 하지만, shard 그 자체도 일종의 비용이라고 할 수 있기 때문에 데이터양의 증가 예측치를 이용하여 적절한 수의 shard를 결정하는 것이 중요합니다.

Elasticsearch에서 Replica는 무엇을 의미할까요?

Replica는 한 마디로 정의하자면 또 다른 형태의 shard라고 할 수 있습니다. Elasticsearch에서 replica의 기본값은 1입니다. 이 때, replica의 값이 1이라는 것은 primary shard가 1개라는 것을 의미하지 않고, primary shard와 동일한 복제본이 1개 있다는 것을 의미합니다. 즉, replica의 개수는 primary shard를 제외한 복제본의 개수 입니다.

Replica가 필요한 이유는 크게 두 가지인데, 그 중 첫 번째는 ‘검색성능(search performance)‘이고, 두 번째는 ‘장애복구(fail-over)‘입니다.

Replica shard는 아래와 같은 중요한 특징을 갖고 있습니다.

Replica shard는 절대로 동일한 데이터를 갖고 있는 primary shard와 같은 Elasticsearch 노드상에 존재할 수 없습니다.

아래의 Figure 3.을 예로 들어 설명하면, 첫 번째 노드의 0, 1, 2번 shard는 primary shard이고, 4, 5번 shard는 replica shard가 됩니다. 마찬가지로 두 번째 노드의 4, 5번 shard는 primary shard이고 1, 2, 3번 노드는 각각 replica shard입니다.

Figure 3. Primary shards & replica shards

이러한 상황에서 하나의 노드에 문제가 발생하게 된다고 하여도 나머지 노드에 모든 데이터 shrad들이 존재하기 때문에 정상적인 서비스가 가능하며, 문제가 없는 노드에 있던 replica shard가 자동적으로 primary shard가 됩니다. 여기에서 우리는 아래와 같은 계산식을 유추할 수 있습니다.

n개의 노드에서 장애가 발생하였을 때, 서비스가 정상적으로 동작하기 위해서는 최소 n+1개의 노드가 동일 클러스터내에 존재해야 하며, replica의 수는 최소 n개여야만 합니다.

다시 말하자면, 1개의 노드에서 장애가 발생하였을 때, 서비스가 정상적으로 동작하기 위해서는 최소 2개의 노드가 동일 클러스터내에 존재해야 하며, replica의 수는 최소 1개여야만 합니다.

또한, replica의 최대 개수는 전체 노드의 수 -1 입니다. 그러므로, 클러스터상의 노드에 문제가 발생하여 가용한 노드의 수가 replica의 수보다 같거나 작은경우 어떠한 노드에서 할당(assign)되지 못한 shard가 발생하게 되며, 이러한 경우 클러스터의 상태가 “YELLOW“가 됩니다. (정상적인 경우의 클러스터 상태는 “GREEN“)

만약, 문제가 발생하였던 노드가 다시 정상적으로 동작하게 되면 해당 노드는 클러스터상에 연결되고, 할당되지 못하고 남아있던 replica shard들이 해당 노드에 할당됩니다.

좀 더 알아보기

검색의 핵심 개념과 엘라스틱서치의 기본에 대해서 좀 더 자세히 알아보고자 한다면 제가 쓴 책 <스타트업 인 액션>을 참고하시기 바랍니다.

ABOUT THE AUTHOR

Passionate, responsible and committed engineer, with experiences of designing, implementing and adapting technically sophisticated online web applications.

PREVIOUS POSTELASTICSEARCH – 1. 시작하기

37 COMMENTS 

  1. 안녕하세요. 항상 글 잘 보고 있습니다 ^^;
    최근 엘라스틱서치에 대해 공부 중 인데, 몇 가지 여쭤보고 싶은게 있어서요.
    솔라 클라우드의 경우엔, 리더가 모든 일을 처리하고, 레플리카는 리더가 다운된 경우 선출되어 일을 하는 방식으로 분산을 지원한다는데, 엘라스틱 서치에서는 어떻게 되는지 궁금합니다 ㅜ_ㅜ. 분산 인덱싱과 분산 검색을 어떻게 지원해 주나요?
    1) 마스터노드 라는게 있던데, master-slave구조로 마스터가 인덱싱을 하고, 나머지 노드들은 마스터의 인덱스를 복사해서 검색만 하는건가요?
    2) 아니면 각각이 document를 받아서 인덱싱하고, 모두 검색을 해주는 방식인건지… 샤딩을 하는 거 보면..그럼 마스터 노드의 역할은 무엇인가요?
    3) 둘 다 아니면 어떤 방식인가요..?

    바쁘시겠지만 부디 답변 부탁드립니다. ㅠㅠ!

    • 안녕하세요.

      조금 늦었지만 말씀하신 내용에 대해서 답변을 달아드립니다. ^^

      1) 먼저, 마스터 노드는 해당 클러스터에 어떤 노드들이 연결되어 있고, 각 샤드들이 어떤 노드에 위치해 있는지에 대한 정보를 담고 있는 노드입니다. 하나의 인덱스는 (기본적으로) 여러개의 샤드들로 구성되어 있으며, 이 샤드들은 클러스터 상의 노드들에 골고루 분산되어 저장됩니다. 정리하자면, 엘라스틱서치에서 마스터-슬레이브는 클러스터와 이 클러스터에 연결된 노드들을 관리하기 위한 개념이며, 인덱싱과 검색에는 크게 영향을 미치는 개념이 아니라고 생각하시면 됩니다.

      2) 먼저, 엘라스틱서치에서 문서가 인덱싱되는 방식을 설명드리겠습니다. 문서에 대한 인덱싱 요청은 먼저 하나의 노드에 전송되는데요, 이 노드는 마스터노드일 수도 있고, 그렇지 않아도 무방합니다. 일반적으로 데이터를 저장하지 않고, 마스터 노드도 아닌 클라이언트 노드(node.master :false node.data:false) 를 두어서 운영하는 경우가 많습니다.
      인덱싱 요청을 받은 노드는 문서의 ID 값을 해싱하여 이 문서가 어느 샤드에 저장되어야 하는지를 결정합니다. 만약, 해당 인덱스를 구성하는 샤드의 개수가 5라면, ID의 해시 값은 다섯 개의 범위 중 하나에 포함되게 됩니다. 이런식으로 최초 인덱싱 요청을 받은 노드가 각 문서를 다른 노드(샤드)에 분산시키게 됩니다.

      검색의 경우 최초 요청을 받은 노드는 해당 검색 요청을 인덱스의 모든 샤드들에 전달하게 되며, 각 샤드들로 부터 받은 검색결과를 취합, 정렬 하여 애플리케이션에 반환합니다. 이 때 검색은 특정 샤드에 대하여 수행되는 것이 아니고, 전체 샤드에 모두 전달되는 것이기 때문에 요청을 받는 노드가 마스터 노드인지 아닌지는 의미가 없다고 보시면 됩니다.

      결론적으로 말씀드리자면, 엘라스틱서치에서 마스터 노드는 클러스터와 이를 구성하는 노드 정보를 담고있을 뿐, 검색과 인덱싱에 중요한 역할을 하는 것이 아니기 때문에 요청이 반드시 마스터 노드에 전달되어야 한다거나 하는 등의 제약사항은 없습니다.

      3) 위의 내용이 답을 포함하고 있는 것 같습니다.

      답변이 도움이 되셨다면 좋겠습니다. ^^

      • 그..럼 정말 죄송하지만 ㅠㅠ…제가 가상머신 2개를 띄워놓고, 클러스터에 각각의 노드를 연결을 해보고 싶은데, 자꾸 에러가 떠서요..클러스터에 노드를 연결했을 때, 샤드가 분할되는걸 보고 싶어서요 ㅠㅠ
        각각의 elasticsearch.yml 인데, 혹시 조언해주실 수 있으신가요?
        192.168.102.133:9200는 다음과 같고

        cluster.name: singer
        node.name: “idol1″
        node.master: true
        node.data: true
        discovery.zen.ping.multicast.enabled: false
        discovery.zen.ping.multicast.unicasy.hosts: [“192.168.203.133:9200″, “192.168.203.134:9200″]

        아래는 192.168.102.134:9200 입니다.

        cluster.name: singer
        node.name: “idol2″
        node.master: false
        node.data: true
        discovery.zen.ping.multicast.enabled: false
        discovery.zen.ping.multicast.unicasy.hosts: [“192.168.203.134:9200″,”192.168.203.133:9200″]

        • 안녕하세요. 출장을 다녀오느라 답변이 좀 늦었습니다. ^^
          가상머신에서 Elasticsearch 클러스터를 구성하려면 multicast discovery를 비활성화 시키고 unicast 방식을 이용하는 것이 맞습니다.
          그래서 입력하신 설정에는 문제가 없는 것 같습니다.

          그러나 설정 내용 중에 discovery.zen.ping.multicast.unicasy.hosts 가 아니라, discovery.zen.ping.unicast.hosts 가 맞는 값이라서 이 부분이 문제가 되었을 수 있을 것 같습니다.
          만약, 이렇게 바꾸어도 안된다면 호스트 목록의 포트들을 9200이 아닌 9300으로 한번 해보시기 바랍니다. unicast discovery는 tranport 모듈을 사용하기 때문입니다.

          그리고, 샤드가 분할 되는 것을 눈으로 직접 확인하실 때는 {elasticsearch home}/bin/plugin -install mobz/elasticsearch-head 명령어를 통해 head 플러그인을 설치하시고, 웹브라우저를 통해 http://master_node_ip:9200/_plugin/head/ 로 접속하시면 직관적으로 확인 하실 수 있습니다.

          마지막으로, public ip를 통해 외부에서 접근이 필요하신 경우에는 설정파일에서 network.publish_host 의 값을 해당 public ip로 입력해 주시면 됩니다.

          감사합니다. ^^

  2. unicasy는 제가 옮겨쓰는 도중 오타가 났었어요 ^^;;;

    계속 에러 검색하다가 보니 방화벽 문제였네요 ㅠ_ㅠㅋㅋ덕분에 4일을 삽질 ㅠㅠㅠ
    도와주셔서 정말정말 감사합니다! 앞으로도 좋은 게시글 부탁드립니다! 좋은하루 되세요!0!

  3. 안녕하세요 현재 ES사용중인 유저입니다.
    현재 클러스터 구성중인데 글을 읽다고 궁금한점이 있어 글을 남깁니다.
    많은 가이드라인에서 client node 즉(node.master :false node.data:false)를 앞에 두고 그뒤로 마스터노드와 데이터노드를 둬서 클라이언트 노드로 통신이 간후 다시 취합하여 사용하기를 권장하는데 여기서 장점은 노드에 많은 요청이안가 부하가 적게걸린다는점인데
    클라이언트에 많은요청이가면 이 노드에서는 부하가 안걸리는지가 궁금합니다.
    또한 클라이언트노드를 사용하지않고 즉 1개의 마스터노드와 2개의 데이터노드로 사용시 검색을 할때 어느 노드 아이피로 검색을 날려야하는지 궁금합니다. ES홈페이지에는 요청순서는 나와있는데 요청을 받아서 모든 shard에게 요청을 전달하여 결과를 다시 취합하여 사용자에게 전달이라고 나와있는데 이때 요청을 받은 노드로 통신을 한다는 뜻이 특정 노드로만 요청을 해야하는건가요? 아님 클러스터로 구성되어 있어서 클러스터 내 모든 노드들한테 요청이 가는지 궁금합니다.
    마지막으로 클라이언트 노드 사용시 요청순서가 궁금합니다.
    클라이언트 노드에서 request를 받으면 연결된 모든 노드안의 shard에게 전달하는 방식인가요?

    • 안녕하세요. ^^
      질문 감사드립니다.

      먼저, 엘라스틱서치에서는 특별한 경우를 제외하고는 검색 요청이 인덱스를 구성하고 있는 모든 샤드들에 전달되고 각 샤드들로부터 받은 결과를 최초 요청을 받은 노드가 취합 및 정렬등의 작업을 수행한 뒤 최종 결과를 애플리케이션에 전달하게 됩니다. 여기에서 특별한 경우란 커스텀 라우팅을 이용하여 조회(retrieve)하고자 하는 샤드를 지정하는 경우입니다.
      만약, 클라이언트 노드를 따로 두지 않는다면 요청을 받은 노드는 자신이 가지고 있는 샤드에 대하여 검색을 수행함은 물론, 다른 샤드들로 부터 받은 결과를 취합하여 반환하는 작업을 수행해야 하기 때문에 혼자서 너무 많은 작업을 수행하게 됩니다.
      이러한 이유로, 가지고 있는 샤드에 대하여 검색을 수행하는 데이터 노드와, 검색은 수행하지 않고 요청을 전달하고 결과를 취합하는 역할만 하는 클라이언트 노드를 별도로 두는 것입니다.
      물론, 많은 요청이 발생하면 클라이언트 노드에 부하가 집중되겠지만, 이것은 클라이언트 노드를 두지 않는 방법으로 해결하는 것이 아니라, 여러대의 클라이언트 노드를 두고 그 앞에 로드밸런싱을 수행하는 노드를 구성하는 것이 바람직합니다.

      마지막으로, 클라이언트를 두지 않고, 1개의 마스터 노드와 2개의 데이터노드를 운용한다고 하시면 어떤 노드에 요청을 보내시더라도 성능은 동일하게 되며, 다만, 특정 노드에 요청이 집중되지 않도록 관리해주시면 될 것 같습니다.

      만족스러운 답변이 되었는지 잘 모르겠습니다만, 혹시 또 궁금하신 내용이 있으시면 언제든지 댓글 달아주시면 감사하겠습니다. ^^

      • 답변 감사합니다.
        한가지 질문이 더 있는데요
        ES에서 split brain문제에 대해서 minimum_master_nodes를 n/2+1개로 지정하라고 하는데
        제가 현재 총 3개의 노드로 클러스터를 구성할려고하는데 하나는 클라이언트 노드(master:false,data:false) ,데이터 노드(master:false,data:true), 마스터노드(master:true,data:false)로 구성할려고하는데 이경우 최소 마스터노드갯수를 2로 지정하면 문제가 생기는거같습니다. 마스터노드는 하나고 만약을 마스터노드가 죽으면 누가 마스터노드를 살리는 역할을 하나요?
        설정은 다음과 같습니다.
        master
        cluster.name: elasticsearch
        node.name: node_data
        node.master: true
        node.data: false
        index.number_of_shards: 5
        index.number_of_replicas: 1
        network.host:마스터노드아이피
        transport.tcp.port: 9300
        transport.tcp.compress: true
        http.enabled: false
        discovery.zen.ping.multicast.enabled: false
        discovery.zen.minimum_master_nodes: 2
        discovery.zen.ping.timeout: 3s
        discovery.zen.ping.unicast.hosts: [“마스터노드아이피:9300″, “데이터노드아이피:9300″,”클라이언트노드아이피:9300″]
        action.auto_create_index: true
        action.disable_shutdown: true
        disable_delete_all_indices: true
        index.mapper.dynamic: true

        data
        cluster.name: elasticsearch
        node.name: node_data
        node.master: false
        node.data: true
        index.number_of_shards: 5
        index.number_of_replicas: 1
        network.host: 데이터노드아이피
        transport.tcp.port: 9300
        transport.tcp.compress: true
        http.enabled: false
        discovery.zen.ping.multicast.enabled: false
        discovery.zen.minimum_master_nodes: 2
        discovery.zen.ping.timeout: 3s
        discovery.zen.ping.unicast.hosts: [“마스터노드아이피:9300″, “데이터노드아이피:9300″,”클라이언트노드아이피:9300″]
        action.auto_create_index: true
        action.disable_shutdown: true
        disable_delete_all_indices: true
        index.mapper.dynamic: true

        client
        cluster.name: elasticsearch
        node.name: node_data
        node.master: false
        node.data: true
        index.number_of_shards: 5
        index.number_of_replicas: 1
        network.host: 데이터노드아이피
        transport.tcp.port: 9300
        transport.tcp.compress: true
        http.port: 9200
        discovery.zen.ping.multicast.enabled: false
        discovery.zen.minimum_master_nodes: 2
        discovery.zen.ping.timeout: 3s
        discovery.zen.ping.unicast.hosts: [“마스터노드아이피:9300″, “데이터노드아이피:9300″,”클라이언트노드아이피:9300″]
        action.auto_create_index: true
        action.disable_shutdown: true
        disable_delete_all_indices: true
        index.mapper.dynamic: true

        위설정으로 실행을하면 마스터노드와 데이터노드는 실행되지만 클라이언트노드 실행시
        waited for 30s and no initial state was set by the discovery
        이와같은 문제가 발생합니다.

      • 다시 실행해보니 실행이되지만 마스터노드를 셧다운 시키면 마스터노드가 없어지기때문에 데이터노드가 아무역할을 할수가 없는거같습니다. 데이터 노드쪽에 master :true로 해놓아야하나요?

        • 안녕하세요. JU님.

          먼저, 간단히 몇 가지를 설명드리면 좋을 것 같습니다.

          1. node.master: true 설정은 해당 노드를 마스터 노드로 선택하겠다는 의미라기 보다, 해당 노드가 마스터 노드가 “될 수도 있게 한다”라는 의미 입니다. 그렇기 때문에 마스터 노드를 한 대와 데이터 노드 한 대를 운영하시기 위해서 하나의 노드는 node.master: true, 다른 하나의 노드는 node.master: false로 지정하실 필요가 없습니다. 다시 말해서 클라이언트 노드를 제외한 두 개의 노드 모두 node.master: true, node.data: true로 설정하시면 클러스터의 master election 과정을 통해 자동으로 하나는 마스터 노드가 되고 다른 하나는 데이터 노드가 됩니다. 이 때, 마스터 노드에 장애가 발생하면 데이터 노드로 사용되던 노드가 마스터가 사라졌음을 인식하고 자신이 마스터로 선출될 수 있는 기회를 얻게 되는 것입니다.

          2. Split brain 이슈를 막기 위해서 discovery.zen.minimum_master_nodes 설정의 값을 (N/2)+1 로 권장한다고 할 때, N은 정확히 말하자면 “클러스터를 구성하는 총 노드의 개수”가 아니고, “클러스터를 구성하는 노드들 중, 마스터로 선출될 수 있는(node.master: true) 노드의 개수” 입니다. 즉, 현재 JU님께서 말씀하신 구성(클라이언트노드 1, 마스터가능 노드1, 마스터불가능 노드1)이라고 하면 N 값은 1이 됩니다.

          결론적으로 말씀드리자면, 클라이언트 노드의 설정은 그대로 두시고, 나머지 두 개의 노드 설정을 node.master: true, node.data: true로 동일하게 하신 뒤에, 두 노드 중에 어떤 노드를 마스터로 사용할지는 엘라스티서치 자체의 마스터 election 에 맡기셔야 합니다. 인덱싱이나 검색 요청은 어떤 노드가 마스터로 선출되었는지와 상관없이 클라이언트 노드에 요청하시면 되고요. ^^

          이렇게 설정을 변경하신 뒤에 discovery.zen.minimum_master_nodes 설정 값을 (2/2)+1 = 2로 설정하시면 마스터 노드를 선정하기 위해 최소 두 개의 노드가 서로 communication 해야 하기 때문에 두 노드간의 통신이 단절되었을 때 마스터 노드가 두 개 선정되는 split brain 문제를 막을 수 있습니다. 다만, 말씀하신 경우에 하나의 마스터 노드에 장애가 발생하면 다른 노드가 discovery.zen.minimum_master_nodes 제한에 따라 마스터로 선정될 수 없기 때문에 전체 클러스터가 사용 불가능 상태가 될 수 있습니다. 이러한 이유로 node.master: true 노드가 두 개일 때에는 발생가능성이 적은 split brain 문제 때문에 minimum_master_nodes 값을 2로 설정하는 것 보다, 가용성을 보장하기 위해서 1로 설정하는 것이 더 좋지 않을까 하는 개인적인 생각입니다. 이 부분에 대해서는 제가 정확히 확신할 수 없어서 다른 내용들을 좀 더 참고해 보시는 것이 좋을 것 같습니다.

          감사합니다. ^^

  4. 답변감사합니다.
    마스터 1 데이터 1 클라이언트1 이렇게 구성하고 discovery.zen.minimum_master_node를 2로 설정하였을 경우 위에서 말씀하신것처럼 마스터 노드에 장애가 발생시 최소 2개의 노드가 통신해야한다는 설정을 위와같이 했기때문에 데이터노드 하나만 남아서 문제가 split brain은 피할수 있지만 남아있는 데이터 노드가 마스터노드로 선정될수없어서 클러스터전체에 장애가 생긴다고 말씀하셨는데 그럼 구성을 마스터1 데이터2 클라이언트1로 구성하고 discovery.zen.minimum_master_node를 2설정시 이문제를 해결할수있겠네요?

    클라이언트노드를 웹어플리케이션 서버에 붙임 클라이언트 노드를 위해 서버를안만들어도 되어서 가용한 서버가 하나 늘어서 그렇게 구성하면 되지 않을까 싶은데요 제가 이해한게 맞나요?

    • 네. 마스터로 선출될 수 있는 노드(master eligible)가 3개라면 discovery.zen.minimum_master_node 값이 2로 설정되어 있어도 하나의 노드에 장애가 발생하여도 다른 노드가 마스터로 재선출 될 수 있습니다.

      클라이언트 노드를 WAS 와 같은 서버에서 운영하시면 하나의 서버가 남기는 하지만 WAS 자체 성능의 저하를 유발하지는 않을지 잘 고민하셔야 할 것 같습니다. 여기에 대해서는 애플리케이션의 트래픽이 어느정도고 동시접속이 어느정도인지 등이 영향을 미치기 때문에 정답은 없고 상황에 맞게 설정하셔야 할 것 같습니다.

      그러나 개인적으로는 총 세대의 노드를 하나의 클라이언트노드(node.master:false, node.data:false) 와 두 개의 데이터 노드(node.master:true, node.data:true) 로 사용하시고, discovery.zen.minimum_master_node 값을 1로 설정하시는 편이 좋지 않을까 생각됩니다. split brain 문제는 마스터 노드가 실제로 장애가 없음에도 불구하고 다른 노드와 통신이 불가능하여 발생하기 때문에 그렇게 흔하게 발생하는 케이스는 아니기 때문입니다.

      물론, 정답은 없기 때문에 운영해보시면서 최적의 설정을 찾아나가시면 좋을 것 같습니다. ^^

      참고로, 클러스터 설정시에 성능 등을 최적화하는 몇 가지 팁을 아래의 링크에서 참고해 보시는 것도 좋을 것 같습니다.
      https://www.loggly.com/blog/nine-tips-configuring-elasticsearch-for-high-performance/

  5. 안녕하세요

    좋은 정보 공유로 인해 많은 도움을 받고 있습니다.

    한가지 궁금한게 있어 문의 드리는데요….

    제가 이번에 클러스터를 두개의 데이터 노드로 구성하여 검색 성능 측정을 하려다 보니…(replica :1, shards :5 로 셋팅)

    primary shard에 대해 궁금점이 생겨서요.

    1. 저는 처음에 primary shard가 실제로 검색시 검색 결과를 반환하는 shard로 알고 있었는데 그게 맞는지? 아니라면
    실제로 primary shard의 정확한 동작 및 의미는 무엇인가요?

    2. 두대의 데이터 노드 중 1대의 데이터 노드를 먼저 start한 후 나머지 1대의 데이터 노드를 start하면 replica는 각각 1개씩 분배되어
    각 노드당 5개의 shard를 가지게 되는데 primary shard는 먼저 start한 노드에만 구성이 되어 있습니다. 이렇게 되면 노드를 2개를
    띄워도 성능 향상에 도움이 되지 않는게 아닌가요?(1번 질문의 첫번째 답이 Yes인 경우로 생각 했습니다.)

    3. primary shard와 master node와는 전혀 관계가 없는 것이 맞는가요? 즉 master node는 실제 검색 시 어떤 노드로 검색 요청을 보내는 지에 대한 meta 정보를 가지고 있을 뿐이고 primary shard는 ….(잘모르겠네요 이게 어떤 역활인지)

    조금 두서없이 질문드린것 같아 죄송합니다.ㅠ.ㅠ 답변 부탁드릴게요

    • 안녕하세요. ^^ 댓글 남겨주셔서 감사합니다. ^^

      1. 먼저, primary shard 뿐만 아니라, replica shard 도 검색시에 결과를 반환하는 shard 입니다. primary shard 와 replica shard 는 검색시에는 동일하게 동작합니다. ^^ 하지만 indexing 시에는 replica shard 보다 primary shard 가 먼저 인덱싱되는 차이점이 있습니다. http://www.slideshare.net/hosangjeon10/introduction-to-elasticsearch-42781557 이 링크에서 24번째 슬라이드를 보시면 조금 더 잘 이해가 되실 것 같습니다. ^^

      2. 말씀하신대로 1번 질문의 답이 No 이기 때문에 성능향상에 도움이 되는 것이 맞습니다. ^^ 첫번째 노드에 primary shard 가 모두 배치된 뒤 replica shard 들이 unassigned 상태에서 두 번째 노드가 start 되면서 배치되었다면 한쪽 노드에 primary shard 가 몰려있어서 어떤 룰이 있는 것처럼 느껴지실 수는 있지만, 노드를 하나 더 띄워보시면 primary shard 와 replica shard 는 서로 구분없이 뒤 섞여 있다는 것을 확인하실 수 있습니다. 다만, 하나의 primary shard 와 이 shard 의 replica shard (즉, 동일한 데이터를 가지고 있는) 는 절대로 같은 노드에 위치할 수 없습니다. ^^

      3. Primary shard 와 master node 는 전혀 관계가 없다고 말씀드릴 수 있습니다. Primary shard 는 마스터 노드에 위치할 수도 있고, 그렇지 않을 수도 있습니다. 마스터 노드가 가지고 있는 메타정보는 검색시 보다는 오히려 인덱싱 과정에서 (또는 retreieve 과정) 문서의 id 값을 기준으로 해당 id 를 갖는 문서가 어떤 샤드에 인덱싱 되어야 하는지 (또는 해당 샤드가 어디에 있는지) 를 결정하는 역할을 합니다. 검색시에는 아무리 마스터 노드라고 해당 검색 결과가 어떤 노드에, 또는 어떤 샤드에 있는지 알 수가 없습니다. ^^

      저도 좀 두서없이 답을 달아서… 도움이 되셨는지 잘 모르겠습니다. ^^

      • 아 정말 많은 도움이 됬습니다.

        shard와 replica와 관련해 한가지 질문이 더 있는데요.

        제가 알기로 shard와 replica는 cluster구성 후 최초 index수행 후에는 변경이 불가능한 것으로 알고 있습니다.

        1. 만약 shard : 5 , replica : 1로 설정되어 있는 cluster에 shard: 2, replica : 1과 같이 설정이 다른 새 노드가 동일한 cluster명으로 접근하게 된다면 어떤 현상이 발생하게 될나요? 실제로 수행하여 보니 별다른 문제 없이 replica가 새 노드에 재분할된 것처럼 보입니다.

        2. 만약 cluster구성 후 elasticsearch로 서비스를 하다 불가피한 이유(막대한 양의 데이터 유입으로 인한 shard 증가 필요)로 이해 shard와 replica에 대한 cluster 설정을 변경해야 한다면 어떤 방법이 있을까요? 전체 full 색인으로 신규 index를 만드는 방법밖에는 없을까요?

        답변 부탁드리겠습니다.

      • 넵~

        먼저, 1번에 대해서 간단히 설명을 드리도록 하겠습니다. ^^

        먼저, shard 와 replica 의 개수는 클러스터의 기본값이 존재하기는 하지만 기본적으로 인덱스별로 각각 개별적인 값을 갖는다고 생각하시면 됩니다. 즉, A 라는 인덱스가 shard 5 개 replica 1로 구성되어 있다면, 그 이후에 노드가 추가되는 것은 해당 인덱스의 설정값에 전혀 영향을 주지 않습니다. 그리고 추가로… shard 의 개수는 한번 정해지면 인덱스를 다시 생성하지 않는 이상 변경이 불가능하지만, replica 의 개수는 실행시점에서도 언제든지 변경할 수 있습니다.

        2. 만약, shard 의 개수를 늘려야하는 상황이 필요하다면 제가 아는 방법으로는 해당 인덱스를 다시 생성하는 방법밖에 없을 것 같습니다. 혹시 더 좋은 방법을 알게 되시면 저도 알려주시면 감사하겠습니다. ^^

  6. 계속 질문에 질문이 꼬리를 무는것 같아 죄송하네요 ㅠ.ㅠ

    또 replica에 대해서 이해가 잘안되는 것이 있는데요.

    위 포스팅 중 replica의 존재 이유인 “검색 성능”과 “장애복구”에서 두 번재 장애 복구에 대해서는 이해가 됬습니다.

    (n개의 노드 이상 발생 시 최소 n+1개의 노드가 존재하며 최소 n-1개의 replica가 존재해야 정상적인 서비스가 유지된다는 점)

    그런데 검색 성능에 관한 언급이 없으셔서요.

    1. replica가 늘어날 수록 검색 시 검색 요청을 보내고 받아서 취합해야 할 shard가 늘어나게 되니 당연히 검색 성능이 떨어지게 되지 않나요?(elasticsearch는 특별한 라우터 기능을 사용하지 않는 한 모든 shard에 검색 요청을 하니깐)

    2. 또한 replica개수 만큼 노드에 복사본이 만들어지게 되니 disk사용량도 급격히 올라갈 것으로 보이는데 이것과 관련해서
    replica의 갯수를 제한을 두지는 않나요?(일반적으로 서비스 시 n+1개의 노드가 있다면 n-1개의 replica를 설정하진 않을것으로 보임)

    많이 배우고 있습니다. 감사합니다.

    • 넵~ 답이 늦어서 죄송합니다. ^^

      1. 먼저, replica 가 “검색 요청”은 인덱스를 구성하는 모든 샤드에 전달되는 것이 아니라, 인덱스를 구성하는 모든 “샤드셋”에 전달된다고 보시면 됩니다. 예를 들어 A 라는 인덱스를 구성하는 샤드가 주샤드 5개 (1, 2, 3, 4, 5) replica 1 (1′, 2′, 3′, 4′, 5′) 으로 굿성되어 있다면, 검색요청이 전달되는 샤드는 (1, 2′, 3, 4′, 5) 또는 (1′, 2′, 3, 4, 5′) 이렇게 주샤드와 복제샤드의 구분없이 전체 셋을 구성하는 샤드에 전달되기 때문에 replica 의 개수가 증가한다고 해서 취합해야하는 데이터의 양이 증가하지는 않습니다. ^^

      2. 말씀하신대로 Replica의 수가 많아지면 디스크의 사용량이 늘어나게 됩니다. Replica 의 개수는 전체 노드 수와 샤드의 수, 그리고 어느정도의 fail over 를 기준으로 할 것인지 운영노하우에 따라서 결정을 하는데요… 이전 글에서 말씀드렸듯이 replica 의 개수는 실행시점에(on the fly) 변경이 가능하기 때문에 디스크 사용량과 장애율에 따라서 조절해 나가시는 것이 맞을 것 같습니다. ^^ 따로 정답이 있는 것은 아니지만 기본적으로 replica 의 수가 노드의 수와 같거나 많으면 불필요하게 unassigned 되는 샤드들이 발생하므로 노드의 수보다 작게 설정합니다. ^^

  7. 안녕하세요.
    ElasticSearch 관련하여 테스트를 하고 있는데 궁금한게 있어 댓글 남깁니다.

    노드가 1개만 있는 상태에서 기본 세팅인 아래 수치로 설정하고 서버를 구성하였습니다.

    index.number_of_shards: 5
    index.number_of_replicas: 1

    여기서 인덱스를 새로 생성하게 되고 _cluster/state를 체크해보면 Unassigned로 된 node가 되어있고 클러스터 상태가 YELLOW로 되게 됩니다.

    같은 노드내에 동일한 replica가 존재하면 안되기 때문인거 같은데요.

    만약 정상적인 shard에 문제가 생기면 Unassigned에 있는 shard가 활성화가 되는건가요?
    아니면 replica를 사용하기 위해서는 반드시 2개 이상의 node가 필요한건가요?

    질문이 두서가 없네요. 양해 부탁 드립니다.

    • 안녕하세요. 질문 주셔서 감사합니다. ^^

      먼저, node 가 하나인 경우 동일한 primary shard 와 replica shard 는 같은 노드에 존재할 수 없기 때문에 unassigned shard 가 존재하게 됩니다.

      이러한 상황에서 primary shard 에 문제가 생기면 replica shard 가 그자리를 대체하는 것이 아니라, 해당 shard 가 가지고 있던 데이터를 조회할 수 없게 됩니다.

      데이터의 유실방지를 위해서 replica 수를 1로 하는 경우 반드시 두 개 이상의 노드가 필요합니다. ^^

      감사합니다. ^^

  8. 안녕하세요
    엘라스틱서치에 대해 많은 궁금증을 해결해주셔서 감사합니다.
    감사하다는 인사와 함께 여쭤볼게 있어 이렇게 글을 남기게 되었습니다.

    그저 그런 초보 프로그래머가 엘라스틱서치를 개발하게 되어 여러가지 어려움이 많네요ㅠㅠ
    바로 질문 드릴게요

    첫째로 클라이언트 노드에 필요성을 느끼지 못해(여기의 글을 보기 전) 3개의 장비를
    1. master true data true 2. master true data true 3. master false data true 로 설정하고 매번 마스터 노드에만 요청을 하고 있었습니다.
    이 상태에서 클라이언트 노드를 설정하고 싶다면 위에서 답변주신 것처럼 장비를
    1. master false data false 2. master true data true 3. master true data true 로 설정하고 클라이언트 노드에 요청을 해야겠지요.
    그런데 제가 하고 싶은거는 현재 1번 노드를 클라이언트로 노드로 바꿀때 기존의 데이터들을(현재 Unassigned) 어떻게하면 나머지 두 데이터 true노드들로 분배할 수 있을까요?

    둘째로 sort와 aggregation의 문제로 질문 드리겠습니다.
    제가 이해한 바로는 sort와 aggregation 은 field data를 생성하기 때문에 많은 량은 메모리를 사용한다고 알고 있습니다. 그리고 무엇보다 느리고요.
    1. 제가 원하는 바 두가지가 있습니다.
    하나는 제가 요청하는 쿼리의 검색 결과만을 가져와 그 결과만을 소팅하거나 aggregation 작업을 했으면 하는데 자료를 찾아봐도 우선 field data를 생성하고 쿼리를 수행하는 것 같았습니다.
    2. sort와 aggregation을 요청하게 되면 지나치게 느려집니다. 한 번의 요청을 900초가 걸려 수행해낸다던지 물론 이 질문을 하기 위해 저의 설정을 보여드려야 하겠지요
    요청하시면 설정, 장비 스펙, 쿼리까지 다 보여드리겠습니다..ㅠㅠ

    너무 바보같고 부족한 질문에도 답변을 주신다면 감사하겠습니다..

    • 안녕하세요. 질문 주셔서 감사합니다. ^^

      1. 우선, 클라이언트 노드는 다음과 같은 이유로 필요합니다. ^^ 실제 클라이언트 노드 없이 aggregation 등을 수행하시다보면, 데이터를 가지는 노드가 자신의 shard 들에 대한 aggregation과 전체 결과를 합치는 역할을 동시에 수행하면서 성능은 물론, heap memory의 out of memory 발생이 빈번히 발생하시게 될 겁니다. 이러한 이유로 결국 로드 밸런싱 역할만을 수행하는 클라이언트 노드가 필요하게 됩니다. ^^ 특정 노드를 클라이언트 노드로 설정하시려면 node.client: true를 설정해 주시면 됩니다. 노드의 설정이 변경된 뒤 해당 노드를 재실행 해주시면 자동적으로 샤드들이 재분배(relocating)됩니다. 만약, 그럼에도 불구하고, unassigned shard 가 존재한다면, (replica의 수 + 1) < = node.data: true인 노드의 수 인지 한번 확인해봐 주시면 좋을 것 같습니다. ^^

      2. 우선 aggregation의 경우, 기본적으로 query 결과에 대해서만 수행하게 됩니다. 그렇기 때문에, 수행시간이 너무 오래 걸린다면, aggregation 자체를 한번 봐야할 것 같습니다. ^^
      Sort의 경우에는 query 결과에 대해서만 정렬을 수행하기 위한 방법은 잘 모르겠습니다만, 성능 등의 이슈가 있다면 필드의 데이터 유형이 문자열인 경우, 해당 필드의 데이터가 analyze 되지 않는 not_analyzed 필드를 만들어서 해당 필드에 대하여 정렬을 수행하시는 것이 바람직합니다.

      답변이 부족할 수도 있는데, 혹시 더 필요하신 내용이 있으시면 말씀해주시면 감사하겠습니다. ^^

  9. 분에 넘치는 빠른 답변을 받아 행복합니다 ㅎㅎ
    답변 주셔서 감사합니다.

    (replica의 수 + 1) < = node.data: true 라고 하셨는데 실제로 저에게는 replica가 필요가 없어 replica는 0입니다. 프라이머리 샤드만을 사용하고 있습니다.
    그런데도 unassigned shard 로 되어 재분배가 일어나지 않고 있습니다..ㅠㅠ
    일단 제가 설정한 부분을 보여드려야 할 것 같아서 보여 드리겠습니다.
    부족한 영어실력에 혼자 이리지리 돌아다니면서 검색하다보니 엉망진창입니다.
    /config/elasticsearch.yml
    개별
    1. node.master: false node.data: false 2. node.master: true node.data: true 3. node.master: true node.data: true
    공통
    index.number_of_shards: 35
    index.number_of_replicas: 0
    index.cache.field.type: soft

    indices.breaker.fielddata.limit: 85%
    indices.breaker.total.limit: 85%
    indices.fielddata.cache.size: 75%
    indices.breaker.request.limit: 55%
    indices.memory.index_buffer_size: 10%

    cluster.routing.allocation.disk.threshold_enabled : false
    cluster.routing.allocation.balance.shard: 0.1
    cluster.routing.allocation.balance.index: 0.9
    #discovery.zen.minimum_master_nodes: 1
    공통
    /bin/elasticsearch
    export ES_HEAP_SIZE=16g

    이렇게 되어있습니다..너무 엉망이라 창피한데 다음에는 안 창피하려고 여쭤봅니다

    • 엉망이라뇨~ ^^; 저도 어차피 같이 배워나가는 사람일 뿐입니다. ^^
      말씀하신 내용만 보면, shard relocation이 정상적으로 이루어져야 하는데, 현재 제가 아는 지식으로는 원인을 잘 모르겠네요. ^^;

      저도 종종 알 수 없는 원인으로 unassigned shard 가 relocating 되지 않는 경우에는 아래와 같은 방식으로 강제 reroute를 하고는 하는데요, 매번 이렇게 하는 게 맞는 건지는 잘 모르겠네요. ^^


      curl -XGET http://localhost:9200/_cat/shards | grep UNASSIGNED | awk '{print $1,$2}'

      curl -XPOST 'localhost:9200/_cluster/reroute' -d '{
      "commands" : [ {
      "allocate" : {
      "index" : "your_index_name", < -- 인덱스 명 "shard" : 4, <-- shard 번호 "node" : "your_node_name", <-- 노드 명 "allow_primary" : true } } ] }'

      혹시, 이 내용이 원하시는 답변이 아니시라면, (그리고 혹시 페이스북을 하신다면) 아래의 페이스북 그룹에 가입하시고 다시 질문을 한번 올려 보시겠어요? ^^ 좋은 분들이 많아서 혹시 원하시는 답을 얻으실 수도 있을 것 같습니다. ^^
      https://www.facebook.com/groups/elasticsearch.kr/

  10. 답변을 빠르게 주셨는데 제가 출장 후 휴가가 바로 있어서 글을 확인하지 못했네요.
    매번 빠른 답변에 감사드립니다.

    샤드의 재분배에 관한 부분은 말씀대로 진했했습니다.
    다른 설정의 변경없이 클라이언트 노드의 생성만으로도 성능이 엄청나게 향상되었습니다. 감사합니다.

    지난번에 제 쿼리에 대해 답변을 주셨는데 그 때도 출장준비 중이었거든요 ㅠㅠ

    1. 우선 sort에 관한 쿼리는
    {
    “query”: {
    “filtered”: {
    “filter”: {
    “bool”: {
    “must”: [
    {
    “range”: {
    “date”: {
    “from”: “2015/06/01 00:00:00″,
    “to”: “2015/06/30 23:59:59″
    }
    }
    }
    ],
    “must_not”: [],
    “should”: []
    }
    }
    }
    },
    “sort”: [
    {
    “date”: {
    “reverse”: true
    }
    }
    ],
    “aggs”: {}
    }
    이런 방법으로 보내고 있으며 전체 인덱스에 쿼리를 날리지 않고 월별로 나누어진 인덱스에 쿼리를 보내고 있습니다.
    인덱스는 평균적으로 docs: 4,502,021,488 size: 568Gi 이정도가 됩니다.

    2. aggregation에 관한 쿼리는
    {
    “query”: {
    “filtered”: {
    “filter”: {
    “bool”: {
    “must”: [
    {
    “range”: {
    “date”: {
    “from”: “2015/06/01 00:00:00″,
    “to”: “2015/06/30 23:55:00″
    }
    }
    },
    {
    “range”: {
    “test_alpha”: {
    “from”: “aaa”,
    “to”: “zzz”
    }
    }
    }
    ],
    “must_not”: [],
    “should”: []
    }
    }
    }
    },
    “sort”: [],
    “size”: 0,
    “aggs”: {
    “test_aggs”: {
    “terms”: {
    “field”: “test_alpha”,
    “size”: 100,
    “order”: {
    “inA.sum”: “desc”
    }
    },
    “aggs”: {
    “inA”: {
    “stats”: {
    “field”: “in_a”
    }
    },
    “outA”: {
    “stats”: {
    “field”: “out_a”
    }
    },
    “inG”: {
    “stats”: {
    “field”: “in_g”
    }
    },
    “outG”: {
    “stats”: {
    “field”: “out_g”
    }
    }
    }
    }
    }
    }
    현재는 sort는 client 노드를 설정하므로 성능이 향상이 되었는데 aggregation 의 성능은 많이 뒤쳐지고 있습니다.
    1100초가 넘게 걸리기도 하고 그러네요 ㅠㅠ

    3. 저는 현재 월별로 나누어진 인덱스에 쿼리를 보내 응답을 기다리고 있습니다. sort와 aggregation 모두 말이죠.
    요청을 주소로만 보내는 것보다 주소뒤에 인덱스를 설정해주는 것이 훨씬 성능이 좋은것 같아서 이렇게 하고 있습니다.
    제가 드는 의문은 제가 took시간에 인덱스를 지정하는 것은 크게 효과가 나타나지만 _type을 지정하여 쿼리를 보내는 부분에서는 크게 효과가 나타나지 않았습니다.
    혹시 _type에는 인덱스와는 다른 개념으로 받아드려야 하는것인가요?

    매번 명쾌한 답변을 주셔서 감사합니다.

    여기 페이스북은 전에 가입하려 했는데 가입승인이 안나서 질문을 못올렸었어요 지금 다시 신청해볼게요 ㅎㅎ

    • 앗. 덧글을 주신지 오래 되었는데 제가 미처 확인을 못했네요… ㅜㅜ

      실제 데이터를 넣어보고 테스트를 해보면 좋을 것 같은데, 제가 최근에 여유가 없어서 좀 어려울 것 같습니다.
      저번에 말씀드린 것 처럼 facebook 그룹에 aggregation 을 올려보시고 다른 분들께 개선 사항이 없는지 여쭤보시는 것도 빠른 방법일 것 같습니다. ^^

      제대로 된 답변 드리지 못해서 죄송합니다. ^^;

  11. 아닙니다. 답변주신거 정말 감사합니다^^
    항상 좋은 답변으로 잘 인도해주셔서 감사합니다!!^^

  12. 안녕하세요~
    검색을 통해 들어오게 됐는데 덕분에 많이 배웠습니다^^

    저는 얼마전에야 비로소 ELK 셋팅을 해봤는데요.
    WAS 10 대의 로그를 logstash 를 이용해서 ES에 넣고 있습니다.
    시험삼아 해 본 것이라 현재는 ES 노드를 하나만 운용하고 있는데요,
    향후 확장성을 고려해서 클라이언트 – Data 노드(2) 구성으로 클러스터링을 하려고 합니다.
    이와 관련해서 궁굼한 점이 있는데요,
    현재 운영중인 ES서버를 클라이언트로 셋팅 하고 데이터 노드 2대를 구성하면 기존에 만들어졌던 데이터들은 새로 구성한 데이터 노드 2대에 자동으로 이전이 되나요?

    • 네. 그렇습니다. ^^
      좀 더 안전하게 처리하기 위해서는 기존의 노드를 그대로 둔 채, 데이터 노드를 추가하고 샤드가 relocate 완료되면 그 때 기존 노드를 client 로 설정하면 더욱 좋을 것 같습니다. ^^
      감사합니다.

      • 이렇게 빨리 답변을 주시다니, 감사합니다 ^^

        그런데 상황이 좀 바뀌어서 하나 더 질문을 드려봅니다.

        원래 계획은 기존에 쓰던 서버를 클라이언트로 바꾸고 데이터 노드를 추가하는 것 이었는데 사정상 다른 공간에 있는 서버로 변경이 필요하게 되었습니다. 그래서 불가피 하게 데이터를 통째로 다른 서버로 옮기는 작업을 해야 하는데 이 때도 ES의 클러스터링을 이용할 수 있을지 궁굼합니다.
        즉, 현재 구조인 ZONE1/ES-OLD 에 ZONE2/ES-DATA-NODE1, ES-DATA-NODE2, ES-NEW-CLIENT(node.data:false, node.master:false) 를 추가하고 relocate가 완료되면 ES-OLD를 빼면 될까요?

  13. 안녕하세요.
    엘라스틱 고수분인듯보이내요^^

    다름이 아니오라 제가현재 6대 was에 같은서버에 엘라스틱을 구상해놨는데 20여일 잘쓰다가 갑자기 out of memory를 만나게 됐습니다.. 이유를 잘 몰라서 그러는데 indexing 도중에는 문제가 없고 검색시 현재 문제가 발생 하더군요 검색은 aggregation, post filter, sorting, search 이4가지 조합으로 검색을합니다.
    현재 엘라스틱 메모리는 1g 디폴트로 구성되어있습니다. 이유가 먼지 알려주시면 고맙습니다 ㅠㅠ
    두서없이 글쓴거 같어 죄송하네요
    혹시 yml설정이 문제이면 다시 올려 드리겠습니다

    • 안녕하세요. ^^ 제가 고수는 아니지만 최대한 아는대로 답변을 적어 드리겠습니다.
      검색에서 메모리가 부족한 경우 대부분 aggregation으로 인한 경우가 많습니다. 최초 요청을 받은 노드는 다른 샤드의 결과들을 취합하여야 하기 때문에 별도의 작업을 추가로 해야하는데요, 만약, 클라이언트 노드를 별로로 두지 않으셨다면 결과를 취합하는 노드의 경우 본인이 가지고 있는 샤드의 aggregation 과 함께 전체 결과를 취합해야 하기 때문에 메모리가 부족할 가능성이 높습니다.
      데이터를 가지고 있지 않고 마스터로도 사용되지 않는 클라이언트 노드를 하나 두시고 해당 노드로 요청을 해보시면 좋을 것 같습니다. ^^

      • 답변 고맙습니다. “Hosang Jeon” 님 말씀대로 그렇다면 먼저 yml에서 노드 한대는 master: false, data : false로 하고 aggregation의 경우 이 노드로 요청을 하라는 말이 맞지요? ^_________^

        후 그 이유 라면 좋겠네요 ㅠㅠ 두다리 뻗고 자고 싶네요..

        • 아 그리고 한개더 질문 드릴께요 … ㅡㅡ;
          index.cache.field.type: soft

          indices.breaker.fielddata.limit: 85%
          indices.breaker.total.limit: 85%
          indices.fielddata.cache.size: 75%
          indices.breaker.request.limit: 55%
          indices.memory.index_buffer_size: 10%
          이 셋팅이 아마도 query cache 관련 인듯 보이는데 설정은 윗분꺼 참조 했습니다.
          이런식으로 셋팅 하면

          settings을 설정 할때
          curl -XPUT localhost:9200/my_index -d’
          {
          “settings”: {
          “index.cache.query.enable”: true
          }
          }
          위 curl도 필요 한지요…
          그럼 말씀 기다리겠습니다. ㅠㅠ


Elasticsearch 인덱싱 최적화


https://brunch.co.kr/@alden/37


오늘 다룰 주제는 ElasticSearch (이하 ES)에서 인덱싱 성능을 최적화하기입니다. 사실 ES의 기본 설정은 잘되어 있기 때문에 RPM으로 설치하고 그냥 사용하기만 해도 별다른 어려움 없이 사용할 수 있습니다. 하지만 몇 가지 설정들을 바꿔줌으로써 더 많은 양의 문서를 인덱싱 하게 할 수 있습니다.

먼저 이번 포스트를 이끌어 갈 세 가지 키워드를 소개하겠습니다.


1. _all 필드

2. static mapping

3. refresh_interval

4. primary shard


우리는 오늘 이 네 가지 키워드를 통해서 ES가 더 많은 문서들을 인덱싱 할 수 있도록 튜닝해 나갈 것입니다. 그럼 하나씩 살펴보겠습니다.


기본 설정에서의 성능


우선 본격적인 튜닝에 앞서 아무것도 변경하지 않은 기본 설정에서는 어느 정도의 성능을 발휘하는지 살펴보겠습니다. 테스트는 총 10개의 text 형태의 필드를 가지고 있는 5000개의 문서를 _bulk API를 통해서 색인한 후 그 결과로 반환되는 소요시간, took 값을 비교합니다.

테스트에 사용한 스크립트는 조금 더 다듬어서 github에 올릴 예정입니다.

먼저 기본 설정에서의 결과입니다.

python ./elasticsearch_perf_test.py --url es --index_name normal_test --documents 5000

{"acknowledged":true}
elapsed time : 1057 ms

head 플러그인을 통해서 잘 생성되었는지 확인해 보겠습니다.

head 플러그인을 통해 인덱스의 생성 확인

기본 설정에서는 5000개의 문서를 색인하는데 약 1초 정도의 시간이 소요된 것을 볼 수 있습니다. 그럼 이제 튜닝을 시작해 보겠습니다.


_all 필드


첫 번째 키워드는 _all 필드입니다. _all 필드는 사용자의 문서에 있는 필드가 아니고 특별한 목적에 의해 ES가 생성하는 필드입니다. 

참고 자료 : https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-all-field.html 

이 필드는 해당 문서의 모든 필드를 하나의 스트링처럼 길게 이어서 새로운 text 형태의 필드를 만들고 이 필드의 내용을 인덱싱 합니다. 사용자의 의도와는 다르게 필드 하나가 추가된다고 볼 수 있습니다. 게다가 문서에 존재하는 필드 수에 따라 아주아주 긴 text 필드가 추가될 수 있습니다. text 필드는 analyzer를 거치기 때문에 길면 길수록 인덱싱 성능에 악영향을 끼칩니다. 성능에 악영향을 끼칠 수 있는 필드를 왜 기본적으로 생성할까요? 사용자는 _all 필드를 통해서 필드명 없는 검색을 할 수 있게 됩니다. 예를 들어 아래와 같은 형태의 문서가 있다고 가정해 보겠습니다.

예제 문서

원래대로 라면 검색을 하기 위해서는 first_name : John 과 같은 형태로 쿼리를 짜야 하지만 _all 필드가 켜져 있다면 그냥 John 이라고 쿼리를 입력하기만 해도 검색이 됩니다. 그래서 _all 필드를 disable 하게 되면 인덱싱 성능이 향상되지만, 필드명 없는 검색이 불가능해집니다. 따라서 사용하는 워크로드에서 필드명 없는 검색이 있는지 없는지를 확인하고 enable/disable을 결정해야 합니다.

그렇다면 _all 필드를 disable 하게 되면 얼마나 빨라지는지 확인해 볼까요? 이 작업을 위해 먼저 아래와 같이 템플릿을 생성해서 등록해 보겠습니다.

curl -XPUT 'http://es:9200/_template/all_field_test' -d '{
  "template":"all_field_test",
  "mappings": {
    "type1": {
      "_all": {
        "enabled": false
      }
    }
  }
}'

all_field_test라는 이름의 템플릿을 만들었으며, 이 템플릿은 all_field_test라는 이름을 가진 인덱스가 생성될 때 type1 이라는 타입은 _all 필드를 생성하지 않게 합니다. 그리고 테스트를 다시 돌려 보겠습니다.

python ./elasticsearch_perf_test.py --url es --index_name all_field_test --documents 5000

{"acknowledged":true}
elapsed time : 770 ms

기존에 1초가량 소요되었던 시간이 700 ms 단위로 내려왔습니다. 무려 30% 정도의 성능 향상이 이루어졌습니다. 


static mapping


두 번째 키워드는 static mapping 입니다. ES는 schemaless 구조이기 때문에 미리 인덱싱할 문서의 필드를 정의하지 않아도 됩니다. ES는 첫 번째로 인입된 문서의 내용을 분석해서 가장 적합한 형태로 각각의 필드를 매핑합니다. 그렇기 때문에 편하지만 최적화되지는 않았습니다. 특히 text 필드와 keyword 필드가 그런데요, 둘 다 문자열을 나타내는 필드이지만, 큰 차이를 가지고 있습니다.

이번는 아래와 같은 템플릿을 만들어 보겠습니다.

curl -XPUT 'http://infra-es-perftest01.dakao.io:9200/_template/static_mapping_test' -d '{
  "template":"static_mapping_test",
  "mappings": {
    "type1": {
      "_all": {
        "enabled": false
      },
      "properties": {
        "field-0": {
          "type": "keyword"
        },
.... (중략) ....
        "field-9": {
          "type": "keyword"
        }
      }
    }
  }
}'

아까 _all 필드를 disable 했던 템플릿의 내용에 static mapping 내용을 추가합니다. static_mapping_test라는 이름으로 인덱스가 만들어지면 field-0부터 field-9까지의 필드가 정의된 채로 생성됩니다.

이때의 결과는 어떨까요?

python ./elasticsearch_perf_test.py --url infra-es-perftest01.dakao.io --index_name static_mapping_test --documents 5000

{"acknowledged":true}
elapsed time : 356 ms

역시 이번에도 많이 줄어들었습니다. 700ms 대에서 300ms 대로 절반 이상 줄어들었습니다. 

text 필드가 keyword 필드로 많이 바뀔수록 그 차이는 더 크게 날 겁니다.

_all 필드와 static mapping 만 적용했는데도 벌써 1초에서 300ms 수준으로 상당한 양의 성능 향상 일어났습니다.


refresh_interval


세 번째 키워드는 refresh_interval입니다. ES는 새로운 문서가 인입되었을 때 인덱싱을 하고 그 결과를 세그먼트라는 단위로 저장합니다. 이렇게 생성되는 작은 크기의 세그먼트들은 백그라운드로 머지되어 점점 큰 세그먼트가 되어 갑니다. 이때 ES가 세그먼트를 생성하는 것을 refresh라고 부르며 이 세그먼트를 생성하는 주기를 refresh_interval을 통해서 조절할 수 있습니다. refresh_interval의 기본값은 1초입니다. 그래서 새롭게 인덱싱 된 문서는 1초 이내에 검색이 되며 이를 통해 near realtime search engine이라는 콘셉트를 구현할 수 있게 됩니다. 당연히 세그먼트를 생성하는 작업은 성능에 영향을 줄 수밖에 없습니다. 그래서 이 값을 기본 1초에서 더 크게 늘리면 더 많은 양의 데이터를 인덱싱 할 수 있게 됩니다. 

그래서 이번 테스트는 조금 다르게 단위 시간 동안 얼마나 많은 양의 문서를 인덱싱 할 수 있느냐를 테스트해 보겠습니다. 아래와 같이 한 인덱스는 기본 값으로, 다른 인덱스는 refresh_interval을 60초로 설정해서 120초 동안 데이터를 넣어 보겠습니다.

python ./elasticsearch_perf_test.py --url infra-es-perftest01.dakao.io --documents 1000 --threads 10 --index_name refresh_interval_test --period 120

사실 결과는 조금 놀랍습니다.refresh_interval 테스트 결과

둘 다 120초 동안 데이터를 입력했는데 기본 1초로 설정된 인덱스는 120초 동안 355,000개의 문서를 refresh_interval이 60초로 설정된 인덱스는 같은 시간 동안 356,000개의 문서를 인덱싱 했습니다. 이 정도면 차이가 없다고 봐도 됩니다. 하지만 다른 사이트에서의 테스트 결과는 refresh_interval을 조절해서 상당한 양의 차이를 얻었다고 합니다. 

참고 자료 : https://sematext.com/blog/elasticsearch-refresh-interval-vs-indexing-performance  

한 번 읽어보시면 좋을 것 같습니다.

아마도 인입되는 데이터의 형태나 기간 등이 영향을 끼친 것이라고 추측합니다.


primary shard


마지막으로 다룰 키워드는 primary shard의 개수입니다. 모든 인덱싱은 primary shard에서 일어납니다. 그래서 primary shard의 개수가 몇 개 이냐에 따라서 인덱싱 성능이 달라질 수 있습니다. 이번에는 primary shard의 개수를 변경해 가면서 테스트해 보겠습니다.primary shard 갯수에 따른 took의 변화

primary shard의 개수가 늘어날수록 took이 줄어드는 것을 볼 수 있습니다. 점점 큰 폭으로 줄어들다가 어느 순간 반대로 늘어나는 것도 확인할 수 있습니다. 

이는, primary shard가 늘어날수록 인덱싱 성능이 좋아지지만, 무조건 많은 양의 primary shard가 좋은 성능을 이끌어 내는 것은 아니다 라는 것을 보여주고 있습니다. 

실제 문서의 크기, 필드 개수, 필드 타입 등의 환경과 서버의 성능에 따라 적합한 양의 primary shard의 개수가 다를 수는 있겠지만, 어쨌든 적합한 양의 primary shard의 개수가 존재한다는 것을 보여주고 있습니다. 그래서 테스트를 통해서 사용하고자 하는 환경에서 적합한 양의 primary shard의 개수를 찾아야 합니다.


마치며


ES는 튜닝하지 않은 기본값으로도 훌륭한 성능을 보여주지만 조금만 관심을 가지고 살펴보면 동일한 환경에서도 더 많은 양의 문서를 색인하게 할 수 있습니다. 이번 글에서 다루었던 주요 내용은 아래와 같습니다.


1. _all 필드는 불필요하면 끄는 것이 인덱싱 성능을 높이는 효과가 있다.

2. 더 많은 양의 문서를 인덱싱 하고 싶다면 가급적 static mapping을 통해서 인덱싱 작업을 최적화해야 할 필요가 있다.

3. refresh_interval은 새로운 세그먼트의 생성 주기를 변경하기 때문에 늘려주면 인덱싱 성능을 높이는데 효과가 있다. 다만, 환경마다 크게 효과가 없을 수도 있다.

4. 적합한 양의 primary shard를 선정하는 것이 더 많은 문서를 인덱싱 하는데에 도움을 준다.


혹시라도 내용이 잘못되었거나 부연 설명이 필요한 부분이 있으면 말씀해 주세요~


ps. 제일 마지막엔 셀프 책 광고를.. ㅋㅋ

http://www.yes24.com/24/goods/44376723?scode=032&OzSrank=3

참고-비교-ulimit vs limits.conf .docx



CentOS7.4 – ulimit vs limits.conf 차이점

http://pchero21.com/?p=918

http://board.theko.co.kr/bbs/board.php?bo_table=Command&wr_id=5

 

* to do

중요:  limit 명령은 일시적인 것이고 영구적으로 설정하려면  /etc/security/limits.conf 에 설정해야 한다.

ulimit 는 하나의 유저(,프로세스)에 대허서 할당할 자원량의 한계를 정하는 것으로서 다중 프로그램/사용자를 기본으로하는 리눅스 시스템에서 과부하를 막아주는 (한프로세스가 미쳐도 다른것에 영향이 덜가도록) 방패가 되어 주는 유용한 설정이다.

 

01. ulimit

- 명령어 (확인-소프트)

[root@localhost ~]#ulimit -a (또는 ulimit -Sa) // soft 한도

core file size                 (blocks, -c) 0

data seg size                (kbytes, -d) unlimited

scheduling priority                  (-e) 20

file size                      (blocks, -f) unlimited

pending signals                     (-i) 16382

max locked memory          (kbytes, -l) 64

max memory size            (kbytes, -m) unlimited

open files                           (-n) 1024

pipe size                  (512 bytes, -p) 8

POSIX message queues         (bytes, -q) 819200

real-time priority                     (-r) 0

stack size                     (kbytes, -s) 8192

cpu time                     (seconds, -t) unlimited

max user processes                   (-u) unlimited

virtual memory                (kbytes, -v) unlimited

file locks                             (-x) unlimited

- 명령어 (확인-하드)

[root@localhost ~]#ulimit -Ha // hard 한도

core file size                 (blocks, -c) unlimited
data seg size                (kbytes, -d) unlimited
scheduling priority                  (-e) 20
file size                      (blocks, -f) unlimited
pending signals                      (-i) 16382
max locked memory         (kbytes, -l) 64
max memory size          (kbytes, -m) unlimited
open files                           (-n) 1024
pipe size                (512 bytes, -p) 8
POSIX message queues      (bytes, -q) 819200
real-time priority                    (-r) 0
stack size                    (kbytes, -s) unlimited
cpu time                   (seconds, -t) unlimited
max user processes                 (-u) unlimited
virtual memory             (kbytes, -v) unlimited
file locks                           (-x) unlimited

- 명령어 (확인-소프트/하드)

[root@localhost ~]#ulimit -Ha / ulimit –Sa     (하드 설정 전체 보기 / 소프트 설정 전체 보기)

è 하드는 해당쉘의 최대값을 뜻한다 하면 되고,  소프트는 현재 설정을 말한다 생각하면 된다.

è soft설정은 hard설정값을 넘을수 없으므로. 그냥 soft/hard같이 설정해주는 버릇을 들이자.

 

- 명령어 (설정-예제)

[root@localhost ~]# ulimit -n 8192     // 오픈 파일의 최대 수

 

- 옵션

 ulimit 옵션

 -a 모든 제한 사항을 보여준다.
 -c
최대 코어 파일 사이즈
 -d
프로세스 데이터 세그먼트의 최대 크기
 -f shell
에 의해 만들어질 수 있는 파일의 최대 크기
 -s
최대 스택 크기
 -p
파이프 크기
 -n
오픈 파일의 최대 수
 -u
프로세스 최대 수
 -v
최대 가상 메모리의 량

 

02. /etc/security/limits.conf 에 설정 파일

: 수정한 내용의 적용은 해당 유저가 새로운 접속을 시도하면 적용됩니다.(짧게 말해 재접^^;)

- 형식은 (limit.conf-)

# /etc/security/limits.conf

[유저이름] [hard/soft] [설정할 항목] [설정값]

 

- ex

mklife hard nofile 320000  => mklife 유저는 한번 접속에 하드세팅으로 32만개 파일까지 열수 있다.

mklife hard nproc 10000   => mklife 유저는 한번 접속에 하드 세팅으로 1만개 프로시져를 생성가능

mklife soft  nproc 10000   => mklife 유저는 한번 접속에 소프트 세팅으로 1만개 프로시져를 생성가능

(주의) soft설정은 hard설정값을 넘을수 없으므로. 그냥 soft/hard같이 설정해주는 버릇을 들이자.

 

- 옵션

# /etc/security/limits.conf
#
#Each line describes a limit for a user in the form:
#
#<domain>        <type>  <item>  <value>
#
#Where:
#<domain> can be:
#       
an user name
#       
a group name, with @group syntax
#       
the wildcard *, for default entry
#       
the wildcard %, can be also used with %group syntax,
#                 for maxlogin limit
#       
NOTE: group and wildcard limits are not applied to root.
#          To apply a limit to the root user, <domain> must be
#          the literal username root.
#
#<type> can have the two values:
#       
– “soft for enforcing the soft limits
#       
– “hard for enforcing hard limits
#
#<item> can be one of the following:
#       
core limits the core file size (KB)
#       
data max data size (KB)
#       
fsize maximum filesize (KB)
#       
memlock max locked-in-memory address space (KB)
#       
nofile max number of open files
#       
rss max resident set size (KB)
#       
stack max stack size (KB)
#       
cpu max CPU time (MIN)
#       
nproc max number of processes
#       
as address space limit (KB)
#       
maxlogins max number of logins for this user
#       
maxsyslogins max number of logins on the system
#       
priority the priority to run user process with
#       
locks max number of file locks the user can hold
#       
sigpending max number of pending signals
#       
msgqueue max memory used by POSIX message queues (bytes)
#       
nice max nice priority allowed to raise to values: [-20, 19]
#       
rtprio max realtime priority
#       
chroot change root to directory (Debian-specific)
#
#<domain>      <type>  <item>         <value>
#

#*               soft    core            0
#root            hard    core            100000
#*               hard    rss             10000
#@student        hard    nproc           20
#@faculty        soft    nproc           20
#@faculty        hard    nproc           50
#ftp             hard    nproc           0
#ftp            
      chroot          /ftp
#@student       
      maxlogins       4

# End of file

 

 

 

 


'Linux > CentOS7 > 비교' 카테고리의 다른 글

[참고] 비교-rpm vs yum  (0) 2018.04.16

참고-비교-rpm vs yum.docx



CentOS7.4 – RPM VS YUM

https://kkimsangheon.github.io/2017/07/03/ThisIsLinux7/

RPM 명령어 옵션: https://zetawiki.com/wiki/%EB%A6%AC%EB%88%85%EC%8A%A4_rpm_%EC%82%AC%EC%9A%A9%EB%B2%95

YUM 명령어 예제: https://www.manualfactory.net/10098

 

* to do (해당 계정으로 진행)

RPM

 

초창기 리눅스는 설치가 어려웠다 .소스코드를 갖고와서 적재적소에 컴파일을 해야했다. 그래서 일반사용자가 사용하기엔 거의 불가능했다. 이러한 이유로 외면을 받았었다. 결국 Redhat에서 RPM(Redhat Package Manager)을 내놓았다. 윈도우의 setup.exe 라고 생각하면 된다.

 

- 파일 : gedit-3.8.3-6.el7.x86_64.rpm

패키지이름 : gedit -> 패키지(프로그램)의 이름

버전 : 3.8.3 -> 대게 3자리수로 구성. 주버전, 부버전, 패치버전

릴리즈번호 : 6 -> 문제점을 개선할 때마다 붙여지는 번호 //noarch:모든CPU

CentOS버전 : el7 -> CentOS에서 배포할 경우에 붙여짐

아키텍처 : x86_64 -> 64비트 CPU를 의미

 

 

- 자주쓰는 RPM 명령어 옵션

$ rpm -Uvh 패키지파일이름.rpm

    // U -> (대문자)패키지가 설치/업그레이드

    // v -> 설치진행과정의 확인

    // h -> 설치진행과정을 "#"마크로 화면에 출력

$ rpm -e 패키지이름  //패키지 삭제

$ rpm -qa 패키지이름  //패키지가 설치되었는지 확인

$ rpm -qf 파일의 절대경로 // 파일이 어느 패키지에 포함된 것인지 확인

$ rpm -qlp 패키지파일이름.rpm //패키지 파일에 어떤 파일들이 포함되었는지 확인

$ rpm -qip 패키지파일이름.rpm //패키지 파일의 상세정보

 

- EX>

$ rpm -qa mc  //mc 패키지가 설치되었나 확인

$ rpm -Uvh mc-4.8.7-8.el7.x86_64.rpm //설치

$ rpm -qa mc  //mc 패키지가 설치되었나 확인

$ rpm -qi mc  //mc 패키지 정보 출력

$ rpm -e mc-4.8.7-8.el7.x86_64.rpm  //삭제

오류: mc-4.8.7-8.el7.x86_64.rpm 패키지가 설치되어 있지 않습니다

//설치파일을 삭제하는것이다.!!!!

$ rpm -e mc  //패키지 이름만으로 삭제

 

 

YUM의 등장

 

Yellowdog Updater Modified

 

rpm의 경우 의존성 문제가 존재함. 이를 해결하기 위해 나온것이 YUM

의존성 문제란? A를 설치하기 위해 B가 먼저 설치되어있어야 하는 것.

rpm B가 설치되어있지 않을 경우 A패키지 설치를 시도할 경우 B를 설치해야 한다고 알려주기는 하나 대략적으로 알려주고, B또한 다른 패키지에 의존성이 있을수도 있어 불편함을 유발한다.

yum은 인터넷이 정상적으로 동작해야 작동함. yum은 의존성이 있는것을 모두 알아서 설치해준다!!! yum 명령어는 내부적으로 rpm명령을 실행하는것!!

 

- yum의 동작과정

1. yum install을 입력한다

2. /etc/yum.repos.d/ 를 참고하여 URL을 확인하고

3. 전체 패키지 목록 파일을 요청한다(CentOS 7 패키지 저장소로)

4. 전체 패키지 목록 파일만 다운로드

5. 설치할 패키지와 관련된 패키지의 이름을 화면에 출력

6. y를 입력하면 설치에 필요한 패키지 파일을 요청함

7. 설치할 패키지 파일을 다운로드해서 자동 설치

 

 

- 자주쓰는 YUM 명령어 옵션

$ yum install 패키지이름             //패키지 설치

$ yum -y install 패키지이름           //패키지 설치(y안눌러도 됨)

$ yum remove 패키지이름            //패키지 제거

$ yum localinstall rpm파일이름.rpm    //로컬에 존재하는 rpm으로 설치

// 만약 의존성이 있을경우 그 대상은 레파지토리에서 깐다.

$ yum info mc                       //mc패키지에 관한 정보확인     

$ yum groupinstall "패키지 그룹이름"  //예로 자바를 깐다면 jdk

//이클립스 등 한꺼번에 설치됭

$ yum clean all                      //캐시를 지우는것.

//yum이 이상할때 써보자

 

 

- yum grouplist

$ yum grouplist                  //그룹리스트 조회

$ yum grouplist hidden           //숨겨진것까지 조회

 

 

 

 


'Linux > CentOS7 > 비교' 카테고리의 다른 글

[참고] 비교-ulimit vs limits.conf  (0) 2018.04.16

+ Recent posts