Оптимизация MySQL запросов. Объединение DDL запросов

  • Разместил Николай Коротков
  • Дата: 8 декабря 2012 в 14:04

Для чего все это нужно? На что влияет? Как воплотить в реальность? На все эти вопросы я постараюсь дать четкий ответ в этом посте!

А теперь небольшая предыстория. В общем, недавно получил письмо на свой e-mail адрес, следующего содержания:

В течение последних 3 дней средний уровень нагрузки, создаваемый Вашим аккаунтом ******* , составил 119% от допустимого уровня Вашего тарифного плана. Мы рекомендуем Вам перейти на тарифы VPS. Обращаем Ваше внимание, что в случае регулярного превышения лимитов, мы оставляем за собой право заблокировать Ваш аккаунт согласно пункту Договора...

Оба на, приплыли — подумал я в тот момент! Согласитесь, не очень приятно получать такие письма. А так как с подобного рода проблемой я столкнулся впервые, представляете, в каком я был недоумении? Моему возмущению не было предела! Какой нафиг VPS? Я можно сказать только обжился на одном тарифе, а мне тут предлагают перейти на виртуальный хостинг, который в три раза дороже. Ну нет уж ребята, — думаю я, — еще рановато.

Пишу в обратку письмо моему хостеру, с просьбой пояснить мне, с какого это перепуга у меня зашкаливает нагрузка? Ведь моему блогу всего-то два с небольшим месяца от роду. Да и посещаемость не велика. В общем, пишу, что категорически против переходить на VPS, считаю, что это не целесообразно на столь раннем этапе развития ресурса и прошу указать мне на мои ошибки, что с ними делать и как в дальнейшем их контролировать!

В ответ получаю следующее:

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

Ну спасибо за разъяснения, — думаю про себя. Пойду изучать проблему. Набрав в интернете запрос «как снизить нагрузку на хостинг» понял, что я не один такой, а на самом деле проблема довольно актуальная. И рано или поздно коснется многих. Ознакомившись более детально с проблемой, понял, что у меня есть два выхода из данной ситуации:

  1. Обратиться за помощью к профессионалам (фрилансерам), заплатив им определенную сумму денег, что всегда успеется.
  2. Постараться устранить проблему самому.

Так вот, я выбрал второй вариант и скажу вам честно, пока что ни на грамм не пожалел. Мне удалось снизить нагрузку на хостинг в два-три раза. Вот посмотрите сами:

Разница, на лицо! Сейчас я вам покажу и расскажу, что я для этого сделал:

— оптимизировал базу данных mysql, что существенно отразилось на снижении нагрузки на хостинг и ускорении wordpress;
— избавился от порядка 8 ненужных плагинов.
— ускорил wordpress, отредактировав несколько файлов темы своего блога.

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

В общем, материал, который вы почерпнете из этих трех постов, будет ну просто обалденным. Не пропустите, !

Оптимизация базы данных

Прежде чем вы начнете производить различные действия с базой данных, обязательно делайте резервную копию . Чтобы в случае возникновения проблем можно было все быстренько восстановить. База данных содержит всю историю вашего ресурса, в ней хранятся все записи, присутствующие на вашем блоге! А вообще, советую вам взять за правило сохранять базу данных каждый день! Это у вас займет буквально 1 минуту, но зато вы будете всегда спать спокойно. Сами понимаете может случится всякое.

1. Делаем резервную копию базы данных

Для удобства соединения с сервером и обработки данных я пользуюсь . Очень классная штука, как-нибудь напишу об этом клиенте отдельный пост, . В общем, вам нужно перейти на свой сервер и найти в нем вкладку «Базы данных» или «Базы данных MySQL», что-то в этом роде. На каждом сервере база данных есть, при переходе сервер может запросить пароль. Он у вас должен быть. При покупке хостинга пароль предоставляется.

В итоге вы должны оказаться вот на такой странице, phpMyAdmin:

Заходите в базу данных, кликнув по ее названию. Перед вами откроется таблица базы данных (кликните для увеличения):

Нажимаете «Экспорт» и «ОК». Сохраняете на своем ПК. Все, база данных сохранена, теперь можем приступать к ее оптимизации. Обратите внимание, если на вашем хостинге присутствует поле «Сохранить как файл» не забудьте напротив него поставить галочку! А также запомните, сколько весит в данный момент ваша база данных, а потом посмотрите сколько она будет весить после оптимизации.

У меня она весила до оптимизации 26 Mb — это УЖАС, а что сейчас? А сейчас она весит всего 2 Mb! Представляете, сколько всякого ненужного хлама она содержала в себе? Представляете, какую нагрузку она создавала на сервере? После оптимизации базы данных, мой блог стал летать, как реактивный самолет! В общем, после того как вы проделаете все ниже описанные действия, вы почувствуете существенную разницу!

2. Отключаем ревизии постов и устанавливаем минимальный срок хранения удаленных файлов в корзине

Что такое ревизия постов? Когда вы пишите пост в блог, wordpress автоматически, через определенный промежуток времени, сохраняет резервную копию каждого поста в базе данных, в общем, делает авто сохранение. А теперь представьте когда вы напишите 50 постов на блоге? Сколько копий постов у вас будет сохранено? Это ЖЕСТЬ! Пока вы пишите пост, у вас уже как минимум проходит 10 авто сохранений!

