COUNT (*) или MAX (id) - что быстрее? - PullRequest
17 голосов
/ 08 апреля 2019

У меня есть веб-сервер, на котором я внедрил собственную систему обмена сообщениями. Я нахожусь на этапе, когда мне нужно создать API, который проверяет, есть ли у пользователя новые сообщения.

Моя таблица БД проста:

ID - Auto Increment, Primary Key (Bigint)
Sender - Varchar (32) // Foreign Key to UserID hash from Users DB Table
Recipient - Varchar (32) // Foreign Key to UserID hash from Users DB Table
Message - Varchar (256) //UTF8 BIN

Я рассматриваю возможность создания API, который будет оценивать наличие новых сообщений для данного пользователя. Я думаю об использовании одного из этих методов:

A) Выберите count(*) сообщений, отправителем или получателем которых является я.
(если этот номер> предыдущий номер, у меня новое сообщение)

B) Выберите max(ID) сообщений, отправителем или получателем которых является я.
(если max (ID)> чем предыдущий номер, у меня новое сообщение)

У меня вопрос: можно ли как-нибудь рассчитать, какой метод потребляет меньше ресурсов сервера? Или есть какая-то статья? Может быть, другой метод, который я не упомянул?

Ответы [ 4 ]

16 голосов
/ 08 апреля 2019

В MySQL InnoDB, SELECT COUNT(*) WHERE secondary_index = ? - дорогостоящая операция, и когда у пользователя много сообщений, этот запрос может занять много времени. Даже при использовании индекса движок все равно должен считать все соответствующие записи. Производительность будет ухудшаться с ростом общего количества сообщений.

С другой стороны, SELECT MAX(id) WHERE secondary_index = ? может очень эффективно доставить самый высокий идентификатор в этом индексе, выполнив так называемое сканирование свободного индекса . Спектакль останется почти постоянным.

Если вы хотите понять, почему, рассмотрите возможность поиска структуры данных B + Tree , которую InnoDB использует для организации своих данных.

Я предлагаю вам перейти с SELECT MAX(id), если требуется только проверить, есть ли новые сообщения (а не их количество).

Кроме того, если вы полагаетесь на количество сообщений, вы можете открыть пробел для условий гонки. Что если пользователь удалит сообщение и получит новое между двумя интервалами опроса?

4 голосов
/ 08 апреля 2019

Чтобы получить информацию о том, что у кого-то есть новые сообщения - сделайте именно это.Обновите поле в таблице users (я предполагаю, что это имя), когда в системе записано новое сообщение.У вас есть идентификатор получателя, это все, что вам нужно.Вы можете создать триггер after insert (при условии, что есть таблица users2messages), который обновляет таблицу пользователей с логическим флагом, указывающим на наличие сообщения.

Этот подход намного быстрее, чем подсчет индексов, будь индекс первичнымили вторичный.Когда пользователь выполняет действие, вы можете обновить таблицу users с помощью has_messages = 0, а при получении нового сообщения вы обновите таблицу с помощью has_messages = 1.Это просто, это работает, оно масштабируется, а использование триггеров для его обслуживания делает его простым и понятным.Я уверен, что найдутся недоговорки, которым не нравятся триггеры, вы можете сделать это вручную, связав пользователя с новым сообщением.

0 голосов
/ 09 апреля 2019

@ FeHora Вы говорите о неиспользовании ключей для экономии места в БД.Таблицы проектируют трату больше места в БД.

ID - Auto Increment, Primary Key (Bigint)

Действительно ли bigint действительно необходимо?Предположим, сообщение отправляется каждую секунду.А int unsigned хватает на 126 лет.И если у вас действительно так много сообщений, ключ обязателен.

Sender - Varchar (32) // Foreign Key to UserID hash from Users DB Table
Recipient - Varchar (32) // Foreign Key to UserID hash from Users DB Table

Почему бы не использовать UserID (обычно int unsigned).

Тогда я бы добавил видимыйфлаги.Кстати, вы можете добавить для всех поданных атрибутов not null.

seen tinyint not NULL.

Не в последнюю очередь я рекомендую вариант @Mjh: определить флаг has_messages, или new_messages, или оба взапись пользователя.Обычно пользовательская запись загружается, поэтому она НЕ является дополнительным запросом к базе данных.

0 голосов
/ 09 апреля 2019

Если вам нужно узнать количество новых сообщений, тогда лучше использовать Select count(*) from Messages where user_id in (sender, recipient) and id > last_seen_id.

Я фанат использования exists, где это возможно, поэтому, чтобы определить, если есть новыесообщения, мой запрос будет Select exists(Select 1 from Messages where user_id in (sender, recipient) and id > last_seen_id).Преимущество существует в том, что как только он находит 1 запись, он возвращает true.

Редактировать: чтобы избежать путаницы при чтении этого ответа, оба эти запроса также будут включать проверку для other_user_id in (sender, recipient) вчтобы вернуть сообщения только между двумя конкретными пользователями.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...