Je développe une application dans Ruby on Rails avec la base de données PostgreSQL (9.4). Pour mon cas d'utilisation, les colonnes des tables seront consultées très fréquemment, car tout l'intérêt de l'application est de rechercher des attributs très spécifiques sur un modèle.
Je décide actuellement d'utiliser un type integer
ou simplement d'utiliser un type de chaîne typique (par exemple character varying(255)
, qui est la valeur par défaut dans Rails ) pour les colonnes, car je ne sais pas quelle sera la différence de performances sur l'indice.
Ces colonnes sont des énumérations . Ils ont une taille fixe pour le nombre de valeurs possibles qu'ils peuvent avoir. La plupart des longueurs d'énumération ne dépassent pas 5, ce qui signifie que l'indice serait plus ou moins fixe pendant toute la durée de vie de l'application ; ainsi, les indices d'entier et de chaîne seraient identiques en nombre de nœuds.
Cependant, la chaîne qui serait indexée pourrait contenir environ 20 caractères, ce qui en mémoire est à peu près 5 fois celui de l'entier (si un entier est de 4 octets et que les chaînes sont pures ASCII à 1 octet par caractère, cela tient) Je ne sais pas comment les moteurs de base de données indexent les recherches, mais s'il a besoin de "scanner" la chaîne jusqu'à ce qu'elle corresponde à exactement, alors en substance cela signifie que la recherche de chaîne serait 5 fois plus lente qu'une recherche entière; le "scan" jusqu'à ce que la correspondance pour la recherche entière soit de 4 octets au lieu de 20. C'est ce que j'imagine:
La valeur de recherche est (entier) 4:
numérisation ............................ TROUVE | obtention d'enregistrements ... | BYTE_1 | BYTE_2 | BYTE_3 | BYTE_4 | BYTE_5 | BYTE_6 | BYTE_7 | BYTE_8 | ... |
La valeur de recherche est (chaîne) "some_val" (8 octets):
balayage................................................. .................................... TROUVE | obtention d'enregistrements ... | BYTE_1 | BYTE_2 | BYTE_3 | BYTE_4 | BYTE_5 | BYTE_6 | BYTE_7 | BYTE_8 | ... |
J'espère que cela a du sens. Fondamentalement, parce que l'entier occupe moins d'espace, il peut être "mis en correspondance" plus rapidement que son homologue de chaîne. C'est peut-être une supposition complètement fausse, mais je ne suis pas un expert, c'est pourquoi je vous pose la question! Je suppose que cette réponse que je viens de trouver semble soutenir mon hypothèse, mais je veux en être sûr.
Le nombre de valeurs possibles dans la colonne ne changerait pas en utilisant l'une ou l'autre, donc l'index lui-même ne changerait pas (sauf si j'ai ajouté une nouvelle valeur à l'énumération). Dans ce cas, y aurait-il une différence de performances dans l'utilisation de integer
ou varchar(255)
, ou l'utilisation d'un type entier est-elle plus logique?
La raison pour laquelle je demande, c'est que le type enum
de Rails mappe les entiers aux clés de chaîne, mais ils ne sont pas destinés à être des colonnes accessibles aux utilisateurs. Essentiellement, vous ne pouvez pas vérifier que la valeur d'énumération est valide, car une valeur non valide provoquera un ArgumentError
avant l'exécution de toute validation. L'utilisation d'un type string
permettrait des validations, mais s'il y a un coût de performance, je préfère simplement contourner le problème de validation.
Réponse courte: integer
est plus rapide que varchar
ou text
dans tous les aspects. Peu importe pour les petites tables et/ou les touches courtes. La différence augmente avec la longueur des clés et le nombre de lignes.
chaîne ... 20 caractères de long, ce qui en mémoire est à peu près 5 fois celui de l'entier (si un entier est de 4 octets, et les chaînes sont pures ASCII à 1 octet par caractère, alors cela tient )
Pour être précis, les types de caractères (text
ou varchar
) occupent exactement 21 octets pour 20 ASCII caractères sur le disque et 23 octets en RAM. Évaluation détaillée:
Également important: COLLATION
les règles peuvent rendre le tri des données de caractères plus cher - contrairement aux types de données numériques:
Index taille est probablement responsable de la part du lion de la différence de performances dans la plupart des cas. Considérez la surcharge par tuple d'index (essentiellement la même que pour une table): 4 octets pour le pointeur d'élément et 24 octets pour l'en-tête Tuple. Ainsi, le Tuple d'index pour integer
équivaudrait à 36 octets (dont 4 octets de remplissage d'alignement ) et pour varchar(20)
avec 20 ASCII caractères ce serait 52 octets (également avec rembourrage). Détails:
Toute la théorie mise à part: il vaut mieux simplement tester:
Postgres 9.5 a introduit une optimisation pour le tri de longues chaînes de données de caractères (mot clé "clés abrégées" ). Mais un bogue dans certaines fonctions de la bibliothèque C sous Linux a forcé le projet à désactiver la fonctionnalité pour les classements non-C dans Postgres 9.5.2. Détails dans les notes de version.
Cependant, si vous utilisez réellement les types Postgres enum
, la plupart de ces considérations ne sont pas pertinentes, car celles-ci sont implémentées avec integer
des valeurs internes de toute façon. Le manuel:
Une valeur
enum
occupe quatre octets sur le disque.
À part: varchar(255)
utilisé pour donner un sens aux premières versions de SQL Server, qui pouvaient utiliser un type de données plus efficace en interne jusqu'à la limite de 255 caractères. Mais la restriction de longueur impaire de 255 caractères n'a aucun impact spécial sur les performances de Postgres.