Статьи

Наш опыт создания крупномасштабной системы поиска журналов с использованием ElasticSearch

Это сообщение от Ли Чже Ика из блога CUBRID.

В NHN у нас есть служба NELO (NHN Error Log System) для управления и поиска журналов, передаваемых в систему различными приложениями и другими веб-службами. Производительность поиска и функциональность NELO2, второго поколения системы, были значительно улучшены благодаря ElasticSearch. Сегодня я хотел бы поделиться нашим опытом в NHN по развертыванию ElasticSearch в системах поиска журналов.

ElasticSearch — это распределенная поисковая система, основанная на Lucene, разработанная Shay Banon. Шей и его команда недавно выпустили долгожданную версию 0.90. Вот ссылка на часовой веб-семинар, записанный в течение часа, на  котором Клинтон Гормли, один из основных разработчиков ElasticSearch, объясняет, что нового в ElasticSearch 0.90.

Если вы разрабатываете систему, которая требует функции поиска, я бы порекомендовал ElasticSearch, поскольку его установка и расширение сервера очень просты. Поскольку это распределенная система, ElasticSearch может легко справиться с увеличением объема поисковых целей. В NHN все журналы, поступающие в NELO2, хранятся и индексируются ElasticSearch для более быстрого поиска результатов в режиме реального времени.

Особенности ElasticSearch

Давайте начнем с ознакомления с терминами, широко используемыми в ElasticSearch. Для тех, кто знаком с системами реляционных баз данных, в следующей таблице сравниваются термины, используемые в реляционных базах данных, с терминами, используемыми в ElasticSearch.

Таблица 1: Сравнение терминов RDBMS и ElasticSearch.

Реляционная БД ElasticSearch
База данных Показатель
Стол Тип
Ряд Документ
колонка поле
схема картографирование
Показатель Все проиндексировано
SQL Запрос DSL

Хранилище без схем на основе JSON

ElasticSearch — это поисковая система, но может использоваться как NoSQL . Поскольку модель данных представлена ​​в JSON, запросы и ответы обмениваются как документы JSON. Кроме того, источники также хранятся в формате JSON. Хотя схема заранее не определена, документы JSON автоматически индексируются при их передаче. Числа и типы даты отображаются автоматически.

Мульти аренды

ElasticSearch поддерживает многопользовательский режим . Несколько индексов могут храниться на одном сервере ElasticSearch, а данные нескольких индексов можно искать с помощью одного запроса. NELO2 разделяет индексы по дате и сохраняет журналы. При выполнении поиска NELO запрашивает индексы дат в области поиска одним запросом.

Код 1: Пример примера с несколькими арендаторами.

# Store logs in the log-2012-12-26 index
curl -XPUT http://localhost:9200/log-2012-12-26/hadoop/1 -d '{
    "projectName": "hadoop",
    "logType": "hadoop-log",
    "logSource": "namenode",
    "logTime":"2012-12-26T14:12:12",
    "host": "host1.nelo2",
    "body": "org.apache.hadoop.hdfs.StateChange: DIR* NameSystem.completeFile"
}'

# Store logs in the log-2012-12-27 index
curl -XPUT http://localhost:9200/log-2012-12-27/hadoop/1 -d '{
    "projectName": "hadoop",
    "logType": "hadoop-log",
    "logSource": "namenode",
    "logTime":"2012-12-27T02:02:02",
    "host": "host2.nelo2",
    "body": "org.apache.hadoop.hdfs.server.namenode.FSNamesystem"
}'

# Request search to the nelo2-log-2012-12-26 and nelo2-log-2012-12-27 indexes at once
curl -XGET http://localhost:9200/nelo2-log-2012-12-26,nelo2-log-2012-12-27/_search

Масштабируемость и гибкость

ElasticSearch обеспечивает отличную масштабируемость и гибкость. Это позволяет расширить функциональность с помощью плагинов, которые были улучшены в последнем выпуске 0.90. Например, используя плагин Thrift или Jetty, вы можете изменить протокол передачи. Если вы устанавливаете BigDesk или Head, который является обязательным плагином, вы можете использовать функции мониторинга ElasticSearch. Как показано в следующем коде 2 , вы также можете динамически регулировать количество реплик. Количество сегментов не может быть изменено, так как оно фиксировано для каждого индекса, поэтому соответствующее количество сегментов должно быть выделено в первый раз с учетом количества узлов и будущего расширения сервера.

