2.X/2. Search in Depth

2-1-6. All About Caching

drscg 2017. 9. 30. 02:10

Earlier in this chapter (Internal Filter Operation), we briefly discussed how non-scoring queries are calculated. At their heart is a bitset representing which documents match the filter. When Elasticsearch determines a bitset is likely to be reused in the future, it will be cached directly in memory for later use. Once cached, these bitsets can be reused wherever the same query is used, without having to reevaluate the entire query again.

이 장(Internal Filter Operation)의 초반에서, non-scoring query의 계산 방법에 대해 간단히 언급한 바 있다. 핵심은 filter와 일치하는 document를 나타내는 bitset이다. 나중에 사용하기 위해 이들 bitset을 적극적으로 cache한다. 일단 cache되면, 이들 bitset은 동일한 query가 사용될 때 마다 , 전체 query를 다시 고려하지 않고, 재사용될 수 있다.

These cached bitsets are "smart": they are updated incrementally. As you index new documents, only those new documents need to be added to the existing bitsets, rather than having to recompute the entire cached filter over and over. Filters are real-time like the rest of the system; you don’t need to worry about cache expiry.

이들 cache는 "smart" 하다. 그들은 증분 업데이트된다. 새로운 document를 색인하면, 반복해서 전체 cachee된 filter를 다시 계산하는 것이 아니라, 이들 새로운 document만 기존의 bitset에 추가된다. filter는 system의 일부인 것처럼, 실시간(real-time)이다. cache 만료에 대해 염려할 필요가 없다.

Independent Query Cachingedit

The bitsets belonging to a query component are independent from the rest of the search request. This means that, once cached, a query can be reused in multiple search requests. It is not dependent on the "context" of the surrounding query. This allows caching to accelerate the most frequently used portions of your queries, without wasting overhead on the less frequent / more volatile portions.

query 요소를 포함하고 있는 bitset은 search request의 나머지 부분에 대해 독립적이다. 이것은 일단 cache되면, query는 다수의 query request에서 재사용될 수 있다는 것을 의미한다. 주변 query의 "context" 에 의존적이지 않다. caching은 가끔 사용되거나 많이 변경되는 부분에 대한 overhead를 낭비하지 않고, query의 가장 흔히 사용되는 부분을 가속화할 수 있다.

Similarly, if a single search request reuses the same non-scoring query, its cached bitset can be reused for all instances inside the single search request.

유사하게, 단일 search request가 동일한 non-scoring query를 재사용한다면, 그 cache된 bitset은 단일 search request 내부의 모든 instance에 대해 재사용될 수 있다.

Let’s look at this example query, which looks for emails that are either of the following:

다음과 같이 서로 다른 email을 찾는 예제 query를 살펴보자.

  • In the inbox and have not been read

    inbox에 있고, 아직 읽지 않은

  • Not in the inbox but have been marked as important

    또는, inbox에는 없지만, 중요하다고 표시된

GET /inbox/emails/_search
{
  "query": {
      "constant_score": {
          "filter": {
              "bool": {
                 "should": [
                    { "bool": {
                          "must": [
                             { "term": { "folder": "inbox" }}, 
                             { "term": { "read": false }}
                          ]
                    }},
                    { "bool": {
                          "must_not": {
                             "term": { "folder": "inbox" } 
                          },
                          "must": {
                             "term": { "important": true }
                          }
                    }}
                 ]
              }
            }
        }
    }
}

 

이들 두 query는 동일하므로, 동일한 bitset을 사용할 것이다

Even though one of the inbox clauses is a must clause and the other is a must_not clause, the two clauses themselves are identical. If this particular term query was previously cached, both instances would benefit from the cached representation despite being used in different styles of boolean logic.

비록 inbox 절 중 하나는 must 절이고, 다른 하나는 must_not 절이지만, 두 절 자체는 동일하다. 이 독특한 term query가 이전에 cache 되었다면, 다른 스타일의 boolean logic에서 사용되었지만, 두 instance 모두는 cache의 혜택을 누릴 것이다.

This ties in nicely with the composability of the query DSL. It is easy to move filtering queries around, or reuse the same query in multiple places within the search request. This isn’t just convenient to the developer—it has direct performance benefits.

