Indizes: Ganzzahl im Vergleich zur Zeichenfolgenleistung, wenn die Anzahl der Knoten identisch ist

26

Ich entwickle eine Anwendung in Ruby on Rails mit der PostgreSQL (9.4) -Datenbank. Für meinen Anwendungsfall werden Spalten in Tabellen sehr häufig nachgeschlagen, da die gesamte Anwendung nach sehr spezifischen Attributen in einem Modell sucht.

Ich bin zur Entscheidung darüber , ob eine verwenden integerArt oder einfach einen typischen String - Typen (zB character varying(255), die Standardeinstellung in Rails ist ) für die Spalten, wie ich bin nicht sicher , was der Unterschied in der Leistung auf dem Index sein.

Diese Spalten sind Aufzählungen . Sie haben eine feste Größe für die Anzahl der möglichen Werte, die sie haben können. Die meisten Aufzählungslängen überschreiten nicht 5, was bedeutet, dass der Index während der gesamten Lebensdauer der Anwendung mehr oder weniger festgelegt wird . Somit wären der Ganzzahl- und der Zeichenfolgenindex in der Anzahl der Knoten identisch.

Die zu indizierende Zeichenfolge könnte jedoch etwa 20 Zeichen lang sein, was ungefähr dem 5-fachen der Ganzzahl entspricht (wenn eine Ganzzahl 4 Byte beträgt und die Zeichenfolgen aus reinem ASCII mit 1 Byte pro Zeichen bestehen, gilt dies). Ich weiß nicht, wie Datenbank-Engines Index-Lookups durchführen, aber wenn der String "gescannt" werden muss, bis er genau übereinstimmt , bedeutet dies im Wesentlichen, dass der String-Lookup 5x langsamer ist als ein Integer-Lookup. Der "Scan" bis zur Übereinstimmung für die Integer-Suche würde 4 Bytes statt 20 sein. Dies ist, was ich mir vorstelle:

Der Suchwert ist (Ganzzahl) 4:

Scannen ........................ GEFUNDEN | Datensätze werden abgerufen ... | BYTE_1 | BYTE_2 | BYTE_3 | BYTE_4 | BYTE_5 | BYTE_6 | BYTE_7 | BYTE_8 | ... |

Der Suchwert ist (String) "some_val" (8 Bytes):

Scannen ............................................. .................................... GEFUNDEN | Datensätze werden abgerufen ... | BYTE_1 | BYTE_2 | BYTE_3 | BYTE_4 | BYTE_5 | BYTE_6 | BYTE_7 | BYTE_8 | ... |

Ich hoffe das macht Sinn. Grundsätzlich kann die Ganzzahl, da sie weniger Platz beansprucht, schneller als ihr String-Gegenstück "angeglichen" werden. Vielleicht ist das eine völlig falsche Vermutung, aber ich bin kein Experte, deshalb frage ich euch! Ich nehme an, diese Antwort, die ich gerade gefunden habe, scheint meine Hypothese zu stützen, aber ich möchte sicher sein.

Die Anzahl der möglichen Werte in der Spalte würde sich bei Verwendung beider Werte nicht ändern, sodass sich der Index selbst nicht ändern würde (es sei denn, ich habe der Aufzählung einen neuen Wert hinzugefügt). Gibt es in diesem Fall einen Leistungsunterschied bei der Verwendung von integeroder varchar(255), oder ist die Verwendung eines Integer-Typs sinnvoller?


Der Grund, den ich frage, ist, dass der Rails- enumTyp Ganzzahlen zu Zeichenfolgenschlüsseln zuordnet, diese jedoch keine benutzerbezogenen Spalten sein sollen. Grundsätzlich können Sie nicht überprüfen, ob der Enum-Wert gültig ist, da ein ungültiger Wert einen verursacht, ArgumentErrorbevor Überprüfungen ausgeführt werden können. Die Verwendung eines stringTyps würde Validierungen ermöglichen, aber wenn es Leistungskosten gibt, würde ich mich lieber um das Validierungsproblem kümmern.

Chris Cirefice
quelle

Antworten:

32

Kurze Antwort: integerist schneller als varcharoder textin jeder Hinsicht. Für kleine Tische und / oder kurze Schlüssel spielt das keine Rolle. Der Unterschied wächst mit der Länge der Tasten und der Anzahl der Zeilen.

Zeichenfolge ... 20 Zeichen lang, was ungefähr dem Fünffachen der Ganzzahl entspricht (wenn eine Ganzzahl 4 Byte lang ist und die Zeichenfolgen reines ASCII mit 1 Byte pro Zeichen sind, gilt dies)

Um genau zu sein, belegen die Zeichentypen ( textoder varchar) genau 21 Byte für 20 ASCII-Zeichen auf der Festplatte und 23 Byte im RAM. Detaillierte Bewertung:

Ebenfalls wichtig: COLLATIONRegeln können das Sortieren von Zeichendaten verteuern - im Gegensatz zu numerischen Datentypen:

Index Größe ist wahrscheinlich verantwortlich für den Löwenanteil des Leistungsunterschied in den meisten Fällen. Betrachten Sie den Overhead pro Indextupel (im Grunde das Gleiche wie für eine Tabelle): 4 Bytes für den Elementzeiger und 24 Bytes für den Tupelkopf. Das Indextupel für integerwürde also 36 Bytes betragen (einschließlich 4 Bytes Ausrichtungsauffüllung ) und für varchar(20)20 ASCII-Zeichen wären es 52 Bytes (auch einschließlich Auffüllung). Einzelheiten:

Abgesehen von der ganzen Theorie ist es am besten, nur zu testen:

In Postgres 9.5 wurde eine Optimierung zum Sortieren langer Zeichenfolgen (Schlüsselwort "Kurzschlüssel" ) eingeführt. Ein Fehler in einigen C-Bibliotheksfunktionen unter Linux zwang das Projekt jedoch, die Funktion für Nicht-C-Kollatierungen in Postgres 9.5.2 zu deaktivieren. Details in den Release Notes.

Wenn Sie jedoch tatsächlich Postgres- enumTypen verwenden, sind die meisten dieser Überlegungen irrelevant, da diese integerohnehin mit internen Werten implementiert werden. Das Handbuch:

Ein enumWert belegt vier Bytes auf der Festplatte.

Nebenbei: Wird varchar(255)verwendet, um Sinn für frühe Versionen von SQL Server zu machen, die intern einen effizienteren Datentyp mit bis zu 255 Zeichen verwenden können. Die Beschränkung auf eine ungerade Länge von 255 Zeichen hat jedoch keine besonderen Auswirkungen auf die Leistung in Postgres.

Erwin Brandstetter
quelle
1
Es gibt keine versteckte Optimierung in SQL Server für varchar(255)vs. zB varchar(260). Möglicherweise gab es so etwas in SQL Server 6.x, aber dies war lange nicht mehr der Fall.
a_horse_with_no_name
@a_horse_with_no_name: danke, das habe ich entsprechend geklärt.
Erwin Brandstetter
Es tut mir leid, dass ich so lange
gebraucht
Gilt diese Antwort noch für Postgres 10, bitte?
Matty
1
@Matty: Immer noch gültig. Und ich sehe auch noch keine Änderung für Seite 11.
Erwin Brandstetter