Blog

2013.09.16 - 번역 - Elasticsearch from the Bottom Up, Part 1 ...

drscg 2019. 1. 5. 23:26

UPDATE: This article refers to our hosted Elasticsearch offering by an older name, Found. Please note that Found is now known as Elastic Cloud.

이 게시물은 기존에 Found라는 이름으로 제공된 Elasticsearch 서비스에 관한 것이다. Found은 이제 Elasticsearch Cloud로 알려져 있다.

In this article series, we look at Elasticsearch from a new perspective. We'll start at the "bottom" (or close enough!) of the many abstraction levels, and gradually move upwards towards the user-visible layers, studying the various internal data structures and behaviours as we ascend.

이 게시물 시리즈에서는 새로운 관점에서 Elasticsearch를 살펴볼 것이다. 우리는 여러 가지 추상화 단계 중에서 "맨 아래"(또는 충분히 가까운 곳)부터 시작하여, 사용자가 볼 수 있는 계층으로 점차적으로 올라가, 다양한 내부 data 구조와 동작을 학습할 것이다.

Introduction

The motivation is to get a better understanding of how Elasticsearch, Lucene and to some extent search engines in general actually work under the hood. While you can drive a car by turning a wheel and stepping on some pedals, highly competent drivers typically understand at least some of the mechanics of the vehicle. The same is true for search engines. Elasticsearch provides APIs that are very easy to use, and it will get you started and take you far without much effort. However, to get the most of it, it helps to have some knowledge about the underlying algorithms and data structures. This understanding enables you to make full use of its substantial set of features such that you can improve your users search experiences, while at the same time keep your systems performant, reliable and updated in (near) real time.

Elasticsearch, Lucene 및 기타 검색 엔진이 핵심부에서 일반적으로 실제로 작동하는 방식에 대한 더 나은 이해를 얻는 것이 목적이다. 바퀴를 돌리고 페달을 밟아 차를 운전할 수 있겠지만, 유능한 운전자는 일반적으로 차량의 메커니즘 중 적어도 일부를 이해한다. 검색 엔진에서도 마찬가지이다. Elasticsearch는 사용하기 간편한 API를 제공하므로, 많은 노력을 들이지 않고도 시작할 수 있으며 활용할 수 있다. 그러나 이를 최대한 활용하려면, 기본 알고리즘과 data 구조에 대한 지식이 있어야 한다. 이러한 이해를 통해, 사용자 검색 환경을 개선하는 동시에 시스템의 성능, 안정성 및 업데이트를 실시간으로 유지할 수 있도록 하는, 실질적인 기능 집합을 충분히 활용할 수 있다.

We will start with the basic index structure, the inverted index. It is a very versatile data structure. At the same time it's also easy to use and understand. That said, Lucene's implementation is a highly optimized, impressive feat of engineering. We will not venture into Lucene's implementation details, but rather stick to how the inverted index is used and built. That is what influences how we can search and index.

기본 index 구조인 inverted index부터 시작하겠다. 이것은 매우 다재다능한 data 구조이다. 동시에 사용하기 쉽고 이해하기 쉽다. 그렇지만, Lucene의 구현은 매우 최적화되어 있고 놀라운 엔지니어링 기술이다. Lucene의 구현 세부 사항을 조사하지 않고, 오히려 inverted index가 어떻게 사용되고 구축되는지에 집중할 것이다. 그것이 바로 검색하고 색인하는 방법에 영향을 준다.

Having introduced the inverted index as the "bottom" of the abstraction levels, we'll look into:

inverted index를 추상화 단계 중 "맨 아래"로 소개했으니, 다음을 살펴보자.

  • How simple searches are performed.
    검색이 얼마나 간단하게 구현되었는가
  • What types of searches can (and cannot) effectively be done, and why, with an inverted index, we transform problems until they look like string-prefix problems.
    어떤 종류의 검색이 효과적으로 수행될 수 있는지(그리고 수행될 수 없는지), 왜 inverted index를 사용하여, 문제를 문자열-접두어처럼 변형하는지
  • Why text processing is important.
    왜 text 처리가 중요한지
  • How indexes are built in "segments" and how that affects searching and updating.
    "segment"에서 index가 구축되는 방법과 그것이 검색과 업데이트에 미치는 영향
  • What constitutes a Lucene-index.
    Lucene-index를 구성하는 요소
  • The Elasticsearch shard and index.
    Elasticsearch의 shard와 index

