Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!
Всем привет.
Я разрабатываю библиотеку для работы с Entity Attribute Value (репозиторий), сокращенно EAV (модель базы данных для хранения произвольных данных). В конце прошлой статьи я спросил у вас о чём мне ещё надо написать, вы попросили показать пример использования и сделать замеры быстродействия.
Что для нас важно при работе с данными ? Скорость записи (добавления или обновления) и скорость чтения (конкретно - фильтрации по моделям одной сущности). При чём скорость поиска в приоритете, потому что записываем мы один раз в цать дней, а читаем каждую минуту/секунду и даже не один раз, а может быть и не одну сотню раз.
Фишка библиотеки в том что бы работать не с представлением построенном на базовых таблицах EAV, а работать с небольшой частью этих данных записанных в отдельное материализованное представление или в отдельную таблицу.
В Новогодние каникулы я сделал замеры производительности и хочу с вами поделиться результатами.
Что будем измерять ?
Чтение:
Время вычитывания всех позиций категории
Время формирования параметров фильтрации
Время фильтрации
Запись:
Время добавления новой характеристики (атрибута)
Время добавления новой товарной позиции
Время обновления товарной позиции
Набор данных
История разработки библиотеки началась с разработки маркет плэйса, поэтому считайте, что мы работаем с каталогом товарных позиций.
Требования к набору данных
Для тестирования времени чтения из MATERIALIZED VIEW и из TABLE не так важно сколько записей было в исходных таблицах модели EAV, поэтому несколько сотен категорий будет достаточно для проверки гипотезы.
Характеристик у категории (и соответственно у товарной позиции) не больше 100 (для обычного интернет магазина это даже много, обычно характеристик у товара не больше пары десятков).
Характеристики набора данных
С учётом таких вводных,с помощью скрипта data-generator/generate.php
, был создан набор данных:
количество сущностей (категорий) - 320
количество атрибутов (характеристик) - 144
количество моделей (позиций во всех категориях) - 29 000
числовых значений - 800 000
строковых значений - 600 000
Суммарный объём EAV таблиц (с индексами) - 310 Мб.
Учёт вариативности данных
Объём данных в которых надо производить поиск (фильтрацию) имеет большое значение для времени поиска.
Разбиваем все сущности на три вида: много атрибутов, среднее количество и малое количество, выбираем по одной сущности из каждого вида:
Много - моделей 238, атрибутов 112, всего значений 26656
Среднее - моделей 75, атрибутов - 34, значений - 2550
Малое - моделей 4, атрибутов - 5, значений 20
Измеренные значения
Все измерения в миллисекундах
Сущность у которой много моделей и атрибутов
Много - моделей 238, атрибутов 112, всего значений 26656.
Чтение
Способ доступа | Получить все записи, без фильтрации | Получить фильтры | Отфильтровать |
VIEW | 400 | 382,6 | 82,8 |
MATERIALIZED VIEW | 5 | 402,8 | 40,9 |
TABLE | 4,9 | 395,9 | 46,1 |
Получить все записи без фильтрации из VIEW невозможно, потому что у нас как минимум один фильтр - по категории - уже должен быть. Почему получилось такое чудовищно большое время, я понять не могу.
Для измерения "Отфильтровать", по сущности у которой много моделей и атрибутов, производился отбор по 46 колонкам (из 112).
В том что касается чтения, то фильтрация по MATERIALIZED VIEW происходит на 10% быстрей чем по TABLE.
Запись
Способ доступа | добавить атрибут | добавить модель | обновить модель |
VIEW | 79,7 | 155,6 | 188,7 |
MATERIALIZED VIEW | 1 338 | 1 112 | 1 462 |
TABLE | 3 | 196,5 | 217 |
Добавление атрибута в представление это его пересоздание.
Добавление атрибута в MATERIALIZED VIEW это его полное обновление.
Добавление атрибута в таблицу это ALTER TABLE, поэтому всего 3 мс.
Добавление модели подразумевает под собой, создание записи в таблице thing и создание пустых значений в таблицах word и number. Соответственно 155 мс для представления, это вставка 112 записей в таблицу значений (числовые - number, строковые - word).
Для MATERIALIZED VIEW нам снова надо его полностью пересчитать - обновить целиком.
Для таблицы мы добавляем запись у которой будет 112 пустых колонок, это всего 40,9 мс, но добавление записи происходит на основе представления, поэтому 155,6 + 40,9 = 196,5.
Обновление записи (обновление всех 112 колонок) выполняется через обновлением данных в базовых таблицах EAV и затем:
Для представления этого достаточно;
Для MATERIALIZED VIEW нам снова надо всё полностью пересчитывать;
Для таблицы обновление происходит независимо от базовых таблиц, просто обновляется одна строка, но эта строка обновляется только после успешного обновления данных в базовых таблицах, поэтому мы видим 217 мс, по сути собственно обновление записи это 30 мс.
Выводы
Как мы видим использование MATERIALIZED VIEW даёт максимальный выигрыш по времени чтения (в два раза быстрей чем из представления и на 10% быстрей чем из таблицы), но при этом обновление данных занимает на порядок больше времени (проигрыш в 10 раз).
Использование таблицы, даёт выигрыш по чтению в два раза, а проигрыш по записи на 15%, и этот проигрыш обусловлен тем что мы ждём успешного обновления мастер данных, если не ждать то выигрыш будет в 6 раз.
Конечно, что бы не ждать, надо доработать напильником библиотеку, завести туда асинхронность или логику создания и выполнения джобов. Конечно, написать логику для eventual consistency.
Если мы обновляем данные в нашем "универсальном" каталоге раз в час, то потеря одной секунды на обновление несущественна, и это точно стоит того что бы получить дополнительный прирост 10% по сравнению с использованием таблицы.
Если обновления данных у нас происходит несколько раз за час, то я бы использовал таблицу.
Сущность у которой среднее количество моделей и атрибутов
Среднее - моделей 75, атрибутов - 34, значений - 2550.
Чтение
Способ доступа | Получить все записи, без фильтрации | Получить фильтры | Отфильтровать |
VIEW | 49,5 | 83 | 27,9 |
MATERIALIZED VIEW | 1,7 | 69 | 12,3 |
TABLE | 1,4 | 66 | 12,6 |
Запись
Способ доступа | добавить атрибут | добавить модель | обновить модель |
VIEW | 24,4 | 50 | 65 |
MATERIALIZED VIEW | 207 | 212 | 242 |
TABLE | 3 | 90 | 90 |
При обновлении записи, мы снова видим порядка 30 мс на собственно обновление записи в таблице, но по сравнению с 65 мс на обновление записи в базовых таблицах, это уже 50%.
Сущность у которой малое количество моделей и атрибутов
Малое - моделей 4, атрибутов - 5, значений 20.
Чтение
Способ доступа | Получить все записи, без фильтрации | Получить фильтры | Отфильтровать |
VIEW | 2,4 | 10 | 5,4 |
MATERIALIZED VIEW | 1 | 13 | 4 |
TABLE | 0,8 | 13 | 2,9 |
Запись
Способ доступа | добавить атрибут | добавить модель | обновить модель |
VIEW | 6,2 | 8,8 | 8,1 |
MATERIALIZED VIEW | 29 | 19 | 27 |
TABLE | 4,4 | 4,4 | 10,7 |
Общий вывод
С уменьшением количества данных, стирается разница между способом доступа к этим данным. Для средних категорий разницы по чтению нет, разница по записи всего в два раза.
Тем не менее общая тенденция сохраняется - поиск по материализованному представлению и таблице происходит в два раза быстрей чем по представлению созданному на основе EAV таблиц.
Если количество сущностей увеличить на порядок, то время поиска в "таблице" не измениться, а время поиска в представлении увеличиться (мне так кажется), поэтому конечно при работе с EAV следует использовать "таблицы".
Вопрос о том будет это материализованное представление, или это будет собственно таблица - решать надо исходя из баланса между чтением и записью.
Что дальше ?
В следующей статье я приведу пример использования библиотеки.
Надеюсь вам было интересно.