MySQL은 쿼리 프로파일 링을 제안하지 않았습니다. MySQL이 Oracle에 의해 손 대어지고 있기 때문에 이것이 계속 될 것이라는 것을 알고 있습니다.
그러나 모든 희망이 사라지지는 않습니다.
2007 년부터 Percona는 Query Profiling을 포함하여 개발자와 DBA가 원하는 모든 것을위한 절대적으로 놀라운 도구를 개발했습니다.
MAATKIT로 알려진 Percona의 첫 번째 도구 세트 는 MySQL을 진지하게 사용하는 사용자를위한 영역을 만들었습니다. 다음 과 같은 많은 기능이 있습니다 .
- 쿼리 프로파일 링
- 복제 하트 비트
- 복제 슬레이브 관리
- 테이블 체크섬 및 동기화
Percona는 최근 MAATKIT를 오늘날 Percona Toolkit 으로 알려진 최신 도구 세트에 포함 시켰습니다 . 이 도구들은 심각한 MySQL 사용자의 활동 영역을 확장하여 MAATKIT가 중단 한 곳에서 찾아 냈습니다.
- 외래 키 오류 확인
- 온라인 스키마 변경
- 시각적 설명 계획
- 그리고 더 ...
원래 질문으로 돌아가서 쿼리 프로파일 링을위한 도구는 다음과 같습니다.
다음은 이러한 도구 중 하나를 사용하여 얻을 수있는 풍부한 정보의 예입니다.
클라이언트가 mk-query-digest를 구현하여 20 분마다 20 개의 최악의 쿼리를보고하도록 도와주었습니다. 이 YouTube 비디오에서 아이디어를 얻었습니다 . 클라이언트는 잘못된 쿼리의 출력을 memcached로 이동하여 쿼리가 데이터베이스에 영향을 미치는 빈도를 줄입니다.
다음은 mk-query-digest를 호출하기 위해 만든 스크립트입니다 (프로세스 목록 만 검사).
#!/bin/sh
RUNFILE=/tmp/QueriesAreBeingDigested.txt
if [ -f ${RUNFILE} ] ; then exit ; fi
MKDQ=/usr/local/sbin/mk-query-digest
RUNTIME=${1}
COPIES_TO_KEEP=${2}
DBVIP=${3}
WHICH=/usr/bin/which
DATE=`${WHICH} date`
ECHO=`${WHICH} echo`
HEAD=`${WHICH} head`
TAIL=`${WHICH} tail`
AWK=`${WHICH} awk`
SED=`${WHICH} sed`
CAT=`${WHICH} cat`
WC=`${WHICH} wc`
RM=`${WHICH} rm | ${TAIL} -1 | ${AWK} '{print $1}'`
LS=`${WHICH} ls | ${TAIL} -1 | ${AWK} '{print $1}'`
HAS_THE_DBVIP=`/sbin/ip addr show | grep "scope global secondary" | grep -c "${DBVIP}"`
if [ ${HAS_THE_DBVIP} -eq 1 ] ; then exit ; fi
DT=`${DATE} +"%Y%m%d_%H%M%S"`
UNIQUETAG=`${ECHO} ${SSH_CLIENT}_${SSH_CONNECTION}_${DT} | ${SED} 's/\./ /g' | ${SED} 's/ //g'`
cd /root/QueryDigest
OUTFILE=QP_${DT}.txt
HOSTADDR=${DBVIP}
${MKDQ} --processlist h=${HOSTADDR},u=queryprofiler,p=queryprofiler --run-time=${RUNTIME} > ${OUTFILE}
#
# Rotate out Old Copies
#
QPFILES=QPFiles.txt
QPFILES2ZAP=QPFiles2Zap.txt
${LS} QP_[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]_[0-9][0-9][0-9][0-9][0-9][0-9].txt > ${QPFILES}
LINECOUNT=`${WC} -l < ${QPFILES}`
if [ ${LINECOUNT} -gt ${COPIES_TO_KEEP} ]
then
(( DIFF = LINECOUNT - COPIES_TO_KEEP ))
${HEAD} -${DIFF} < ${QPFILES} > ${QPFILES2ZAP}
for QPFILETOZAP in `${CAT} ${QPFILES2ZAP}`
do
${RM} ${QPFILETOZAP}
done
fi
rm -f ${QPFILES2ZAP}
rm -f ${QPFILES}
rm -f ${RUNFILE}
mk-query-digest를 사용하여 mysql에 연결 한 사용자는 다음과 같습니다.
GRANT PROCESS ON *.* TO 'queryprofiler'@'%' IDENTIFIED BY 'queryprofiler';
다음은 마지막 144 개 사본 (프로파일 링 48 시간)을 유지하면서 20 분마다 (10 초 미만) 실행 한 crontab입니다.
*/20 * * * * /root/QueryDigest/ExecQueryDigest.sh 1190s 144 10.1.1.8
놀라운 부분 : mk-query-digest의 출력
다음은 1190 초 (20 분 10 초) 동안 2011-12-28 11:20:00에 실행 된 프로필입니다.
마지막 22 줄
# Rank Query ID Response time Calls R/Call Item
# ==== ================== ================ ======= ========== ====
# 1 0x5E994008E9543B29 40.3255 11.2% 101 0.399263 SELECT schedule_occurrence schedule_eventschedule schedule_event schedule_eventtype schedule_event schedule_eventtype schedule_occurrence.start
# 2 0x392F6DA628C7FEBD 33.9181 9.4% 17 1.995184 SELECT mt_entry mt_objecttag
# 3 0x6C6318E56E149036 26.4695 7.3% 102 0.259505 SELECT schedule_occurrence schedule_eventschedule schedule_event schedule_eventtype schedule_event schedule_eventtype schedule_occurrence.start
# 4 0x00F66961DAE6FFB2 25.5472 7.1% 55 0.464495 SELECT mt_entry mt_placement mt_category
# 5 0x99E13015BFF1E75E 22.3618 6.2% 199 0.112371 SELECT mt_entry mt_objecttag
# 6 0x84DD09F0FC444677 22.3516 6.2% 39 0.573118 SELECT mt_entry
# 7 0x440EBDBCEDB88725 21.1817 5.9% 36 0.588380 SELECT mt_entry
# 8 0x8D258C584B858811 17.2402 4.8% 37 0.465951 SELECT mt_entry mt_placement mt_category
# 9 0x4E2CB0F4CAFD1400 16.9768 4.7% 40 0.424419 SELECT mt_entry mt_placement mt_category
# 10 0x377E0D0898266FDD 16.6979 4.6% 150 0.111319 SELECT polls_pollquestion mt_category
# 11 0x3B9686D98BB8E054 16.2089 4.5% 32 0.506529 SELECT mt_entry mt_objecttag mt_tag
# 12 0x97F670B604A85608 15.6158 4.3% 34 0.459287 SELECT mt_entry mt_placement mt_category
# 13 0x3F5557DA231225EB 14.4309 4.0% 36 0.400859 SELECT mt_entry mt_placement mt_category
# 14 0x191D660A10738896 13.1220 3.6% 31 0.423290 SELECT mt_entry mt_placement mt_category
# 15 0xF88F7421DD88036D 12.1261 3.4% 61 0.198788 SELECT mt_entry mt_blog mt_objecttag mt_tag mt_author
# 16 0xA909BF76E7051792 10.3971 2.9% 53 0.196172 SELECT mt_entry mt_objecttag mt_tag
# 17 0x3D42D07A335ED983 9.1424 2.5% 20 0.457121 SELECT mt_entry mt_placement mt_category
# 18 0x59F43B57DD43F2BD 9.0533 2.5% 21 0.431111 SELECT mt_entry mt_placement mt_category
# 19 0x7961BD4C76277EB7 8.5564 2.4% 47 0.182052 INSERT UNION UPDATE UNION mt_session
# 20 0x173EB4903F3B6DAC 8.5394 2.4% 22 0.388153 SELECT mt_entry mt_placement mt_category
쿼리 응답 시간을 기반으로 한 20 개의 최악의 쿼리 목록을 쿼리를 호출 한 횟수로 나눈 것에 주목하십시오.
쿼리 ID # 1 0x5E994008E9543B29
을 보면 출력 파일에서 해당 쿼리 ID를 찾고 다음은 해당 특정 쿼리에 대한 보고서입니다.
# Query 1: 0.09 QPS, 0.03x concurrency, ID 0x5E994008E9543B29 at byte 0 __
# This item is included in the report because it matches --limit.
# pct total min max avg 95% stddev median
# Count 4 101
# Exec time 7 40s 303ms 1s 399ms 992ms 198ms 293ms
# Lock time 0 0 0 0 0 0 0 0
# Users 1 mt
# Hosts 101 10.64.95.73:33750 (1), 10.64.95.73:34452 (1), 10.64.95.73:38440 (1)... 97 more
# Databases 1 mt1
# Time range 1325089201 to 1325090385
# bytes 0 273.60k 2.71k 2.71k 2.71k 2.62k 0 2.62k
# id 4 765.11M 7.57M 7.58M 7.58M 7.29M 0.12 7.29M
# Query_time distribution
# 1us
# 10us
# 100us
# 1ms
# 10ms
# 100ms ################################################################
# 1s ######
# 10s+
# Tables
# SHOW TABLE STATUS FROM `mt1` LIKE 'schedule_occurrence'\G
# SHOW CREATE TABLE `mt1`.`schedule_occurrence`\G
# SHOW TABLE STATUS FROM `mt1` LIKE 'schedule_eventschedule'\G
# SHOW CREATE TABLE `mt1`.`schedule_eventschedule`\G
# SHOW TABLE STATUS FROM `mt1` LIKE 'schedule_event'\G
# SHOW CREATE TABLE `mt1`.`schedule_event`\G
# SHOW TABLE STATUS FROM `mt1` LIKE 'schedule_eventtype'\G
# SHOW CREATE TABLE `mt1`.`schedule_eventtype`\G
# SHOW TABLE STATUS FROM `schedule_occurrence` LIKE 'start'\G
# SHOW CREATE TABLE `schedule_occurrence`.`start`\G
# EXPLAIN
SELECT `schedule_occurrence`.`id`, `schedule_occurrence`.`schedule_id`, `schedule_occurrence`.`event_id`, `schedule_occurrence`.`start`, `schedule_occurrence`.`end`, `schedule_occurrence`.`cancelled`, `schedule_occurrence`.`original_start`, `schedule_occurrence`.`original_end`, `schedule_occurrence`.`all_day`, `schedule_occurrence`.`ongoing`, `schedule_occurrence`.`featured`, `schedule_eventschedule`.`id`, `schedule_eventschedule`.`event_id`, `schedule_eventschedule`.`start`, `schedule_eventschedule`.`end`, `schedule_eventschedule`.`all_day`, `schedule_eventschedule`.`ongoing`, `schedule_eventschedule`.`min_date_calculated`, `schedule_eventschedule`.`max_date_calculated`, `schedule_eventschedule`.`rule`, `schedule_eventschedule`.`end_recurring_period`, `schedule_eventschedule`.`textual_description`, `schedule_event`.`id`, `schedule_event`.`title`, `schedule_event`.`slug`, `schedule_event`.`description`, `schedule_event`.`host_id`, `schedule_event`.`cost`, `schedule_event`.`age_restrictions`, `schedule_event`.`more_info`, `schedule_event`.`photo_id`, `schedule_event`.`contact_email`, `schedule_event`.`event_type_id`, `schedule_event`.`featured`, `schedule_event`.`staff_pick`, `schedule_event`.`futuremost`, `schedule_event`.`creator_id`, `schedule_event`.`created_on`, `schedule_event`.`allow_comments`, `schedule_event`.`mt_entry`, `schedule_eventtype`.`id`, `schedule_eventtype`.`parent_id`, `schedule_eventtype`.`name`, `schedule_eventtype`.`slug`, `schedule_eventtype`.`lft`, `schedule_eventtype`.`rght`, `schedule_eventtype`.`tree_id`, `schedule_eventtype`.`level`, T5.`id`, T5.`title`, T5.`slug`, T5.`description`, T5.`host_id`, T5.`cost`, T5.`age_restrictions`, T5.`more_info`, T5.`photo_id`, T5.`contact_email`, T5.`event_type_id`, T5.`featured`, T5.`staff_pick`, T5.`futuremost`, T5.`creator_id`, T5.`created_on`, T5.`allow_comments`, T5.`mt_entry`, T6.`id`, T6.`parent_id`, T6.`name`, T6.`slug`, T6.`lft`, T6.`rght`, T6.`tree_id`, T6.`level` FROM `schedule_occurrence` INNER JOIN `schedule_eventschedule` ON (`schedule_occurrence`.`schedule_id` = `schedule_eventschedule`.`id`) INNER JOIN `schedule_event` ON (`schedule_eventschedule`.`event_id` = `schedule_event`.`id`) INNER JOIN `schedule_eventtype` ON (`schedule_event`.`event_type_id` = `schedule_eventtype`.`id`) INNER JOIN `schedule_event` T5 ON (`schedule_occurrence`.`event_id` = T5.`id`) INNER JOIN `schedule_eventtype` T6 ON (T5.`event_type_id` = T6.`id`) WHERE (EXTRACT(MONTH FROM `schedule_occurrence`.`start`) = 8 AND EXTRACT(DAY FROM `schedule_occurrence`.`start`) = 6 AND `schedule_occurrence`.`start` BETWEEN '2011-01-01 00:00:00' and '2011-12-31 23:59:59.99') ORDER BY `schedule_occurrence`.`ongoing` ASC, `schedule_occurrence`.`all_day` DESC, `schedule_occurrence`.`start` ASC\G
히스토그램은 텍스트를 기반으로하지만 쿼리의 전체 성능을 1 초에 걸쳐 실행하며 대부분 0.01 ~ 0.1 초 사이의 정확한 그림을 제공합니다. 여기에서 쿼리를 리팩터링하고 쿼리 결과를 memcached에 배치하거나 누락되거나 포함 된 인덱스 추가 등을 수행하여 성능 조정을 진행할 수 있습니다.
결론
IMHO Percona가 프로파일 러 도구를 Windows GUI에 배치 한 경우 Microsoft의 SQL Server 프로파일 러와 쉽게 경쟁 할 수 있습니다.
국방 달려라 !!!