At that point, we'll know a lot about what happens inside a single Elasticsearch node when searching as well as indexing. The second article in the series will cover the distributed aspects of Elasticsearch.

이 시점에서, index과 검색시에 단일 Elasticsearch node 내부에서 일어나는 일에 대해 많이 알게 될 것이다. 이 시리즈의 두 번째 게시물에서는 Elasticsearch의 배포 측면에 대해 다룰 것이다.

Inverted Indexes and Index Terms

Sample documents and resulting inverted index

Let's say we have these three simple documents: "Winter is coming.", "Ours is the fury." and "The choice is yours.". After some simple text processing (lowercasing, removing punctuation and splitting words), we can construct the "inverted index" shown in the figure.

다음과 같은 3개의 간단한 document가 있다고 가정해 보자. "Winter is comong.", "Ours is the fury." 그리고 "The choice is yours.". 간단한 text 처리 (소문자화, 구두점 제거 및 단어-word 분리) 후에, 그림에 표시된 것처럼, "inverted index"를 구성할 수 있다.

The inverted index maps terms to documents (and possibly positions in the documents) containing the term. Since the terms in the dictionary are sorted, we can quickly find a term, and subsequently its occurrences in the postings-structure. This is contrary to a "forward index", which lists terms related to a specific document.

inverted index는 용어(term)를 포함하는 document (그리고, document에서의 위치)에 term(용어)를 매핑한다. 사전(dictionary)에 있는 용어(term)로 정렬되기 때문에, 빠르게 용어(term)와 그 이후에 posting-structure(위치 구조)에서 occurrence를 찾을 수 있다. 이는 특정 document 관련된 용어(term)를 나열하는 "forward index"와 반대이다.

A simple search with multiple terms is then done by looking up all the terms and their occurrences, and take the intersection (for AND searches) or the union (for OR searches) of the sets of occurrences to get the resulting list of documents. More complex types of queries are obviously more elaborate, but the approach is the same: first, operate on the dictionary to find candidate terms, then on the corresponding occurrences, positions, etc.

여러 용어(term)에 대한 간단한 검색은 결과 document 목록을 가져오기 위하여, 모든 용어(term)와 그 occurrence를 찾아보고, occurrence 집합의 교집합(AND 검색의 경우) 또는 합집합(OR 검색의 경우)을 수행한다. 보다 복잡한 유형의 query는 분명 더 복잡하지만, 접근 방식은 처음과 동일하다. 먼저 사전을 사용하여 후보가 되는 용어(term)를 찾은 다음, 해당 occurrence, 위치 등을 찾는다.

Consequently, an index term is the unit of search. The terms we generate dictate what types of searches we can (and cannot) efficiently do. For example, with the dictionary in the figure above, we can efficiently find all terms that start with a "c". However, we cannot efficiently perform a search on everything that contains "ours". To do so, we would have to traverse all the terms, to find that "yours" also contains the substring. This is prohibitively expensive when the index is not trivially small. In terms of complexity, looking up terms by their prefix is \(\mathcal{O}\left(\mathrm{log}\left(n\right)\right)\), while finding terms by an arbitrary substring is \(\mathcal{O}\left(n\right)\).

따라서, index term검색 단위(unt of search)이다. 생성하는 용어(term)는 효율적으로 수행할 수 있는(그리고 할 수 없는) 검색 유형을 결정한다. 예를 들어, 위 그림에서 사전(dictionary)을 사용하면, "c"로 시작하는 모든 용어를 효율적으로 찾을 수 있다. 그러나, "ours"를 포함하는 모든 것에 대해 효율적으로 검색을 수행 할 수 없다. 그렇게 하기 위해서는, 모든 용어(term)를 거쳐야 하고, "yours" 또한 일부를 포함하고 있다는 것을 알아야 한다. 이 경우, index가 작은 경우가 아니라면, 엄청난 비용이 소모된다. 복잡성면에서, 접두어(prefix)로 용어(term)를 찾는 것은 \(\mathcal{O}\left(\mathrm{log}\left(n\right)\right)\) 이며, 임의의 부분 문자열로 용어(term)를 찾는 것은 \(\mathcal{O}\left(n\right)\) 이다.

