Pull to refresh

Comments 60

MySQL определяет, равны ли первые три символа запроса «SEL»

— запросы, в начале которых есть пробел или написан комментарий никогда не будут браться из кеша


Не расскажете откуда такая информация? Первый раз про такое слышу.
Даже проверил на всякий случай — у меня на сервере это утверждение не соответствует действительности, по крайней мере запрос

/* test */ SELECT * FROM `prod` WHERE prodname LIKE 'пиво%' LIMIT 10


благополучно забирается из кэша
хм… возможно mysql умнее чем вы думаете: он вырезает комментарии и обрамляющие пробелы в запросах.
хотя это всего лишь мои домыслы:)
Возможно. А еще это может делать клиент, скорее всего в нем и дело.
Действительно странное дело. Информация эта была в выступлении Baron Schwartz'а на одной из конференций и нескольких других публикациях.
Сам проверил сейчас — тоже работает все как нужно. Но это притоворечит логике — MySQL смотрит в кеш ДО парсинга запроса. Возможно, что это проделки клиента. Как выясню точно, напишу.
Проверил. В бинлог валится полный запрос вместе с начальным комментарием. Так что сомнения остаются и клиент ничего не делает (и правильно что не делает).

Насчет логики, тут может быть все проще. Если упрощенно, то при поступлении запроса он очищается от конечных проблелов, потом от него берется какой либо ключ, допустим MD5, смотрится есть ли такой ключ в кэше, если есть — отдаем результат, если нет — процессим запрос (проверяем что он select, что нет now() etc), вычисляем, кладем в кэш вместе с ключем. Как то так. По крайней мере я был уверен что mysql именно так и делает :)
не бинлог, а query-лог, конечно
Заглянул в код. В коде все по написаному:
/*
Test if the query is a SELECT
(pre-space is removed in dispatch_command)

First '/' looks like comment before command it is not
frequently appeared in real life, consequently we can
check all such queries, too.
*/

if ((my_toupper(system_charset_info, sql[i]) != 'S' ||
my_toupper(system_charset_info, sql[i + 1]) != 'E' ||
my_toupper(system_charset_info, sql[i + 2]) != 'L') &&
sql[i] != '/')
{
DBUG_PRINT(«qcache», («The statement is not a SELECT; Not cached»));
goto err;
}

При выборке из кеша идет именно проверка на эквивалентность первых символов запроса строке «SEL». Так что, все-таки — это происки клиента и комментарии в начале запроса писать не стоит.
Может у меня глаза уже замылилсь, но тут вроде написано, что
если первые три буквы не SEL и первый символ не '/', то «The statement is not a SELECT; Not cached».

Т. е. если первый символ '/' — то видимо обработка запроса где то дальше все таки произойдет, разве нет?

В моем вольном переводе —
начальный '/' указывающий на комментарий перед запросом не часто встречается в реальной жизни, поэтому мы проверяем все такие запросы тоже.


Вообще стиль программирования — высший класс, один «goto err» чего стоит :)
кстати и про начальный пробел тоже явно указано — что он предварительно удаляется (а вы в статье пишете об обратном — что он влияет)
На самом деле Вы абсолютно правы.
Такое поведение, которое описывает автор было характерно для MySQL версии до 4.1

Сейчас проблема с предшествующими комментариями в SQL запросах исправлена, MySQL парсер делает trim перед тем как класть/проверять наличие запроса в кеше. Также проблемы с комментариями внутри запроса также нет, т. к. они вырезаются парсером. Исходя из этого следует, что даже если в теле одного и того же запроса присутствуют разные комментарии, то это не повлияет на кеш попадание, т. к. и в первом и во втором случае они будут удалены из тела запроса.

Чтобы не быть голословным, нашел линку, доказывающую мои слова — www.mysqlperformanceblog.com/2008/03/20/mysql-query-cache-whitespace-and-comments/
Ну вот, только мне пришла в голову мысль, что мы с комрадом tuta_larsen правы оба, и дело в версиях mysql и вы это подтвердили.
Спасибо.

Статью бы поправить (поставить ссылку на версию), а то хорошая же статья, а начинается с неактуальной для многих информации
На самом деле тут нужно еще кое-что про кеш добавить:

Во-вернвых, и это очень важно — MySQL никогда не кеширует подзапросы, это, на самом деле, следствие всего вышесказанного, но когда вы используете derived table, и допустив где-то в кеше у вас уже есть такой закешированный запрос, то результат выполнения этого запроса не будет браться из кеша, т. е.

