Ich arbeite an einem Schema für ein Analysesystem, das die Nutzungszeiten verfolgt, und es besteht die Notwendigkeit, die Gesamtnutzungszeit in einem bestimmten Datumsbereich anzuzeigen.
Um ein einfaches Beispiel zu nennen: Diese Art von Abfrage wird häufig ausgeführt:
select sum(diff_ms) from writetest_table where time_on > ("2015-07-13 15:11:56");
Diese Abfrage dauert in der Regel etwa 7 Sekunden für eine Tabelle, die stark gefüllt ist. Es hat ~ 35 Millionen Zeilen, MyISAM unter MySQL läuft unter Amazon RDS (db.m3.xlarge).
Wenn Sie die WHERE-Klausel entfernen, dauert die Abfrage nur 4 Sekunden, und durch Hinzufügen einer zweiten Klausel (time_off> XXX) werden zusätzliche 1,5 Sekunden hinzugefügt, wodurch sich die Abfragezeit auf 8,5 Sekunden erhöht.
Da ich weiß, dass diese Art von Abfragen häufig durchgeführt wird, möchte ich die Dinge so optimieren, dass sie schneller sind, idealerweise unter 5 Sekunden.
Ich habe zunächst einen Index für time_on hinzugefügt, und obwohl dies eine WHERE "=" - Abfrage drastisch beschleunigte, hatte dies keine Auswirkungen auf die ">" - Abfrage. Gibt es eine Möglichkeit, einen Index zu erstellen, der die WHERE ">" - oder "<" - Abfragen beschleunigt?
Oder wenn es andere Vorschläge zur Durchführung dieser Art von Abfrage gibt, lassen Sie es mich bitte wissen.
Hinweis: Ich verwende das Feld "diff_ms" als Denormalisierungsschritt (es entspricht time_off - time_on), wodurch die Leistung der Aggregation um etwa 30% bis 40% verbessert wird.
Ich erstelle den Index mit diesem Befehl:
ALTER TABLE writetest_table ADD INDEX time_on (time_on) USING BTREE;
Wenn Sie "EXPLAIN" für die ursprüngliche Abfrage ausführen (mit "time_on>"), wird angegeben, dass time_on ein "möglicher_ Schlüssel" und der select_type "EINFACH" ist. In der Spalte "extra" steht "Using where" und "type" ist "ALL". Nach dem Hinzufügen des Index wird in der Tabelle angegeben, dass "time_on" der Schlüsseltyp "MUL" ist, was korrekt erscheint, da dieselbe Zeit zweimal vorhanden sein kann.
Hier ist das Tabellenschema:
CREATE TABLE `writetest_table` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`sessionID` int(11) DEFAULT NULL,
`time_on` timestamp NULL DEFAULT NULL,
`time_off` timestamp NULL DEFAULT NULL,
`diff_ms` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `time_on` (`time_on`)
) ENGINE=MyISAM AUTO_INCREMENT=50410902 DEFAULT CHARSET=latin1;
UPDATE: Ich habe den folgenden Index basierend auf der Antwort von ypercube erstellt, aber dies erhöht die Abfragezeit für die erste Abfrage auf ungefähr 17 Sekunden!
ALTER TABLE writetest_table ADD INDEX time_on__diff_ms__ix (time_on, diff_ms) ;
UPDATE 2: EXPLAIN-Ausgabe
mysql> explain select sum(diff_ms) from writetest_table where time_on > '2015-07-13 15:11:56';
+----+-------------+---------------------+-------+----------------------+----------------------+---------+------+----------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------------------+-------+----------------------+----------------------+---------+------+----------+--------------------------+
| 1 | SIMPLE | writetest_table_old | index | time_on__diff_ms__ix | time_on__diff_ms__ix | 10 | NULL | 35831102 | Using where; Using index |
+----+-------------+---------------------+-------+----------------------+----------------------+---------+------+----------+--------------------------+
1 row in set (0.00 sec)
Update 3: Ergebnis der angeforderten Abfrage
mysql> SELECT time_on FROM writetest_table ORDER BY time_on LIMIT 1;
+---------------------+
| time_on |
+---------------------+
| 2015-07-13 15:11:56 |
+---------------------+
1 row in set (0.01 sec)
SELECT COUNT(*), COUNT(diff_ms) FROM writetest_table;
writetest_table_old
", während die Abfrage hat from writetest_table
. Ist das ein Tippfehler oder führen Sie die Abfrage in einer anderen Tabelle aus?
time_on
unddiff_ms
)? Was passiert, wenn Sie der Abfrage hinzufügenWHERE ... AND diff_ms IS NOT NULL
?