2016-03-24 4 views
11

I Tabelle mit 10 Millionen Zeilen mit 2 GB Daten Auswahl IN LIFO Formatdaten in MySQL ist langsam2 GB-Tabelle mit 10 Millionen Zeilen wählen späte Paginierung ist langsam

Tabelle Motor ist = InnoDB

Tabelle hat eine Primärschlüssel und ein eindeutigen Schlüssel

SELECT * FROM link LIMIT 999999 , 50; 

Wie verbessere ich die Leistung der Tabelle. ?

Tabellenstruktur

id int(11) NO PRI NULL auto_increment 
url varchar(255) NO UNI NULL  
website varchar(100) NO  NULL  
state varchar(10) NO  NULL  
type varchar(100) NO  NULL  
prio varchar(100) YES  NULL  
change varchar(100) YES  NULL  
last varchar(100) YES  NULL 

HINWEIS: SELECT * FROM link LIMIT 1 , 50; wird .9ms nimmt aber die aktuellen SQL 1000ms seine 100 Mal nehmen mehr

+0

Sie alles, was Sie die Spalten müssen aus der Tabelle können Sie auf slowness- erarbeiten, wie es lange dauert? Was ist der Datentyp der Spalten? – Sanj

+0

können Sie unter Abfrage ausführen und das Ergebnis 'EXPLAIN SELECT * FROM Link LIMIT 999999, 50 einfügen;' –

+0

seine fast nehmen Sie 1 sec aber, wenn keine der Reihe wird noch größer dann Zeit holen Zeit mehr SELECT * sein FROM Link LIMIT 1, 50; nimmt .9ms aber gegenwärtiges sql nimmt 1000ms seine 100 Zeit, die mehr nimmt. –

Antwort

4

Dies ist höchstwahrscheinlich ist darauf zurückzuführen, "early Reihe Lookup" nimmt

MySQL kann gezwungen werden, "Late-Row-Lookup" zu machen. Versuchen Sie unter Abfrage

SELECT l.* 
FROM (
     SELECT id 
     FROM link 
     ORDER BY 
       id 
     LIMIT 999999 , 50 
     ) q 
JOIN link l 
ON  l.id = q.id 

prüfen Sie diesen Artikel

MySQL limit clause and rate low lookups

+2

Ich glaube nicht, bis ich es selbst getestet habe. In meinem Test war es 50% Leistung zu gewinnen. Warum kann MySQL solche Abfragen immer noch nicht ohne Hilfe optimieren? –

+0

@PaulSpiegel: Es war auch eine Überraschung für mich, als ich es zum ersten Mal sah. – Sanj

0

Ich habe meine SQL-Abfrage auf diese aktualisiert und dies nimmt weniger Zeit.

SELECT * FROM link ORDER BY id LIMIT 999999 , 50 ; 
5

Für die Weiter und Zurück Tasten können Sie eine WHERE Klausel statt OFFSET verwenden.

Beispiel (mit LIMIT 10 - Beispieldaten siehe unten): Sie befinden sich auf einigen Seite sind, die Sie 10 Zeilen mit den ids [2522,2520,2514,2513,2509,2508,2506,2504,2497,2496] zeigt. Dies ist in meinem Fall erstellt mit

select * 
from link l 
order by l.id desc 
limit 10 
offset 999000 

Für die nächste Seite Sie

limit 10 
offset 999010 

immer Reihen mit ids [2495,2494,2493,2492,2491,2487,2483,2481,2479,2475] verwenden würden.

Für die vorherige Seite würden Sie

limit 10 
offset 998990 

immer Reihen mit [2542,2541,2540,2538,2535,2533,2530,2527,2525,2524] IDs verwenden.

Alle obigen Abfragen werden in 500 ms ausgeführt. Mit dem von Sanj vorgeschlagenen "Trick" dauert es noch 250 ms.

nun mit der angegebenen Seite mit minId=2496 und maxId=2522 können wir Anfragen für die und Letzte Tasten Als Nächstes erstellen Sie die WHERE-Klausel.

Schaltfläche Weiter:

select * 
from link l 
where l.id < :minId -- =2496 
order by l.id desc 
limit 10 

