2010-08-22 1 views
5

Suposse Ich habe die nächste MySQL-Datenbank mit 500.000 Zeilen:Optimieren dieser Abfrage abrufen Benutzer aus einer MySQL-DB mit 500.000 Benutzern und eine bedingte

users 
{ 
    id  - int, 
    name  - varchar(32), 
    verified - tinyint(1) 
} 

primary { id } 
index { verified } 

Und ich brauche letzten 20 nicht verifizierte Nutzer zu erhalten, so verwende ich die nächste Abfrage:

SELECT * FROM users WHERE verified != 1 ORDER BY id DESC LIMIT 20 

Aber es dauert 1,2 Sekunden abzuschließen.

Wie kann ich es optimieren? Oder erhalten Sie das gleiche Ergebnis mit anderen Weg in php.

[EDIT]

ID ist der Primärindex, VERIFIED auch ein Index

[EDIT 2] ist

CREATE TABLE `users` (
    `id` int(10) unsigned NOT NULL auto_increment COMMENT 'Identificador del usuario', 
    `login` varchar(32) NOT NULL COMMENT 'Login para entrar', 
    `password` varchar(32) NOT NULL COMMENT 'Password para entrar', 
    `email` varchar(384) NOT NULL COMMENT 'Email del usuario', 
    `group_id` int(10) unsigned default NULL, 
    `display_name` varchar(64) NOT NULL COMMENT 'Nombre para mostrar', 
    `email_verified` tinyint(3) unsigned default '0' COMMENT 'Email verificado?', 
    `banned` tinyint(3) unsigned default '0' COMMENT 'Baneado?', 
    `admin` tinyint(3) unsigned default '0' COMMENT 'Es un super administrador del sitio?', 
    `registered` int(10) unsigned NOT NULL COMMENT 'Fecha del registro', 
    PRIMARY KEY (`id`), 
    KEY `login` (`login`), 
    KEY `password` (`password`), 
    KEY `email` (`email`(333)), 
    KEY `group_id` (`group_id`), 
    KEY `email_verified` (`email_verified`), 
    KEY `banned` (`banned`), 
    KEY `admin` (`admin`), 
    KEY `registered` (`registered`) 
) ENGINE=MyISAM AUTO_INCREMENT=500002 DEFAULT CHARSET=utf8; 

[EDIT 3]

EXPLAIN(SELECT id FROM users WHERE email_verified != 1 ORDER BY id DESC LIMIT 20) 

ist

id: 1 
select_type: SIMPLE 
table: users  
type: range 
possible_keys: email_verified 
key: email_verified 
key_len: 2  
ref: 
rows: 345195  
Extra: Using where; Using filesort 

Und ein Profil der Abfrage:

Status Duration 
(initialization) 0.0000307 
Opening tables 0.000003 
System lock 0.0000017 
Table lock 0.0000042 
init 0.000017 
optimizing 0.0000077 
statistics 0.000097 
preparing 0.000054 
executing 0.0000007 
Sorting result 1.2321507 
Sending data 0.000272 
end 0.000004 
query end 0.0000025 
freeing items 0.0000099 
closing tables 0.0000025 
logging slow query 0.0000005 
+0

Das sollte wie 0 Sekunden dauern, um zu berechnen. Sind Sie sicher, dass Sie den Index überprüft haben und nicht (id, verifiziert)? – Johan

+0

@Johan: Primärschlüssel auf ID und ein einfacher Schlüssel auf VERIFIZIERT, die Sache, die mehr Zeit ist VERIFIZIERT! = 1 – Wiliam

+0

Können Sie die Abfrage in eine 'EXPLAIN()' wickeln und die Ergebnisse veröffentlichen? –

Antwort

3

Sie benötigen einen Index, der dann EXPLAIN(SELECT id FROM users WHERE email_verified != 1 ORDER BY id DESC LIMIT 20) druckt

+----+-------------+-------+-------+----------------+---------+---------+------+------+--------------------------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref | rows | Extra     | 
+----+-------------+-------+-------+----------------+---------+---------+------+------+--------------------------+ 
| 1 | SIMPLE  | users | index | email_verified | Index 4 | 6  | NULL | 5 | Using where; Using index | 
+----+-------------+-------+-------+----------------+---------+---------+------+------+--------------------------+ 

