
Многие из продуктов LinkedIn очень сильно зависят от интенсивно вычисляемых data mining алгоритмов. Например, такие модули как "Люди, которых вы возможно знаете" (People You May Know), "Просмотревшие этот профайл также просмотрели" (Viewers of This Profile Also Viewed) и большинство модулей по подбору работы, которые мы предоставили для тех, кто размещает вакансии на сайте. Чтобы иметь возможность поддерживать эти интенсивно вычисляемые модули, мы начали перемещать самые большие оффлайновые обработчики в Hadoop. Данные были перемещены как из приблизительно 20 online хранилищ (Oracle, MySQL, Voldemort и т.д) так и из централизированного сервиса логгирования в такие offline системы, как например Hadoop, AsterData, и в наш Oracle Data Warehouse. Перемещение в централизированную offline систему обработки существенно упростило реализацию сложных алгоритмов, которые должны были использовать данные из большого количества источников. Один раз извлекая данные, запускалась последовательность offline заданий для обработки на этих системах. В результате обработки, данные автоматически загружались обратно в live систему для наполнения различных частей сайта. Все производимые данные становятся "только для чтения", как только они размещаются на сайте. Это позволяет избежать сложностей со слиянием данных, посчитанных offline, и online обновлений.
Сложность в этой системе появляется в связи с тем, что необходимо перемещать большие объемы данных каждый день. Сотни гигабайт или даже терабайты данных не проблема, когда они находятся в системе хранения данных. Сложности появляются когда их нужно трансформировать для быстрого поиска и перемещения между подсистемами для ежедневных задач.
В этой статье описывается система, которую мы построили для размещения данных на живом сайте применяя key-value систему хранения данных Project Voldemort.
Зачем мы рассчитываем столько информации ? Размер выходных данных обычно определяется количеством различных блоков на сайте: мы можем рассчитывать что-либо для каждого профайла пользователя, каждого заданного вопроса, каждой размещенной статьи и т.д. Эти задачи могут обрабатывать большое количество информации, особенно когда мы включаем сюда очень большие потоки информации, но результаты, не смотря на то, что они достаточно велики, всё же поддаются управлению. У нас есть другой тип задач, который производит результат на основе пары пользователей или компаний, или, скажем, на основе связи между пользователями и вопросами или другим типом контента на сайте. Как вы можете представить, количество интересующих нас пар намного больше, чем количество элементов (это не так много, как квадрат от количества элементов т.к. многие пары не интересуют, но всё равно это огромное число). Раньше мы не должны были противостоять этой проблеме, так как размер наших данных был меньше, а также потому, что наша способность производить крупные offline наборы данных был ограничен в вычислениях. Hadoop был стал полезным в устранении проблем масштабируемости в offline части этой системы, но при этом он создает огромные узкие места в нашей способности доставлять данные на сайт. Как это часто бывает, устранение узких мест в одном месте создает новое узкое место где-нибудь еще.
Для решения этой проблемы мы провели некоторое время думая о том, как организовать ежедневные задачи по обработке данных. Voldemort был разработан для поддержки быстрых, масштабируемых по чтению-записи операций. Он не проектировался специально для пакетных вычислений, однако он поддерживает систему плагинов, что позволяет организовывать доступ к нескольким движкам хранения данных посредством стандартизированной библиотеки. Это позволяет интегрировать нашу быструю, устойчивую к сбоям online систму хранения данных с тяжелой системой offline обработки, работающей поверх Hadoop.
картина нашего мира
Есть много других способов подхода к этой проблеме, но никто с кем бы мы говорили не подсказал нам хорошее решение. Мы видели много различных методов, в том числе ручное использование статических текстовых файлов, передача по FTP гигантских XML файлов или использование JDBC и пакетных вставок в (Oracle) БД. Ни один из них не является хорошим подходом к этой проблеме, поскольку они, как правило, имеют одну из двух общих проблем. Первая заключается в том, что передача данных становится централизованной, создавая не расширяемое узкое место в плане доставке данных. Вторая заключается в том, что процесс создания индекса (обычно btree), происходит на том же сервере, который выполняет поиск. Это является большой проблемой, поскольку строительство большого индекса представляет собой огромную и вычислительно-интенсивную операцию, которая может потребовать для выполнения несколько часов, и, делая это на live сервере мы смешиваем эту огромную, ориентированную на пропускную способность операцию с критичным к задержкам поиском. Обычно с плохими результатами для наших пользователей.
Лучшая онлайн система для поиска данных в данный момент -- это memcached. Memcached является стабильным и имеет отличную производительность кэширования для общих нужд. Очевидные проблемы с memcached -- это "mem" и "cache" части. Memcached полностью располагается в памяти, поэтому вы должны "втискивать" все ваши данные в память, чтобы иметь возможность работать с ним (что может быть дорогостоящим предложением, если результирующий набор данных слишком большой). Кроме того memcached представляет собой кэш, поэтому, если вам нужно перезапустить серверы тогда ваши данные исчезнут и возникнет необходимость повторного наполнения! Еще одной проблемой является очевидное отсутствие пакетного набора операций. Без этого большая часть времени неизбежно будет израсходована на ненужные сетевые посылки независимо от того, насколько эффективно мы их реализуем. Мы могли бы легко организовать map-reduce задачу, чтобы сделать это в параллельном режиме, но это упирается в нижележащий недостаток в пропускной способности каждого узла.
Следующая лучшая онлайн система -- MySQL. С помощью MySQL можно избежать проблемы one-round-trip для каждой вставки, делая пакетную вставку, но даже это, как представляется, даёт довольно низкую пропускную способность. Формат таблиц InnoDB имеет слишком большие накладные расходы по свободному месту, чтобы он стал реальным конкурентом. Однако MySQL имеет очень тонкий и простой формат MyISAM. MyISAM не требует столько ресурсов для обычного чтения/записи, поскольку он использует глобальную таблицу блокировок для записи. Ему также не хватает многих транзакционных достоинств, но это не является проблемой для данных "только для чтения", поскольку запись практически не используется. MySQL также поддерживает оптимизированный "LOAD DATA INFILE LOCAL", что дает необходимый потенциал по нагрузке. Это чрезвычайно важная особенность для хранилищ данных на диске. В этом случае, построение индекса на 100ГБ не может осуществляться эффективно, как последовательность обновлений b-tree, которые постепенно реорганизовывают данные, как только они поступают, потому что общая интенсивность ввода/вывода, обеспечиваемая всеми этими маленькими обновлениями, чрезвычайно высока. Чтобы избежать этого, дерево должно сделать пакетное задание, которое создает как можно больше частей дерева сразу, и это именно то, что "load data" делает.Решение All-in-all MySQL прозрачное, быстрое и лучшее среди готовых решений этой проблемы, которое мы видим. Но чтобы сделать это эффективно нужна тонна памяти, и таблица будет блокироваться во время построения индекса. Это означает, что если вы запускаете это на ваших live серверах, они будут очень активно работать в течение всего срока нагрузки (которая может легко занять несколько часов). Не говоря уже о том, что MySQL предоставляет мало возможностей на пути распараллеливания. В результате, построение системы на основе этого подхода влечет большие сложности.
Очевидно, что создание такого индекса -- это offline операция и она не должна делаться на сервере, который обслуживает живой сайт, так как это скорее всего потратит все ресурсы процессора и весь ввод/вывод. В принципе это возможно, так как MySQL, как кажется, позволяет просто скопировать файлы базы данных, каталог с таблицами БД запущенного сервера. Они сразу будут доступны даже без перезагрузки сервера. Но это означало бы поддержку целого отдельного кластера серверов MySQL только для необходимости построения индекса, а также разработки некоторых способов распараллеливания данного процесса. В заключение один практический момент заключается в том, что вам, вероятно, придется записать данные на диск несколько раз: один раз, чтобы скопировать его на сервер в виде текстового файла, а затем снова, как только он будет построен как база данных, и, наконец, в третий раз, если он копируется на live сервер (не говоря уже о том, что, к сожалению, MySQL делает свою собственную внутреннюю копию данных, когда вы создаете индекс, чтобы поддержать свои требования для транзакций). Т.к. load data не поддерживает компрессию, сохраняя данные в формате CSV можно получить огромный файл. Эти вещи не кажутся большой проблемой, но если ваш 400ГБ набор данных превращается в 1200 ГБ, потому что все номера записываются в ASCII, и этот файл затем копируется несколько раз, что создает серьезную проблему.
Описанные альтернативы небыли привлекательными, поэтому мы подумали о том, что нам нужно, чтобы решить эту проблему красиво. Мы пришли к следующим выводам:
Защитить live сервера. Загрузка нового набора данных не должна отражаться на сервисах, использующих эти данные.Мы хотели загружать новую информацию как можно быстрее. Это значит перенос как можно большего числа вычислений из online систем, гарантируя, что live сервера не попадут под негативное влияние.
Горизонтальное расширение на каждом шагу. Hadoop дает нам масштабируемый подход к формированию системы, а Voldemort дает масштабируемые возможности поиска. Хитрость заключается в обеспечении того, чтобы вообще не существовало централизованного узкого места в этом процессе.
Возможность отката (rollback). Как и любой код, процесс, который генерирует данные может иметь какую-то ошибку или баг, который приводит к генерации поврежденных данных, но в отличие от большинства проблем в коде, починка может не быть такой быстрой. Поскольку процесс может занять много времени для запуска, и данные автоматически переходят на live сервер без человеческого прочтения. Такая неудача может привести нас к плохой ситуации. Это может занять много часов чтобы перезапустить (по некоторым очень вычислительно-интенсивным процессам, несколько дней), и мы можем застрять с плохими данными пока не удается исправить ошибку, перезапустить все, и вновь выложить исправленные данные. Это явно неприемлемо. В результате мы хотели бы сохранить несколько копий одного и того же набора данных, по одному для каждой из последних N публикаций (где в общем случае N = 1), с тем чтобы мы могли бы вернуться к этому известному правильному состоянию. Это позволяет нам иметь постоянное время отката к предыдущему набору данных.
Устойчивость к сбоям. Это место, где консистентное хэширование Voldemort вступает в дело. Сбой сервера в live системе будет перенаправить 1/К от трафика этого сервера каждому из оставшихся серверов. Это не должно влиять на клиента. Мы также получили аналогичную устойчивость к сбоям при использовании Hadoop.
Поддержка большого количества данных в RAM. Первоначальная проблема, которую мы пытались решить состоит в том, что размер данных очень велик. Поэтому мы должны проектировать систему с учётом этого требования. Повышение производительности в случае, когда все данные находятся в памяти не очень важно. Основное внимание уделяется поддержке данных, размер которых значительно больше, чем память в каждом узле.
Читайте во второй части.
Перевод, оригинал находится в блоге проекта Voldemort.
Этот проект посвящен интересным и позновательным фактам, новостям, событиям из жизни web-разработчика.
Акцент размещенных здесь статей смещен в сторону решения задач, связанных построения сложных, нетривиальных и просто необычных систем.
