Мы видим много транзакций в PayPal. Миллионы каждый день.

Эти транзакции происходят как извне (клиент использует PayPal для оплаты покупки на веб-сайте), так и изнутри, когда деньги перемещаются через нашу систему. Регулярные отчеты об этих транзакциях доставляются продавцам в виде файлов csv или pdf. Продавцы используют эти отчеты для сверки своих книг.

Недавно мы решили создать REST API, который мог бы возвращать данные транзакции продавцам. Мы также хотели предложить возможность фильтрации по различным критериям, таким как имя, адрес электронной почты или сумма транзакции. Поддержка легких сценариев использования агрегирования / анализа была непростой задачей. Этот API будет использоваться нашими партнерами, продавцами и внешними разработчиками.

Мы выбираем Elastic в качестве хранилища данных. За последние 6 лет Elastic зарекомендовал себя как активно развивающийся продукт, который постоянно развивается, чтобы адаптироваться к потребностям пользователей. Благодаря большому набору основных улучшений, представленных в версии 2.x (значения документов с отображением в памяти, автоматическое регулирование слияния), нам не нужно было больше искать.

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

Будет ли он опрокинуться

После запуска система будет иметь десятки терабайт данных, охватывающих более 40 миллиардов документов. У каждого документа будет более сотни атрибутов. Ежедневно добавлялись бы десятки миллионов документов. Каждый из запланированных блейд-серверов Elastic имеет 20 ТБ хранилища SSD, 256 ГБ ОЗУ и 48 ядер (гипер).

Хотя мы знали, что Elastic обладает всеми замечательными функциями, которые мы искали, мы не были уверены, сможет ли он масштабироваться для работы с таким объемом (и скоростью) данных. Существует множество нефункциональных требований, которые естественным образом возникают в финансовых системах, и они должны быть выполнены. Давайте ограничимся обсуждением в этом посте производительностью, а точнее, временем ответа.

Важность схемы

Ключевым моментом является получение правильных основ.

При первоначальной настройке Elastic мы включили строгую проверку полей в документах. Хотя это дало нам ощущение безопасности, сродни тому, к чему мы привыкли с реляционными системами (строгие проверки полей и типов), это ухудшило производительность.

Мы проверили содержимое Lucene index Elastic, созданного с помощью Luke. При нашей первоначальной настройке индекса Elastic создавал неоптимальные индексы. Например, в тех местах, где мы определили вложенные массивы (помечены index = no), Elastic все еще создавал дочерние скрытые документы в Lucene, по одному на элемент в массиве. Этот документ объясняет, почему, но он все еще занимал место, когда мы даже не могли запросить свойство через индекс. В связи с этим мы изменили настройку индекса динамический со строгого на ложное. Avro помогла обеспечить соответствие документа действующей схеме, когда мы подготовили документы для загрузки.

Шард должен иметь не более 2 миллиардов родительских и вложенных дочерних документов, если вы планируете в конечном итоге выполнить принудительное слияние (Lucene doc_id - целое число). Это может показаться высоким, но на удивление легко превысить его, особенно при переносе данных с высокой мощностью в источник. Неправильно настроенный индекс может иметь большое количество скрытых документов Lucene, создаваемых под обложками.

Слишком много вещей для измерения

При наличии схемы индекса нам потребовалось средство тестирования для измерения производительности кластера. Мы хотели измерить производительность Elastic при различных условиях нагрузки, конфигурациях и шаблонах запросов. В совокупности размеры составляют более 150 тестовых сценариев. Отобрать каждый образец вручную было бы практически невозможно. Здесь действительно помогли сценарии jMeter и Beanshell для автоматической генерации сценариев из кода и получения образца jMeter сотни раз. Затем результаты загружаются в Tableau, чтобы облегчить анализ тестов.

  • Стратегия индексации
  • Данные за 1 месяц на сегмент, данные за 1 неделю на сегмент, данные за 1 день на сегмент
  • # используемых осколков
  • Сравнение различных форм запроса (постоянная оценка, фильтр со всеми совпадениями и т. Д.)
  • Объем транзакции пользователя
  • Тестирование со счетами, имеющими 10/100/1000/1000/1000000 транзакций в день.
  • Диапазон времени запроса
  • 1/7/15/30 дней
  • Хранить исходные документы в Elastic? Или сохранить источник в другой базе данных и получить только соответствующие идентификаторы из Elastic?