SELECT
p.*, c.*
FROM
(
SELECT
*
FROM
`products` as p
WHERE
p.category = 10
ORDER BY
p.price DESC
LIMIT 100, 10
) as p
JOIN customer as c
ON p.cus_id = c.id

И у вас в кеше есть результат для запроса
SELECT
*
FROM
`products` as p
WHERE
p.category = 10
ORDER BY
p.price DESC
LIMIT 100, 10

то, здесь для выполнения подзапроса кеш использоваться не будет.

Ну и второе, я, конечно, может быть придираюсь…
>Например, есть таблица статей, в которой также хранятся и количество комментариев к ней. >Посколько количество комментариев изменяется часто, а сама статья практически не меняется >имеет смысл вынести количество комментариев в отдельную таблицу, позволяя при этом избегать >частой инвалидации кеша статей.

А что Вы где-то видели, что комментарии к статье и тело статьи хранят в одной таблице? Если кто-то так делает, то нужно начинать писать статьи не с темы кеширования запросов, а с темы нормализации БД. :-)

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

Ну да и последнее, что хотел добавить, и что обязательно нужно сказать — кеш мускула это не «серебрянная пуля» и не стоит на него очень сильно расчитывать, точнее, при правильной стратегии кеширования и конфигурационных параметров вы можете получить прирост производительности, но это не панацея.
Также нужно всегда учитывать, что кеш мускула это централизованное хранилище, а не распределенное, т. е. на каждом сервере БД, свой кеш.
Притом большой кеш мускула и большое кол-во запросов в нем, могут достаточно серьезно замедлить опперации на изменение данных (UPDATE/INSERT/DELETE)

Поэтому использования Memcached вам как-правило не избежать :-)

А MySQL кещ всего лишь один из уровней многоуровневого кеширования в веб приложениях.
А можно подробнее про «Притом большой кеш мускула и большое кол-во запросов в нем, могут достаточно серьезно замедлить опперации на изменение данных (UPDATE/INSERT/DELETE)»?

Насколько я понимаю, удалить запись из кеша на запрос — это операция сложности O(1).
> А что Вы где-то видели, что комментарии к статье и тело статьи хранят в одной таблице?
Я видел, когда вместе со статьями хранят счетчики комментов к ней (об этом и написано).
Замечательная статья на русском языке, очень странно, что разработчики популярных движков (CMS) совершенно об этом забывают.
Потому постоянно встречаю в одной таблице и кол-во просмотров (то есть кеш запроса вообще отсутствует) и сумма рейтинга статьи, и кол-во комментариев, etc. Исправить, конечно, можно, но тогда и забываем об обновлении используемой системы.
Не пойму смысла выносить в отдельную таблицу post_id | comments_count | rating_points | что бы потом при показе поста объединять 2 таблицы…

Или я чего то не понимаю?
когда на каждый просмотр страницы идет запрос «UPDATE article SET count_view = новое значение» — то кеширование запроса, который собственно выбирает текущую статью для просмотра пользователем — сбрасывается. Кеширования данных отсутствует как факт. Да и при некотором кол-ве посетителей нагрузка при JOIN comments_count (к примеру) будет лучше, нежели отсутствия кеша на глобальную таблицу article
А вы когда джойните вторую таблицу, но она была измененена, тоесть при многотаблицном селекте, когда одна (все) таблицы были изменены запрос разве закешируется?
Признаю свою ошибку, действительно, если хоть в одной таблице кешированного запроса что-либо меняется — кеш сбрасывается.
Тогда имеет смысл рассмотреть два запроса (один для articles+кеш, второй только count), но по поводу производительности не скажу — надо будет потестировать на нагруженных системах
UFO just landed and posted this here
UFO just landed and posted this here
UFO just landed and posted this here
P.S. пишите в личку темы статей про MySQL, которые вы хотели бы прочитать.


Разрешения спросить? :-)
UFO just landed and posted this here
UFO just landed and posted this here
«Многие СУБД имеют подобную функциональность, но в отличие от MySQL они кешируют планы выполнения запросов, тогда как MySQL кеширует результаты запросов.»

Что-то как-то тут не срастается. Я про Oracle. Я думаю, что негоже писать о том, в чем не разбираешся.
В чем не срастается-то? «Многие» != «все»
В общем конечно справедливо, но для меня есть RDBMS Oracle, ну и всякие прочие… поделки. :)
А в чем соль статьи?

Или это просто перевод?

Если перевод, то где значок перевода?

Я думал, тут что-то оригинальное…
Стало интересно насколько часто данные берутся из кеша у меня, запустил Putty:

[root@v5311 ~]# mysql -u root -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 238460
Server version: 5.0.45 Source distribution

Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

