Das Thema:
Wir haben eine soziale Website, auf der Mitglieder sich gegenseitig auf Kompatibilität oder Übereinstimmung bewerten können. Diese user_match_ratings
Tabelle enthält über 220 Millionen Zeilen (9 Gig-Daten oder fast 20 Gig-Indizes). Abfragen für diese Tabelle werden routinemäßig in slow.log angezeigt (Schwellwert> 2 Sekunden) und sind die am häufigsten protokollierte langsame Abfrage im System:
Query_time: 3 Lock_time: 0 Rows_sent: 3 Rows_examined: 1051
"select rating, count(*) as tally from user_match_ratings where rated_user_id = 395357 group by rating;"
Query_time: 4 Lock_time: 0 Rows_sent: 3 Rows_examined: 1294
"select rating, count(*) as tally from user_match_ratings where rated_user_id = 4182969 group by rating;"
Query_time: 3 Lock_time: 0 Rows_sent: 3 Rows_examined: 446
"select rating, count(*) as tally from user_match_ratings where rated_user_id = 630148 group by rating;"
Query_time: 5 Lock_time: 0 Rows_sent: 3 Rows_examined: 3788
"select rating, count(*) as tally from user_match_ratings where rated_user_id = 1835698 group by rating;"
Query_time: 17 Lock_time: 0 Rows_sent: 3 Rows_examined: 4311
"select rating, count(*) as tally from user_match_ratings where rated_user_id = 1269322 group by rating;"
MySQL-Version:
- Protokollversion: 10
- Version: 5.0.77-log
- Version BDB: Sleepycat Software: Berkeley DB 4.1.24: (29. Januar 2009)
- version compile machine: x86_64 version_compile_os: redhat-linux-gnu
Tischinfo:
SHOW COLUMNS FROM user_match_ratings;
Gibt:
╔═══════════════╦════════════╦════╦═════╦════════╦════════════════╗
║ id ║ int(11) ║ NO ║ PRI ║ NULL ║ auto_increment ║
║ rater_user_id ║ int(11) ║ NO ║ MUL ║ NULL ║ ║
║ rated_user_id ║ int(11) ║ NO ║ MUL ║ NULL ║ ║
║ rating ║ varchar(1) ║ NO ║ ║ NULL ║ ║
║ created_at ║ datetime ║ NO ║ ║ NULL ║ ║
╚═══════════════╩════════════╩════╩═════╩════════╩════════════════╝
Beispielabfrage:
select * from mutual_match_ratings where id=221673540;
gibt:
╔═══════════╦═══════════════╦═══════════════╦════════╦══════════════════════╗
║ id ║ rater_user_id ║ rated_user_id ║ rating ║ created_at ║
╠═══════════╬═══════════════╬═══════════════╬════════╬══════════════════════╣
║ 221673540 ║ 5699713 ║ 3890950 ║ N ║ 2013-04-09 13:00:38 ║
╚═══════════╩═══════════════╩═══════════════╩════════╩══════════════════════╝
Indizes
In der Tabelle sind 3 Indizes eingerichtet:
- einzelner Index auf
rated_user_id
- zusammengesetzter Index auf
rater_user_id
undcreated_at
- zusammengesetzter Index auf
rated_user_id
undrater_user_id
zeige Index von user_match_ratings;
gibt:
╔════════════════════╦════════════╦═══════════════════════════╦══════════════╦═══════════════╦═══════════╦═════════════╦══════════╦════════╦═════════════════════════╦════════════╦══════════════════╗
║ Table ║ Non_unique ║ Key_name ║ Seq_in_index ║ Column_name ║ Collation ║ Cardinality ║ Sub_part ║ Packed ║ Null ║ Index_type ║ Comment ║
╠════════════════════╬════════════╬═══════════════════════════╬══════════════╬═══════════════╬═══════════╬═════════════╬══════════╬════════╬═════════════════════════╬════════════╬══════════════════╣
║ user_match_ratings ║ 0 ║ PRIMARY ║ 1 ║ id ║ A ║ 220781193 ║ NULL ║ NULL ║ BTREE ║ ║ ║
║ user_match_ratings ║ 1 ║ user_match_ratings_index1 ║ 1 ║ rater_user_id ║ A ║ 11039059 ║ NULL ║ NULL ║ BTREE ║ ║ ║
║ user_match_ratings ║ 1 ║ user_match_ratings_index1 ║ 2 ║ created_at ║ A ║ 220781193 ║ NULL ║ NULL ║ BTREE ║ ║ ║
║ user_match_ratings ║ 1 ║ user_match_ratings_index2 ║ 1 ║ rated_user_id ║ A ║ 4014203 ║ NULL ║ NULL ║ BTREE ║ ║ ║
║ user_match_ratings ║ 1 ║ user_match_ratings_index2 ║ 2 ║ rater_user_id ║ A ║ 220781193 ║ NULL ║ NULL ║ BTREE ║ ║ ║
║ user_match_ratings ║ 1 ║ user_match_ratings_index3 ║ 1 ║ rated_user_id ║ A ║ 2480687 ║ NULL ║ NULL ║ BTREE ║ ║ ║
╚════════════════════╩════════════╩═══════════════════════════╩══════════════╩═══════════════╩═══════════╩═════════════╩══════════╩════════╩═════════════════════════╩════════════╩══════════════════╝
Selbst mit den Indizes sind diese Abfragen langsam.
Meine Frage:
Würde das Trennen dieser Tabelle / Daten in eine andere Datenbank auf einem Server, der über genügend RAM verfügt, um diese Daten im Speicher zu speichern, diese Abfragen beschleunigen? Enthalten die Tabellen / Indizes irgendetwas, das wir verbessern können, um diese Abfragen zu beschleunigen?
Derzeit haben wir 16 GB Speicher; Wir möchten jedoch entweder die vorhandene Maschine auf 32 GB aufrüsten oder eine neue Maschine mit mindestens so vielen, möglicherweise auch Solid-State-Laufwerken hinzufügen.
quelle
SELECT QUERY
. Würden Sie bitte vorschlagen? PS Ihre Frage hat mich gezwungen, dieser Community beizutreten (y);)Antworten:
Gedanken zum Thema, in zufälliger Reihenfolge geworfen:
Der offensichtliche Index für diese Abfrage ist:
(rated_user_id, rating)
. Eine Abfrage, die Daten für nur einen der Millionen Benutzer abruft und 17 Sekunden benötigt, führt zu einem Fehler: Aus dem(rated_user_id, rater_user_id)
Index lesen und dann aus der Tabelle die (Hundert- bis Tausend-) Werte für dierating
Spalte lesen , wierating
es in keinem Index der Fall ist. Die Abfrage muss also viele Zeilen der Tabelle lesen, die sich an vielen verschiedenen Speicherorten befinden.Versuchen Sie vor dem Hinzufügen zahlreicher Indizes zu den Tabellen, die Leistung der gesamten Datenbank, die Gesamtheit der langsamen Abfragen zu analysieren, und überprüfen Sie erneut die Auswahl der Datentypen, die von Ihnen verwendete Engine und die Konfigurationseinstellungen.
Ziehen Sie die Umstellung auf eine neuere Version von MySQL, 5.1, 5.5 oder sogar 5.6 in Betracht (auch: Percona und MariaDB-Versionen). Mehrere Vorteile wurden behoben, der Optimierer wurde verbessert und Sie können den unteren Schwellenwert für langsame Abfragen auf weniger als 1 Sekunde festlegen (wie 10 Millisekunden). Dadurch erhalten Sie weitaus bessere Informationen zu langsamen Abfragen.
Die Wahl für den Datentyp von
rating
ist komisch.VARCHAR(1)
? Warum nichtCHAR(1)
? Warum nichtTINYINT
? Dies spart Platz, sowohl in der Tabelle als auch in den Indizes, die diese Spalte enthalten (werden). Eine varchar (1) -Spalte benötigt ein Byte mehr als char (1), und wenn es sich um utf8 handelt, benötigen die (var) char -Spalten 3 (oder 4) Byte anstelle von 1 (tinyint).quelle
Ich habe Tische für die Bundesregierung mit teilweise 60 Millionen Platten bearbeitet.
Wir hatten viele dieser Tische.
Und wir mussten viele Male die Gesamtanzahl der Zeilen aus einer Tabelle kennen.
Nach einem Gespräch mit Oracle- und Microsoft-Programmierern waren wir nicht so glücklich ...
Deshalb haben wir, die Gruppe der Datenbankprogrammierer, entschieden, dass in jeder Tabelle immer der Datensatz ist, in dem die gesamten Datensatznummern gespeichert sind. Wir haben diese Nummer abhängig von INSERT- oder DELETE-Zeilen aktualisiert.
Wir haben alle anderen Möglichkeiten ausprobiert. Dies ist bei weitem der schnellste Weg.
Wir verwenden diesen Weg jetzt seit 1998 und hatten in all unseren Multi-Millionen-Datensatztabellen nie eine falsche Anzahl von Zeilen.
quelle
count(*)
hat einige Verbesserungen.Ich werde versuchen, nach Bewertungstypen zu partitionieren, wie zum Beispiel:
gegenseitige_Match_Ratings_N, gegenseitige_Match_Ratings_S, etc.
Sie sollten für jeden Typ eine Abfrage durchführen, dies ist jedoch möglicherweise schneller als auf die andere Weise. Versuche es.
Dies setzt voraus, dass Sie eine feste Anzahl von Bewertungstypen haben und diese Tabelle nicht für andere Abfragen benötigen, die mit dieser neuen Struktur am schlimmsten wären.
In diesem Fall sollten Sie nach einem anderen Ansatz suchen oder zwei Kopien der Tabelle (Ihre ursprüngliche Tabelle und partitionierte) verwalten, wenn dies in Bezug auf Speicherplatz und Wartbarkeit (oder Anwendungslogik) erschwinglich ist.
quelle