In other words, we can efficiently find things given term prefixes. When all we have is an inverted index, we want everything to look like a string prefix problem. Here are a few examples of such transformations. Some are simple, the last one is bordering on magic.

즉, 주어진 접두어가 있는 용어(term)을 효과적으로 찾을 수 있다. inverted index만 있다면, 모든 것은 접두어 문제일 것이다. 다음은 그러한 변환의 몇 가지 예이다. 어떤 것은 간단하고, 마지막은 거의 마법이다.

  • To find everything ending with "tastic", we can index the reverse (e.g. "fantastic" → "citsatnaf") and search for everything starting with "citsat".
    "tastic"으로 끝나는 모든 항목을 찾으려면, 역(reverse)으로 (예 : "fantastic" → "citsatnaf") index를 생성하고, "citsat"으로 시작하는 모든 항목을 검색할 수 있다.
  • Finding substrings often involves splitting terms into smaller terms called "n-grams". For example, "yours" can be split into "^yo", "you", "our", "urs", "rs$", which means we would get occurrences of "ours" by searching for "our" and "urs".
    부분 문자열 찾기는 종종 용어(term)를 "n-grams" 라고하는 더 작은 용어(term)로 분리하는 것을 포함한다. 예를 들어, "yours" 는 "^yo", "you", "our", "urs", "rs$" 로 분리할 수 있다. 즉, "ours" 는 "our"와 "urs" 로 검색하여 occurrence를 구할 수 있다.
  • For languages with compound words, like Norwegian and German, we need to "decompound" words like "Donaudampfschiff" into e.g. {"donau", "dampf", "schiff"} in order to find it when searching for "schiff".
    노르웨이어나 독일어와 같은 복합어를 사용하는 언어의 경우, "schiff"를 검색할 경우, "Donaudampfschiff"와 같은 단어를 {"donau", "dampf", "schiff"}로 분해(decompound)해야 한다.
  • Geographical coordinate points such as (60.6384, 6.5017) can be converted into "geo hashes", in this case "u4u8gyykk". The longer the string, the greater the precision.
    (60.6384, 6.5017) 같은 지리적 좌표 point는 "geo hashes" 로 변환될 수 있다. 이 경우 "u4u8gyykk" 이다. 문자열이 길수록 정확도가 더 높다.
  • To enable phonetic matching, which is very useful for people's names for instance, there are algorithms like Metaphone that convert "Smith" to {"SM0", "XMT"} and "Schmidt" to {"XMT", "SMT"}.
    예를 들어, 사람들의 이름에 매우 유용한 음성 일치(phonetic matching)를 사용하려면 "Smith"를 {"SM0", "XMT"}로, "Schmidt"를 {"XMT", "SMT"}로 변환하는 Metaphone 같은 알고리즘이 있다.
  • When dealing with numeric data (and timestamps), Lucene automatically generates several terms with different precision in a trie-like fashion, so range searches can be done efficiently1. Simplified, the number 123 can be stored as "1"-hundreds, "12"-tens and "123". Hence, searching for everything in the range [100, 199] is therefore everything matching the "1"-hundreds-term. This is different to searching for everything starting with "1", of course, as that would also include "1234", and so on.
    숫자 데이터(그리고 timestamp)를 처리할 때, Lucene은 자동으로 다양한 정밀도(precision)로 여러 용어(term)를 생성하여, 범위(range) 검색을 효율적으로 수행할 수 있다1. 간단히 말하자면, 숫자 123은 "1"-백, "12"-십 그리고 "123" 으로 저장될 수 있다. 따라서 [100, 199] 범위(range)의 모든 것을 검색하는 것은 "1"-백-용어(term)와 일치하는 모든 것이다. 물론, 이것은 "1"로 시작하는 모든 것을 검색하는 것과는 다르다. "1234" 등을 포함할 수도 있기 때문이다.
  • To do "Did you mean?" type searches and find spellings that are close to the input, a "Levenshtein" automaton can be built to effectively traverse the dictionary. This is exceptionally complex, here's a fascinating story on how it ended up in Lucene.
    검색 제안(Did you mean?) 유형의 검색과 입력에 가까운 철자를 찾기 위해, 사전(dictionary)을 효과적으로 탐색할 수 있도록, Levenshtein automation이 만들어질 수 있다. 이것은 매우 복잡하다. Lucene이 어떻게 했는지에 대한 흥미로운 이야기가 있다.

