2010-04-06 6 views
5

Ich habe eine Tabelle, die einige grundlegende Daten über Besucher Sitzungen auf Websites von Drittanbietern speichert. Dies ist seine Struktur:Mysql-Index-Optimierung für eine Tabelle mit mehreren Indizes, die einige der gleichen Spalten indexieren

id, site_id, unixtime, unixtime_last, ip_address, uid 

Es vier Indizes sind: id, site_id/unixtime, site_id/ip_address und site_id/uid

Es gibt viele verschiedene Arten von Möglichkeiten, wie wir diese Tabelle abzufragen, und alle von ihnen sind spezifisch für die Webseitenadresse. Der Index mit unixtime wird verwendet, um die Liste der Besucher für ein bestimmtes Datum oder einen bestimmten Zeitraum anzuzeigen. Die anderen beiden werden verwendet, um alle Besuche von einer IP-Adresse oder einer "UID" zu finden (ein eindeutiger Cookie-Wert, der für jeden Besucher erstellt wurde), und um zu bestimmen, ob dies ein neuer Besucher oder ein wiederkehrender Besucher ist.

Offensichtlich Speichern von site_id in 3 Indizes ist ineffizient für Schreibgeschwindigkeit und Speicher, aber ich sehe keine Möglichkeit, da ich in der Lage sein, diese Daten für eine bestimmte Site-ID schnell abzufragen.

Irgendwelche Ideen, um dies effizienter zu machen?

Ich verstehe nicht wirklich B-Bäume neben einigen sehr grundlegenden Sachen, aber es ist effizienter, die linke Spalte eines Index der mit der geringsten Varianz zu haben - richtig? Weil ich dachte, dass die site_id die zweite Spalte des Indexes für ip_address und uid ist, aber ich denke, das würde den Index weniger effizient machen, da die IP und UID mehr variieren als die Site ID, weil wir nur ungefähr 8000 haben einzigartige Websites pro Datenbankserver, aber täglich Millionen von einzigartigen Besuchern auf allen ~ 8000 Websites.

Ich habe auch darüber nachgedacht, site_id aus den IP- und UID-Indizes vollständig zu entfernen, da die Chancen, dass der gleiche Besucher zu mehreren Sites mit demselben Datenbankserver geht, ziemlich gering sind, aber in Fällen, in denen dies passiert, befürchte ich Es könnte ziemlich langsam sein, festzustellen, ob dies ein neuer Besucher dieser site_id ist oder nicht. Die Abfrage wäre so etwas wie:

select id from sessions where uid = 'value' and site_id = 123 limit 1 

... also, wenn diese Besucher diese Seite zuvor besucht hatte, wäre es nur eine Zeile mit diesem site_id finden müssen, bevor sie gestoppt. Dies wäre nicht unbedingt schnell, aber akzeptabel schnell. Aber sagen wir, wir haben eine Seite, die 500.000 Besucher pro Tag erreicht, und ein bestimmter Besucher liebt diese Seite und fährt dort 10 Mal am Tag. Jetzt treffen sie zum ersten Mal eine andere Site auf demselben Datenbankserver. Die obige Abfrage kann ziemlich lange dauern, um alle potenziell Tausende von Zeilen für diese UID zu durchsuchen, die auf der gesamten Festplatte verstreut ist, da sie keine für diese Standort-ID finden würde.

Einsicht auf machen diese so effizient wie möglich würde :)

-Update erkannt werden - das ist eine MyISAM-Tabelle mit MySQL 5.0. Meine Bedenken betreffen sowohl die Leistung als auch den Speicherplatz. Diese Tabelle liest und schreibt schwer. Wenn ich zwischen Leistung und Speicher wählen sollte, ist Leistung meine größte Sorge - aber beide sind wichtig.

Wir verwenden memcached stark in allen Bereichen unseres Dienstes, aber das ist keine Entschuldigung, sich nicht um das Datenbankdesign zu kümmern. Ich möchte, dass die Datenbank so effizient wie möglich ist.

+0

Speichermodul? MySQL-Version? Und wie möchten Sie dies effizienter machen - disk-Nutzung oder Leistung? Und haben Sie tatsächliche Probleme zu lösen oder ist das nur eine rhetorische Frage? – ggiroux

+0

mysql 5.0, myisam engine. Es geht mir sowohl um Speicherplatz als auch um Performance, da es sich sowohl um eine Lese- als auch um eine Write-Tabelle handelt. Ja, tatsächliches Problem. :) – Sean

+0

Haben Sie High Performance MySQL gelesen? –

Antwort

0

