PHP - кэширование MySQL-запроса и его периодическое обновление. - PullRequest
0 голосов
/ 21 июня 2011

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

SELECT COUNT(*) FROM USERS

Это плохая идея - делать запрос COUNT (*) каждый раз, когда пользователь запрашивает страницу приветствия?

Было бы лучше кэшировать результат этого запроса, а затем использовать это кэшированное значение, чтобы показать его на странице приветствия, и обновлять значение, например, каждые 10 минут.

Спасибо!

Ответы [ 5 ]

3 голосов
/ 21 июня 2011

Я думаю, что хорошей идеей также является сохранение номера зарегистрированного пользователя на вашем сайте (или количества сообщений на форуме, записей в блогах, любого числа, которое вам нужно получить count(*)) в качестве счетчика в отдельном счетчике.table and increment - уменьшает счетчик всякий раз, когда пользователь регистрирует себя / отменяет себя с веб-сайта (например, с помощью триггеров).

COUNT (*) запросов могут быть довольно тяжелыми и, таким образом, вместо count(*) вымог бы сделать select counter from counter_table, что является довольно непосредственным* Агрегатная функция COUNT () и способы оптимизации запросов, использующих ее, вероятно, входят в десятку самых неправильно понимаемых тем в MySQL.Вы можете выполнить поиск в Интернете и найти больше дезинформации по этой теме, чем мы думаем.Прежде чем мы перейдем к оптимизации, важно понять, что на самом деле делает COUNT ().

Что делает COUNT ()

COUNT () - это специальная функция, которая работает двумя совершенно разными способами:он считает значения и строки.Значение является ненулевым выражением (NULL - это отсутствие значения).Если вы указываете имя столбца или другое выражение в скобках, COUNT () подсчитывает, сколько раз это выражение имеет значение.Это сбивает с толку многих людей, отчасти потому, что значения и NULL сбивают с толку.Если вам нужно узнать, как это работает в SQL, мы предлагаем хорошую книгу по основам SQL.(Интернет также не обязательно является хорошим источником точной информации по этой теме.) Другая форма COUNT () просто подсчитывает количество строк в результате.Это то, что делает MySQL, когда знает, что выражение внутри скобок никогда не может быть NULL.Наиболее очевидным примером является COUNT (*), который представляет собой специальную форму COUNT (), которая не расширяет подстановочный знак * до полного списка столбцов в таблице, как вы могли бы ожидать;вместо этого он полностью игнорирует столбцы и считает строки.Одна из наиболее распространенных ошибок, которые мы видим, - это указание имен столбцов в скобках, когда вы хотите посчитать строки.Если вы хотите узнать количество строк в результате, вы всегда должны использовать COUNT (*).Это четко сообщает о ваших намерениях и позволяет избежать низкой производительности.

Мифы о MyISAM

Распространенным заблуждением является то, что MyISAM чрезвычайно быстр для запросов COUNT ().Это быстро, но только для очень особого случая: COUNT () без предложения WHERE, которое просто подсчитывает количество строк во всей таблице.MySQL может оптимизировать это, потому что механизм хранения всегда знает, сколько строк в таблице.Если MySQL знает, что col никогда не может быть NULL, он также может оптимизировать выражение COUNT (col), преобразовав его в COUNT () для внутреннего использования.MyISAM не имеет никаких волшебных оптимизаций скорости для подсчета строк, когда в запросе есть предложение WHERE, или для более общего случая подсчета значений вместо строк.Это может быть быстрее, чем другие механизмы хранения для данного запроса, или это может быть не так.Это зависит от множества факторов.

Простые оптимизации