A technical deep dive into text-processing is food for many future articles, but we have highlighted why it is important to be meticulous about index term generation: to get searches that can be performed efficiently.

text 처리에 대한 기술적 깊은 접근은 많은 미래의 작품을 위한 재료이지만, index 용어(term) 생성(검색을 효율적으로 수행하기 위해) 세심한 주의를 기울이는 것이 중요한지 강조했다.

Building Indexes

When building inverted indexes, there's a few things we need to prioritize: search speed, index compactness, indexing speed and the time it takes for new changes to become visible.

inverted index를 작성할 때, 검색 속도, index 압축, indexing 속도 및 새로운 변경 사항을 표시하는 데 걸리는 시간 등 우선 순위를 지정해야 하는 몇 가지 사항이 있다.

Search speed and index compactness are related: when searching over a smaller index, less data needs to be processed, and more of it will fit in memory. Both, particularly compactness, come at the cost of indexing speed, as we'll see.

검색 속도와 index 압축은 관련이 있다. 더 작은 index를 검색할 경우, 처리해야 할 data가 더 적어지고, 더 많은 data가 memory에 저장된다. 두 가지 모두, 특히 압축은 indexing 속도가 느려진다.

To minimize index sizes, various compression techniques are used. For example, when storing the postings (which can get quite large), Lucene does tricks like delta-encoding (e.g., [42, 100, 666] is stored as [42, 58, 566] ), using variable number of bytes (so small numbers can be saved with a single byte), and so on.

index 크기를 최소화하기 위해, 다양한 압축 기술이 사용된다. 예를 들어, 게시물을 저장할 경우(상당히 커질 수 있다), Lucene은 가변 byte 수(작은 숫자는 단일 bute로 저장될 수 있도록)를 사용하여, delta-encoding(예,[42, 100, 666][42, 58, 566] 로 저장된다) 같은 트릭을 수행한다.

Keeping the data structures small and compact means sacrificing the possibility to efficiently update them. In fact, Lucene does not update them at all: the index files Lucene write are immutable, i.e. they are never updated. This is quite different to B-trees, for instance, which can be updated and often lets you specify a fill factor to indicate how much updating you expect.

data 구조를 작게 압축해서 유지한다는 것은 효율적인 update가 어렵다는 것을 의미한다. 실제로, Lucene은 data를 전혀 update하지 않는다 : Lucene index file은 불변(immutable)이다. 즉, 절대로 update되지 않는다. 예를 들어, update가 가능하고, fill factor를 지정하여 예상되는 update의 양을 나타낼 수 있는 B-tree와는 아주 다르다.

The exception is deletions. When you delete a document from an index, the document is marked as such in a special deletion file, which is actually just a bitmap which is cheap to update. The index structures themselves are not updated.

예외는 삭제이다. index에서 document를 삭제하면, document는 특별한 deletion file에 표시(mark)된다. 이 file은 실제로 update하는데 비용이 적게 드는 bitmap이다. index 구조 자체는 update되지 않는다.

Consequently, updating a previously indexed document is a delete followed by a re-insertion of the document. Note that this means that updating a document is even more expensive than adding it in the first place. Thus, storing things like rapidly changing counters in a Lucene index is usually not a good idea – there is no in-place update of values.