Код 2: запрос динамического изменения конфигурации.

$ curl -XPUT http://localhost:9200/log-2012-12-27/ -d '{
    "settings": {
        "number_of_shards": 10,
        "number_of_replicas": 1
    }
}'

Распределенное хранилище

ElasticSearch — это распределенная поисковая система. Он распределяет данные путем настройки нескольких шардов в соответствии с ключами. Индекс настроен для каждого шарда. Каждый осколок имеет 0 или более реплик. Кроме того, ElasticSearch поддерживает кластеризацию, и при запуске кластера один из многих узлов выбирается в качестве главного узла для управления метаданными. В случае сбоя главного узла другой узел в кластере автоматически становится главным. Также очень легко добавлять узлы. Когда узел добавляется в ту же сеть, добавленный узел автоматически находит кластер с помощью многоадресной рассылки и добавляет себя в кластер. Если та же сеть не используется, адрес главного узла должен быть указан в одноадресной рассылке (см. Соответствующее видео: http://youtu.be/l4ReamjCxHo ).

Установка

Быстрый старт

ElasticSearch поддерживает установку без конфигурации. Как показано в следующих фрагментах кода, все, что вам нужно сделать для выполнения, это загрузить файл с официальной домашней страницы и распаковать его.

  1. Скачать
    ~$ wget http://download.ElasticSearch.org/ElasticSearch/ElasticSearch/ElasticSearch-0.20.1.tar.gz
    ~$ tar xvzf ElasticSearch-0.20.1.tar.gz
  2. Исполнительный сервер
    ~$ bin/ElasticSearch -f

Установка плагинов

Вы можете легко расширить функциональность ElasticSearch с помощью плагинов. Вы можете добавить функции управления, изменить анализатор Lucene и изменить базовый модуль переноса с Netty на Jetty. Ниже приведена команда, которую мы используем для установки плагинов для NELO2. Head и bigdesk, которые находятся в первой и второй строках, являются плагинами, необходимыми для мониторинга ElasticSearch. Настоятельно рекомендуется установить их и проверить их функциональность. После их установки посетите http: // localhost: 9200 / plugin / head / и http: // localhost: 9200 / plugin / bigdesk / , и вы можете увидеть статус ElasticSearch в своем веб-браузере.

bin/plugin -install Aconex/ElasticSearch-head
bin/plugin -install lukas-vlcek/bigdesk
bin/plugin -install ElasticSearch/ElasticSearch-transport-thrift/1.4.0
bin/plugin -install sonian/ElasticSearch-jetty/0.19.9

Основные конфигурации

Вам не нужно менять конфигурации при проведении простого теста функциональности. Когда вы выполняете тест производительности или применяете его к производственным сервисам, вам следует изменить некоторые конфигурации по умолчанию. Посмотрите следующий фрагмент и попытайтесь найти для себя конфигурации, которые должны быть изменены из исходного файла конфигурации.

Код 5: Основные конфигурации ( config / ElasticSearch.yml ).

# As it is a name used to identify clusters, use a name with uniqueness and a meaning.
cluster.name: ElasticSearch-nelo2

# A node name is automatically created but it is recommended to use a name that is discernible in a cluster like a host name.
node.name: "xElasticSearch01.nelo2"

# The default value of the following two is all true. node.master sets whether the node can be the master, while node.data is a configuration for whether it is a node to store data. Usually you need to set the two values as true, and if the size of a cluster is big, you should adjust this value by node to configure three types of node. More details will be explained in the account of topologies configuration later.
node.master: true
node.data: true

# You can change the number of shards and replicas. The following value is a default value:
index.number_of_shards: 5
index.number_of_replicas: 1

#To prevent jvm swapping, you should set the following value as true:
bootstrap.mlockall: true

# It is a timeout value for checking the status of each node in a cluster. You should set an appropriate value; if the value is too small, nodes may frequently get out of a cluster. The default value is 3 seconds.
discovery.zen.ping.timeout: 10s

# The default value is multicast, but in an actual environment, unicast should be employed due to the possibility of overlapping with other clusters. It is recommended to list servers that can be a master in the second setting.
discovery.zen.ping.multicast.enabled: false
discovery.zen.ping.unicast.hosts: ["host1", "host2:port", "host3[portX-portY]"]

Использование REST API

ElasticSearch предоставляет REST API, как показано ниже. Он предоставляет большую часть своих функций через REST API, включая создание и удаление индексов, сопоставлений, а также поиск и изменение настроек. В дополнение к REST API, он также предоставляет различные клиентские API для Java, Python и Ruby.

Код 6: Формат API REST в ES.

http://host:port/(index)/(type)/(action|id)

Как упоминалось ранее, NELO2 классифицирует индексы ( базы данных в терминах RDBMS) по дате, а тип ( таблица ) разделяется по проекту. Приведенный ниже код 7 показывает процесс создания журналов, поступивших в проект hadoop 27 декабря 2012 года , в единицу документа с использованием REST API.

Код 7: Пример использования REST API ElasticSearch.

#Creating documents
curl -XPUT http://localhost:9200/log-2012-12-27/hadoop/1
curl -XGET http://localhost:9200/log-2012-12-27/hadoop/1
curl -XDELETE http://localhost:9200/log-2012-12-27/hadoop/1

#Search
curl -XGET http://localhost:9200/log-2012-12-27/hadoop/_search
curl -XGET http://localhost:9200/log-2012-12-27/_search
curl -XGET http://localhost:9200/_search

#Seeing the status of indexes
curl -XGET http://localhost:9200/log-2012-12-27/_status

Создание документов и индексов

Как показано в следующем коде 8 , при отправке запроса ElasticSearch автоматически создает индекс log-2012-12-27 и тип hadoop без предварительно определенного индекса или типа. Если вы хотите создать их специально вместо использования автоматического создания, вы должны указать настройку action.auto_create_indexи index.mapper.dynamicкак falseв файле конфигурации.

Код 8: Создание документов.

# Request
curl -XPUT http://localhost:9200/log-2012-12-27/hadoop/1 -d '{
    "projectName": "hadoop",
    "logType": "hadoop-log",
    "logSource": "namenode",
    "logTime":"2012-12-27T02:02:02",
    "host": "host2.nelo2",
    "body": "org.apache.hadoop.hdfs.server.namenode.FSNamesystem"
}'