Resultierende ids: [2495,2494,2493,2492,2491,2487,2483,2481,2479,2475].

Zurück-Taste:

select * 
from link l 
where l.id > :maxId -- =2522 
order by l.id asc 
limit 10 

Resultierende ids: [2524,2525,2527,2530,2533,2535,2538,2540,2541,2542].

die Reihenfolge umkehren möchten Sie die Abfrage in einem subselect verwenden können:

select * 
from (
    select * 
    from link l 
    where l.id > 2522 
    order by l.id asc 
    limit 10 
) sub 
order by id desc 

Resultierende ids: [2542,2541,2540,2538,2535,2533,2530,2527,2525,2524].

Diese Abfragen werden in "keine Zeit" (weniger als 1 ms) ausgeführt und liefern das gleiche Ergebnis.

Sie können diese Lösung nicht zum Erstellen von Seitenzahlen verwenden. Aber ich glaube nicht, dass Sie 200K Seitenzahlen ausgeben werden.

Testdaten:

Daten für das Beispiel verwendet und Benchmarks wurden mit

erstellt
CREATE TABLE `link` (
    `id` INT(11) NOT NULL AUTO_INCREMENT, 
    `url` VARCHAR(255) NOT NULL, 
    `website` VARCHAR(100) NULL DEFAULT NULL, 
    `state` VARCHAR(10) NULL DEFAULT NULL, 
    `type` VARCHAR(100) NULL DEFAULT NULL, 
    `prio` VARCHAR(100) NULL DEFAULT NULL, 
    `change` VARCHAR(100) NULL DEFAULT NULL, 
    `last` VARCHAR(100) NULL DEFAULT NULL, 
    PRIMARY KEY (`id`), 
    UNIQUE INDEX `url` (`url`) 
) COLLATE='utf8_general_ci' ENGINE=InnoDB; 

insert into link 
    select i.id 
     , concat(id, '-', rand()) url 
     , rand() website 
     , rand() state 
     , rand() `type` 
     , rand() prio 
     , rand() `change` 
     , rand() `last` 
    from test._dummy_indexes_2p23 i 
    where i.id <= 2000000 
     and rand() < 0.5 

wo test._dummy_indexes_2p23 eine Tabelle enthält, ist 2^23-IDs (etwa 8 M). So enthalten die Daten etwa 1M Zeilen, die nach jeder zweiten ID zufällig fehlen. Tischgröße: 228 MB

0

Vor allem ohne jede Ordnung auf dem Tisch läuft nicht garantiert Ihre Abfrage die gleichen Daten zurück, wenn zweimal läuft. Es ist besser, eine ORDER BY Klausel hinzuzufügen. Nehmen Sie id als einen guten Kandidaten, da es Ihr Primärschlüssel ist und einzigartig scheint (wie es ein auto_increment Wert ist).

Sie könnten dies als Basis verwenden:

SELECT * FROM link ORDER BY id LIMIT 50; 

Dies gibt Ihnen die ersten 50 Zeilen in Ihrer Tabelle.

Jetzt für die nächsten 50 Zeilen, anstatt OFFSET zu verwenden, konnten wir unseren letzten Standort in der Abfrage speichern.

Sie würden die ID aus der letzten Reihe letzte ID aus der vorherige Abfrage speichern und es in der nächsten Abfrage verwenden:

SELECT * FROM link WHERE id > last_id ORDER BY id LIMIT 50; 

Dies Ihnen die nächsten 50 Zeilen nach dem letzten id geben.

Der Grund Ihrer Anfrage langsam auf hohe Werte von OFFSET läuft, weil MySQL auf alle Zeilen in der gegebenen OFFSET und gibt die letzten LIMIT Anzahl der Zeilen zu laufen hat. Das heißt, je größer OFFSET ist, desto langsamer wird die Abfrage ausgeführt.

Die Lösung, die ich oben gezeigt habe, hängt nicht von OFFSET ab, daher wird die Abfrage unabhängig von der aktuellen Seite mit derselben Geschwindigkeit ausgeführt.

Sehen Sie diese nützlichen Artikel auch, dass ein paar andere Optionen erklärt Sie wählen können: http://www.iheavy.com/2013/06/19/3-ways-to-optimize-for-paging-in-mysql/