따라서, 이전에 index된 document를 update하는 것은, 삭제 후 document를 다시 insert하는 것이다. 이는 document를 update하는 것이 처음부터 document를 추가하는 것보다 훨씬 비용이 많이 든다는 것을 의미한다. 따라서, 빠르게 변화하는 counter 같은 것을 Lucene index에 저장하는 것은 좋은 생각이 아니다. 즉, 그 자리에 값을 update하는 것은 없다.

When new documents are added (perhaps via an update), the index changes are first buffered in memory. Eventually, the index files in their entirety, are flushed to disk. Note that this is the Lucene-meaning of "flush". Elasticsearch's flush operation involves a Lucene commit and more, covered in the transaction log-section.

새 document가 추가되면(아마도 update를 통해), index 변경 내용이 먼저 memory에 buffering된다. 결국 index file 전체가 disk로 flush된다. 이것이 Lucene에서의 "flush" 이다. Elasticsearch의 flush 연산은, the transaction log-section에서 다루었던, Lucene commit과 그 이상을 포함한다.

When to flush can depend on various factors: how quickly changes must be visible, the memory available for buffering, I/O saturation, etc. Generally, for indexing speed, larger buffers are better, as long as they are small enough that your I/O can keep up2. We go a bit more into detail in the next section.

flush할 시기는 다양한 요소에 따라 달라질 수 있다. 변경 내용을 얼마나 빨리 표시해야 하는지, buffering에 사용할 수있는 memory, I/O 상태 등이 있다. 일반적으로, index 속도의 경우, I/O가 유지2될 수 있을만큼 buffer가 작은 한, buffer가 클수록 더 좋다. 다음 섹션에서 좀 더 자세하게 설명할 것이다.

The written files make up an index segment.

작성된 file은 index segment를 구성하게 된다.

Index Segments

A Lucene index is made up of one or more immutable index segments, which essentially is a "mini-index". When you do a search, Lucene does the search on every segment, filters out any deletions, and merges the results from all the segments. Obviously, this gets more and more tedious as the number of segments grows. To keep the number of segments manageable, Lucene occasionally merges segments according to some merge policy as new segments are added. Lucene-hacker Michael McCandless has a great post explaining and visualizing segment merging.3 When segments are merged, documents marked as deleted are finally discarded. This is why adding more documents can actually result in a smaller index size: it can trigger a merge.

Lucene index는 하나 이상의 불변인 index segment로 구성되며, 기본적으로 "mini-index"이다. 검색을 하면, Lucene은 모든 segment에서 검색을 하고, 모든 deletion을 filtering하고, 모든 segment의 결과를 병합한다. 분명히, 이것은 segment의 수가 증가함에 따라 점점 더 오래 걸린다. segment 수를 관리 가능하게 유지하기 위해, 새로운 segment가 추가되면, Lucene은 어떤 병합(merge) 정책에 따라 segment병합(merge)하기도한다. Lucene-hacker Michael McCandless는 explaining and visualizing segment 병합을 설명하고 보여주는3 멋진 게시물을 작성하였다. segment가 병합되면 삭제된 것으로 표시된 document가 최종적으로 삭제된다. 따라서 더 많은 document를 추가하면, 실제로 index 크기가 작아질 수 있다. 즉, 병합을 트리거할 수 있다.

Elasticsearch and Lucene generally do a good job of handling when to merge segments. Elasticsearch's policies can be tweaked by configuring merge settings. You can also use the optimize API to force merges.

Elasticsearch와 Lucene은 segment 병합을 잘 처리한다. merge settings을 구성하여, Elasticsearch의 정책을 조정할 수 있다. 또한 optimize API를 사용하여, 병합을 강제 실행할 수 있다.

Before segments are flushed to disk, changes are buffered in memory. In the old days (Lucene <2.3), every added document actually existed as its own tiny segment4, and all were merged on flush. Nowadays, there is a DocumentsWriter, which can make larger in-memory segments from a batch of documents. With Lucene 4, there can now be one of these per thread, increasing indexing performance by allowing for concurrent flushing. (Earlier, indexing would have to wait for a flush to complete.)

