Ich habe eine Abfrage, die zum Abrufen von Internet-Verkehrsstatistiken bestimmter IP-Adressen verwendet wird.
Es gibt separate IP-Adressfelder für hosts
und IP- Blocks, die aufgerufen werden assignments
. Die Daten werden in 5-Minuten-Intervallen gespeichert.
Die Abfrageergebnisse werden in der Zeitspalte gruppiert, und die Gesamtzahl der SUMs in und aus diesen 5-Minuten-Intervallen wird zum Zeichnen eines Diagramms verwendet.
Die Tabelle wird aufgerufen traffic
und enthält (Ende des Monats) rund 21 Millionen Datensätze.
SHOW CREATE table traffic:
CREATE TABLE `traffic` (
`type` enum('v4_assignment','v4_host','v6_subnet','v6_assignment','v6_host') NOT NULL,
`type_id` int(11) unsigned NOT NULL,
`time` int(32) unsigned NOT NULL,
`bytesin` bigint(20) unsigned NOT NULL default '0',
`bytesout` bigint(20) unsigned NOT NULL default '0',
KEY `basic_select` (`type_id`,`time`,`type`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
SELECT traffic.time, SUM(traffic.bytesin), SUM(traffic.bytesout) FROM traffic
WHERE (
( traffic.type = 'v4_assignment' AND type_id IN (231, between 20 to 100 ids,265)) OR
( traffic.type = 'v4_host' AND type_id IN (131, ... a lot of ids... ,1506)))
AND traffic.time >= 1343772000 AND traffic.time < 1346450399
GROUP BY traffic.time
ORDER BY traffic.time;
explain
Für die obige Abfrage wird Folgendes ausgegeben:
+----+-------------+---------+-------+---------------+--------------+---------+------+--------+----------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+-------+---------------+--------------+---------+------+--------+----------------------------------------------+
| 1 | SIMPLE | traffic | range | basic_select | basic_select | 8 | NULL | 891319 | Using where; Using temporary; Using filesort |
+----+-------------+---------+-------+---------------+--------------+---------+------+--------+----------------------------------------------+
show indexes from traffic;
+---------+------------+--------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+---------+------------+--------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| traffic | 1 | basic_select | 1 | type_id | A | 13835 | NULL | NULL | | BTREE | |
| traffic | 1 | basic_select | 2 | time | A | 18470357 | NULL | NULL | | BTREE | |
| traffic | 1 | basic_select | 3 | type | A | 18470357 | NULL | NULL | | BTREE | |
+---------+------------+--------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
Diese Abfrage dauert zwischen 30 Sekunden und 30 Minuten. Ich hoffe, ich kann die Dinge mit besseren Indizes oder vielleicht mit einer anderen Abfrage verbessern, aber ich kann es nicht herausfinden.
AKTUALISIEREN:
Auf Anraten der hilfreichen Kommentatoren habe ich einen Primärschlüssel erstellt und den Index hinzugefügt traffic_pk (time, type, type_id, id)
. Leider stellt sich heraus, dass die Kardinalität dieses neuen Index gleich / niedriger als mein ursprünglicher Index (basic_select) ist und MySQL weiterhin meinen ursprünglichen Schlüssel verwendet.
UPDATE 2:
Ich habe meinen ursprünglichen Index gelöscht basic_select
und jetzt EXPLAIN
zeigt der einen höheren rows
Wert, aber weniger Schritte in den EXTRA
Feldern. Auch die Ausführungszeit der Abfrage ist auf unter eine Minute gesunken! (immer noch etwas zu langsam, aber eine große Verbesserung!).
mysql> SHOW CREATE TABLE traffic_test \G;
*************************** 1. row ***************************
Table: traffic_test
Create Table: CREATE TABLE `traffic_test` (
`traffic_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`type` enum('v4_assignment','v4_host','v6_subnet','v6_assignment','v6_host') NOT NULL,
`type_id` int(11) unsigned NOT NULL,
`time` int(32) unsigned NOT NULL,
`bytesin` bigint(20) unsigned NOT NULL DEFAULT '0',
`bytesout` bigint(20) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`time`,`type`,`type_id`,`traffic_id`),
KEY `traffic_id_IDX` (`traffic_id`)
) ENGINE=InnoDB AUTO_INCREMENT=24545159 DEFAULT CHARSET=latin1
Die Indizes in der Tabelle:
mysql> SHOW INDEX FROM traffic;
+--------------+------------+----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+--------------+------------+----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| traffic_test | 0 | PRIMARY | 1 | time | A | 18 | NULL | NULL | | BTREE | |
| traffic_test | 0 | PRIMARY | 2 | type | A | 38412 | NULL | NULL | | BTREE | |
| traffic_test | 0 | PRIMARY | 3 | type_id | A | 24545609 | NULL | NULL | | BTREE | |
| traffic_test | 0 | PRIMARY | 4 | traffic_id | A | 24545609 | NULL | NULL | | BTREE | |
| traffic_test | 1 | traffic_id_IDX | 1 | traffic_id | A | 24545609 | NULL | NULL | | BTREE | |
+--------------+------------+----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
Außerdem habe ich die Abfrage vereinfacht, indem ich nicht Folgendes verwendet habe OR
:
SELECT SQL_NO_CACHE traffic.time, SUM(traffic.bytesin), SUM(traffic.bytesout)
FROM traffic
WHERE traffic.type LIKE 'v4_host' AND type_id IN (131,1974,1976,1514,1516,2767,2730,2731,2732,2733,2734,2769,2994,2709,1,4613,4614,4615,4616,326,1520,2652,1518,1521,1522,1523,1524,1525,2203,1515,1513,1467,1508,1973,1510,1975,1511,1475,1476,1468,1469,1470,1471,1472,1473,1500,1507,1478,1480,1481,1482,1483,1484,1485,1479,1486,1487,1488,1489,1490,1491,1495,1499,1494,2269,1474,1519,2204,2976,1922,1493,1492,1497,1496,1498,1501,1502,1503,1526,1509,1506)
AND traffic.time >= 1342181721
AND traffic.time < 1343391321
GROUP BY traffic.time ASC;
Alte Ausführung dieser Abfrage:
3980 rows in set (6 min 15.27 sec)
Neue Ausführungszeit:
3980 rows in set (24.80 sec)
EXPLAIN-Ausgabe:
+----+-------------+---------+-------+---------------+---------+---------+------+----------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+-------+---------------+---------+---------+------+----------+-------------+
| 1 | SIMPLE | traffic | range | PRIMARY | PRIMARY | 4 | NULL | 12272804 | Using where |
+----+-------------+---------+-------+---------------+---------+---------+------+----------+-------------+
Der Zeilenwert ist immer noch ziemlich hoch. Ich denke, ich kann dies verbessern, indem ich die Reihenfolge von type
und type_id
im Index ändere, da nur 4 Typen möglich sind und viele weitere type_ids.
Ist das eine richtige Annahme?