Плюс ко всему этому, если вы удаляете файлы, они у вас скапливаются в корзине, что также нагружает базу данных. Конечно, хорошо если вы сразу удалите файл и из корзины, но частенько случается, что многие про это забываю, а некоторые просто забивают! А это ой как не хорошо... База все растет, нагрузка на сервер все больше и больше, блог грузится все медленнее и медленнее... Вы задумывались, к каким последствиям это может привести?

Вот основная часть последствий, но далеко не всех: снижение , частые отказы, ухудшение , понижение позиций в выдаче поисковиков... А дальше, автор в подает в отчаяние от не оправданных ожиданий. Желание вести блог со временем пропадает и все! КРАХ!

Все это я к чему говорю? За базой данных постоянно нужно следить и содержать ее в надлежащем состоянии. Поймите, база данных — это как сердце блога. При постоянной нагрузке на сердце не нужным хламом, со временем оно не выдержит и ОСТАНОВИТСЯ! Я думаю, вы меня поняли? Поэтому хватит ужастиков и переходим к оптимизации базы данных.

Итак, открываем файл wp-config.php, он находится в корне вашего блога, т.е. ваш хостинг/httpdocs или public_html (в зависимости от хостинга)/wp-config.php. И вставляем в него две строчки:

1 2 define ("WP_POST_REVISIONS" , false) ; define ("EMPTY_TRASH_DAYS" , 1 ) ;

Строка №1 отключает ревизию постов, строка №2 означает, сколько дней будут храниться удаленные файлы в вашей корзине. Как видите, я поставил «1», можно конечно поставить и «0», но если вдруг по неосторожности у вас дрогнет рука и вы нажмете на ссылку «удалить», все — КАПЕЦ!

А после просиживания за компом 5-8 часов, поверьте мне, это возможно! Так что я предпочитаю оставить циферку «1». Конечно, после удаления файла лучше сразу же почистить корзину вручную, но если даже вы забудете это сделать, спустя сутки файл из корзины автоматически удалится! Вот как это выглядит у меня:

3. Удаляем ревизии постов

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

Переходим снова в базу данных MySQL, как описано в первом пункте. Заходим во вкладку SQL, вставляем в поле скопированную строчку и нажимаем «ОК»:

База данных спросит:

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

Я делал чистку 3 дня назад, поэтому у меня она еще не обросла ревизиями. Когда я первый раз почистил базу, у меня было удалено аж 1800 с чем-то строк! Представляете, сколько копий ненужных постов в ней хранилось? Идем дальше.

4. Оптимизируем записи в wp-post

Папка wp-post содержит все записи блога. Точно так же как и в предыдущем пункте, копируем строку:

OPTIMIZE TABLE wp_posts;

И вставляем в поле SQL запроса. Нажимаем «ОК», смотрим:

Все, запрос выполнен!

5. Чистим wp-postmeta

Что именно будем чистить? Папка wp-postmeta содержит в себе:

— время последнего редактирования какого-либо из постов. Значения никакого не имеет, а нагрузку на сервер, какую никакую, а дает;
— содержание предыдущего (человека понятного урла). Если вы когда-нибудь меняли постоянную ссылку в любом посте. То при смене ее, она не удаляется, а оседает в папке wp-postmeta и нагружает вашу базу.

Делаем все тоже самое, копируем вот этот код:

Вставляем его в поле запроса SQL, и жмем «ОК». Смотрим на результат:

6. Удаляем спам-комментарии

Делается аналогично, копируем код:

Вставляем в поле SQL запроса, жмем «ОК», смотрим результат:

Как вы видите «0». После выполнения этого запроса, вы забудете про спам комментарии!

7. Удаляем пингбеки

Пингбеки — это уведомления о том, что на ваш пост или страницу кто-то ссылается. Нам это не нужно, лишняя нагрузка! Удаляем!

8. Отключаем пингбеки

Из прошлого пункта мы выяснили, что пингбеки не несут никакой пользы для нашего ресурса, а только его засоряют. Поэтому давайте их и вовсе отключим. Копируем этот код:

UPDATE wp_posts p SET p. ping_status = "closed"

Ну как вам такая чистка? Понравилась? А теперь посмотрите, сколько стала весить ваша база данных после ее оптимизации? Заметно уменьшился размер? А я вам говорил! Посмотрите, как стал грузиться ваш блог! Он должен летать! Но и это еще не все на сегодня. Сейчас мы рассмотрим еще один последний пункт, который также существенно улучшит оптимизацию.

9. Устанавливаем плагин Optimize DB

Об этом плагине я уже вкратце упоминал . Ну давайте более подробно рассмотрим, как им пользоваться. Данный плагин, как вы уже догадались, способствует оптимизации базы данных! Скачайте архив с плагином себе на ПК, вот и активируйте его:

Все, ваша база данных оптимизирована дополнительно при помощи плагина:

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

А в следующей части поста, я вам покажу, как заменить часть не ненужных плагинов кодами. Обязательно , чтобы ничего не пропустить. Это будет мощный пост, после которого ваш сервер будет легок как пушинка!

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

И на последок порция приколов:

Ну как вам статья? Я уверен, что вы останетесь довольны после ее прочтения и проделанных рекомендаций со своим ресурсом! Жду ваших комментариев!