segment가 disk로 flush되기 전에, 변경 사항이 memory에 buffering된다. 예전에는(Lucene < 2.3), 추가된 모든 document는 실제로 아주 작은 segment4로 존재했으며, 모든 document는 flush될 때 병합되었다. 요즘에는, document batch에서 더 큰 memory segment를 만들 수 있는 DocumentsWriter가 있다. Lucene 4를 사용하면, thread 당 하나가 될 수 있으므로, 동시 flushing을 허용하여, indexing 성능을 향상시킬 수 있다. (이전에는, indexing은 flush 완료까지 기다려야 했다.)

As new segments are created (either due to a flush or a merge), they also cause certain caches to be invalidated, which can negatively impact search performance. Caches like the field and filter caches are per segment. Elasticsearch has a warmer-API5, so the necessary caches can be "warmed" before the new segment is made available for search.

새로운 segment가 만들어지면(flush 또는 병합으로 인해), 특정 cache가 무효화되어, 검색 성능에 부정적인 영향을 미칠 수 있다. field와 filter cache 같은 cache는 segement별이다. Elasticsearch는 warmer-API5를 가지고 있으므로, 새로운 segment를 검색할 수 있도록, 필요한 cache를 "준비(warmed)"할 수 있다.

The most common cause for flushes with Elasticsearch is probably the continuous index refreshing, which by default happens once every second. As new segments are flushed, they become available for searching, enabling (near) real-time search. While a flush is not as expensive as a commit (as it does not need to wait for a confirmed write), it does cause a new segment to be created, invalidating some caches, and possibly triggering a merge.

Elasticsearch에서 가장 일반적인 flush의 원인은 아마도 연속적인 index refresh일 것이다. refresh는 기본적으로 매 초마다 한 번씩 발생한다. 새로운 segment가 flush되면, 검색할 수 있게 되어, 실시간(에 가까운) 검색이 가능해진다. flush가 commit만큼 비용이 많이 들지는 않지만(확인된 쓰기를 기다릴 필요가 없기 때문에), 새로운 segment를 만들고, 일부 cache를 무효화하며, 병합을 트리거할 수 있다.

When indexing throughput is important, e.g. when batch (re-)indexing, it is not very productive to spend a lot of time flushing and merging small segments. Therefore, in these cases it is usually a good idea to temporarily increase the refresh_interval-setting, or even disable automatic refreshing altogether. One can always refresh manually, and/or when indexing is done.

indexing 처리량이 중요한 경우, 예를 들어, batch (re-)indexing을 수행 같은 경우, 작은 segment를 flush하고 병합하는 데 많은 시간을 소비하는 것은 그리 생산적이지 않다. 따라서 이러한 경우, 일반적으로 refresh_interval설정을 일시적으로 늘리거나, 자동 refreshing을 완전히 비활성화는 것이 좋다. index 생성이 완료되면, 항상 수동으로 refresh할 수 있다. 

Elasticsearch Indexes

"All problems in computer science can be solved by another level of indirection." – David J. Wheeler

컴퓨터 과학의 모든 문제는 다른 차원의 간접성(indirection)으로 해결될 수 있다.

An Elasticsearch index is made up of one or more shards, which can have zero or more replicas. These are all individual Lucene indexes. That is, an Elasticsearch index is made up of many Lucene indexes, which in turn is made up of index segments. When you search an Elasticsearch index, the search is executed on all the shards - and in turn, all the segments - and merged. The same is true when you search multiple Elasticsearch indexes. Actually, searching two Elasticsearch indexes with one shard each is pretty much the same as searching one index with two shards. In both cases, two underlying Lucene indexes are searched.

Elasticsearch index는 하나 이상의 shard로 구성되며, 0개 이상의 replica를 가질 수 있다. 이들 shard는 모두 개별적인 Lucene index이다. 즉, Elasticsearch index는 많은 Lucene index로 구성되며, 이는 다시 index segment로 구성된다. Elasticsearch index를 검색하면, 모든 shard에서, 다시 모든 segment에서 검색이 수행되고, 그 결과가 병합된다. 여러 Elasticsearch index를 검색할 경우에도 마찬가지이다. 실제로, 하나의 shard로 이루어진 2개의 Elasticsearch index를 검색하는 것은, 2개의 shard로 이루어진 하나의 index를 검색하는 것과 거의 같다. 2가지 경우 모두, 2개의 기본 Lucene index가 검색된다.

