#java #mysql #indexing #aggregate-functions
#java #mysql #индексирование #агрегатные функции
Вопрос:
Предыстория:
У меня есть схема Mysql со следующей структурой
@Data
public class DBQueryEvaluation {
//primary key
private final Long id;
//The combination of a 'query' and an 'evaluationId' will always be unique.
private final Long evaluationId;
private final String query;
private final Date createdAt;
private final Date updatedAt;
}
Ограничение:
Комбинация ‘query‘ и ‘evaluationId‘ всегда будет уникальной.
Для данного evaluationId может быть довольно много запросов. Всего в таблице 5 миллионов записей. (~ 50 000 запросов на evaluationId, при 100 таких оценках = 5 миллионов записей).
Цель:
Хочу выполнить подсчет (записей) для данного evaluationId.
Вопросы:
Учитывая, что мощность evaluationId довольно низкая (один и тот же evaluationId повторяется для ~ 50 тыс. записей) :
- Рекомендуется ли здесь индексировать ‘evaluationId’? Ожидаемая реализация BTree должна быть способна обеспечить подсчет в порядке миллисекунд. (< 10 мс)
- Каковы могут быть возможные недостатки, если таковые имеются, для индексации такого атрибута с низкой мощностью?
- Каковы были бы другие наилучшие подходы для получения count (*).
=== Обновление ===
- Я ожидаю полностью согласованного представления. Никаких приближений.
- Обновления могут быть применены поверх существующих строк.
Комментарии:
1. Данные записываются один раз? Затем рассмотрим сводную таблицу, подобную хранилищу данных
2. @Rick James : Данные записываются довольно часто.
3. Упс, я имел в виду «данные не изменяются после их записи».
4. Np .. это может быть изменено после написания. (Также я ожидаю полностью согласованного представления). Обновит сообщение
Ответ №1:
- Рекомендуется ли здесь индексировать ‘evaluationId’? Ожидаемая реализация BTree должна быть способна обеспечить подсчет в порядке миллисекунд. (< 10 мс)
Да, без индекса движок должен выполнить полное сканирование таблицы. Однако при использовании индекса ему не нужно будет обращаться к записям данных, но он может получить количество только из индекса. Для этого ему необходимо прочитать количество записей индекса, которое намного меньше, чем количество записей данных, поскольку:
- Конечные блоки индекса имеют несколько указателей на записи, которые считываются блоками.
- Количество дополнительных операций чтения блоков, необходимых для изоляции конечных блоков, которые относятся к идентификатору оценки, логарифмично общему количеству блоков.
Например, если размер блока равен 10, а 50 000 записей имеют идентификатор оценки, то необходимо прочитать около 5555 блоков. Сравните это по крайней мере с 500 000 блоками, которые необходимо прочитать при сканировании таблицы. Очевидно, что в базах данных есть методы оптимизации, которые усложнили бы справедливое сравнение, и поэтому имеет смысл просто попробовать это.
- Каковы могут быть возможные недостатки, если таковые имеются, для индексации такого атрибута с низкой мощностью?
Роль мощности зависит от того, сколько записей данных умещается в одном блоке (т.е. <= recordsize / размер блока). Если это число приближается к мощности, то преимущество индекса исчезнет.
- Каковы были бы другие наилучшие подходы для получения count (*).
Вы могли бы переоценить, насколько важно иметь точный подсчет, когда такие подсчеты составляют порядка 50 000, и зная, что в ту же секунду после получения подсчета, возможно, уже были новые вставки / удаления. Имеет ли значение, действительно ли это 49 756, а не 49 695?
Если приближение в порядке, то запустите запланированное пакетное задание, которое выполняет подсчет всех идентификаторов оценки и сохраняет это в отдельной таблице «count» (в которой будет около 100 записей). В зависимости от ваших потребностей, вы могли бы запланировать его выполнение один раз в день, в час, … в зависимости от оборота и необходимой точности. Тогда вы получаете молниеносную скорость ценой незначительной неточности.
Для повышения точности вы могли бы объединить приведенную выше таблицу «count» с триггером вставки / удаления в таблице данных, который вставил бы эффект этого изменения (в виде значения 1 или -1) в таблицу журнала. Затем запрос количества записей будет использовать таблицу «count» в качестве отправной точки и изменять результат на основе этих 1 / -1 в таблице журнала. Приведенное выше задание очищало бы журнал всякий раз, когда оно выполняется.
Комментарии:
1. Спасибо @trincot. Я искал полностью согласованное представление. Кроме того, был бы признателен, если бы вы могли объяснить, как вы пришли к 5 555. В моем понимании таким же должно быть значение log (база 2) 500000. «‘Например, если размер блока равен 10, а 50 000 записей имеют идентификатор оценки, тогда необходимо прочитать около 5555 блоков»‘
2. Как я пришел к 5,555: база журнала — это то, какова арность блоков B-дерева. Я посмотрел на пирамиду (поддерево) блоков в B-дереве, у которых есть конечные блоки с искомым идентификатором. Нижний уровень этой пирамиды будет содержать 5000 блоков, в каждом из которых по 10 записей (= 50 000 указателей на записи). На родительском уровне было бы в 10 раз меньше блоков (500), на уровне прародителя было бы 50 блоков … и т.д. Я игнорирую путь, необходимый для доступа к этой пирамиде, поскольку это составляет еще только два уровня (= еще 2 чтения). «Полностью согласованный вид»: вы видите несоответствие?
3. Спасибо. Под полностью согласованным представлением я имел в виду «без приближения» (в ответ на ваш комментарий «Имеет ли значение, действительно ли это 49 756, а не 49 695»)
4. Мне любопытно, какова бизнес-ценность такого строгого требования к количеству. В основном потому, что эта информация немедленно устаревает. Но в любом случае, тогда я бы посоветовал метод, описанный в последних абзацах моего ответа.
5. По моему опыту, типичная величина равна 100.