Пользователь
0,0
рейтинг
23 июля 2011 в 15:59

Разработка → Быстрый полнотекстовый поиск ElasticSearch из песочницы

image
При разработке высоконагруженных сайтов или корпоративных систем частенько возникает проблема с разработкой быстрого и удобного поискового движка. Ниже перечислены наиболее важные, на мой взгляд, требования к такому движку:

  • Скорость
  • Простота установки и настройки
  • Цена (желательно бесплатно и с открытым кодом)
  • Обмен информацией в формате JSON (по HTTP)
  • Масштабируемость (возможность распределения на несколько серверов)
  • Индексация в режиме реального времени
  • Multi-tenancy (гибкость в настройках под индивидуального пользователя)
  • Возможность переноса системы в облако

Хочу рассказать вам о новом поисковом движке Elasticsearch, который полностью удовлетворяет всем этим требованиям. В статье будет краткое описание, ссылка на авторитетную презентацию, а также описание установки и работы с ним.

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

Так вот, недавно на глаза мне попалась презентация Андрея Змиевского (Andrei Zmievski), где он описывал возможности elasticsearch. Презентацию можно посмотреть тут (на английском).

Сайт проекта http://www.elasticsearch.org/

К сожалению, никакой информации на русском языке я найти не смог.

Что же это такое?


По сути — это новый фронт-енд к широко известному индексу Lucene. Главное отличие от конкурентов — это гибкость и простота в использовании. Добавление информации в индекс и поиск по индексу производятся с помощью простых HTTP запросов.

Установка и примеры работы с движком


Меня эта тема заинтересовала и я решил собственноручно испытать этот чудо-движок.
Итак, приступим

