#mysql #sql #performance #query-optimization
#mysql #sql #Производительность #оптимизация запросов
Вопрос:
У меня есть страница на моем сайте, которая отслеживает количество людей, обращающихся к ней, в другой части я отображаю данные, содержащие информацию о пользователях, которые обращаются к этой странице, одновременно отображается только около 10.
Проблема в том, что мне нужно создать разбивку на страницы, поэтому мне нужно знать, сколько данных находится в моей таблице каждый раз, и это приводит к тому, что отображаемой странице требуется некоторое время для загрузки 2-3 секунды, иногда 7-10, потому что у меня миллионы записей. Мне интересно, как мне заставить эту страницу загружаться быстрее.
Select COUNT(*) as Count from visits
Комментарии:
1. У вас есть столбец автоматического увеличения? будете ли вы удалять записи из этой таблицы?
2. @Prdp У меня есть автоматическое увеличение, и я пока не удаляю данные, но я сделаю это в будущем
3. Спасибо @GordonLinoff
4. Какой движок БД используется? Решение зависит от того, является ли это MyISAM, InnoDB или что-то еще.
5. Если вы не удаляете, вы можете запустить это
select auto_increment_column from visits order by auto_increment_column desc Limit 1
Ответ №1:
Мой первый ответ . , , если вы просматриваете записи по 10 за раз, зачем вам общее количество записей больше миллиона?
Во-вторых, подсчет миллиона строк не должен занимать много времени, если только ваши строки не широкие (много столбцов или широкие столбцы). Если это так, то:
select count(id) from t;
может помочь, потому что он явно будет использовать индекс. Обратите внимание, что первый запуск может быть медленнее, чем последующие запуски из-за кэширования.
Если вы решите, что вам действительно нужно точное количество строк, то ваш единственный реальный вариант ускорить его с помощью MySQL — создать триггеры для поддержания количества в другой таблице. Однако это замедлит вставки и удаления, что может быть не очень хорошей идеей.
Комментарии:
1.
COUNT(id)
не может быть быстрее, чемCOUNT(*)
. Первый проверяет каждыйid
из них на наличиеNULL
. И в InnoDB это запрещает выбор меньшего индекса.2. @RickJames . , , Вы знаете, будет ли
COUNT(*)
выбран наименьший индекс для подсчета? Меня беспокоит то, что MySQL может читать страницы данныхCOUNT(*)
, а не просто читать страницы индекса.3. InnoDB может выбрать любой индекс для сканирования всей таблицы
COUNT(*)
. И, да, он выбирает «наименьший», хотя я не знаю, какую метрику он использует для «наименьшего». Не учитывается, кэшируется ли наименьший индекс в данный момент; это может измениться с версией 8.0.4. Если
id
равноPRIMARY KEY
, то этот индекс кластеризуется с данными, следовательно, `COUNT(id) вызывает полное сканирование таблицы.5. @RickJames . . . «Наименьшим» будет наименьшее количество индексных страниц. Я думал, что если записи будут широкими, то
count(*)
, как правило, будут прочитаны все записи. Тем не менее,count(othercol)
можно было бы обойтись простым чтением индекса дляothercol
— и это было бы экономией средств.
Ответ №2:
Лучший ответ — сказать «Около 1 234 000 посещений», а не точное число. Затем рассчитывайте его ежедневно (или что угодно еще).
Но если вам нужен точный подсчет, …
Если эта таблица «только для записи», то решение есть. Это включает в себя обработку его как таблицы «Фактов» в хранилище данных. Затем создайте и поддерживайте «Сводную таблицу» со строкой, скажем, для каждого часа. Затем COUNT
становится:
SELECT SUM(hourly_count) FROM SummaryTable;
Это будет намного быстрее, потому что для сканирования требуется гораздо меньше. Однако существует проблема в том, что он не включает в себя количество за последний (неполный) час. Но это можно решить, если вы используете INSERT ... ON DUPLICATE KEY UPDATE ...
для увеличения счетчика за текущий час или вставляете новую строку с «1».
Еще немного информации здесь .
Но прежде чем мы зайдем слишком далеко, пожалуйста, сообщите нам, как часто происходят новые «визиты».
Комментарии:
1. Извините за поздний ответ на ваш вопрос, по крайней мере, одно посещение происходит каждую секунду, в настоящее время в среднем 10 посещений в секунду
2. 10 / секунда = 8M / год. По мнению большинства людей, это не сильно отличается от 5M или 10M. Кроме того, за время, необходимое для вычисления и отображения точного количества, оно увеличится на 20-30. (Это аргументы против того, чтобы усердно работать над получением точного количества.)
3. Другой подход: раз в минуту вычисляйте точное количество и сохраняйте его в таблице с одной ячейкой для использования пользовательским интерфейсом. Округление до ближайшего 1000 при отображении; это даст пользователям понять, что оно может быть неточным.
Ответ №3:
Вы не можете ускорить выполнение этого запроса без изменения аппаратного обеспечения сервера или добавления дополнительных серверов для его параллельного выполнения. Во втором случае было бы лучше перейти к базе данных nosql.
Мой подход заключался бы в уменьшении количества записей. Это можно сделать, создав некоторую временную таблицу, в которую вы записываете журналы доступа за последний час / день, и по истечении этого времени запустите cronjob, который удаляет данные или перемещает их в другую таблицу для хранения журнала.
Комментарии:
1. Мне нужны все отзывы о действиях пользователей в реальном времени, я не могу использовать cronjob. Изменение баз данных в это время может быть не лучшим вариантом для нас.
Ответ №4:
Обычно вам не нужно знать точное количество строк для разбивки на страницы
SELECT COUNT(*) FROM
(SELECT TOP 10000 * FROM visits) as v
я бы сказал Вам, что там не менее 1000 страниц. В большинстве случаев вам не нужно знать больше.
Вы можете где-то сохранить общее количество и время от времени обновлять его, если вам нужна какая-то разумная оценка. Если вам нужно точное число, вы можете использовать триггер, чтобы сохранить его актуальным. Чем больше актуальной информации, тем, конечно, дороже.
Комментарии:
1. Это пахнет синтаксисом SQL Server, а не MySQL.
2. Вы правы, слишком грустно, что это не унифицировано. Вместо этого представьте ОГРАНИЧЕНИЕ.
Ответ №5:
Определите лимит (скажем, 1000 последних) с практической (бизнес-требования) точки зрения. Иметь индекс авто_инкремента (id) или временную метку (createdon). Захватите максимум 1000 записей
select count(*) from (select id from visits order by id desc limit 1000)
или возьмите все 1000 и посчитайте разбиение на страницы на стороне клиента (php) (как будто вы разбиваете mysql на страницы, он все равно будет просматривать эти записи):
select * from visits order by id desc limit 1000