Vor allem die langsame Using filesort weg beide id und email_verfied

KEY `foo` (`id`,`email_verified`) 

umfasst.

+0

Jetzt ist eine kleine Sekunde Abfrage :) Warum das funktioniert? Wie funktioniert dieser Index mit 2 Spalten? – Wiliam

+0

AH, dies funktioniert nur, wenn Sie den vorherigen "email_verified" -Index löschen. – Wiliam

+0

das ist faszinierend. widerspricht aber völlig meiner Intuition, wie Indizes funktionieren. Volker, kannst du vielleicht erklären, warum zwei getrennte Indizes es nicht geschafft haben? – Nicolas78

0

einen Index für verified hinzufügen, und verwenden Sie die folgende Abfrage statt:

SELECT * FROM users WHERE verified = 0 ORDER BY id DESC LIMIT 20 

(unter der Annahme, dass ca n sei 0 oder 1).

Wenn Sie <> anstelle von = verwenden, ignoriert MySQL Indizes und führt einen vollständigen Tabellenscan durch, wodurch Ihre Abfrage dramatisch verlangsamt wird.

+0

das gleiche Ergebnis mit = 0, 1,2 Sekunden – Wiliam

1

Fügen Sie einen neuen Index hinzu: {verified, id}. Andernfalls müssen Sie einen vollständigen Tabellenscan durchführen.

+0

Es wird eine vollständige Tabelle scannen, unabhängig davon, ob er den '<>' Operator behält. – quantumSoup

+0

Das war eine gültige Lösung, aber ich habe das zum ersten Mal nicht verstanden. Vielen Dank. – Wiliam

+0

@quantumSoup: Es funktioniert mit <> – Wiliam

0

Bearbeiten: Ihre Abfrage ist langsam, wenn Sie das Ergebnis sortieren - dies kann durch den Index verursacht werden, wenn die angegebene Reihenfolge falsch ist. MySQL verwendet standardmäßig die ASC-Sortierreihenfolge.

Siehe MySQL-Handbuch: http://dev.mysql.com/doc/refman/5.0/en/create-index.html

An index_col_name specification can end with ASC or DESC. These keywords are permitted for future extensions for specifying ascending or descending index value storage. Currently, they are parsed but ignored; index values are always stored in ascending order. 

Bitte diese Links überprüfen:

http://www.ozzu.com/programming-forum/mysql-index-cardinality-t71876.html

Why does the cardinality of an index in MySQL remain unchanged when I add a new index?

Sie könnten Ihren Index optimieren müssen:

OPTIMIZE TABLE users; 

Es könnte sinnvoll sein, einen Index mit ID und verifiziert kombiniert zu haben.

Sie haben viele Indizes - das könnte mysql verwirren.

+0

OPTIMIZE hat nicht funktioniert, daher Kardinalität des verifizierten Index ist 2 – Wiliam

+0

Ah, es gibt viele Index, weil ich alle sie verwenden. – Wiliam

0

Erstellen Sie eine weitere Tabelle ohne verifizierte Benutzer. Dies ist die letzte Lösung, die ich verwenden möchte.

0

Wie quantumSoup Antwort, sondern einen Index auf (überprüft, id) (Angenommen, Sie sind unter der Annahme, id immer steigt)

SELECT * FROM users WHERE verified = 0 ORDER BY id DESC LIMIT 20 

ich auch gehe davon aus, dass überprüft nur 0,1

Verwenden Sie nicht die <> als das den Index besiegt. Setzen Sie "Verified" zuerst in den Index, damit ein einfacher Bereichsscan durchgeführt werden kann.

+0

Warum vor der ID verifiziert? – Wiliam

+0

Da Sie den ersten Teil des Index den konstanten Ausdruck (= 0) machen möchten, damit es einen Bereichsscan durchführen kann. – MarkR

0

Wenn nur Werte für geeichte sind 0 und 1, erstellen Sie einen Index für

(verified, id) 

und schreiben Sie Ihre Anfrage:

SELECT * 
FROM users 
WHERE verified = 0 
ORDER BY 
     id DESC 
LIMIT 20 

Diese Lösung verwendet einen einzelnen Index sowohl für die Filterung und für die Bestellung (und funktioniert gut, unabhängig von Ihrer Datenverteilung).

Verwandte Themen