Ich optimiere eine Firebird 2.5-Datenbank mit Arbeitstickets. Sie werden in einer als solche deklarierten Tabelle gespeichert:
CREATE TABLE TICKETS (
TICKET_ID id PRIMARY KEY,
JOB_ID id,
ACTION_ID id,
STATUS str256 DEFAULT 'Pending'
);
Ich möchte in der Regel das erste Ticket finden, das noch nicht bearbeitet wurde und sich im Pending
Status befindet.
Meine Verarbeitungsschleife wäre:
- Holen Sie sich das erste Ticket wo
Pending
- Arbeiten Sie mit Ticket.
- Ticketstatus aktualisieren =>
Complete
- Wiederholen.
Nichts Besonderes. Wenn ich die Datenbank beobachte, während diese Schleife läuft, sehe ich die Anzahl der indizierten Lesezugriffe für jede Iteration. Die Leistung scheint sich nicht zu verschlechtern, aber der Rechner, auf dem ich teste, ist ziemlich schnell. Ich habe jedoch von einigen meiner Benutzer Berichte über Leistungseinbußen im Laufe der Zeit erhalten.
Ich habe einen Index für Status
, aber es scheint immer noch, als würde er die Ticket_Id
Spalte bei jeder Iteration durchsuchen . Es scheint, als würde ich etwas übersehen, aber ich weiß nicht, was. Wird die steigende Anzahl indizierter Lesevorgänge für so etwas erwartet, oder verhält sich der Index in irgendeiner Weise schlecht?
- Bearbeitungen für Kommentare -
In Firebird beschränken Sie den Zeilenabruf wie folgt:
Select First 1
Job_ID, Ticket_Id
From
Tickets
Where
Status = 'Pending'
Wenn ich also "zuerst" sage, frage ich nur nach einem begrenzten Datensatz, wo Status = 'Pending'
.
quelle
ticket_id
, benötigen Sie wahrscheinlich einen Index für(status, ticket_id)
ticket_id
tatsächlich einschließt, ist dies schlechter, als wenn nur der Status indiziert wird.id
(der Datentyp) eine von Ihnen definierte Domain?Antworten:
Die Verschlechterung im Laufe der Zeit tritt aufgrund der erhöhten Anzahl von Elementen auf, die sich im Status "Vollständig" befinden. Denken Sie eine Sekunde darüber nach - Sie werden beim Testen keine Leistungseinbußen feststellen, da Sie wahrscheinlich eine kleine Anzahl von Zeilen mit dem Status "Abgeschlossen" haben. In der Produktion können jedoch Millionen Zeilen mit dem Status "Vollständig" vorhanden sein, und diese Anzahl wird mit der Zeit zunehmen. Dies führt im Wesentlichen dazu, dass Ihr Index zum Status im Laufe der Zeit immer weniger nützlich wird. Aus diesem Grund entscheidet die Datenbank wahrscheinlich nur, dass der Status fast immer den Wert 'Vollständig' hat und die Tabelle nur durchsucht wird, anstatt den Index zu verwenden.
In SQL Server (und möglicherweise in anderen RDBMS?) Kann dies mithilfe von gefilterten Indizes umgangen werden. In SQL Server würden Sie dem Ende Ihrer Indexdefinition eine WHERE-Bedingung hinzufügen, die besagt: "Diesen Index nur auf Datensätze mit dem Status <> 'Abgeschlossen' anwenden". Dann verwendet jede Abfrage, die dieses Prädikat verwendet, höchstwahrscheinlich den Index für die kleine Menge von Datensätzen, die nicht auf "Vollständig" gesetzt sind. Basierend auf der Dokumentation hier: http://www.firebirdsql.org/refdocs/langrefupd25-ddl-index.html sieht es jedoch nicht so aus, als ob Firebird gefilterte Indizes unterstützt.
Eine Problemumgehung besteht darin, 'Complete'-Datensätze in eine ArchiveTickets-Tabelle einzufügen. Erstellen Sie eine Tabelle mit genau derselben Definition (jedoch ohne automatisch generierte ID) wie Ihre Tickets-Tabelle und pflegen Sie die Zeilen zwischen ihnen, indem Sie die Option 'Vollständige' Datensätze in die ArchiveTickets-Tabelle übernehmen. Der Index auf Ihrem Tickettisch wird sich dann über eine viel kleinere Anzahl von Datensätzen erstrecken und eine viel höhere Leistung aufweisen. Dies bedeutet wahrscheinlich, dass Sie alle Berichte usw. ändern müssen, die auf 'Complete'-Tickets verweisen, um auf die Archivtabelle zu verweisen, oder eine UNION für Tickets und ArchiveTickets ausführen. Dies hat den Vorteil, dass Sie nicht nur schnell sind, sondern auch bestimmte Indizes für die ArchiveTickets-Tabelle erstellen können, um die Leistung für andere Abfragen zu verbessern (z. B .:
Sie sollten sich darüber Gedanken machen, wenn Ihre Produktion in die Tausende von Reihen geht. Die Leistung verschlechtert sich mit der Zeit und wirkt sich negativ auf die Benutzerfreundlichkeit aus.
quelle
Ob die Leistung beeinträchtigt wird oder nicht, hängt vom Datenvolumen und der Maschinenkapazität ab. Angesichts der Kapazität moderner Hardware ist es schwer vorstellbar, dass das Ticketverkaufsvolumen mit dem von Ihnen beschriebenen Design nicht bewältigt werden kann. Es gibt jedoch Änderungen, die ich aus Gründen der Korrektheit empfehlen würde, und die die Leistung möglicherweise als sekundären Vorteil verbessern.
Ihre erste ausstehende Abfrage ist nicht deterministisch. Zuerst in welcher Reihenfolge? Eine SQL-Tabelle hat keine eigene Reihenfolge. der
First 1
Hack gibt dir nur einen willkürlichen ersten. Warum nicht ausstehende Jobs in der Reihenfolge Job_ID verarbeiten, um sie deterministisch zu machen?Wenn Sie über zwei Indizes {Job_ID} und {Status, Job_ID} verfügen, gibt diese Abfrage vorhersehbar und effizient eine Zeile zurück:
Ich bin kein Firebird-Benutzer, daher müssen Sie den Abfrageplan überprüfen. Er sollte jedoch effizient sein, da die Unterabfrage nur auf den zweiten Index verweist und einen Wert für den ersten Index erzeugt. (Möglicherweise stehen Ihnen andere Effizienztricks zur Verfügung. Sie können die physische Tabelle möglicherweise als B + -Baum organisieren oder beispielsweise auf eine ausgeblendete row_id zugreifen.)
Die andere Änderung, die ich aus Gründen der Korrektheit vornehmen würde, besteht darin,
Status
ein einzelnes, eingeschränktes Byte zu erstellen und die Anwendung die Zeichenfolge "Ausstehend" bereitstellen zu lassen. Dies schützt vor fehlerhaftenStatus
Werten und verringert wahrscheinlich den Index im Handel. Etwas wie:Natürlich können Sie eine Ansicht (oder eine abgeleitete Spalte) verwenden, um die kanonischen Zeichenfolgen für Status anzugeben.
quelle