# Result
{
    "ok": true,
    "_index": "log-2012-12-27",
    "_type": "hadoop",
    "_id": "1",
    "_version": 1
}

Как показано в коде 9 ниже, вы можете сделать запрос после включения типа в документе.

Код 9: тип запроса.

curl -XPUT http://localhost:9200/log-2012-12-27/hadoop/1 -d '{
    "hadoop": {
        "projectName": "hadoop",
        "logType": "hadoop-log",
        "logSource": "namenode",
        "logTime":"2012-12-27T02:02:02",
        "host": "host2.nelo2",
        "body": "org.apache.hadoop.hdfs.server.namenode.FSNamesystem"
    }
}'

Если  id значение не указано, как в коде 10id при создании документа оно будет создано автоматически. Обратите внимание, что POST метод использовался вместо того,  PUT когда был сделан запрос.

Код 10: Запрос Создание документа без идентификатора.

# Request
curl -XPOST http://localhost:9200/log-2012-12-27/hadoop/ -d '{
    "projectName": "hadoop",
    "logType": "hadoop-log",
    "logSource": "namenode",
    "logTime":"2012-12-27T02:02:02",
    "host": "host2.nelo2",
    "body": "org.apache.hadoop.hdfs.server.namenode.FSNamesystem"
}'

# Result
{
    "ok": true,
    "_index": "log-2012-12-27",
    "_type": "hadoop",
    "_id": "kgfrarduRk2bKhzrtR-zhQ",
    "_version": 1
}

Удаление документа

 Приведенный ниже код 11 показывает, как удалить документ ( запись  в терминах СУБД) по типу ( таблица ). Вы можете удалить Hadoop тип документа с id=1из лог-2012-12-27 индекса с помощью DELETEметода.

Код 11: запрос на удаление документа.

