Как разработать систему, в которой мы можем запрашивать лучшие результаты за последние n часов

Мне задали этот вопрос в интервью. Детали заключались в том, что предполагается, что мы получаем миллионы событий. Каждое событие имеет отметку времени и другие детали. Дизайн системы требует, чтобы конечный пользователь мог запрашивать наиболее частые записи за последние 10 минут или 9 часов или, возможно, за 3 месяца.

Событие можно рассматривать следующим образом

event_type: {CRUD + Search}
event_info: xxx
timestamp : ts...

person wayfare    schedule 25.05.2017    source источник
comment
Поддерживайте heap для лучших результатов.   -  person 6324    schedule 25.05.2017
comment
Что такое главное событие?   -  person j_random_hacker    schedule 25.05.2017
comment
@j_random_hacker означает наиболее частый. Я также обновил текст вопроса. Спасибо   -  person wayfare    schedule 25.05.2017
comment
Куча @YilongWang может быть нежизнеспособным подходом, поскольку миллионы записей могут не поместиться, их необходимо сохранить на диск.   -  person Amit Kumar    schedule 25.05.2017
comment
Каков ожидаемый результат? Список записей за последние X единиц времени или, возможно, их количество или какое-либо другое агрегирование?   -  person Bernhard Barker    schedule 25.05.2017
comment
@YilongWang, можете ли вы объяснить больше?   -  person wayfare    schedule 26.05.2017


Ответы (2)


Самый простой способ понять это — посмотреть, как это делают другие библиотеки потоковой обработки или сокращения карт (и я чувствую, что ваши интервьюеры видели эти библиотеки). В основном это уменьшение карты в реальном времени (вы также можете посмотреть, как это работает).

Я опишу два метода обработки событий. На самом деле большинству компаний необходимо делать и то, и другое.

Новая школа потоковой обработки (в реальном времени)

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

Примером проекта потоковой обработки является pipelinedb (у них в нижней части домашней страницы написано, как это работает).

  1. События переходят в очередь/кольцевой буфер
  2. Рабочий процесс считывает эти события пакетами и объединяет их в неполные сегменты или окна.
  3. Наконец, есть объединитель или редуктор, который принимает микропакеты и фактически выполняет обновление. Примером может служить подсчет событий. Поскольку мы используем очередь из вышеперечисленных, события поступают упорядоченно, и в зависимости от очереди у нас может быть несколько потребителей, которые выполняют операцию прочесывания.

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

Если вам нужны эти подсчеты за месяц, день или даже год, вы просто суммируете все сегменты подсчета минут.

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

Но вы получаете очень быстрый поиск результатов.

Хранилище данных старой школы (разделение) и Map Reduce (пакетная обработка)

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

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

  1. Вам нужна какая-то очередь для потока событий.
  2. Рабочий процесс читает очередь и разделяет события по времени. Например, у вас будет раздел на определенный день. Это похоже на шардинг. Многие системы хранения поддерживают это (например, разделы postgres).
  3. Когда вам нужно определенное количество событий за период, вы объединяете разделы.
  4. Разделение по существу иерархическое (минуты ‹ часы ‹ дни и т. д.), что означает, что вы можете выполнять над ними древовидные операции.

Существуют определенные способы хранения таких событий, которые называются данными временных рядов, чтобы индекс секционирования был автоматическим и быстрым. Они называются TSDB, дополнительную информацию о которых можно найти в Google.

Примером продукта TSDB может быть influxdb.

Теперь, возвращаясь к тому факту, что время (или, по крайней мере, то, как его представляют люди) представляет собой организованное дерево, мы можем преформировать операции распараллеливания. Это потому, что дерево является DAG (ориентированным ациклическим графом). С помощью DAG вы можете выполнять некоторый анализ и в основном рекурсивно работать с ветвями (также известными как fork/join).

Примером универсального продукта параллельного хранения может быть citusdb.

Теперь, конечно, у этого метода есть огромный недостаток. Это дорого! Даже если вы сделаете это быстро, увеличив количество узлов, вам придется платить за эти узлы (распределенные осколки). Теоретически производительность должна масштабироваться линейно, но на практике этого не происходит (детали я сохраню).

person Adam Gent    schedule 25.05.2017

Я думаю, вам нужно будет сохранить данные на диск, как

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

  • вы не можете хранить все события в памяти из-за ограничений памяти (миллионы событий)

Я бы предложил использовать mysql в качестве хранилища данных с отметкой времени в качестве одного из ключей индекса. Но два события могут иметь одинаковую отметку времени. Поэтому создайте составной индексный ключ с автоинкрементным идентификатором + отметкой времени.

Преимущества Mysql:

  • Сверхнадежный с репликацией
  • Поддержка всех видов операций и запросов CRUD

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

  • Сначала подсчитайте нет. событий, удовлетворяющих запросу.

    select count(*) from `events` where timestamp >= x and timestamp <=y.
    
  • Если запросу удовлетворяет слишком много событий, запрашивайте их пакетами.

     select * from 'events' where timestamp >= x and timestamp <=y limit 1000 offset 0; 
     select * from 'events' where timestamp >= x and timestamp <=y limit 1000 offset 1000;
    

    и так далее... до смещения ‹= количество событий, соответствующих первому запросу.

person Amit Kumar    schedule 25.05.2017
comment
Если кто-то ответит использовать базу данных SQL на такой вопрос в интервью, я полагаю, что следующим вопросом будет: можете ли вы объяснить, как бы вы сделали это с точки зрения структур данных и алгоритмов, то есть без использования базы данных. Использование правильных инструментов для работы важно, но собеседования, как правило, больше связаны с возможностью решить проблему с нуля, а не просто применить к ней существующий инструмент. - person Bernhard Barker; 25.05.2017
comment
@Dukeling Согласен с этой частью, однако, он упомянул запросы продолжительностью около 3 месяцев или около того, и в теме говорится о разработке системы ... которая, как я думал, может использовать внешние инструменты. - person Amit Kumar; 25.05.2017