Недавно мы протестировали подход, именуемый нами QDM, при работе с большими объемами данных — сотни гигабайт. В рамках задачи мы обрабатывали по 12-24 млн записей и сравнивали производительность квинтетного решения с аналогичным функционалом в обычных таблицах.
Мы не сделали каких-то новых открытий, но подтвердили те гипотезы, что озвучивали ранее: насколько всё таки универсальный конструктор в руках условного «чайника» проигрывает профессионально настроенной базе данных.
Также мы теперь знаем, что делать в подобной ситуации — решение достаточно простое и надежное, и имеем опыт организации компромиссного решения для сколько угодно больших данных.
![](https://habrastorage.org/webt/cv/c7/db/cvc7db2bi5ew6z0cmki6u2ow_yk.png)
В ходе разработки системы для сверки отчетности нам понадобилось загрузить данные одной из отчетных форм, в которой на каждую отчетную дату содержится до 24 млн записей. Данных там должно быть на 7 лет ежедневных расчетов. Объемы, прямо скажем, не для универсального конструктора, а для узкоспециализированной системы, но мы уже ввязались в эту затею и надо было искать решение.
Пользователи работают с этим большим массивом данных только в пределах одной отчетной даты, поэтому в эталонной системе всё это хранится в таблице, партиционированной по этой дате. Индексы по остальным 26 полям данных для этой формы не используются.
Первое, что мы сделали, разумеется — создали нужную структуру данных в конструкторе и загрузили туда несколько дат. Загрузка одной самой небольшой даты занимает около 14 часов, что уже неприемлемо долго: 12.5 млн записей по 27 атрибутов, положенные в полмиллиарда записей из 5 полей с построением трёх индексов, два из которых — составные.
Общий объем этих данных на диске чуть больше 18 ГБ.
Стоит заметить две особенности этой формы:
- Она почти не поддается нормализации, в отличие, например, от Формы 110, рассмотренной в предыдущей публикации
- В ней не используются индексы по атрибутам записей — пользователю выгоднее подождать минуту, чем тратиться на индексы
Это, можно сказать, самый радикальный случай, какой можно выбрать для сравнения. В большинстве случаев разница в объемах данных QDM и обычной базы не такая драматичная, а то и совсем мала.
Для сравнения, те же данные, загруженные в обычную таблицу реляционной базы данных, занимают 2.3 ГБ (в 8 раз меньше) вместе с индексом по дате, а их загрузка длится менее получаса (в 28 раз быстрее). В обоих случаях данные вставлялись напрямую из файла порциями по 100 тысяч записей, без отключения индексов.
Памятуя, что для таких объемов данных использовать конструктор нецелесообразно, мы, тем не менее, сделали тесты производительности: для разных случаев сравнили массовую обработку записей конструктором и нашей неиндексированной таблицей. Нам нужно было определить границу объема данных, для хранения которых мы будем отныне использовать обычную таблицу, а не наш конструктор.
Как мы ожидали, работа с небольшими наборами данных, например, по отдельному счету или клиенту, в конструкторе выглядит достаточно комфортно (время отклика в пределах секунды), в отличие от таблицы без индексов, где отклика приходится ждать минутами. В то же время основная задача приложения — массовая выборка и агрегация данных в различных разрезах — может занимать в конструкторе в разы больше времени.
Ниже приведены сводные результаты выборок, которые мы делали для ступенчато возрастающего объема агрегируемых данных:
К-во записей | Время выборки, с | |
---|---|---|
Конструктор | Таблица без индексов | |
1 | 0.16 | 56 |
5 | 0.23 | 55 |
50 | 1.86 | 53 |
600 | 2.35 | 56 |
5000 | 14.7 | 56 |
12000 | 125 | 56 |
100000 | 254 | 57 |
650000 | 2663 | 57 |
1000000 | 2314 | 57 |
5000000 | 9675 | 69 |
12500000 | 764 | 89 |
Там, где это было возможно, мы делали несколько замеров, предварительно выбрав статистику и отбирая количество записей по маске номера счета.
Из таблицы и графика ниже видно, что агрегация полного набора данных внутри дня занимает заметно меньшее время, чем выборка свыше 5% данных с использованием индекса. Именно поэтому оптимизаторы запросов РСУБД игнорируют индекс в подобной ситуации, в то время как движок нашего сервиса на данных момент лишён такой возможности.
Графическое отображение тех же результатов с использованием логарифмической шкалы, чтобы сравнить числа разных порядков:
![](https://habrastorage.org/webt/ts/pf/ye/tspfyede8jvir1cegofu_v-clss.png)
Быстродействие конструктора при обработке полного набора данных почти на порядок ниже, чем у обычной таблицы, что вполне нормально для конструктора — важно избежать лавинообразной деградации производительности, и эта цель достигнута.
Исследование показало, что количество записей в базе практически не влияет на скорость построения страниц, навигации и небольших выборок в квинтетной модели данных. При количестве обрабатываемых данных до 10 000 записей (а это максимальная выборка связанных данных для экземпляра любой бизнес-сущности в информационной системе) можно комфортно работать с базой в сотни гигабайт и больше.
Далее мы научили наш табличный плагин (описанный здесь) вызывать коннектор к произвольной базе данных, чтобы он прозрачно работал как с квинтетной моделью данных, так и с традиционными таблицами.
С точки зрения пользователя, ему не важно, с каким источником данных он работает, пока он может делать важную для него работу в привычном интерфейсе, получая свои отчеты:
![](https://habrastorage.org/webt/eh/v5/x1/ehv5x1yy8nx98oedlouxda0i-kk.png)
Теперь мы вынесем остальные подобные огромные таблицы, которых в нашей базе всего две дюжины из сотен, в отдельное хранилище, чтобы комфортно работать с ними.
Итак, мы можем использовать конструктор для небольших и средних таблиц, требующих интенсивного поиска и агрегацию по произвольным атрибутам, а большие неиндексированные объекты хранить в плоских традиционных таблицах, вызывать из стороннего хранилища или специализированных баз данных (Hadoop и прочих NoSQL).
Наилучшим образом конструктор подходит для CRM-систем и подобных продуктов, где пользователь работает с отдельно взятым клиентом, счетом или иной сущностью, а аналитические функции вынесены в отдельное специализированное хранилище.