# Request
$ curl -XDELETE 'http://localhost:9200/log-2012-12-27/hadoop/1'

# Result
{
    "ok": true,
    "_index": "log-2012-12-27",
    "_type": "hadoop",
    "_id": "1",
    "found": true
}

Получение документа

Вы можете получить Hadoop тип документа с id=1из лог-2012-12-27 индекса, используя GETметод , как показано в коде 12.

Код 12: Запрос на получение документа.

#Request
curl -XGET 'http://localhost:9200/log-2012-12-27/hadoop/1'

# Result
{
    "_index": "log-2012-12-27",
    "_type": "hadoop",
    "_id": "1",
    "_source": {
        "projectName": "hadoop",
        "logType": "hadoop-log",
        "logSource": "namenode",
        "logTime":"2012-12-27T02:02:02",
        "host": "host2.nelo2",
        "body": "org.apache.hadoop.hdfs.server.namenode.FSNamesystem"
    }
}

Поиск

Когда вызывается API поиска, ElasticSearch выполняет API поиска и возвращает результаты поиска, которые соответствуют содержанию запроса. Код 13 показывает пример использования API поиска.

Код 13: Пример запроса об использовании API поиска.

# All types of a specific index
$ curl -XGET 'http://localhost:9200/log-2012-12-27/_search?q=host:host2.nelo2'

# A specific type of a specific index
$ curl -XGET 'http://localhost:9200/log-2012-12-27/hadoop,apache/_search?q=host:host2.nelo2'

# A specific type of all indexes
$ $ curl - XGET 'http://localhost:9200/_all/hadoop/_search?q=host:host2.nelo2'

# All indexes and types
$ curl -XGET 'http://localhost:9200/_search?q=host:host2.nelo2'

API поиска с помощью запроса URI

Таблица 2: Основные параметры.

имя Описание
Q Строка запроса.
default_operator Оператор используется по умолчанию ( ANDили OR). По умолчанию это OR.
поля Поле для получения в результате. По умолчанию используется поле «_source».
Сортировать Метод сортировки. Пример) fieldName: asc / fieldName: desc.
Тайм-аут Время ожидания поиска. По умолчанию это «безлимитный».
размер Количество значений результата. По умолчанию 10.

Если вы используете URI, вы можете легко выполнять поиск, используя параметры в таблице 2 и строку запроса. Поскольку он не предоставляет все параметры поиска, он полезен при использовании для тестов.

Код 14: Поиск запроса с использованием запроса URI.

# Request
$ curl -XGET 'http://localhost:9200/log-2012-12-27/hadoop/_search?q=host:host2.nelo2'

# Result
{
    "_shards":{
        "total": 5,
        "successful": 5,
        "failed": 0
    },
    "hits":{
        "total": 1,
        "hits": [
            {
                "_index": "log-2012-12-27",
                "_type": "hadoop",
                "_id": "1",
                "_source": {
                    "projectName": "hadoop",
                    "logType": "hadoop-log",
                    "logSource": "namenode",
                    "logTime":"2012-12-27T02:02:02",
                    "host": "host2.nelo2",
                    "body": "org.apache.hadoop.hdfs.server.namenode.FSNamesystem"
                }
            }
        ]
    }
}

API поиска с использованием тела запроса

Когда используется тело HTTP, выполните поиск с помощью запроса DSL. Так как запрос DSL содержит большое количество содержимого, рекомендуется обратиться к руководству с официального сайта .

Код 15: Поиск с использованием Query DSL.

# Request
$ curl -XPOST 'http://localhost:9200/log-2012-12-27/hadoop/_search' -d '{
    "query": {
        "term": { "host": "host2.nelo2" }
    }
}'

# Result
{
    "_shards":{
        "total": 5,
        "successful": 5,
        "failed": 0
    },
    "hits":{
        "total": 1,
        "hits": [
            {
                "_index": "log-2012-12-27",
                "_type": "hadoop",
                "_id": "1",
                "_source": {
                    "projectName": "hadoop",
                    "logType": "hadoop-log",
                    "logSource": "namenode",
                    "logTime":"2012-12-27T02:02:02",
                    "host": "host2.nelo2",
                    "body": "org.apache.hadoop.hdfs.server.namenode.FSNamesystem"
                }
            }
        ]
    }
}

