성능 테스트 Elasticsearch

대형 Elasticsearch 집단이 있고 색인을 변경하고 싶을 때 (비추거나 설정하든) 변경이 성능을 향상시킬 수 있다는 보증을 받고 싶습니다.변경도 어려울 수 있습니다. 따라서 성능 이득이 코드를 업데이트하는 데 시간이 걸릴만한 가치가 있는지 알고 싶습니다.

우리 생방송!



나는 너를 속이지 않는다. 케나의 초기에, 우리가 아주 어렸을 때, 우리는 우리의 생산 집단에서 테스트를 진행할 것이다.새로운 조각 설정이 더 좋을 것 같은데?물론 모든 것을 다시 설정해서 무슨 일이 일어날지 봅시다.최적화 오류로 인해 우리는 심야 전투와 소방으로 끝난 적이 한 번이 아니다.이것은 가파른 학습 곡선이지만, 결국 우리는 살아남았다.
이제 우리는 더 커지고 더 똑똑해졌다😉, 우리는 먼저 전면적인 테스트를 실시하여 이러한 변화를 더욱 책임감 있게 하는 것을 선택한다.🎉

Elasticsearch 변화를 테스트하는 방법


TL;박사


이제 Elasticsearch 변경 사항을 테스트하기 위해 작성된 RubySearchTesting class를 사용합니다.이 클래스는 응용 프로그램에서 두 개의 다른 인덱스를 동시에 요청할 수 있도록 합니다.이 두 요청은 모두 감시 코드에 봉인되어 우리가 감시 서비스에서 그것들을 추적하고 볼 수 있도록 한다.추적을 통해 우리는 요청 시간 등 내용을 비교하여 어느 색인의 성능이 더 좋은지 볼 수 있다.다음은 우리가 이 종류를 사용하는 예이다.

최적화: ID를 키워드로 저장


Elasticsearch 훈련에서 ID를 키워드로 저장하면 검색할 때 성능을 향상시킬 수 있다는 것을 반복적으로 알려 왔습니다.Elasticsearch의 정수 데이터 형식이 범위 조회에 최적화되었기 때문이다.키워드는 용어 조회에 최적화되었다.용어 질의는 다음과 같습니다.
{
  "terms": {
    "id": [1, 5, 9]
  }
}
ID로만 용어 질의를 수행하는 경우 키워드로 저장해야 합니다.이것은 믿을 만한 조언처럼 들리지만, 우리의 맵을 업데이트하고 30억 개의 문서를 색인화하는 작업을 해야 하기 때문에, 우리는 어떤 변경을 하기 전에, 우리가 정말 좋은 점을 볼 수 있도록 확보하고 싶다.

테스트 설정


테스트를 시작하기 전에, 우리는 먼저 생산 인덱스의 중복 인덱스를 만들어야 한다.이외에도 이 새로운 테스트 인덱스는 모든 ID를 키워드로 저장합니다.우리는 생산 색인에 있는 모든 데이터를 새로운 테스트 색인에 다시 색인 (복제) 할 것이기 때문에, 그것들은 모두 완전히 같은 데이터를 포함한다.
다음에 Redis에 산열을 넣어서 이 인덱스를 테스트할 신호를 응용 프로그램에 보내야 합니다.검색 테스트 클래스에서 Elasticsearch 요청을 보낼 때, Redis를 먼저 검사해서 요청한 색인에 대한 테스트를 실행하고 있는지 확인하십시오.만약 그렇다면, 요청을 원시 인덱스로 보낼 뿐만 아니라, 테스트 인덱스로도 보낼 것입니다.
다음 방법으로 SearchTesting 클래스를 사용하여 이 해시를 만들었습니다.
def self.set_test_index(original_index_name, test_index_name)
  index_hashes = cache_index_hashes
  index_hashes[original_index_name] = {
    :index_name => test_index_name,
    :test_searching => true,
    :test_indexing => true
  }
  update_index_cache(index_hashes)
end
Redis에서 생성된 해시는 다음과 같습니다.
{
  "test_indexes" => {
    "prod_index" => {
      "index_name" => "test_index",
      "test_searching" => true,
      "test_indexing" => true
    }
  }
}
산열이 일단 자리에 앉으면 테스트가 시작됩니다!검색 속도와 색인 속도 두 가지를 SearchTesting 클래스로 테스트하고 싶습니다.

검색 속도 테스트


우리가 이 새로운 검색 테스트 클래스를 도입했을 때, 우리는 이러한 방식으로 그것을 실현했다. 즉, 우리가 Elasticsearch에 보낸 모든 요청이 그것을 통과할 것이다.
.
@connection.send(m, *args, &block)
# was replaced with
SearchTesting.new(@connection).send(m, *args, &block)
@connection 이것은 우리elasticsearch-rubygem의 연결 대상입니다. 이것은 우리가Elasticsearch와 대화할 수 있도록 합니다.우리는 이를 SearchTesting 클래스에 전달하고 이 클래스는 우리가 테스트하고 싶은 방법을 정의합니다.정의되지 않은 모든 방법은 방해 없이 Elasticsearch로 전송하기만 하면 됩니다.
def method_missing(m, *args, &block)
  connection.send(m, *args, &block)