mysql> SHOW GLOBAL STATUS LIKE 'Qcache%';
+-------------------------+-------+
| Variable_name           | Value |
+-------------------------+-------+
| Qcache_free_blocks      | 0     |
| Qcache_free_memory      | 0     |
| Qcache_hits             | 0     |
| Qcache_inserts          | 0     |
| Qcache_lowmem_prunes    | 0     |
| Qcache_not_cached       | 0     |
| Qcache_queries_in_cache | 0     |
| Qcache_total_blocks     | 0     |
+-------------------------+-------+
8 rows in set (0.00 sec)

Судя по всему кеш не работает. Подскажите, где и как его можно «включить».
UFO just landed and posted this here
У меня такая же табличка по нулям, но кэш вроде как работает, потому что первый тяжелый запрос выполяняется за 10с, последующие такие же за 0.01
UFO just landed and posted this here
fulltext поиск по 2 полям из ~2млн записей
UFO just landed and posted this here
Просто первый раз mysql засасывает в буфер (в ОЗУ) страницы с диска, сколько может, Второй раз уже «бежит» по тем страницам, что попали в буфер. А еще и операционка кеширует файлы.
перечитайте статью, там написано :)
query_cache_type = (ON, DEMAND, OFF) — определяет включено ли кеширование или нет(ON, OFF). При использовании DEMAND кешироваться будут только запросы, в которых есть директива SQL_CACHE;

т. е.

query_cache_type = 1
у вас написанно в my.cnf?
UFO just landed and posted this here
а что такое Com_select? Первый раз встречается в формуле расчета эффективности, больше нигде не упоминается
Количество SELECT-запросов надо полагать
show global status like 'Com_select'; — показыват сколько запросов было непосредственно выполнено сервером, а не взято из кеша
Спасибо, теперь понятно.
У мена получилось 34%, лучше чем ожидалось
Кэш запросов mysql живет своей жизнью, и, надеяться, как-то его переборость — бессмысленно. Удобнее кэшировать именно то, что нужно на уровне приложения или memchache. В таком случае, зная специфику обновления данных, можно гораздо точнее рулить кэшированием.
Сегодня с утра экспериментирую с кешем, по статье прирост страничка грузиться на от 0.5-1 сек. быстрее
+-------------------------+-----------+
| Variable_name           | Value     |
+-------------------------+-----------+
| Qcache_free_blocks      | 309       |
| Qcache_free_memory      | 270714864 |
| Qcache_hits             | 261275    |
| Qcache_inserts          | 255794    |
| Qcache_lowmem_prunes    | 0         |
| Qcache_not_cached       | 1149609   |
| Qcache_queries_in_cache | 577       |
| Qcache_total_blocks     | 1551      |
+-------------------------+-----------+
8 rows in set (0.00 sec)


вроде все понятно, но возможно будут какие-то коментарии?
смущает очень большое количество Qcache_not_cached и большое кол-во незанятой памяти
Хм, значит нет необходимости кэшировать запросы на уровне веб-приложения?
Вы не можете гарантировать, что запрос не будет вытеснен из кеша, тогда как на уровне приложения можете. Если это тяжелая выборка, и вы хотите, чтобы результат был в обязательно в кеше, то кешируйте на стороне приложения.
звучит вполне убедительно, спасибо
Одна из самых бесполезных вещей в мускуле. Вроде бы даже слышал что ее уберут в 6 или 7.
Для проектов с большой нагрузкой мускуль кеш использовать бессполезно, ООЧЕНЬ не эффективно. Для остальных особой разницы и востребованности нет.
Не хотите обосновать данное утверждение?
Хотя бы по тому, что они достаточно глупые. Пример, мы в селекте выбираем А, создался кеш, потом пришел запрос на изменение Б(таже таблица), кеш селекта А испарился. В подавляющем большинстве проектов, высоконагруженых, такая инвалидация кеша не приемлима. Как Вы понимаете, все это можно и нужно делать гораздо умнее.
не убедили. разные задачи — разные решения.
даже не пытался, если у вас получиться на этой штуке что-нить построить, флаг, как говориться, в руки
Одна из самых бесполезных вещей в мускуле. Вроде бы даже слышал что ее уберут в 6 или 7.
Для проектов с большой нагрузкой мускуль кеш использовать бессполезно, ООЧЕНЬ не эффективно. Для остальных особой разницы и востребованности нет.
query_cache_limit — размер максимальной выборки, хранимой в кеше.

А существует ли нижний порог? Или в кеш попадают любые резалт-сеты, пусть даже состоящие из 1 строки длиной в 1 символ?
Sign up to leave a comment.

Articles