картографирование

Put Mapping API

Чтобы добавить сопоставление для определенного типа, вы можете определить сопоставление в форме, показанной в коде 16 .

Код 16: Запрос на регистрацию сопоставления.

$ curl -XPUT 'http://localhost:9200/log-2012-12-27/hadoop/_mapping' -d '
{
    "hadoop": {
        "properties": {
            "projectName": {"type": "string", "index": "not_analyzed"},
            "logType": {"type": "string", "index": "not_analyzed"},
            "logSource": {"type": "string", "index": "not_analyzed"},
            "logTime": {"type": "date"},
            "host": {"type": "string", "index": "not_analyzed"},
            "body": {"type": "string"},
        }
    }
}'

Get Mapping API

Чтобы получить определенную информацию о сопоставлении, вы можете использовать запрос в форме, показанной в коде 17 .

Код 17: запрос для получения сопоставления.

 $ curl -XGET 'http://localhost:9200/log-2012-12-27/hadoop/_mapping'

Удалить сопоставление API

Код 18 показывает пример удаления определенного отображения.

Код 18: Запрос на удаление сопоставления.

$ curl -XDELETE 'http://localhost:9200/log-2012-12-27/hadoop'

Как оптимизировать производительность

Память и количество открытых файлов

Если объем данных для поиска увеличивается, вам потребуется больше памяти. Когда вы запускаете ElasticSearch, вы сталкиваетесь со многими проблемами из-за использования памяти. В методе работы, рекомендованном сообществом ElasticSearch, при запуске сервера исключительно для ElasticSearch рекомендуется выделить только половину объема памяти для ElasticSearch и разрешить ОС использовать другую половину для системного кэша. Вы можете установить размер памяти, установив ES_HEAP_SIZEпеременную окружения или с помощью -Xmsи -Xmxиз виртуальной машины Java.

Код 19: выполнение с указанием размера кучи.

bin/ElasticSearch -Xmx=2G -Xms=2G

При использовании ElasticSearch вы будете OutOfMemoryчасто видеть ошибки. Эта ошибка возникает, когда кэш-память поля превышает максимальный размер кучи. Если вы измените настройку index.cache.field.typeс резидентного (по умолчанию) на мягкое , будет использоваться мягкая ссылка, а область кэша будет преимущественно GC , и эту проблему можно решить.

Код 20: Настройка типа кэша поля.

index.cache.field.type: soft

Если объем данных увеличивается, число файлов индекса также увеличивается. Это связано с тем, что Lucene, который используется ElasticSearch, управляет индексами в единицах сегментов . Иногда количество будет даже превышать количество MAX_OPENфайлов. По этой причине вам нужно изменить максимальный лимит открытого файла с помощью ulimitкоманды. Рекомендуемое значение — 32000-64000 , но иногда вам может потребоваться установить большее значение в зависимости от размера системы или данных.

Оптимизация индекса

NELO2 manages indexes by date. If indexes are managed by date, you can delete old logs that don’t need to be managed easily and quickly, as shown in Code 21. In this case, the overhead imposed on the system is smaller than when deleting logs by specifying the TTL value for each document.

Code 21: Deleting an Index.

$ curl -XDELETE 'http://localhost:9200/log-2012-10-01/'

If index optimization is performed, segments are incorporated. Using this method, you can enhance search performance. As index optimization can impose a burden on the system, it is better to perform it when the system is being used less.

Code 22: Index Optimization.

$ curl -XPOST 'http://localhost:9200/log-2012-10-01/_optimize'

Shards and Replicas

You can’t change the number of shards after setting it. For this reason, you need to decide this value carefully by taking the current number of nodes in the system and the number of nodes expected to be added in the future into account. For example, if there are 5 nodes and the number is expected to reach 10 in the future, it is recommended to set the number of shards as 10 from the beginning. If you set it as 5 in the beginning, you can add 5 more nodes later, but you won’t be able to use the added 5 nodes. If you set the number of replicas to 1, of course, you can utilize the added 5 nodes as nodes exclusively for replication.

If the number of shards increases, it is more advantageous to process a large amount of data because queries are distributed as much as the number of shards. But you need to set this value appropriately, because the performance could be deteriorated due to increasing traffic if the value is too high.