Понравилась статья? Поделись с друзьями!

Каждому комментатору книга в подарок!

Книга включает в себя подробное описание самых эффективных методов продвижения вашего ресурса!


    60 комментариев

  1. Александр 8 декабря 2012 15:18

    А я знаю почему у Тебя нагрузка так выросла. Просто я тут у Тебя прижился, и постоянно что то изучаю. А что делать если инфа здесь классная. А если серьезно, то все вышеперечисленные советы рекомендую сделать всем блоговодам в первую очередь. Я это давно сделал, поэтому сплю спокойно. И еще, плагин Optimize DB, это вообще обязательный атрибут любого Блога. Спасибо Коля, как всегда, все полезно и актуально. А вот следующий пост вообще жду с нетерпением. Так что давай, пиши

  2. 9 декабря 2012 16:19

    Я в базе данных ковыряться побаиваюсь, но после установки и чистки плагином WP-Cleanup она у меня уменьшилась с почти 50 до 7Mb. Блог действительно стал грузиться намного быстрее.

  3. 9 декабря 2012 20:39

    Строго говоря, спрашивает при операциях с базой данных не сама БД (СУБД вообще все действия одинаковы, ничего не спрашивает), а клиент, phpMySql.

    Касательно пингбэков, «Из прошлого пункта мы выяснили, что пингбеки не несут никакой пользы для нашего ресурса, а только его засоряют.» — строго говоря, ничего не выяснили.

    Вы просто сказали, не аргументируя, что они не нужны, вот и всё. На самом деле, польза от них вполне может быть, просто употреблять этот инструмент нужно по назначению. Например, ключевое слово «семантическая сеть» вам говорит что-нибудь?

  4. 10 декабря 2012 08:36
  5. Юрий 16 декабря 2012 23:49

    Привет, дружище!

    Твой пост и в самом деле классный. В Интернете столько много бредни написано, что информацию приходится искать по крупицам. А здесь я зашел, и на тебе, все доходчиво и понятно. У меня как раз началась проблема с нагрузкой на сервер. Еще советую установить плагин WP Super Cache. Только его нужно грамотно настроить. Классный плагин! Может у тебя в остальных постах о нем что-то и сказано, но я еще не читал. Спешу перейти ко второй части оптимизации. Удачи тебе и твоему блогу

  6. 25 декабря 2012 11:40
  7. 28 января 2013 11:24

    Добрый день! Очень интересно, а как быть мне с блогом на Blogger? Все плагины для WP не годятся для Блогспот, нужно искать методы оптимизации самостоятельно в инете.

    С уважением, Вадим.

  8. Антон 2 апреля 2013 20:34

    Спасибо, пост действительно добротный. У меня, кстати, после проделывания пункта №3 — «Удалено 4145 строк. (Запрос занял 7.0269 сек.)»

  9. 14 июля 2013 19:04

    Интересно, а как-то можно почистить базу от старых плагинов? наверняка там от них тоже следы какие-то остались?

  10. 14 июля 2013 19:06

    Вдогонку: а еще очень похоже на ваш текст вот тут dayafternight.ru/wordpress/baza-dannih-mysql-optimizacia

  11. 12 сентября 2013 12:57

    Спасибо Николай, нужная вещь.

    Все доступно и понятно написано.

    А статья про коды уже вышла?

  12. 12 сентября 2013 13:05

    Николай забыла спросить, подскажите пожалуйста. Когда делала оптимизацию обнаружила у себя в PhpMyadmin новую базу данных information_schema

    Подскажите откуда она могла появиться?

    в последнее время только код яндекс-метрики вставляла.

    Наталья Гегер

    Не обращайте на это внимание... На большинстве современных серверов она есть! Связано это с выходом MySQL версии 5.0 и выше...

    INFORMATION_SCHEMA — это виртуальная база, которая формируется во время запуска сервера и содержит в себе метаданные всех баз данных, т.е. информацию о структуре баз данных. Доступна она только для чтения.

  13. 27 октября 2013 01:06

    ох, почистил базу по вашему методу + от себя ручками, результат на лицо. Раньше база весила 20мб, сейчас 5мб

  14. 29 октября 2013 23:34

    Спасибо огромное за статью. Сегодня тоже получил втык от хостера. В результате действий, база из 25Мб стала 5,2. Есть 2 вопроса, эти все манипуляции надо делать периодически? И второй вопрос, установил плагин, нажимаю оптимизировать, в результате напротив каждой строки пишется,

    note: Table does not support optimize, doing recreate + analyze instead

    Не похоже, что всё хорошо?!

    Пожалуйста! Да, я делаю все эти манипуляции, примерно один раз в месяц. А вот насчет плагина пока не могу ничего сказать, видимо вы что-то сделали не правильно. Попробуйте поискать информацию в интернет по этому поводу. Но есть и приятные события. Вы оставили на моем блоге 2100-й комментарий и за это вам полагается приз в размере 100 рублей:

    Присылайте номер своего wmr-кошелька и я перечислю вам деньги.

  15. 30 октября 2013 13:27

    Спасибо, приз получен. Как я оказался на Вашем сайте?! Вчера очередной раз сайт перестал работать, а на экране писалось «Ошибка соединения с базой данных». Написал хостеру, там подтвердили что большая нагрузка на MySQL и что-то с этим делайте, а пока перевели на тариф выше. Сразу же начал искать, что же делать и нашёл Вашу статью, которая уменьшила базу в 5 раз. Плагин который сначала не хотел работать, всё таки заработал, но основная проблема, убрать лишние запросы, так и не была решена. У меня уже стоит плагин WP Super Cache, но он кеширует страницы, а не запросы к БД. И вот я до четырёх часов утра искал плагин, который мне сможет помочь с запросами и нашёл. WP File Cache кеширует запросы, количество запросов и МБ памяти, уменьшается в разы. На страницах где до этого было 40 запросов и 35МБ, теперь запросов 9 и 12МБ. Единственное, скорость загрузки вроде чуток увеличилось, но незначительно, учитывая что скорость загрузки страниц у меня, в среднем 0,15-0,5 секунды. Может кому то данная информация будет интересна.

  16. 7 декабря 2013 15:41

    выше указанные действия могут повлиять на работу плагина nrelate-flyout ?