Установка

	1. Скачиваем архив (http://www.elasticsearch.org/download/) и распаковываем его
	2. Запускаем сервер 
		Unix :   bin/elasticsearch –f
		Windows :  bin/elasticsearch.bat
	3. Проверяем сервер 
		curl -X GET http://localhost:9200/
	Если все работает, сервер вернет вам JSON массив с какой-то информацией.

Индексация данных

Для примера создадим индекс пользователей хабра

Добавляем данные о первом пользователе
$ curl -XPUT 'http://localhost:9200/habrahabr/users/1' -d '
{ 
 "firstname"   : "Piotr",
 "surname"     : "Petrov",
 "birthDate"   : "1981-01-01",
  "location"    : "Moscow, Russian Federation",
 "skills"      : ["PHP", "HTML", "C++", ".NET", "JavaScript"]
}'

Добавляем данные о втором пользователе
$ curl -XPUT 'http://localhost:9200/habrahabr/users/2' -d '
{ 
 "firstname"   : "Ivan",
 "surname"     : "Sidorov",
 "birthDate"   : "1978-12-13",
  "location"    : "Briansk, Russian Federation",
 "skills"      : ["HTML", "Ruby", "Python"]
}'

Добавляем третьего пользователя
$ curl -XPUT 'http://localhost:9200/habrahabr/users/3' -d '
{ 
 "firstname"   : "Stepan",
 "surname"     : "Fomenko",
 "birthDate"   : "1985-06-01",
  "location"    : "Ukraine",
 "skills"      : ["HTML", "XML", "Java", "JavaScript"]
}'

Поиск: пробуем в деле

Для ознакомления я приведу несколько простых примеров поиска. На самом деле этот движок полностью соответствует своему названию “elastic” и можно создавать самые разнообразные запросы. Подробнее о запросах можно прочитать на сайте проекта www.elasticsearch.org/guide/reference/api

параметр pretty=true отображает ответ в более читабельном виде

пример 1: ищем всех пользователей с именем Ivan
$ curl -XGET 'http://localhost:9200/habrahabr/users/_search?q=firstname:Ivan&pretty=true'

пример 2: ищем всех пользователей из Украины со знанием PHP
$ curl -XGET 'http://localhost:9200/habrahabr/users/_search?pretty=true' -d '
{ 
    "query" : { 
        "term" : { "location": "Ukraine", "skills": "PHP" } 
    } 
}'

пример 3: ищем пользователей из России
$ curl -XGET 'http://localhost:9200/habrauser/_search?q=location:Russian%20Federation&pretty=true'

пример 4: подсчитываем количество пользователей из России
$ curl -XGET 'http://localhost:9200/habrauser/_count?q=location:Russian%20Federation&pretty=true'

P.S. UTF8 поддерживает нормально

Тестирование с большим объёмом данных


К сожалению у меня нет большого опыта работы с другими поисковыми движками, поэтому нет возможности и сравнить их с elasticsearch. Любопытства ради решил создать индекс из 5,000,000 пользователей.

Простенький скрипт для заполнения индекса (данные генерируются, но информация более-менее похожа на реальную)

<?php

ini_set('max_execution_time', 36000);

class userGenerator {

  // вписал только несколько стран для примера
  private $countries = array(
      'Russian Federation',
      'Ukraine',
      'Germany',
      'France',
      'Lithuania',
      'Latvia',
      'Poland',
      'Finland',
      'Sweden'
  );

  public function run($cnt) {

    for ($i = 0; $i < $cnt; $i++) {
      $query = $this->generateQuery($i);
      echo "generating user " . $i . " ... ";
      exec($query);
      echo "done" . PHP_EOL;
    }
  }

  private function generateQuery($id) {
    
    // Допустим самый старый программист родился в 1960-ом году
    $date = new DateTime('1960-01-01');

    return 'curl -XPUT \'http://localhost:9200/habrahabr/users/' . $id . '\' -d \'
     { 
       "id"          : "' . $id . '",
       "firstname"   : "' . ucfirst($this->generateWord(10)) . '",
       "surname"     : "' . ucfirst($this->generateWord(10)) . '",
       "birthDate"   : "' . $date->modify('+' . rand(0, 14600) . ' days')->format('Y-m-d') . '",
       "location"    : "' . $this->generateWord(10) . ', ' . $this->countries[array_rand($this->countries)] . '",
       "skills"      : ["' . strtoupper($this->generateWord(3)) . '", "' . strtoupper($this->generateWord(4)) . '", "' . strtoupper($this->generateWord(3)) . '"]
     }\' ';
  }

  private function generateWord($length) {

    $letters = array(
        "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
        "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z");

    $word = '';
    for ($i = 0; $i < $length; $i++) {
      $word .= $letters[rand(0, 25)];
    }
    return $word;
  }

}

$generator = new userGenerator();
$generator->run(5000000);
echo "complete";
?>


На создание индекса на моём домашнем (не особо мощном) ПК ушло где-то 5 часов. Учитывая то что я абсолютно ничего не настраивал и не оптимизировал, считаю что результат довольно неплохой. Тем более что время генерирования индекса для меня не особо критично. Думаю если покопаться в настройках, да ещё и оптимизировать мой скрипт так чтобы высылал не одиночные а групповые запросы (подробнее тут), то время сократилось бы в разы. Ну а если ещё и распараллелить этот процесс — тогда время можно сократить до часа.

Проверяем количество записей в индексе
curl -XGET 'http://localhost:9200/habrahabr/users/_count?q=*&pretty'
{
  "count" : 5128888,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "failed" : 0
  }


Проверяем скорость добавления новой записи
time curl -XPUT 'http://localhost:9200/habrahabr/users/5128889' -d '
{ 
 "firstname"   : "Василий",
 "surname"     : "Фёдоров",
 "birthDate"   : "1975-07-11",
  "location"    : "Riga, Latvia",
 "skills"      : ["PERL", "PYTHON", "ActionScript"]
}'
{"ok":true,"_index":"habrahabr","_type":"users","_id":"5128891","_version":2}

real	0m0.007s
user	0m0.004s
sys	0m0.000s


Проверяем скорость поиска информации
time curl -XGET 'http://localhost:9200/habrahabr/users/_search?q=location:Riga&pretty'
{
  "took" : 5,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "failed" : 0
  },
  "hits" : {
    "total" : 1,
    "max_score" : 8.854725,
    "hits" : [ {
      "_index" : "habrahabr",
      "_type" : "users",
      "_id" : "5128891",
      "_score" : 8.854725, "_source" : 
{ 
 "firstname"   : "Василий",
 "surname"     : "Фёдоров",
 "birthDate"   : "1975-07-11",
  "location"    : "Riga, Latvia",
 "skills"      : ["PERL", "PYTHON", "ActionScript"]
}
    } ]
  }
}
real	0m0.011s
user	0m0.004s
sys	0m0.000s


