воскресенье, 7 сентября 2014 г.

Выявление "плохих" запросов в БД MySQL

Основную часть ресурсов на веб-сервере (apache+nginx+php+mysql) использует MySQL.
Иногда бывает так, что MySQL не хватает по каким-либо причинам ресурсов, забивается полностью память, система уходит в swap. В этом виноваты длинные запросы, которые очегь сильно нагружают MySQL. Так случилось и со мной. Дальше я расскажу, как обнаружить проблему и как ей противостоять.

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

Представим ситуацию, что сервер нагружен.
Заходим в atop, убеждаемся, что именно MySQL грузит систему
Проверяем список процессов в MySQL:
# mysql -u root -p
Enter password:
mysql> SHOW PROCESSLIST;
У меня на всякий случай есть маленький скрипт, который проверяет каждые 5 минут состояние процессов, использую его чисто для просмотра динамики:
# vim /root/mysql_processes
#!/bin/bash
date +"%d/%m/%Y %T" >> /etc/mysql/processes.report
mysql -uroot -ppass -e "SHOW FULL PROCESSLIST;" >> /etc/mysql/processes.report
echo "" >> /etc/mysql/processes.report

# chmod +x mysql_processes
# crontab -e
*/5   *   *   *   *   /root/mysql_processes

Среди запросов можно увидеть очень длинные, я приведу пример средненького запроса из одной базы:
# cat /etc/mysql/processes.report
31/08/2014 12:30:01
Id      User    Host       db       Command Time    State   Info
329103  user    localhost  database Query   0       Sending data   SELECT a.id, a.title, a.alias, a.title_alias, a.introtext, a.language, a.checked_out, a.checked_out_time, a.catid, a.created, a.created_by, a.created_by_alias, CASE WHEN a.modified = 0 THEN a.created ELSE a.modified END as modified, a.modified_by, uam.name as modified_by_name,CASE WHEN a.publish_up = 0 THEN a.created ELSE a.publish_up END as publish_up,a.publish_down, a.images, a.urls, a.attribs, a.metadata, a.metakey, a.metadesc, a.access, a.hits, a.xreference, a.featured, LENGTH(a.fulltext) AS readmore,CASE WHEN badcats.id is not null THEN 0 ELSE a.state END AS state,c.title AS category_title, c.path AS category_route, c.access AS category_access, c.alias AS category_alias,CASE WHEN a.created_by_alias > ' ' THEN a.created_by_alias ELSE ua.name END AS author,ua.email AS author_email,(\nSELECT MAX(contact.id) AS id\nFROM tar_contact_details AS contact\nWHERE contact.published = 1 AND contact.user_id = a.created_by) as contactid,parent.title as parent_title, parent.id as parent_id, parent.path as parent_route, parent.alias as parent_alias,ROUND(v.rating_sum / v.rating_count, 0) AS rating, v.rating_count as rating_count,c.published, CASE WHEN badcats.id is null THEN c.published ELSE 0 END AS parents_published\nFROM tar_content AS a\nLEFT JOIN tar_content_frontpage AS fp ON fp.content_id = a.id\nLEFT JOIN tar_categories AS c ON c.id = a.catid\nLEFT JOIN tar_users AS ua ON ua.id = a.created_by\nLEFT JOIN tar_users AS uam ON uam.id = a.modified_by\nLEFT JOIN tar_categories as parent ON parent.id = c.parent_id\nLEFT JOIN tar_content_rating AS v ON a.id = v.content_id\nLEFT OUTER JOIN (SELECT cat.id as id FROM tar_categories AS cat JOIN tar_categories AS parent ON cat.lft BETWEEN parent.lft AND parent.rgt WHERE parent.extension = 'com_content' AND parent.published != 1 GROUP BY cat.id ) AS badcats ON badcats.id = c.id\nWHERE a.access IN (1) AND c.access IN (1) AND CASE WHEN badcats.id is null THEN a.state ELSE 0 END = 1 AND a.featured = 0 AND a.catid IN (85) AND (a.publish_up = '0000-00-00 00:00:00' OR a.publish_up <= '2014-08-31 08:29:59') AND (a.publish_down = '0000-00-00 00:00:00' OR a.publish_down >= '2014-08-31 08:29:59')\nORDER BY a.created DESC LIMIT 0, 3329104  

Таким образом можно искать проблемные базы, но это самый примитивный вариант.
В интернете есть отличный скрипт, который анализирует slow-log MySQL.
Если этот лог не ведётся, то рекомендую его задать.
# vim /etc/mysql/my.cnf
...
log_slow_queries = /var/log/mysql/mysql-slow.log

Итак, качаем скрипт и анализируем наш slow-log.
# cd /tmp
# wget http://www.maatkit.org/get/mk-query-digest
# chmod +x mk-query-digest
# ./mk-query-digest /var/log/mysql/mysql-slow.log

По выводу работы скрипта можно сделать для себя выводы о проблемных базах и запросах.
Далее же нужно либо оптимизировать запросы, либо базы.

Комментариев нет: