2016-10-20 3 views
-1

ich eine Abfrage haben Daten zwischen der aktuellen Zeit und INTERVAL 15 Minutenmysql query in großen Tabellen verbessern

Tabelle Anrufe 39.790.720 Elemente zu erhalten;

SELECT src,unique,dstchannel,chan,calldate 
FROM calls 
WHERE calldate BETWEEN (NOW() - INTERVAL 15 MINUTE) AND NOW() 
    AND (dstchannel LIKE '%TEXT1/%' 
     OR dstchannel LIKE '%TEXT2%' 
     OR dstchannel LIKE '%TEXT3%' 
     OR dstchannel REGEXP '^SIP/[[:digit:]]{10}-' 
     OR dstchannel LIKE '%TEXT4%' 
     OR dstchannel LIKE '%TEXT5%' 
     OR dstchannel LIKE '%TEXT6%' 
     OR dstchannel LIKE '%TEXT7%' 
    ) 
    AND lastdata NOT LIKE '%TEXT8%' 
LIMIT 39780720,39790720 


Query 1 row in set (1 min 7.38 sec) 

    +-------------+--------------+------+-----+---------------------+-------+ 
    | Field  | Type   | Null | Key | Default    | Extra | 
    +-------------+--------------+------+-----+---------------------+-------+ 
    | calldate | datetime  | NO |  | 0000-00-00 00:00:00 |  | 
    | colum1  | varchar(80) | NO |  |      |  | 
    | colum11  | varchar(80) | NO |  |      |  | 
    | src   | varchar(80) | NO |  |      |  | 
    | colum12  | varchar(80) | NO |  |      |  | 
    | chan  | varchar(80) | NO |  |      |  | 
    | dstchannel | varchar(80) | NO |  |      |  | 
    | colum2  | varchar(80) | NO |  |      |  | 
    | colum3  | varchar(80) | NO |  |      |  | 
    | colum4  | int(11)  | NO |  | 0     |  | 
    | colum5  | int(11)  | NO |  | 0     |  | 
    | colum6  | varchar(45) | NO |  |      |  | 
    | colum7  | int(11)  | NO |  | 0     |  | 
    | colum8  | varchar(20) | NO |  |      |  | 
    | colum9  | varchar(32) | NO |  |      |  | 
    | colum10  | varchar(255) | NO |  |      |  | 
    +-------------+--------------+------+-----+---------------------+-------+ 

wie Abfrage verbessern?

UPDATE

+----+-------------+-------+------+---------------+------+---------+------+----------+-------------+ 
| id | select_type | table | type | possible_keys | key | key_len | ref | rows  | Extra  | 
+----+-------------+-------+------+---------------+------+---------+------+----------+-------------+ 
| 1 | SIMPLE  | calls | ALL | NULL   | NULL | NULL | NULL | 39791545 | Using where | 
+----+-------------+-------+------+---------------+------+---------+------+----------+-------------+ 
+0

Das ist eine sehr allgemeine Frage, und ich würde Ihnen raten, 1) Ausführen EXPLAIN auf diese Abfrage und fügen Sie die Ergebnisse zu Ihrer Frage und 2) geben Sie Informationen über Indizes auf dieser Tabelle, die möglicherweise + die Tabelle existieren DDL. Abschließend noch ein Wort zur Hardware, auf der diese Abfrage ausgeführt wird. Vielleicht ist Ihr Server in der Praxis nur zu klein dimensioniert oder hat langsame Festplatten, zwei Faktoren, die für die Datenbankarbeit absolut tödlich sind. – fvu

+0

Meiner Erfahrung nach kann der Wechsel zu einem INNODB-Tabellentyp helfen, wenn dies nicht bereits der Fall ist. Außerdem sollte "calldate" wahrscheinlich einen nicht eindeutigen Index haben, wie auch 'dstchannel'. Die Spalten, die Sie in' WHERE'-Klauseln verwenden. Ähm ... Ich verstehe diese LIMIT-Klausel allerdings nicht. –

+0

% TEXT1% ist Referenz für die Kombination Variable in meinem Skript, aber hier nicht schreiben ... im meine Frage aktualisieren – pedroooo

Antwort

0

Heilige extreme Abfrage Pessimierung, Batman!

Ihre Abfrage sieht wie folgt aus:

SELECT src,unique,dstchannel,chan,calldate 
    from calls 
WHERE calldate BETWEEN (NOW() - INTERVAL 15 MINUTE) AND NOW() 
    AND ( dstchannel LIKE '%TEXT1/%' 
     OR dstchannel LIKE '%TEXT2%' 
     OR dstchannel LIKE '%TEXT3%' 
     OR dstchannel REGEXP '^SIP/[[:digit:]]{10}-' 
     OR dstchannel LIKE '%TEXT4%' 
     OR dstchannel LIKE '%TEXT5%' 
     OR dstchannel LIKE '%TEXT6%' 
     OR dstchannel LIKE '%TEXT7%') 
    AND lastdata NOT LIKE '%TEXT8%' 
LIMIT 39780720,39790720 

Sie diese Abfrage leicht, indem sie einen Index für calldate verbessern können. Ihre calldate BETWEEN (NOW() - INTERVAL 15 MINUTE) AND NOW() Klausel wird einige Verbesserungen davon sehen.