$ time curl -XGET 'http://localhost:9200/habrahabr/users/_count?q=location:Germany&pretty'
{
  "count" : 570295,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "failed" : 0
  }
}
real	0m0.079s
user	0m0.004s
sys	0m0.000s


Выводы

На мой взгляд движок быстрый, качественный, простой в использовании. По ощущениям он гораздо быстрее того же Zend_Search_Lucene.

В этой статье я описал лишь небольшую часть его функционала — самые простые и примитивные функции. За пределами этой статьи остались транзакции, репликaции, фильтры и очень много других полезных функций. Также стоит упомянуть что к этому движку уже написаны библиотеки на Java и PHP (возможно и на других языках).

П.С. Прошу прощения за некоторое косноязычие текста и терминов.
Леонид @brujeo
карма
12,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (61)

  • –4
    А чем вам не угодил Lucene? Всем требованиям удовлетворяет, есть обертки под все популярные языки. А уж о качестве и говорить нечего: даже твиттер на него перешел.
    • +6
      Или Sphinx или Solr
      • –3
        > Sphinx

        Спасибо, не надо.
        • –3
          +1
          глядя на результаты поиска по Хабру — связываться со Sphinks нет никакого желания… хотя конечно виной тому могут быть кривые руки программистов ТМ
          • +5
            Очень зря, судите лучше по поиску ребят, перечисленных вот здесь:
            http://sphinxsearch.com/info/powered/
          • 0
            Использую сфинкс уже года четыре. Во многих проектах. Принципиальных претензий нет. Скорость просто замечательная! Жаль, что в качестве ключа нельзя использовать текст.
    • +9
      «Что же это такое? По сути — это новый фронт-енд к широко известному индексу Lucene.»
      Автор как бы намекает…
      • –1
        тогда автор неправильно создал название топику, т.к. поисковым движком тут не пахнет, просто обертка над ним.
        • 0
          Lucene — это не поисковый движок, это библиотека для написания поисковых движков.
          • –1
            с этим я согласен, т.к.:

            • –1
              блин, парсер съел:

              Lucene itself is just an indexing and search library and does not contain crawling and HTML parsing functionality.
              • +1
                Да кравлинг и парсинг хтмл тут не при чëм. Люцен — это не демон, у него нет никакого внешнего интерфейса, и т.п. Он умеет строить индексы и по ним искать, и только этим и занимается. Кроме того, эластиксëрч, например, неплохо реализует реалтаймовый поиск, с чем солр не очень справляется — так что свойства поисковика определяются не только библиотекой.

                Короче, нормальный заголовок, хотя статья фактически ни о чëм — намного интереснее почитать блог-посты на сайте эластиксëрча.
                • 0
                  я так понял, что ты используешь его?

                  Напиши статью, будет интересно сравнить, например, с тем же sphinx (понимаю, что полноценно не сравнить) из личного опыта (если конечно ты сфинкс использовал).
                  • 0
                    Не, но игрался. Я сейчас использую сфинкс, а не его, только потому, что сфинкс мало памяти ест (эластиксëрч на старте от 100 мб, для маленького впс неприятно ;).

                    Но у эластика есть куча приятных фич, которых в сфинксе нет никак, можно вот посмотреть в их постах:

                    www.elasticsearch.org/blog/2011/05/13/data-visualization-with-elasticsearch-and-protovis.html
                    www.elasticsearch.org/blog/2011/02/08/percolator.html
                    www.elasticsearch.org/blog/2011/02/08/versioning.html
                    www.elasticsearch.org/blog/2010/08/16/geo_location_and_search.html
                    • +1
                      так вроде ж геолокейшн есть и в сфинксе
                      • 0
                        Та есть, но он убог. В эластике можно искать на каком-то расстоянии, в ограничивающем прямоугольнике, в полигоне. А в сфинксе только ранжировать на расстоянии, насколько я понимаю, и всë. :(
                        • 0
                          ты смотрел последние беты сфинкса?
                          • 0
                            Ну, 2.0.1-beta. Я даже доку еще раз перелистал. Либо ничего нет, либо только ранжирование.
          • 0
            Я больше о несоответствии названия топика его содержимому, с Lucene никогда не работал, поэтому этот ньюанс от меня ускользнул :)
          • 0
            хотя даже в википедии написано, что:

            ElasticSearch – A Distributed, Highly Available, RESTful Search Engine.

            Кому интересно верить. :)
            • –1
              Мне верить, википедии верить. Я сказал, что люцен — не полноценный движок, что не противоречит тому, что эластиксëрч — он самый.
              • –1
                я о том, верить ли названию топика или википедии :) Впрочем, ответ уже понятен :)
          • –1
            Позволю себе не согласиться с этим (цитата с офф сайта: «Apache Lucene(TM) is a high-performance, full-featured text search engine library»). Lucene предоставляет сам по себе вполне удобный апи. На одном из проектов, на котором я работал, мы использовали чисто его (Lucene.NET, точнее), без Solr или ElasticSearch, и весьма успешно.

            В конце концов, Solr или ElasticSearch--это всего лишь обертки (Facade, Wrapper, Adapter--как хотите) для Lucene, вызовы функций которых выполняются через Remote Procedure Call. Ничто вам не мещает написать свою обертку на любимом языке программирования и вызывать ее методы локально.

            Но, на мой взгляд, необходимости в этом часто нет--опять же, интерфейс Lucene очень хорош сам по себе.
            • 0
              " search engine library" — слово «library» тут ключевое. Вы использовали Lucene.NET как раз потому, что Lucene без полноценного сервера использовать не могли.

              В любом случае, они-то конечно обëртки, но поведение у них весьма разное.
      • 0
        Виноват, пропустил эту строчку. С другой стороны, как пишут в комментарии ниже, действительно, название не совсем соответствует предмету обсуждения.
    • 0
      Lucene — это либа, на которой построены Solr и Elasticsearch.
  • +1
    Мне кажеться, сравнивать с Zend_Search_Lucene не совсем корректно — это все же не самостоятельный продукт, а просто РНР-порт (с учетом ограничений и особенностей) и работает как просто библиотека, а не самостоятельный сервис (хотя используя его можно написать аналог еластиксерча на РНР)
    • 0
      Полностью с вами согласен… поэтому в данной статье я просто хотел ознакомить читателей хабра с этим движком и не старался сравнивнивать его с конкурентами. Было бы очень интересно почитать про сравнение Elasticsearch с тем же Solr, Sphinx, Xapian и т.д.
  • +5
    о каком вообще измерении скорости может идти речь, если вместо curl_init или на худой конец fsockopen вы делаете exec('curl -XPUT \'http://localhost:9200/habrahabr/users/'. $id. '\' -d \'… )? вы понимаете, время чего именно вы измеряете?
    • 0
      какой-то детский сад штаны на лямках
  • +3
    Как со стеммингом и морфологией? Русской, в частности?
    • +2
      Elasticsearch может использовать snowball, который поддерживает русский язык.
      • 0
        Спасибо. Надо попробовать.
      • 0
        По сравнению с английским, весьма криво поддерживает, надо сказать.
        Приходилось иногда вручную подчищать резльутаты, т.к. некоторые суффиксы им игнорировались начисто.

        Пример из кода:
        # if adjective
        my $stemmer = Lingua::Stem::Snowball->new(lang => $self->{locale}{lang}, encoding => 'UTF-8');
        $stemmed = $stemmer->stem($self->{query});
        $stemmed =~ s{$_$}{} for qw(ец ск ий); # these are most common suffixes that are not processed by Porter's snowball
        • 0
          А у Вас опыт продакшн использования ElasticSearch был?
          • 0
            Сейчас крутится в продакшене в качестве поисковика по структурированным документам. В будущем будет задействован и для полнотекстового поиска.
  • +2
    Multi-tenancy — это нисколько не «гибкость в настройках под индивидуального пользователя», если верить Гуглу, Вики и другим.
  • +1
    было
    $generator->run(5000000);
    получили
    «count»: 5128888,

    чему верить?
    • 0
      Я не очистил индекс перед запуском скрипта и в индексе остались проиндексированные данные предыдущих тестов (128888 записей). Думаю это нисколько не повлияло на результаты теста.
  • +1
    Все примеры — поиск по полям (имя, страна...), приведите пример полнотекстового поиска, речь же об этом.
  • +1
    Тоже видел ElasticSearch, тоже понравился. Предлагал на пару проектов, но пока все приросли к Solr. Ничего, думаю, наберет популярность, все, что есть в Solr, вроде бы есть и в нем, но конфигурируется более удобно/динамично, и есть то, чего в Solr нет или только будет, может в виде не слишком красивой прилуды.
  • 0
    А Sphinx чем не устраивает?
    • 0
      это разные вещи (кроме базы — поискового движка). у сфинкса при всех его достоинствах, нет REST и JSON интерфейса
      • –2
        А зачем он? У Sphinx есть клиент, который отдает id-шники найденных документов. Потом взял их в базе и все.
        • 0
          для того чтобы поиск был отдельным. И универсальный интерфейс доступа, не связанный с конкретикой реализации записи.

          К еластиксу можно обращаться напрямую с веб-страницы, получая в виде JSON результат.
          • –2
            А Sphinx можно встроить в MySQL как плагин и использовать его прямо в SQL-запросах. :)
  • +1
    Тем, кто всерьез интересуется, но пока еще сомневается: у вас есть шанс пообщаться с одним из разработчиков ElasticSearch на конференции YAPC::Europe. Конференция пройдет в Риге, с 13 по 15 августа.
    yapceurope.lv/ye2011/talk/3371
    yapceurope.lv/ye2011/talk/3372
    • +1
      Виноват, с 15 по 17.
  • 0
    Кстати, еще один вариант применения: java.dzone.com/articles/jetslide-uses-elasticsearch
  • –1
    Если информация о том, что есть подобные движки пойдет в массы, то тогда можно ждать новых поисковиков тима ВЯндексе, ВГугле, ВРамблере и т.п.
  • +1
    >К сожалению, никакой информации на русском языке я найти не смог.
    Напиши Андрею Змиевскому письмо, он ответит на все твои вопросы. Лмчно знаю его, очень дружелюбный…
  • +1
    что было бы действительно интересно Хабросообществу — эта сравнение со Сфинксом,
    что такого может эластик-сеач, чего не может сфинкс. Если сказать, что сфинкс не может JSONто это дело двух дней и небольшой демонёнок sphinx-proxy сможет понимать JSON
    меня интересует функционал и скорость.
    • 0
      Как минимум, невооружённым глазом видны «живые» апдейты и даже больше — “realtime get” ( github.com/elasticsearch/elasticsearch/issues/1060 )
      • –1
        это не показатель
        меня функционал интересует, частично информацию подчерпнул из обсуждения, что нужно было упомянуть в статье обязательно: эластик-сёрч — это REST обертка основанная на поисковом движке Lucene.
        По сравнению с Lucene, Sphinx дает +10 очков
        • 0
          Для Вас не показатель, для меня показатель. То, что эластик — фронтенд к люцену, написано явным образом в статье.
  • 0
    примерчик бедненький, подскажите,
    вот в сфинксе можно назначить вес полям записей при индексации,
    ну например «Заголовок_резюме» более значим чем «Опыт_работы» (указываем коэф.),
    а в Эластике (Люценте) можно так индексировать?
  • 0
    Сколько занимает на диске подобный индекс?
  • 0
    Мы кстати сделали hosted ElasticSearch со множеством плюшек вокруг: http://indexisto.com/ )
    Есть готовая AJAX строка поиска которая поддерживает кучу всего из стандартного ответа кластика: фасеты, сортировки, хайлайт, саджесты

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.