From this point onwards in this article, when we refer to an "index" by itself, we mean an Elasticsearch index.

이 게시물의 이 시점부터, "index" 자체를 언급할 경우, Elasticsearch index를 의미한다.

A "shard" is the basic scaling unit for Elasticsearch. As documents are added to the index, it is routed to a shard. By default, this is done in a round-robin fashion, based on the hash of the document's id. In the second part of this series, we will look more into how shards are moved around. It is important to know, however, that the number of shards is specified at index creation time, and cannot be changed later on. An early presentation on Elasticsearch by Shay has excellent coverage of why a shard is actually a complete Lucene index, and its various benefits and tradeoffs compared to other methods.

shard는 Elasticsearch의 기본 확장(scale) 단위이다. document가 index에 추가되면, 해당 document는 shard로 전달(route)된다. 기본적으로 이 작업은 document ID의 hash를 기반으로 한, round-robin 방식으로 수행된다. 이 시리즈의 두 번째 part에서는, shard가 어떻게 움직이는지 자세히 살펴볼것이다. 그러나, shard의 수는 index  생성 시에 지정되며, 나중에 변경할 수 없다. Shay의 Elasticsearch에 대한 초기 presentation은 shard가 실제로 완전한 Lucene의 index인 이유와 다른 방법과 비교하여 다양한 이점과 절충점(tradeoff)에 대한 훌륭한 자료이다.

Which Elasticsearch indexes, and what shards (and replicas) search requests are sent to, can be customized in many ways. By combining index patterns, index aliases, and document and search routing, lots of different partitioning and data flow strategies can be implemented. We will not go into them here, but we can recommend Zachary Tong's article on customizing document routing and Shay Banon's presentation on big data, search and analytics. Just to give you some ideas, here are some examples:

Elasticsearch에서 index 처리하는 방법과 어떤 shard(그리고 replica)로 검색 요청이 전송되는지는 다양한 방법으로 사용자 정의할 수 있다. index pattern, index alias 및 document 및 search routing을 결합하여, 다양한 분할 및 data 흐름 전략을 구현할 수 있다. 여기서 다루지는 않겠지만, 사용자 정의 document routing에 대한 Zachary Tong의 게시물과 big data의 검색과 분석에 대한 Shay Banon의 presentation을 추천할 수 있다. 몇 가지 아이디어를 제공하기 위해, 다음과  같은 몇 가지 예를 들어보겠다.

  • Lots of data is time based, e.g. logs, tweets, etc. By creating an index per day (or week, month, …), we can efficiently limit searches to certain time ranges - and expunge old data. Remember, we cannot efficiently delete from an existing index, but deleting an entire index is cheap.
    많은 data가 시간 기반이다. log, tweet 등. 일(또는 주, 월 ...)별 index를 작성하여 검색을 특정 시간 범위로 효율적으로 제한할 수 있으며, 오래된 데이터를 영구 삭제할 수 있다. 기존 index에서 효율적으로 삭제할 수는 없지만, 전체 index를 삭제하는 것은 저렴하다는 점을 기억하자.
  • When searches must be limited to a certain user (e.g. "search your messages"), it can be useful to route all the documents for that user to the same shard, to reduce the number of indexes that must be searched.
    검색을 특정 사용자(예 : "메시지 검색")로 제한해야 하는 경우, 검색해야하는 index 수를 줄이기 위해, 해당 사용자의 모든 document를 동일한 shard로 route트하는 것이 유용할 수 있다.

Transactions

While Lucene has a concept of transactions, Elasticsearch does not. All operations in Elasticsearch add to the same timeline, which is not necessarily entirely consistent across nodes, as the flushing is reliant on timing.

Lucene은 transaction 개념을 가지고 있지만, Elasticsearch는 그렇지 않다. Elasticsearch의 모든 작업은 동일한 timeline에 추가된다. 이는 flushing이 timing에 따라 다르므로, 반드시 전체 node간에 일관된 것은 아니다.

Managing the isolation and visibility of different segments, caches and so on across indexes across nodes in a distributed system is very hard. Instead of trying to do this, it prioritizes being fast.