이것은 query DSL의 구성과 잘 엮인다. search request 내에서 filtering query를 다른 곳으로 옮기거나, 여러 장소에서 동일한 query를 재사용하는 것은 쉽다. 이것은 단지 개발자에게만 편리한 것은 아니다. 직접적인 성능상의 이점이다.

Autocaching Behavioredit

In older versions of Elasticsearch, the default behavior was to cache everything that was cacheable. This often meant the system cached bitsets too aggressively and performance suffered due to thrashing the cache. In addition, many filters are very fast to evaluate, but substantially slower to cache (and reuse from cache). These filters don’t make sense to cache, since you’d be better off just re-executing the filter again.

Elasticsearch의 예전 버전에서, 기본적인 동작 방식은 cache 가능한 모든 것을 cache하는 것이었다. 이것은 종종 시스템이 너무 공격적으로 bitset을 cache하여, cache로 인한 성능 이슈가 발생하였다. 또한, 많은 filter는 매우 빠르다. 하지만, cache는 상당히 느리다. (그리고 cache를 재사용한다.) 이들 filter는 cache에 적당하지 않다. filter를 다시 실행하는 것이 더 낫기 때문이다.

Inspecting the inverted index is very fast and most query components are rare. Consider a termfilter on a "user_id" field: if you have millions of users, any particular user ID will only occur rarely. It isn’t profitable to cache the bitsets for this filter, as the cached result will likely be evicted from the cache before it is used again.

inverted index를 확인하는 것은 매우 빠르고, 대부분의 query 요소는 드물다. "user_id" field에 대한 term filter를 생각해 보자. 수백만 명의 사용자가 있다면, 특정 사용자 ID는 거의 발생하지 않을 것이다. cache된 결과는 다시 사용되기 전에 cache에서 추출될 것이기 때문에, 이 filter에 대한 bitset을 cache하는 것은 이득이 없다.

This type of cache churn can have serious effects on performance. What’s worse, it is difficult for developers to identify which components exhibit good cache behavior and which are useless.

이런 형태의 cache 변경은 성능에 심각한 영향을 준다. 더 나쁜 것은, 어떤 요소가 cache 동작에 좋은지, 어느 것이 쓸모 없는지를 구분하는 것이 어렵다는 점이다.

To address this, Elasticsearch caches queries automatically based on usage frequency. If a non-scoring query has been used a few times (dependent on the query type) in the last 256 queries, the query is a candidate for caching. However, not all segments are guaranteed to cache the bitset. Only segments that hold more than 10,000 documents (or 3% of the total documents, whichever is larger) will cache the bitset. Because small segments are fast to search and merged out quickly, it doesn’t make sense to cache bitsets here.

이를 해결하기 위해, Elasticsearch는 사용 빈도를 기반으로 자동으로 query를 cache한다. (query 형태에 의존적인) non-scoring query가 최근 256 query에서 몇 번 사용되었다면, query는 caching의 후보이다. 그러나, 모든 segment에서 bitset을 cache하는 것을 보장하지는 않는다. 10,000 document 이상을 가지고 있는 segment(또는 모든 document의 3%, 어느 것이든지 더 큰)만 bitset을 cache한다. 작은 segment는 검색이 빠르고 곧 병합되기 때문에, 여기에서 bitset을 cache하는 것은 비합리적이다.

Once cached, a non-scoring bitset will remain in the cache until it is evicted. Eviction is done on an LRU basis: the least-recently used filter will be evicted once the cache is full.

일단 cache되면, non-scoring bitset은 추출될 떄까지 cache에 남아 있을 것이다. 추출은 LRU를 근거로 이루어진다. cache가 가득 차면 가장 최근에 사용된 filter는 추출될 것이다.


'2.X > 2. Search in Depth' 카테고리의 다른 글

2-1-4. Ranges  (0) 2017.09.30
2-1-5. Dealing with Null Values  (0) 2017.09.30
2-2. Full-Text Search  (0) 2017.09.30
2-2-1. Term-Based Versus Full-Text  (0) 2017.09.30
2-2-2. The match Query  (0) 2017.09.30