Порыскав на досуге по тырнету, удивился, что специальных статей-руководств по оптимизации SQL-запросов нет. Перелистав различную информацию и книги, я постараюсь дать некоторое руководство к действию, которое поможет научиться писать правильные запросы.

  1. Оптимизация таблиц . Необходима, когда было произведено много изменений в таблице: либо удалена большая часть данных, либо много изменений со строками переменной длины - text, varchar, blob. Дело в том, что удалённые записи продолжают поддерживаться в индексном файле, и при последующей вставке новых записей используются позиции старых записей. Чтобы дефрагментировать файл с данными, используюется команда OPTIMIZE.

    OPTIMIZE TABLE `table1`, `table2`…

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

  2. Перестройка данных в таблице . После частых изменений в таблице, данная команда может повысить производительность работы с данными. Она перестраивает их в таблице и сортирует по определённому полю.

    ALTER TABLE `table1` ORDER BY `id`

    Тип данных . Лучше не индексировать поля, имеющие строковый тип, особенно поля типа TEXT. Для таблиц, данные которых часто изменяются, желательно избегать использования полей типа VARCHAR и BLOB, так как данный тип создаёт динамическую длину строки, тем самым увеличивая время доступа к данным. При этом советуют использовать поле VARCHAR вместо TEXT, так как с ним работа происходит быстрее.

    NOT NULL и поле по умолчанию . Лучше всего помечать поля как NOT NULL, так как они немного экономят место и исключают лишние проверки. При этом стоит задавать значение полей по умолчанию и новые данные вставлять только в том случае, если они от него отличаются. Это ускорит добавление данных и снизит время на анализ таблиц. И стоит помнить, что типы полей BLOB и TEXT не могут содержать значения по умолчанию.

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

    Разделение данных. Длинные не ключевые поля советуют выделить в отдельную таблицу в том случае, если по исходной таблице происходит постоянная выборка данных и которая часто изменяется. Данный метод позволит сократить размер изменяемой части таблицы, что приведёт к сокращению поиска информации.
    Особенно это актуально в тех случаях, когда часть информации в таблице предназначена только для чтения, а другая часть - не только для чтения, но и для модификации (не забываем, что при записи информации блокируется вся таблица). Яркий пример - счётчик посещений.
    Есть таблица (имя first) с полями id, content, shows. Первое ключевое с auto_increment, второе - текстовое, а третье числовое - считает количество показов. Каждый раз загружая страницу, к последнему полю прибавляется +1. Отделим последнее поле во вторую таблицу. Итак, первая таблица (first) будет с полями id, content, а вторая (second) с полями shows и first_id. Первое поле понятно, второе думаю тоже - отсыл к ключевому полю id из первой таблицы.
    Теперь постоянные обновления будут происходить во второй таблице. При этом изменять количество посещений лучше не программно, а через запрос:

    А выборка будет происходить усложнённым запросом, но одним, двух не нужно:

    SELECT first.id, first.content, second.first_id, second.shows FROM second INNER JOIN first ON (first.id = second.first_id)

    Стоит помнить, что всё это не актуально для сайтов с малой посещаемостью и малым количеством информации.

    Имена полей , по которым происходит связывание, к примеру, двух таблиц, желательно, чтобы имели одинаковое название. Тогда одновременное получение информации из разных таблиц через один запрос будет происходить быстрее. Например, из предыдущего пункта желательно, чтобы во второй таблице поле имело имя не first_id, а просто id, аналогично первой таблице. Однако при одинаковом имени становится внешне не очень наглядно что, куда и как. Поэтому совет на любителя.

    Требовать меньше данных . При возможности избегать запросов типа:

    SELECT * FROM `table1`

    Запрос не эффективен, так как скорее всего возвращает больше данных, чем необходимо для работы. Вариантом лучше будет конструкция:

    SELECT id, name FROM table1 ORDER BY id LIMIT 25

    Тут же сделаю добавление о желательности использования LIMIT. Данная команда ограничивает количество строк, возвращаемых запросом. То есть запрос становится "легче" и производительнее.
    Если стоит LIMIT 10, то после получения десяти строк запрос прерывается.
    Если в запросе применяется сортировка ORDER BY, то она происходит не по всей таблице, а только по выборке.
    Если использовать LIMIT совместно с DISTINCT, то запрос прервётся после того, как будет найдено указанное количество уникальных строк.
    Если использовать LIMIT 0, то возвращено будет пустое значение (иногда нужно для определения типа поля или просто проверки работы запроса).

    Ограничить использование DISTINCT . Эта команда исключает повторяющиеся строки в результате. Команда требует повышенного времени обработки. Лучше всего комбинировать с LIMIT.
    Есть маленькая хитрость. Если необходимо просмотреть две таблицы на тему соответствия, то приведённая команда остановится сразу же, как только будет найдено первое соответствие.

    Ограничить использование SELECT для постоянно изменяющихся таблиц .

  3. Не забывайте про временные таблицы типа HEAP . Несмотря на то, что таблица имеет ограничения, в ней удобно хранить промежуточные данные, особенно когда требуется сделать ещё одну выборку из таблицы без повторного обращения. Дело в том, что эта таблица хранится в памяти и поэтому доступ к ней очень быстрый.
  4. Поиск по шаблону . Зависит от размера поля и если уменьшить размер с 400 байтов до 300, то время поиска сократиться на 25%.

