Skip to content

Commit 380c0c8

Browse files
committed
Merge pull request #660 from DataDog/issue591
Minimize number of MySQL queries in agent check
2 parents e72edf6 + a092fe3 commit 380c0c8

File tree

2 files changed

+46
-61
lines changed

2 files changed

+46
-61
lines changed

checks.d/mysql.py

Lines changed: 45 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -9,32 +9,21 @@
99
GAUGE = "gauge"
1010
RATE = "rate"
1111

12-
QUERIES_COMMON = [
13-
('mysql.net.connections', "SHOW STATUS LIKE 'Connections'", RATE),
14-
('mysql.net.max_connections', "SHOW STATUS LIKE 'Max_used_connections'", GAUGE),
15-
('mysql.performance.open_files', "SHOW STATUS LIKE 'Open_files'", GAUGE),
16-
('mysql.performance.table_locks_waited', "SHOW STATUS LIKE 'Table_locks_waited'", GAUGE),
17-
('mysql.performance.threads_connected', "SHOW STATUS LIKE 'Threads_connected'", GAUGE),
18-
('mysql.innodb.data_reads', "SHOW STATUS LIKE 'Innodb_data_reads'", RATE),
19-
('mysql.innodb.data_writes', "SHOW STATUS LIKE 'Innodb_data_writes'", RATE),
20-
('mysql.innodb.os_log_fsyncs', "SHOW STATUS LIKE 'Innodb_os_log_fsyncs'", RATE),
21-
('mysql.innodb.buffer_pool_size', "SHOW STATUS LIKE 'Innodb_data_reads'", RATE),
22-
]
23-
24-
QUERIES_GREATER_502 = [
25-
('mysql.performance.created_tmp_disk_tables', "SHOW GLOBAL STATUS LIKE 'Created_tmp_disk_tables'", GAUGE),
26-
('mysql.performance.slow_queries', "SHOW GLOBAL STATUS LIKE 'Slow_queries'", RATE),
27-
('mysql.performance.questions', "SHOW GLOBAL STATUS LIKE 'Questions'", RATE),
28-
('mysql.performance.queries', "SHOW GLOBAL STATUS LIKE 'Queries'", RATE),
29-
]
30-
31-
QUERIES_OLDER_502 = [
32-
('mysql.performance.created_tmp_disk_tables', "SHOW STATUS LIKE 'Created_tmp_disk_tables'", GAUGE),
33-
('mysql.performance.slow_queries', "SHOW STATUS LIKE 'Slow_queries'", RATE),
34-
('mysql.performance.questions', "SHOW STATUS LIKE 'Questions'", RATE),
35-
('mysql.performance.queries', "SHOW STATUS LIKE 'Queries'", RATE),
36-
]
37-
12+
STATUS_VARS = {
13+
'Connections': ('mysql.net.connections', RATE),
14+
'Max_used_connections': ('mysql.net.max_connections', GAUGE),
15+
'Open_files': ('mysql.performance.open_files', GAUGE),
16+
'Table_locks_waited': ('mysql.performance.table_locks_waited', GAUGE),
17+
'Threads_connected': ('mysql.performance.threads_connected', GAUGE),
18+
'Innodb_data_reads': ('mysql.innodb.data_reads', RATE),
19+
'Innodb_data_writes': ('mysql.innodb.data_writes', RATE),
20+
'Innodb_os_log_fsyncs': ('mysql.innodb.os_log_fsyncs', RATE),
21+
'Innodb_data_reads': ('mysql.innodb.buffer_pool_size', RATE),
22+
'Created_tmp_disk_tables': ('mysql.performance.created_tmp_disk_tables', GAUGE),
23+
'Slow_queries': ('mysql.performance.slow_queries', RATE),
24+
'Questions': ('mysql.performance.questions', RATE),
25+
'Queries': ('mysql.performance.queries', RATE),
26+
}
3827

3928
class MySql(AgentCheck):
4029
def __init__(self, name, init_config, agentConfig):
@@ -93,25 +82,19 @@ def _connect(self, host, port, mysql_sock, user, password, defaults_file):
9382
return db
9483

9584
def _collect_metrics(self, host, db, tags, options):
96-
if self._version_greater_502(db, host):
97-
queries = QUERIES_GREATER_502 + QUERIES_COMMON
98-
else:
99-
queries = QUERIES_OLDER_502 + QUERIES_COMMON
100-
101-
for metric_name, query, metric_type in queries:
102-
value = self._collect_scalar(query, db)
103-
if value is not None:
104-
if metric_type == RATE:
105-
self.rate(metric_name, value, tags=tags)
106-
elif metric_type == GAUGE:
107-
self.gauge(metric_name, value, tags=tags)
85+
cursor = db.cursor()
86+
cursor.execute("SHOW /*!50002 GLOBAL */ STATUS;")
87+
results = dict(cursor.fetchall())
88+
self._rate_or_gauge_statuses(STATUS_VARS, results, tags)
89+
cursor.close()
90+
del cursor
10891