Configuring Cluster Topologies

The content of the configuration file of ElasticSearch is shown in Code 23 below. There are three types of nodes:

  1. data node
    This does not act as the master, and only stores data. When it receives a request from a client, it searches data from shards or creates an index. 
  2. master node
    It functions to maintain a cluster, and requests indexing or search to data nodes.  
  3. search balancer node
    If it receives a search request, it requests data, gathers data and delivers the result.

You can have one node which will function both like a master and a data node. But if you use the three types of node separately, you can reduce the burden of the data node. In addition, if you configure the master node separately, you can improve the stability of a cluster. Also, you can reduce operation costs by using low-spec. server equipment for the master and search node.

Code 23: Settings Related to Topology.

# You can exploit these settings to design advanced cluster topologies.
#
# 1. You want this node to never become a master node, only to hold data.
#    This will be the "workhorse" of your cluster.
#
# node.master: false
# node.data: true
#
# 2. You want this node to only serve as a master, to not store any data and
#    to have free resources. This will be the "coordinator" of your cluster.
#
# node.master: true
# node.data: false
#
# 3. You want this node to be neither a master nor a data node, but
#    to act as a "search load balancer" (fetching data from nodes,
#    aggregating results, etc.)
#
# node.master: false
# node.data: false

Figure 1 below shows the configuration of NELO2 topologies that use ElasticSearch. The efficiency of equipment use and the stability of the entire cluster has been improved as follows: only ElasticSearch runs on the 20 data nodes (server) so that they can achieve sufficient performance, while other daemon server processes in addition to ElasticSearch run on the 4 master nodes and 3 search nodes.

nhn_nelo2_elasticsearch_topologies.png

Figure 1: NELO2 ElasticSearch Topologies.

Configuring Routing

When a large amount of data needs to be indexed, increasing the number of shards will improve the overall performance. On the other hand, if the number of shards increases, the traffic among nodes will also go up. For example, when there are 100 shards, if it receives a single search request, it sends the request to all the 100 shards and aggregates data, and this imposes a burden on the entire cluster.

If you use routing, data will be stored only in a specific shard. Even if the number of shards increases, the application will still send a request only to a single shard, and consequently the traffic can be reduced dramatically. Figure 2, 3, and 4 are excerpted from the slides Rafal Kuc presented at Berlin Buzzwords 2012. If you don’t use routing, as shown in Figure 2, the application will send a request to all the shards. But if you use routing, it will send a request only to a specific shard, as shown in Figure 3.

According to the material cited, in Figure 4 when there are 200 shards, the response time is over 10 times faster with routing than without routing. If routing is applied, the number of threads will increase by 10 to 20 times compared to when it is not applied, but the CPU usage is much smaller. In some cases, however, the performance will be better when routing is not applied. For a search query whose result should be collected from multiple shards, it could be more advantageous in terms of performance to send the request to multiple shards. To complement this, NELO2 determines the use of routing depending on the log usage of the project.

nhn_nelo_before_using_routing.png

Figure 2: Before Using Routing.

nhn_nelo2_after_using_routing.png

Figure 3: After Using Routing.

nhn_nelo2_performance_comparison_before_after_using_routing.png

Figure 4: Performance Comparison before and after Using Routing.

Conclusion

The number of users of ElasticSearch is increasing rapidly, thanks to its easy installation and high scalability. It was several days only since the release of the latest ElasticSearch version 0.90. Its functionality is improving very quickly thanks to its active community. In addition, more and more companies are beginning to use ElasticSearch for their services. Recently, some committers, including the developer Shay Banon, gathered together and established ElasticSearch.com, which provides consulting and training services.

In this article I have explained the basic information on the installation of ElasticSearch, how to use it, and do performance tuning. We have started testing the latest 0.90 release and soon will migrate the current 0.20.1 ES deployment. In the next post I will continue this topic and tell you about our experience with 0.90 as well as the critical split-brain problem we have previously experienced. Due to the scarcity of solutions for this problem, I believe it will be very useful for our readers.

By Lee Jae Ik, Senior Software Engineer at Global Platform Development Lab, NHN Corporation.

References