Иногда вы можете использовать оптимизацию COUNT () MyISAM, когда вы хотите сосчитать все, кроме очень небольшого количества строк.которые хорошо проиндексированы.В следующем примере используется стандартная база данных World, чтобы показать, как можно эффективно найти количество городов, чей ID больше 5. Вы можете написать этот запрос следующим образом: mysql> SELECT COUNT () FROM world.City WHERE ID>5;Если вы профилируете этот запрос с помощью SHOW STATUS, вы увидите, что он сканирует 4079 строк.Если вы отменяете условия и вычитаете количество городов, чьи идентификаторы меньше или равны 5, из общего числа городов, вы можете уменьшить его до пяти строк: mysql> SELECT (SELECT COUNT () FROMworld.City) - COUNT () -> ОТ world.City WHERE ID <= 5;Эта версия читает меньше строк, потому что подзапрос превращается в константу на этапе оптимизации запроса, как вы можете увидеть с помощью EXPLAIN: ... + ------ + ------------------------------ + |id |select_type |стол | ... |строки |Extra |... + ------ + ------------------------------ + |1 |ПЕРВИЧНЫЙ |Город | ... |6 |Используя где;Используя индекс ||2 |SUBQUERY |NULL | ... |NULL |Выберите таблицы оптимизированы прочь |... + ------ + ------------------------------ + Частый вопрос о списках рассылки и IRCКаналы - это то, как получить счетчики для нескольких различных значений в одном столбце одним запросом, чтобы уменьшить количество требуемых запросов.Например, скажем, вы хотите создать один запрос, который подсчитывает, сколько элементов имеют каждый из нескольких цветов.Вы не можете использовать ИЛИ (например, ВЫБЕРИТЕ СЧЕТЧИК (color = 'blue' ИЛИ ​​color = 'red') FROM items;), потому что это не разделит различные значения для разных цветов.И вы не можете поместить цвета в предложении WHERE (например, SELECT COUNT (*) FROM элементов WHERE color = 'blue' и color = 'red';), потому что цвета являются взаимоисключающими.Вот запрос, который решает эту проблему: mysql> SELECT SUM (IF (color = 'blue', 1, 0)) AS blue, SUM (IF (color = 'red', 1, 0)) -> AS red FROMПредметы;А вот другой эквивалентный, но вместо использования SUM () используется COUNT () и гарантирует, что выражения не будут иметь значений при ложных критериях: mysql> SELECT COUNT (color = 'blue' OR NULL) AS blue,COUNT (color = 'red' ИЛИ ​​NULL) -> КАК красный ОТ элементов;

Более сложные оптимизации

В общем, запросы COUNT () сложно оптимизировать, так как обычно им нужно подсчитатьмного строк (то есть доступ к большому количеству данных).Единственный другой способ оптимизации внутри самого MySQL - это использование индекса покрытия, который мы обсуждали в главе 3. Если это не поможет, вам нужно внести изменения в архитектуру вашего приложения.Рассмотрим сводные таблицы (также описанные в главе 3) и, возможно, внешнюю систему кэширования, такую ​​как memcached.Вы, вероятно, окажетесь перед знакомой дилеммой: «Быстро, точно и просто: выберите любые два».

2 голосов
/ 21 июня 2011

В MyISAM это значение уже кэшировано.
Так что вам не нужно снова его кэшировать.

0 голосов
/ 21 июня 2011

Два предложения:

A. Вместо COUNT(*) сделайте COUNT(1). Это намного быстрее.

B. Используйте кеширование, например, модуль Pear Cache. Идея состоит в том, чтобы кэшировать количество пользователей в файле в файловой системе.

Когда читать из кеша : При чтении количества пользователей на странице приветствия проверьте, существует ли оно уже в кеше; если найден прочитанным, запустите запрос к БД.

Когда создавать кеш : каждый раз, когда счетчик извлекается из базы данных, кеш создается и счет сохраняется в нем.

Когда аннулировать кеш : всякий раз, когда учетная запись пользователя создается или уничтожается, кеш аннулируется (удаляется). Поэтому в следующий раз, когда какой-либо пользователь попадет на страницу приветствия, будет запущен запрос БД и кэш будет восстановлен. Все последующие запросы будут считываться из кэша, если другой пользователь не создан или не уничтожен.

Полагаю, вам может понадобиться чтение Pear Cache, но оно простое и мощное.

Надеюсь, это поможет.

0 голосов
/ 21 июня 2011

Если вы не используете механизмы кэширования, такие как APC, xcache или Memcache, вам не нужно кэшировать это значение.

Размер всей таблицы - быстро доступная вещь.

Используйте xdebug для профилирования своего приложения и найдите real узких мест.

0 голосов
/ 21 июня 2011

Если вы не ожидаете, что счет резко изменится или каким-либо образом повлияет на каждого пользователя, вы можете получить реальную экономию, сохраняя COUNT(*) в $_SESSION для каждого пользователя. Тогда каждый запрашивает это только один раз. Это действительно вопрос того, насколько важны своевременность этих данных для ваших пользователей и действительно ли они вызывают у вас проблемы.

Тем не менее, если он в настоящее время масштабируется так, как вам нужно, то это не повредит каждый раз вызывать вызов базы данных. Если у вас начнутся проблемы с масштабируемостью, вы можете рассмотреть это как возможное действие.

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