10992
# Compute InnoDB buffer metrics
110-
page_size = self._collect_scalar("SHOW STATUS LIKE 'Innodb_page_size'", db)
11193
# Be sure InnoDB is enabled
112-
if page_size:
113-
innodb_buffer_pool_pages_total = self._collect_scalar("SHOW STATUS LIKE 'Innodb_buffer_pool_pages_total'", db)
114-
innodb_buffer_pool_pages_free = self._collect_scalar("SHOW STATUS LIKE 'Innodb_buffer_pool_pages_free'", db)
94+
if 'Innodb_page_size' in results:
95+
page_size = self._collect_scalar('Innodb_page_size', results)
96+
innodb_buffer_pool_pages_total = self._collect_scalar('Innodb_buffer_pool_pages_total', results)
97+
innodb_buffer_pool_pages_free = self._collect_scalar('Innodb_buffer_pool_pages_free', results)
11598
innodb_buffer_pool_pages_total = innodb_buffer_pool_pages_total * page_size
11699
innodb_buffer_pool_pages_free = innodb_buffer_pool_pages_free * page_size
117100
innodb_buffer_pool_pages_used = innodb_buffer_pool_pages_total - innodb_buffer_pool_pages_free
@@ -120,12 +103,22 @@ def _collect_metrics(self, host, db, tags, options):
120103
self.gauge("mysql.innodb.buffer_pool_used", innodb_buffer_pool_pages_used, tags=tags)
121104
self.gauge("mysql.innodb.buffer_pool_total", innodb_buffer_pool_pages_total, tags=tags)
122105

123-
if 'galera_cluster' in options.keys() and options['galera_cluster']:
124-
value = self._collect_scalar("SHOW STATUS LIKE 'wsrep_cluster_size'", db)
106+
if 'galera_cluster' in options and options['galera_cluster']:
107+
value = self._collect_scalar('wsrep_cluster_size', results)
125108
self.gauge('mysql.galera.wsrep_cluster_size', value, tags=tags)
126109

127-
if 'replication' in options.keys() and options['replication']:
110+
if 'replication' in options and options['replication']:
128111
self._collect_dict(GAUGE, {"Seconds_behind_master": "mysql.replication.seconds_behind_master"}, "SHOW SLAVE STATUS", db, tags=tags)
112+
113+
def _rate_or_gauge_statuses(self, statuses, dbResults, tags):
114+
for status, metric in statuses.iteritems():
115+
metric_name, metric_type = metric
116+
value = self._collect_scalar(status, dbResults)
117+
if value is not None:
118+
if metric_type == RATE:
119+
self.rate(metric_name, value, tags=tags)
120+
elif metric_type == GAUGE:
121+
self.gauge(metric_name, value, tags=tags)
129122

130123
def _version_greater_502(self, db, host):
131124
# show global status was introduced in 5.0.2
@@ -170,21 +163,13 @@ def _get_version(self, db, host):
170163
self.mysql_version[host] = version
171164
return version
172165

173-
def _collect_scalar(self, query, db):
174-
self.log.debug("Collecting data with %s" % (query))
175-
try:
176-
cursor = db.cursor()
177-
cursor.execute(query)
178-
result = cursor.fetchone()
179-
cursor.close()
180-
del cursor
181-
if result is None:
182-
self.log.debug("%s returned None" % query)
183-
return None
184-
self.log.debug("Collecting done, value %s" % result[1])
185-
return float(result[1])
186-
except Exception:
187-
self.log.exception("Error while running %s" % query)
166+
def _collect_scalar(self, key, dict):
167+
self.log.debug("Collecting data with %s" % key)
168+
if key not in dict:
169+
self.log.debug("%s returned None" % key)
170+
return None
171+
self.log.debug("Collecting done, value %s" % dict[key])
172+
return float(dict[key])
188173

189174
def _collect_dict(self, metric_type, field_metric_map, query, db, tags):
190175
"""

tests/test_mysql.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ def testChecks(self):
2020
'api_key': 'toto' }
2121

2222
# Initialize the check from checks.d
23-
c = load_check('mysql', {'init_config': {}, 'instances':{}},agentConfig)
23+
c = load_check('mysql', {'init_config': {}, 'instances': {}}, agentConfig)
2424
conf = c.parse_agent_config(agentConfig)
2525
self.check = load_check('mysql', conf, agentConfig)
2626

0 commit comments

Comments
 (0)