450M рядов довольно большой.Итак, я буду обсуждать различные вопросы, которые могут помочь.
Сокращение данных Большая таблица приводит к увеличению количества операций ввода-вывода, что является основным фактором снижения производительности.(«Маленькие» таблицы, как правило, остаются в кэше и не несут бремени ввода-вывода.)
- Любой тип
INT
, даже INT(2)
занимает 4 байта.«Час» может легко уместиться в 1 байт TINYINT
.Это экономит более 1 ГБ в данных, плюс аналогичное количество в INDEX(hour)
. - Если можно получить
hour
и day_of_week
, не беспокойтесь о том, чтобы иметь их как отдельные столбцы.Это сэкономит больше места. - Какая причина использовать 4-байтовый
day_epoch
вместо 3-байтового DATE
?Или, возможно, вам нужен 5-байтный DATETIME
или TIMESTAMP
.
Оптимальный INDEX (дубль № 1)
Если это всегдаодиночный venue_id
, то либо это хорошее первое сокращение с оптимальным индексом:
INDEX(venue_id, zone_id, day_epoch)
Сначала константа, затем IN
, затем диапазон.Оптимизатор справляется с этим во многих случаях.(Неясно, может ли число элементов в предложении IN
привести к неэффективности.)
Лучший первичный ключ (лучший индекс)
При AUTO_INCREMENT
, вероятно, нет веской причины включать столбцы после столбца auto_inc в PK.То есть PRIMARY KEY(id, venue_id)
не лучше, чем PRIMARY KEY(id)
.
InnoDB упорядочивает BTree данных в соответствии с PRIMARY KEY
.Таким образом, если вы выбираете несколько строк, и могут расположить их рядом друг с другом в зависимости от PK, вы получите дополнительную производительность.(cf "Clustered".) Итак:
PRIMARY KEY(venue_id, zone_id, day_epoch, -- this order, as discussed above;
id) -- to make sure that the entire PK is unique.
INDEX(id) -- to keep AUTO_INCREMENT happy
И я согласен с DROPping любых индексов, которые не используются, включая тот, который я рекомендовал выше.Редко полезно индексировать флаги (is_repeat
).
UUID
Индексирование UUID может быть смертельно опасным для производительности, если таблица действительно большая.Это происходит из-за случайности UUID / GUID, что приводит к увеличению нагрузки ввода-вывода для вставки новых записей в индекс.
Многомерный
Предполагается, чтоday_epoch
иногда несколько дней, у вас, кажется, есть 2 или 3 «измерения»:
- Диапазон дат
- Список зон
- Место проведения.
INDEXes
являются одномерными.В этом и заключается проблема.Однако PARTITIONing
иногда может помочь.Я кратко обсуждаю это как «случай 2» в http://mysql.rjweb.org/doc.php/partitionmaint.
Нет хорошего способа получить 3 измерения, поэтому давайте сосредоточимся на 2.
- Вы должныразделить что-то, что является «диапазоном», таким как
day_epoch
или zone_id
. - После этого вы должны решить, что добавить в
PRIMARY KEY
, чтобы вы могли в дальнейшем воспользоваться «кластеризацией»".
План A: Предполагается, что вы ищете только один venue_id
одновременно:
PARTITION BY RANGE(day_epoch) -- see note below
PRIMARY KEY(venue_id, zone_id, id)
План B: Предполагается, что иногда вы выполняете srefineearch для venue_id IN (.., .., ...)
следовательно, он не является хорошим первым столбцом для PK:
Ну, у меня нет хорошего совета здесь;так что давайте перейдем к плану А.
Выражение RANGE
должно быть числовым.Ваш day_epoch
отлично работает как есть.Изменение на DATE
потребует BY RANGE(TO_DAYS(...))
, который работает нормально.
Вы должны ограничить количество разделов до 50. (81, упомянутый выше, неплох). Проблема в том, что "много"из перегородок вводит разные неэффективности;«слишком мало» разделов приводит к «зачем»?
Обратите внимание, что почти всегда оптимальный PK отличается для многораздельной таблицы от эквивалентной однораздельной таблицы.
Обратите внимание, что я не согласен с разбиением на venue_id
, поскольку вместо этого легко разместить этот столбец в начале PK.
Анализ
Предполагается, что вы выполняете поискдля одного venue_id
и использования предложенного мной разбиения & PK, вот как работает SELECT
:
- Фильтр по диапазону дат.Это может ограничить активность одним разделом.
- Детализация BTree данных для этого одного раздела, чтобы найти один
venue_id
. - Хопскотч через данные оттуда, посадка на нужную
zone_ids
.
- Для каждого последующего фильтра на основе даты.