분산 시스템의 node간, index간 여러 segment, cache 등의 분리 및 가시성을 관리하는 것은 매우 어렵다. 이렇게 하지 않고,빠른 작업을 우선시한다.

Elasticsearch has a "transaction log" where documents to be indexed are appended. Appending to a log file is a lot cheaper than building segments, so Elasticsearch can write the documents to index somewhere durable - in addition to the in-memory buffer, which is lost on crashes. You can also specify the consistency level required when you index. For example, you can require every replica to have indexed the document before the index operation returns.

Elasticsearch는 indexing할 document가 추가되는 "transaction log"가 있다. log file에 추가하는 것은 segment를 작성하는 것보다 훨씬 저렴하므로, Elasticsearch는 crash되면 잃어버리는 in-memory buffer 외에, 어딘가에 오래 지속되는 document를 기록할 수 있다. indexing할 때, 필요한 일관성 수준(consistency level)을 지정할 수도 있다. 예를 들어, index 작업이 반환되기 전에, 모든 replica가 document를 indexing하도록 요구할 수 있다.

Summary

To summarize, these are the important properties to be aware of when it comes to how Lucene builds, updates and searches indexes on a single node:

요약하자면, Lucene이 단일 node에서 index를 작성, update 및 검색하는 방법과 관련하여, 알아 두어야 할 중요한 속성은 다음과 같다.

  • How we process the text we index dictates how we can search. Proper text analysis is important.
    indexing하려는 text를 처리하는 방법에 따라 검색하는 방법이 결정된다. 적절한 text 분석은 중요하다.
  • Indexes are built first in-memory, then occasionally flushed in segments to disk.
    index는 먼저 memory에 저장된 다음, segment에서 disk로 가끔씩 flush된다.
  • Index segments are immutable. Deleted documents are marked as such.
    index segment는 불변이다. 삭제된 document는 삭제된 것으로 표시된다.
  • An index is made up of multiple segments. A search is done on every segment, with the results merged.
    index는 여러 개의 segment로 구성된다. 검색은 모든 segment에서 수행되며.결과는 병합된다.
  • Segments are occasionally merged.
    segment는 가끔 병합된다.
  • Field and filter caches are per segment.
    field와 filter cache는 segment별로 있다.
  • Elasticsearch does not have transactions.
    Elasticsearch에는 transaction이 없다.

In the next article in this series, we'll look at how search and indexing is done across a cluster.

이 시리즈의 다음 게시물에서는, cluster에서 검색과 indexing을 수행하는 방법을 살펴볼 것이다.

References

Busch, Michael: Realtime search with lucene – http://2010.berlinbuzzwords.de/sites/2010.berlinbuzzwords.de/files/busch_bbuzz2010.pdf

Elasticsearch: Guide – https://www.elastic.co/guide

Lucene aPI documentation – http://lucene.apache.org/core/4_4_0/core/overview-summary.html

McCandless, Michael: Visualizing lucene's segment merges, 2011 – http://blog.mikemccandless.com/2011/02/visualizing-lucenes-segment-merges.html

Willnauer, Simon: Gimme all resources you have - i can use them!, 2011 – http://blog.trifork.com/2011/04/01/gimme-all-resources-you-have-i-can-use-them/

  1. Lucene aPI documentation – http://lucene.apache.org/core/4_4_0/core/overview-summary.htmlNumericRangeQuery.
  2. Simon Willnauer, Gimme all resources you have - i can use them!, 2011 – http://blog.trifork.com/2011/04/01/gimme-all-resources-you-have-i-can-use-them/.
  3. Michael McCandless, Visualizing lucene's segment merges, 2011 – http://blog.mikemccandless.com/2011/02/visualizing-lucenes-segment-merges.html.
  4. Michael Busch, Realtime search with lucene – http://2010.berlinbuzzwords.de/sites/2010.berlinbuzzwords.de/files/busch_bbuzz2010.pdf.
  5. Elasticsearch, Guide – https://www.elastic.co/guidewarmer-API.

원문 : Elasticsearch from the Bottom Up, Part 1