
Первые впечатления от работы с «большими» данными
По большинству личных проектов трудно получить представление о масштабе.
Когда вы работаете над проектами с относительно небольшим количеством пользователей и небольшими наборами данных с менее чем миллионом записей, трудно получить представление о масштабе.
Я часто игнорирую сырую производительность в пользу других факторов (таких как более высокая скорость разработки), потому что варианты использования, для которых я создавал, никогда не получат существенной выгоды от увеличенного прироста скорости.
Вот почему я был взволнован, когда недавнее собеседование по программированию познакомило меня с Quandl.
Quandl предоставляет бесплатный набор данных, содержащий исторические данные о сделках с ценными бумагами. Доступные данные восходят к 90-м годам и показывают цены открытия/закрытия и минимальные/максимальные цены с дневным разрешением.
Имея более 15 000 000 записей, это была прекрасная возможность окунуться в работу с данными, поэтому я решил создать веб-приложение для их изучения.
Это была бы хорошая возможность изучить некоторые проблемы работы с наборами данных, превышающими пару сотен тысяч записей.
Если вам интересен сам проект, вы можете ознакомиться с ним на Stockerize или просмотреть исходный код.
Получение данных в базе данных
К счастью, Quandl предоставляет загружаемый архив данных в формате CSV. После загрузки 450 МБ (без сжатия до 1,7 ГБ) у меня было примерно 15 миллионов записей, готовых к импорту.
Я написал задачу на чтение CSV и вставку записей построчно.
Это оказалось невероятно медленным. Выполнение вставки для каждой строки в CSV было неэффективным! Я подсчитал, что импорт записей займет несколько дней.
Я решил группировать свои вставки по символу тикера, предварительно вычислив требуемые значения и выравнивая их, запуская только одну команду вставки для каждых нескольких тысяч записей.
Это оказалось значительно быстрее. Вместо того, чтобы занять несколько дней, это займет всего пару часов. Все еще не быстро, но достаточно быстро для моих целей.
Тайминги
В проектах, над которыми я работал в прошлом, были небольшие наборы данных, поэтому запросы обычно не превышали 200 мс, даже без индексов. В сочетании со стабильной моделью использования это означало, что производительность находилась в пределах допустимого диапазона бизнес-требований.
Вскоре стало очевидно, что мое апатичное отношение к производительности вообще не сработает для большого набора данных.
- Запрос на загрузку первых 100 записей, упорядоченных по дате, занял 4,12 секунды. без индекса.
- Изменение таблицы для добавления одного столбца со значением по умолчанию заняло 13,89 секунды.
- Обновление всех записей с расчетным значением заняло около 4,3 минуты пакетами по 1000.
- Само добавление индекса заняло 25,2 секунды.
Требуемые оптимизации
Сохранение рассчитанных значений
Один из моих запросов касался упорядочения торговых данных на основе разницы между ценой закрытия и ценой открытия.
Первоначально я выполнял запрос, который выглядел следующим образом:
@stock_prices .order('close_price_cents - open_price_cents desc') .limit(100) SELECT * FROM stock_prices WHERE ORDER BY (close_price_cents - open_price_cents) DESC LIMIT 100
Этот запрос занял 4 секунды — слишком долго!
Я создал столбец в качестве кэша для вычисляемого значения и начал использовать его вместо этого:
@stock_prices .order(open_close_price_delta_cents: :desc) .limit(100)
Этот новый запрос занял всего 0,4 секунды, что на порядок быстрее!
Индексы — ваши друзья
Обычно я из тех людей, которые не добавляют индексы до тех пор, пока это возможно.
Это помогает мне увидеть проблемы с производительностью на ранней стадии, и добавление индексов на основе фактических запросов, которые я использую, кажется лучше, чем добавление их путем угадывания.
Однако в данном случае получается, что без индекса я даже не могу делать сколько-нибудь осмысленной работы. Базовые запросы выполнялись за 5–8 секунд — слишком долго для любого использования.
Один запрос, который упорядочивал цены акций на основе даты и суммы, загружался 7,1 секунды.
После добавления многоколоночного индекса это заняло всего 0,9 мс.
Этот проект стал отличной отправной точкой для изменения моего представления о факторах производительности.
В будущем я мог бы сделать много вещей, чтобы еще больше повысить производительность, включая разбиение на страницы, поиск в кэше и т. д. Эти изменения потребуются для любой значимой нагрузки или в производственных настройках.
Однако с тех пор я начал работать над другим личным проектом, личным помощником чат-бота, и отложил этот проект.
Вы нашли эту историю интересной? Пожалуйста, хлопайте, чтобы выразить свою поддержку!
Чтение было пустой тратой времени? Пожалуйста, дайте мне знать, почему с помощью комментария!