От автора: один мой знакомый решил оптимизировать свой автомобиль. Сначала одно колесо снял, потому крышу спилил, затем мотор… В общем, сейчас он пешком ходит. Это все последствия неправильного подхода! Поэтому, чтобы ваша СУБД продолжала «ездить», оптимизация MySQL должна проходить правильно.

Когда оптимизировать и зачем?

Лишний раз лезть в настройки сервера и изменять значения параметров (особенно, если не знаете, чем это может закончиться) не стоит. Если рассматривать данную тему с «колокольни» улучшения производительности веб-ресурсов, то она настолько обширная, что ей нужно посвящать целое научное издание в 7 томах.

Но такого писательского терпения у меня явно нет, да и у вас читательского тоже. Мы поступим проще, и постараемся лишь слегка углубиться в чащи оптимизации MySQL сервера и его составляющих. С помощью оптимальной установки всех параметров СУБД можно достигнуть нескольких целей:

Увеличить скорость выполнения запросов.

Повысить общую производительность сервера.

Уменьшить время ожидания загрузки страниц ресурса.

Снизить потребление серверных мощностей хостинга.

Снизить объем занимаемого дискового пространства.

Постараемся всю тематику оптимизации разбить на несколько пунктов, чтоб было более-менее понятно, от чего «котелок» закипает .

Зачем настраивать сервер

В MySQL оптимизацию производительности следует начинать с сервера. Прежде всего, следует ускорить его работу и уменьшить время обработки запросов. Универсальным средством для достижения всех перечисленных целей является включения кэширования. Не знаете, «what is it»? Сейчас все поясню.

Если на вашем экземпляре сервера включено кэширование, то система MySQL автоматически «запоминает» введенный пользователем запрос. И в следующий раз при его повторении данный результат запроса (на выборку) будет не обработан, а взят из памяти системы. Получается, что таким образом сервер «экономит» время на выдачу ответа, и вследствие чего скорость реагирования сайта повышается. В том числе это касается и общей скорости загрузки.

В MySQL оптимизация запросов применима к тем движкам и CMS, которые работают на основе данной СУБД и PHP. При этом код, написанный на языке программирования, для генерации динамической веб-страницы запрашивает некоторые ее структурные части и содержимое (записи, архивы и другие таксономии) из БД.

Благодаря включенному кэшированию в MySQL выполнение запросов к серверу СУБД происходит намного быстрее. За счет чего и повышается скорость загрузки всего ресурса в целом. А это положительно отражается и на пользовательском опыте, и на позиции сайта в выдаче.

Включаем и настраиваем кэширование

Но давайте вернемся от «скучной» теории к интересной практике. Дальнейшую оптимизацию базы MySQL продолжим с проверки состояния кэширования на вашем сервере БД. Для этого с помощью специального запроса мы выведем значения всех системных переменных:

Совсем другое дело.

Сделаем маленький обзор полученных значений, которые пригодятся нам для оптимизации баз данных MySQL:

have_query_cache – значение показывает «ВКЛ» кэширование запросов или нет.

query_cache_type – отображает активный тип кэша. Нам нужно значение «ON». Это говорит о том, что кэширование включено для всех видов выборки (команда SELECT). Кроме тех, в которых используется параметр SQL_NO_CACHE (запрещает сохранение информации об этом запросе).

У нас все настройки заданы правильно.

Отмеряем кэш под индексы и ключи

Теперь нужно проверить, сколько отведено оперативной памяти под индексы и ключи. Рекомендуется устанавливать этот важный для оптимизации БД MySQL параметр на 20-30% от объема оперативки, доступной для сервера. Например, если под экземпляр СУБД выделено 4 «гектара», то смело ставьте 32 «метра». Но все зависит от особенностей определенной базы и ее структуры (типов) таблиц.

Для установки значения параметра нужно отредактировать содержимое конфигурационного файла my.ini, который в Денвере находится по следующему пути: F:\Webserver\usr\local\mysql-5.5

Файл открываем с помощью Блокнота. Затем находим в нем параметр key_buffer_size и устанавливаем оптимальный для вашей системы ПК (в зависимости от «гектаров» оперативки) размер. После этого нужно перезапустить сервер БД.

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