end
검색 방법을 테스트하고 싶어서 다음과 같이 정의합니다.
def search(*args)
  self.index_name = args&.first&.dig(:index)
  test_hash = cache_index_hashes.dig(index_name)

  test_search_thread = (Thread.new { test_search(*args.deep_dup, test_hash) } if test_searching_enabled?(test_hash))

  connection.search(*args)
end
나는 이곳에서 발생한 몇 가지 일을 확대하고 싶다.우선, 우리가 요청한 인덱스의 이름을 가져옵니다.그리고 우리는 이 지수가 테스트 중인지 검사한다.
self.index_name = args&.first&.dig(:index)
test_hash = cache_index_hashes.dig(index_name)
만약 Redis에 테스트 해시가 존재하고 이 테스트 해시에 대한 검색이 활성화된다면, 우리는 테스트 요청을 실행하기 위한 새로운 라인을 만들 것입니다.
test_search_thread = (Thread.new { test_search(*args.deep_dup, test_hash) } if test_searching_enabled?(test_hash))
test_search 방법은 매우 간단하다.테스트 인덱스 이름으로 원시 인덱스 이름을 바꾼 다음 테스트 인덱스에 대한 요청을 실행합니다.
def test_search(*args, test_hash)
  test_index_name = test_hash[:index_name] # fetch test index name
  args.first[:index] = test_index_name # replace original index name with test
  connection.search(*args) # execute request against the test index
end
test_search 방법이 서로 다른 라인에서 실행되기 때문에, 우리는 그것이 완성되기를 기다릴 필요가 없다.테스트_인덱스 요청을 시작하면 원본 인덱스에 요청을 보내고 그 결과를 되돌려줍니다!
현재 수색은 절반밖에 차지하지 못했다.우리는 색인이 여전히 유효하다는 것을 확보하고 싶다.

인덱스 속도 테스트


색인 속도를 측정하기 위해서, 우리는 반드시 대량 방법을 사용하여 테스트를 진행해야 한다. 왜냐하면 우리의 모든 색인은 대량으로 완성되었기 때문이다.테스트 인덱스를 실행하기 위한 단독 라인을 만들었습니다. 이것은 검색과 유사합니다.
def bulk(*args)
  test_bulk_thread = Thread.new { test_bulk(*args.deep_dup) }
  connection.bulk(*args)
end
여기서 가장 어려운 부분은 때때로 서로 다른 색인의 산열이 한데 묶여 있다는 것이다.따라서, 우리는 모든 색인 해시를 단독으로 검사해야 한다.만약 테스트 중인 생산 인덱스에 산열이 있다면, 우리는 이 산열 인덱스를 테스트 인덱스에 보낼 것이다.
def test_bulk(*args)
  new_bulk_hashes = test_bulk_hashes(*args) # select hashes to go to the test index
  return unless new_bulk_hashes.any? # if there are none return 
  connection.bulk({ :body => new_bulk_hashes }) # index hashes to the test index
end

def test_bulk_hashes(*args)
  @test_bulk_hashes ||= args.first[:body].map do |index_hash|
    index_name = test_index_name(index_hash.values.first[:_index])
    next unless index_name # if there is an index name, a test is running
    index_hash.values.first[:_index] = index_name # replace original index with test index
    index_hash # return new index hash to the map
  end.compact
end
색인은 단독 라인에서 진행되기 때문에 검색과 같습니다. 색인을 시작하면 문서를 원본 색인으로 계속 색인해서 결과를 되돌릴 수 있습니다.

조급해하지 마라


Elasticsearch에서 색인에 대한 성능 테스트의 관건 중 하나는 테스트에 실행 시간을 제공하는 것이다.Elasticsearch는 검색할 때 캐시를 설정합니다.이것은 원시 인덱스 (캐시 설정) 가 처음에는 새 인덱스보다 빠를 수 있음을 의미합니다.테스트가 같은 캐시를 만들기 위해 충분한 시간을 실행하기를 원하기 때문에 테스트는 진정한 1-1 테스트입니다.우리는 보통 테스트를 적어도 며칠 동안 운행하게 한다.이것은 인덱스가 캐시를 만들 수 있을 뿐만 아니라, 각종 검색과 인덱스 요청도 허용한다.
지금까지 우리는 이 검색 클래스를 사용하여 ID를 키워드와 조각수로 바꾸는 변화를 테스트했습니다.ID를 키워드로 변경하여 검색 속도를 30% 증가시켰지만 색인 속도는 변경되지 않았습니다.조각 수를 줄이는 것은 우리의 전체 성능에 영향을 미치기 때문에 우리는 코드 라이브러리에서 변경하지 않았다.
이 코드를 공유하고 싶습니다. 다른 사람들이 루비에서 자신의 Elasticsearch 테스트를 설정할 수 있기를 바랍니다!만약 당신에게 어떤 문제가 있거나 내가 분명하게 설명하지 못한다면, 주저하지 말고 물어보세요!

좋은 웹페이지 즐겨찾기