Aber es wird nie so schnell sein, wie Sie es strukturiert haben. Warum nicht?

  1. dstchannel LIKE '%TEXT2%' und ähnliche Klauseln nicht, jemals, ausnutzen Indizes. Warum nicht? Weil sie eine ganze Spalte nach einer Zeichenfolge durchsuchen müssen und nicht nur die ersten Zeichen der Spalte betrachten können. Beachten Sie, dass dstchannel LIKE 'TEXT2%' einen Index mit wahlfreiem Zugriff ausnutzen kann. Es ist eine verankerte Suche, beginnend am Anfang der Spalte.
  2. lastdata NOT LIKE '%TEXT8%' hat das gleiche Problem. Aber selbst wenn es lastdata NOT LIKE TEXT8% wäre, würde es dir Probleme bereiten, weil jede Reihe untersucht werden muss. Der Server kann nicht herausfinden, wie auf eine Reihe von Daten zugegriffen wird.
  3. OR Klauseln sind eine Katastrophe. Sie veranlassen MySQL häufig, dieselben Daten mehrmals zu scannen.
  4. LIMIT 39780720,39790720 zwingt MySQL, durch fast vierzig Megarows in seiner Ergebnismenge durchzuspielen, die sie verwirft. Das brennt MySQL-Serverspeicher, Prozessorzeit und Festplatten-IO, nur um es zu verwerfen. Können Sie irgendwie eine ORDER BY-Klausel auf clevere Weise verwenden, damit Sie die ersten Zeilen der Ergebnismenge abrufen können, statt sie zu überspringen?

Was können Sie tun, um dies zu beheben? Ihre beste Wette ist es, diese ganze LIKE '%something%' Geschäft zu überdenken.

Wenn Sie das nicht können, können Sie vielleicht versuchen, Ihre Anfrage wie folgt neu zu schreiben. Ich nehme an, Sie haben einen Primärschlüssel auf Ihrer calls Tabelle. Ich werde es id nennen.

SELECT a.src, a.unique, a.dstchannel, a.chan, a.calldate 
    FROM a.calls 
    JOIN (
      SELECT id FROM calls 
      WHERE calldate BETWEEN (NOW() - INTERVAL 15 MINUTE) AND NOW() 
      AND dstchannel LIKE '%TEXT1/%' 
      UNION 
      SELECT id FROM calls 
      WHERE calldate BETWEEN (NOW() - INTERVAL 15 MINUTE) AND NOW() 
      AND dstchannel LIKE '%TEXT2/%' 
      UNION 
      SELECT id FROM calls 
      WHERE calldate BETWEEN (NOW() - INTERVAL 15 MINUTE) AND NOW() 
      AND dstchannel LIKE '%TEXT3/%' 
      UNION 
        etcetera. 
      UNION 
      SELECT id FROM calls 
      WHERE calldate BETWEEN (NOW() - INTERVAL 15 MINUTE) AND NOW() 
      AND dstchannel REGEXP '^SIP/[[:digit:]]{10}-' 
      UNION 
        etcetera. 
     ) b ON a.id = b.id 
WHERE lastdata NOT LIKE '%TEXT8%' 

Dann auf den Säulen (calldate, dstchannel, id) eine Verbindung Index für die Tabelle erstellen. Dann kann der MySQL-Abfrageplaner diesen Index verwenden, um den geeigneten Bereich calldate zu finden, dann die im Index gespeicherten dstchannel Werte nach Übereinstimmungen durchsuchen und dann die ID-Werte extrahieren. Dann wird es sich umdrehen und in den JOIN, diese id Werte verwenden, um genau die Daten zu holen, die Sie von der Haupttabelle benötigen.

Wenn Sie mit Anrufdetails arbeiten, müssen Sie die Indizierung wirklich verstehen.Lesen Sie dies: http://use-the-index-luke.com/

+0

Nicht _ "alle außer einer Zeile" _. 'LIMIT X, Y' wird bis zu Y Zeilen, beginnend bei Offset X. Auch bei den Start-Wildcards bezweifle ich, dass alle diese' UNION's besser sind als seine 'OR' Liste; Das Hauptproblem bei OR-Listen ist die Nichtbeachtung von Indizes, die die Start-Platzhalter selbst von den einfacheren UNION-Unterabfragen ausschließen. – Uueerdo

+0

Hoppla, ich habe die Tatsache übersehen, dass die beiden Nummern in LIMIT groß waren. dachte, sie wären die gleiche Nummer. Fest. Soweit der UNION-Kram geht, sollte der von mir vorgeschlagene Deckungsindex einen engen Index-Scan für jede einzelne 'LIKE'% stuff% '' Jagd ermöglichen, aber nur für die fünfzehnminütige Teilmenge der Anrufdetaildatensätze. Enge Scans der entsprechenden Indizes sparen eine Menge Zeit für den IO-Kanal (Datenträger) gegenüber Tabellenscans. –

+0

Der Abstand zwischen X und Y spielt keine Rolle, Y ist ein Count-Specifier (obwohl op vielleicht nur 10k zurückgeben soll). Aber yeah, ich arbeite unter der Annahme, dass MySQL nur die OR-Bedingungen über die Teilmenge ausführen wird, die durch die Calldate-Bedingung definiert ist; Wenn die OR-Bedingungen damit enden, könnte die UNION besser sein. – Uueerdo