Измеряем уровень индексов

Использование индексов в таблицах значительно повышает скорость обработки и формирования ответа СУБД на введенный запрос. MySQL постоянно «измеряет» уровень применения индексов и ключей в каждой БД. Для получения данного значения используйте запрос:

SHOW STATUS LIKE "handler_read%"

SHOW STATUS LIKE "handler_read%"

В полученном результате нас интересует значение в строке Handler_read_key. Если указанное там число маленькое, то это говорит о том, что индексы почти не используются в данной базе. А это плохо (как у нас ).

В повседневной работе приходится сталкиваться с довольно однотипными ошибками при написании запросов.

В этой статье хотелось бы привести примеры того, как НЕ надо писать запросы.

  • Выборка всех полей
    SELECT * FROM table

    При написании запросов не используйте выборку всех полей - "*". Перечислите только те поля, которые вам действительно нужны. Это сократит количество выбираемых и пересылаемых данных. Кроме этого, не забывайте про покрывающие индексы. Даже если вам на самом деле необходимы все поля в таблице, лучше их перечислить. Во-первых, это повышает читабельность кода. При использовании звездочки невозможно узнать какие поля есть в таблице без заглядывания в нее. Во-вторых, со временем количество столбцов в вашей таблице может изменяться, и если сегодня это пять INT столбцов, то через месяц могут добавиться TEXT и BLOB поля, которые будут замедлять выборку.

  • Запросы в цикле.
    Нужно четко представлять себе, что SQL - язык, оперирующий множествами. Порой программистам, привыкшим думать терминами процедурных языков, трудно перестроить мышление на язык множеств. Это можно сделать довольно просто, взяв на вооружение простое правило - «никогда не выполнять запросы в цикле». Примеры того, как это можно сделать:

    1. Выборки
    $news_ids = get_list("SELECT news_id FROM today_news ");
    while($news_id = get_next($news_ids))
    $news = get_row("SELECT title, body FROM news WHERE news_id = ". $news_id);

    Правило очень простое - чем меньше запросов, тем лучше (хотя из этого, как и из любого правила, есть исключения). Не забывайте про конструкцию IN(). Приведенный код можно написать одним запросом:
    SELECT title, body FROM today_news INNER JOIN news USING(news_id)

    2. Вставки
    $log = parse_log();
    while($record = next($log))
    query("INSERT INTO logs SET value = ". $log["value"]);

    Гораздо более эффективно склеить и выполнить один запрос:
    INSERT INTO logs (value) VALUES (...), (...)

    3. Обновления
    Иногда бывает нужно обновить несколько строк в одной таблице. Если обновляемое значение одинаковое, то все просто:
    UPDATE news SET title="test" WHERE id IN (1, 2, 3).

    Если изменяемое значение для каждой записи разное, то это можно сделать таким запросом:
    UPDATE news SET
    title = CASE
    WHEN news_id = 1 THEN "aa"
    WHEN news_id = 2 THEN "bb" END
    WHERE news_id IN (1, 2)

    Наши тесты показывают, что такой запрос выполняется в 2-3 раза быстрее, чем несколько отдельных запросов.

  • Выполнение операций над проиндексированными полями
    SELECT user_id FROM users WHERE blogs_count * 2 = $value

    В таком запросе индекс использоваться не будет, даже если столбец blogs_count проиндексирован. Для того, чтобы индекс использовался, над проиндексированным полем в запросе не должно выполняться преобразований. Для подобных запросов выносите функции преобразования в другую часть:
    SELECT user_id FROM users WHERE blogs_count = $value / 2;

    Аналогичный пример:
    SELECT user_id FROM users WHERE TO_DAYS(CURRENT_DATE) - TO_DAYS(registered) <= 10;

    Не будет использовать индекс по полю registered, тогда как
    SELECT user_id FROM users WHERE registered >= DATE_SUB(CURRENT_DATE, INTERVAL 10 DAY);
    будет.

  • Выборка строк только для подсчета их количества
    $result = mysql_query(«SELECT * FROM table», $link);
    $num_rows = mysql_num_rows($result);
    Если вам нужно выбрать количество строк, удовлетворяющих определенному условию, используйте запрос SELECT COUNT(*) FROM table, а не выбирайте все строки лишь для того, чтобы подсчитать их количество.
  • Выборка лишних строк
    $result = mysql_query(«SELECT * FROM table1», $link);
    while($row = mysql_fetch_assoc($result) && $i < 20) {

    }
    Если вам нужны только n строк выборки, используйте LIMIT, вместо того, чтобы отбрасывать лишние строки в приложении.
  • Использование ORDER BY RAND()
    SELECT * FROM table ORDER BY RAND() LIMIT 1;

    Если в таблице больше, чем 4-5 тысяч строк, то ORDER BY RAND() будет работать очень медленно. Гораздо более эффективно будет выполнить два запроса:

    Если в таблице auto_increment"ный первичный ключ и нет пропусков:
    $rnd = rand(1, query("SELECT MAX(id) FROM table"));
    $row = query("SELECT * FROM table WHERE id = ".$rnd);

    Либо:
    $cnt = query("SELECT COUNT(*) FROM table");
    $row = query("SELECT * FROM table LIMIT ".$cnt.", 1");
    что, однако, так же может быть медленным при очень большом количестве строк в таблице.

  • Использование большого количества JOIN"ов
    SELECT
    v.video_id
    a.name,
    g.genre
    FROM
    videos AS v
    LEFT JOIN
    link_actors_videos AS la ON la.video_id = v.video_id
    LEFT JOIN
    actors AS a ON a.actor_id = la.actor_id
    LEFT JOIN
    link_genre_video AS lg ON lg.video_id = v.video_id
    LEFT JOIN
    genres AS g ON g.genre_id = lg.genre_id

    Нужно помнить, что при связи таблиц один-ко многим количество строк в выборке будет расти при каждом очередном JOIN"е. Для подобных случаев более быстрым бывает разбить подобный запрос на несколько простых.

  • Использование LIMIT
    SELECT… FROM table LIMIT $start, $per_page

    Многие думают, что подобный запрос вернет $per_page записей (обычно 10-20) и поэтому сработает быстро. Он и сработает быстро для нескольких первых страниц. Но если количество записей велико, и нужно выполнить запрос SELECT… FROM table LIMIT 1000000, 1000020, то для выполнения такого запроса MySQL сначала выберет 1000020 записей, отбросит первый миллион и вернет 20. Это может быть совсем не быстро. Тривиальных путей решения проблемы нет. Многие просто ограничивают количество доступных страниц разумным числом. Также можно ускорить подобные запросы использованием покрывающих индексов или сторонних решений (например sphinx).

  • Неиспользование ON DUPLICATE KEY UPDATE
    $row = query("SELECT * FROM table WHERE id=1");

    If($row)
    query("UPDATE table SET column = column + 1 WHERE id=1")
    else
    query("INSERT INTO table SET column = 1, id=1");

    Подобную конструкцию можно заменить одним запросом, при условии наличия первичного или уникального ключа по полю id:
    INSERT INTO table SET column = 1, id=1 ON DUPLICATE KEY UPDATE column = column + 1