Zuerst, wenn Sie IP als String verwenden, dann ändern Sie es in die Spalte INT UNSIGNED und verwenden Sie die INET_ATON (expr) und INET_NTOA (expr) -Funktion, um damit umzugehen. Die Indizierung für Ganzzahlen ist effizienter als die Indizierung für Strings variabler Länge.

+0

Alle Felder sind natürlich bereits ganze Zahlen ... – Sean

+0

Stellen Sie sicher, dass IPv6-inkompatibel ist. Jahr 2000, hier kommen wir! – derobert

0

Well Indizes Handel Speicher für die Leistung. Es ist schwer, wenn Sie beides wollen. Es ist schwer, dies weiter zu optimieren, ohne alle Abfragen und deren Mengen pro Intervall zu kennen.

Was Sie haben, wird funktionieren. Wenn Sie in einen Engpass geraten, müssen Sie herausfinden, ob CPU, RAM, Festplatte und/oder Netzwerk und entsprechend anpassen. Es ist schwer und falsch, vorzeitig zu optimieren.

Sie möchten wahrscheinlich zu innodb wechseln, wenn Sie irgendwelche Updates haben, andere weise myisam ist gut für Einfügen/Auswählen. Da Ihre Zeilengröße klein ist, können Sie auch in mysql cluster (nbd) schauen. Es gibt auch eine Archivierungs-Engine, die bei Speicheranforderungen helfen kann, aber die Partitionierung in 5.1 ist wahrscheinlich eine bessere Sache, in die man schauen muss.

Das Umkehren der Reihenfolge Ihres Index macht keinen Sinn, wenn diese Indizes bereits in allen Ihren Abfragen verwendet werden.

aber es ist effizienter, wenn die linke Spalte eines Index die mit der geringsten Varianz ist - richtig?

nicht sicher, aber ich habe das vorher nicht gehört. Scheint mir nicht wahr für diese Anwendung. Die Indexreihenfolge ist für die Sortierung wichtig, und da mehrere eindeutige erste Indexfelder vorhanden sind, können mehr Abfragen den Index verwenden.

4
Ich verstehe nicht wirklich B-Bäume neben einigen sehr grundlegenden Sachen, aber es ist effizienter, die linke Spalte eines Index der mit der geringsten Varianz zu haben - richtig?

Es ist eine wichtige Eigenschaft von B-Tree-Indizes Sie sich bewusst sein müssen: Es ist möglich (effizient) für einen beliebigen Präfix des vollständigen Schlüssels zu suchen, aber keine Suffix. Wenn Sie einen Index site_ip(site_id, ip) haben und nach where ip = 1.2.3.4 fragen, verwendet MySQL den Index site_ip nicht. Wenn Sie stattdessen ip_site(ip, site_id) hätten, könnte MySQL den ip_site-Index verwenden.

Das ist eine zweite Eigenschaft von B-Tree-Indizes, die Sie ebenfalls beachten sollten: Sie sind sortiert. Ein B-Tree-Index kann für Abfragen wie where site_id < 40 verwendet werden.

Es gibt auch eine wichtige Eigenschaft von Festplattenlaufwerken zu beachten: sequentielle Lesevorgänge sind billig, Suchvorgänge sind nicht. Wenn Spalten verwendet werden, die nicht im Index enthalten sind, muss MySQL die Zeile aus den Tabellendaten lesen. Das ist im Allgemeinen eine Suche und langsam. Wenn MySQL also davon ausgeht, dass nur ein kleiner Prozentsatz der Tabelle gelesen wird, wird der Index ignoriert. Ein großer Tabellen-Scan (ein sequentieller Lesevorgang) ist normalerweise schneller als zufällige Lesevorgänge von nur wenigen Prozent der Zeilen in einer Tabelle.

Das gleiche gilt übrigens für sucht durch einen Index. Das Suchen eines Schlüssels in einem B-Baum erfordert möglicherweise einige Suchvorgänge. Sie werden also feststellen, dass WHERE site_id > 800 AND ip = '1.2.3.4' den Index site_ip nicht verwenden kann, da jede site_id mehrere Indexsuchen benötigt, um den Anfang der 1.2.3.4-Datensätze für diese Site zu finden. Der Index ip_site würde jedoch verwendet werden.

Schließlich müssen Sie Benchmarking und EXPLAIN liberal verwenden, um die besten Indizes für Ihre Datenbank herauszufinden. Denken Sie daran, dass Sie Indizes nach Bedarf hinzufügen und löschen können. Nicht eindeutige Indizes sind nicht Teil Ihres Datenmodells. Sie sind nur eine Optimierung.

PS: Benchmark InnoDB, es hat oft bessere gleichzeitige Leistung. Gleiches gilt für PostgreSQL.

Verwandte Themen