Установление базовой линии

Следующим шагом было определение исходного уровня. Мы решили начать с одного узла с одним осколком. Мы загрузили данные за месяц (2 ТБ).

Тесты показали, что при использовании всего одного узла мы можем искать и получать 500 записей за 15 дней примерно за 10 секунд. Это были хорошие новости, так как отсюда можно было только поправиться. Это также доказывает, что шард Elastic (сегменты Lucene) может обрабатывать 2 миллиарда проиндексированных в него документов - больше, чем мы в конечном итоге будем использовать.

Один вывод: большое количество сегментов значительно увеличивает время отклика. Это может быть не так очевидно при запросе нескольких сегментов, но очень очевидно, когда есть только один. Используйте принудительное слияние, если у вас есть возможность (автономное построение индекса). Использование достаточно высокого значения для refresh_interval и translog.flush_threshold_size позволяет Elastic собирать достаточно данных в сегмент перед фиксацией. С другой стороны, это увеличивало задержку для того, чтобы данные стали доступны в результатах поиска (мы использовали 4 ГБ и 180 секунд для этого варианта использования). Мы могли синхронизировать более 70 000 операций записи в секунду всего с одного клиентского узла.

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

Разделение данных

Те же данные за один месяц (2 ТБ) были загружены на 5 узлов без реплик. На каждом узле Elastic был по одному осколку. Выбираем 5 узлов, чтобы осталось несколько неиспользуемых узлов. Они пригодятся в случае, если кластер начнет давать сбои и потребуются дополнительные мощности, а также для тестирования сценариев восстановления. Между тем, свободные узлы использовались для загрузки данных в Elastic и действовали как подчиненные узлы jMeter.

С 5 узлами в игре,

  • Время ответа упало до 6 секунд (прирост 40%) для запроса, который сканировался 15 дней.
  • Отфильтрованные запросы были наиболее эффективными
  • Поскольку запрос сканировал больше записей из-за увеличения диапазона дат, время ответа также увеличивалось с ним линейно.
  • В результате принудительного объединения 20 сегментов время отклика составило 2,5 секунды. Это показало, что на обработку результатов отдельных сегментов, которых в данном случае было более 300, было потрачено много времени. Хотя настройка процесса слияния сегментов в основном выполняется автоматически, начиная с Elastic 2.0, мы можем влиять на количество сегментов. Это делается с использованием описанных ранее настроек транслога. Также помните, что мы не можем запустить принудительное слияние для динамического индекса, выполняющего операции чтения или записи, так как это может привести к насыщению доступного ввода-вывода диска.
  • Обязательно установите для параметра throttle.max_bytes_per_sec значение 100 МБ или более, если вы используете твердотельные накопители, значение по умолчанию слишком мало.
  • Хранение исходных документов в Elastic не сильно повлияло на время ответа, может быть, на 20 мс. Это, безусловно, более производительно, чем отключение их от кластера, скажем, на Couchbase или Oracle. Это связано с тем, что Lucene хранит источник в отдельной структуре данных, которая оптимизирована для формата запроса сбора данных Elastic и отображается в памяти (см. Раздел Файлы fdx и fdt в документации Lucene). Наличие SSD, конечно, помогло

Окончательная настройка

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

Мы увидели время отклика около 200 мс для получения 500 записей после сканирования данных за 15 дней с этой настройкой. К этому моменту в кластер были загружены данные за 6 месяцев.

Подсчет осколков влияет не только на время чтения; они влияют на использование кучи JVM из-за метаданных сегмента Lucene, а также на время восстановления в случае отказа узла или сетевого раздела. Мы обнаружили, что Elastic помогает при перебалансировке, если есть несколько мелких осколков, а не несколько больших.

Мы также планируем развернуть несколько экземпляров на каждом узле, чтобы лучше использовать оборудование. Не забудьте взглянуть на планировщик ввода-вывода вашего ядра (см. Рекомендации по оборудованию) и влияние NUMA и режима восстановления зоны на Linux.

Вывод

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

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