Читайте

Использование баз данных в значительной степени облегчает человеку жизнь, работу с данными, позволяя получать в краткие сроки нужную информацию из базы, либо записывать в неё. Однако работа с данными требует должного подхода, программисту следует учитывать некоторые аспекты взаимодействия с базами данных. В частности речь идет о MySQL. Далее давайте рассмотрим выжимку из советов по оптимизации взаимодействия с базами данных MySQL.

Делайте запросы MySQL удобными для кэширования

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

// этот запрос MySQL закэшировать не сможет $res = mysql_query("SELECT username FROM user WHERE signup_date >= CURDATE()"); // сделать можно иначе $today = date("Y-m-d"); $res = mysql_query("SELECT username FROM user WHERE signup_date >= "$today"");

Дело в том, что в первом запросе была использована функция CURDATE(), особенность её работы не позволяет помещать результаты запроса в кэш. Значение даты можно предварительно записать в строку запроса, это позволит исключить использование функции CURDATE() в запросе.
По аналогии есть и другие функции, которые не кэшируются самим сервером MySQL, среди них RAND(), NOW() а так же другие функции, результат которых недетерминирован.

Просмотрите как выполняется ваш запрос с помощью синтаксиса EXPLAIN

Посмотреть, как MySQL выполняет ваш запрос можно с помощью синтаксиса EXPLAIN . Его использование может помочь определить слабые места в производительности запроса, а так же в структуре таблиц. В качестве результата запроса EXPLAIN возвратит данные, которые покажут, какие используются индексы, каким образом выбираются данные из таблиц, как сортируются, и т.д. Для этого достаточно добавить вначале SELECT-запроса ключевое слово EXPLAIN, после чего будет показана таблица, с данными.

Когда вам нужна одна запись, выставляйте LIMIT 1

Не мало случаев, когда из таблицы вам требуется проверить наличие хотябы одной записи, в этом случае рекомендуется добавить к запросу параметр LIMIT 1. Это сделает его более оптимальным, т.к. механизм базы данных после нахождения первой записи остановит выборку данных, вместо того чтобы выбирать все данные. Вы экономите ресурсы.

// запрос города с кодом Shymkent из базы $res = mysql_query("SELECT * FROM location WHERE city = "Shymkent""); if (mysql_num_rows($res) > 0){ } // добавляем LIMIT 1 для оптимизации запроса $res = mysql_query("SELECT * FROM location WHERE city = "Shymkent" LIMIT 1"); if (mysql_num_rows($res) > 0){ }

Индексируйте поля по которым производится поиск

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

// тут сработает индекс city LIKE ‘shym%’ // тут же индекс задействован не будет city LIKE ‘%shymkent%’

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

Индексируйте поля по которым объединяются таблицы

Если вы используйте множество объединений таблиц, то вам стоит задуматься о том, чтобы поля, участвующих в объединении были проиндексированы в обеих таблицах. Это дело влияет на то, как MySQL будет производить внутреннюю оптимизацию объединений полей таблицы. Поля объединения должны быть одного типа и одной кодировки. Т.е. к примеру, если одно поле будет иметь тип DECIMAL, а другое INT, то MySQL не сможет воспользоваться индексом.

Найдите альтернативу вместо ORDER BY RAND()

Использование рандомной сортировки действительно является весьма удобным, и об этом такого же мнения многие начинающие программисты. Однако тут есть подводные камни, и очень весомые, используя подобный метод выборки в своих запросах, вы оставляете узкое место в производительности. Здесь же рекомендуется прибегнуть к дополнительному коду вместо использования ORDER BY RAND(), в качестве альтернативы, чтобы избавиться от слабого места в производительности, которое напомнит о себе при увеличении объема данных.

Используйте выборку конкретных полей, вместо SELECT *

Не ленитесь указывать конкретные нужные поля в запросе при выборке, вместо использования «*» — выборка всех полей, дело в том, что чем больше данных считывается из таблицы, тем медленнее становиться ваш запрос.

Добавляйте поле ID для всех таблиц

Каждая таблица в хорошем её исполнении должна иметь поле id типа INT, которое является первичным ключом (PRIMARY_KEY), и AUTO_INCREMENT. Кроме того, для поля нужно указать параметр UNSIGNED, который означает то, что значение всегда будет положительным.
В MySQL есть внутренние операции, которые могут использовать первичный ключ, это играет роль для сложных конфигураций баз данных, таких как кластеры, распараллеливание, и т.д.
Кроме того, если есть несколько таблиц, и необходимо выполнить объединенный запрос, то тут ID таблиц окажется весьма кстати.

ENUM как альтернатива VARCHAR

Давайте представим, вы хотите добавить поле в таблице, которое должно содержать определенный набор значений. Традиционно многие программисты выставляют тип VARCHAR для полей. Однако есть и другой тип поля, который гораздо быстрей и компактнее. Значения в данном типе хранятся так же, как и TINYINT, но отображаются как в строковом типе.

Используйте значение NOT NULL вместо NULL

Поля NULL занимают больше места в записи, из-за того что возникает необходимость отмечать это NULL значение. Таблицы MyISAM, поля с NULL хранятся таким образом, что каждое поле занимает 1 дополнительный бит, который округляется до ближайшего байта. Если использование NULL в поле не принципиально, то рекомендуется использовать NOT NULL.

Пользуйтесь Prepared Statements

$res = "UPDATE hosts SET ip = INET_ATON("{$_SERVER["REMOTE_ADDR"]}") WHERE id = $host_id";

Используйте статичные таблицы

Статичная таблица это обычная таблица в базе, за исключеним того, что каждое поле в таблице имеет фиксированный размер. Если в таблице есть колонки, не фиксированной длины, к примеру, это могут быть: VARCHAR, TEXT, BLOB, она перестает быть статичной, и будет обрабатываться MySQL немного иначе. Статичные таблицы, или их можно ещё назвать таблицами фиксированного размера работают быстрее не статичных. Записи из таких таблицах будут просматриваться быстрее, при необходимости выбора нужной строки MySQL быстро вычислит её позицию. Если поле имеет не фиксированный размер, то в этом случае поиск производиться по индексу. Есть и другие плюсы использования статических таблиц, дело в том, что эти таблицы проще кэшируются, а так же восстанавливаются после падения базы данных.

Используйте вертикальное разделение

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

Разделяйте объемные запросы INSERT и DELETE

Выполнение большого объема запросов такого рода может привести к блокировке таблицы, вследствие чего, к неправильной работы приложения в целом. Параллельные запросы на веб-сервер могут порождать дополнительное обращение к таблице. Если таблица заблокирована предыдущим запросом, последующие запросы выстраиваются в очередь, и как следствие это проявляется в виде торможения сайта, а то и падения сервера.
Если вам необходимо сделать множество запросов, постарайтесь контролировать их, отдавая небольшими сериями, а не скидывать всё на базу данных. При этом возможно ваш запрос будет выполняться дольше, но это менее скажется на других пользователях.
Пример:

While (1){ mysql_query("DELETE FROM logs WHERE log_date <= "2015-07-20" LIMIT 1000"); if (mysql_affected_rows() == 0){ // записи удалены успешно break; } usleep(50000); // делаем небольшую паузу }

Стремитесь использовать поля небольшого размера

Как известно данные базы хранятся на жестком диске, это зачастую это может оказаться одним из слабых мест в веб-приложении. Дело в том, что записи небольшого размера являются более предпочтительными, т.к. использование их уменьшает работу с жестким диском. Если вы уверенны, что конкретная таблица будет хранить мало строк, то рациональным решением будет использование типов полей, с минимальными возможными значениями. К примеру, если основной ключ имеет тип INT, и вы будете хранить в таблице лишь небольшое кол-во данных, то лучше сделать его типа MEDIUMINT, SMALLINT или даже TINYINT.

Выбирайте тип таблиц под свои задачи

Два широко известных типа таблиц на сегодняшний день, это MyISAM и InnoDB , каждый из них имеет свои положительные и отрицательные стороны. К примеру, MyISAM хорошо считывает данные из таблиц в большом объеме, одно он более медлителен при записи. Он так же хорошо выполняет запросы вида SELECT COUNT(*).
Механизм хранения данных у InnoDB более сложный, чем у MyISAM, однако, он поддерживает блокировку строк, что является положительной стороной при масштабировании. Поэтому сказать, что одно лучше другого нельзя, да и не правильно, нужно выбирать тип исходя из своих потребностей.