Dois-je investir du temps pour changer le type de colonne de CHAR (36) en UUID?

14

J'ai déjà quelques millions de lignes dans ma base de données. Je ne connaissais pas le type de données PostgreSQL UUID lorsque j'ai conçu mon schéma.

L'une des tables contient 16 millions de lignes (environ 3,5 millions à 4 millions d'enregistrements par fragment), augmentant à environ 500 000 enregistrements par jour. J'ai encore le luxe de démonter le système de production pendant quelques heures si nécessaire. Je n'aurai pas ce luxe dans une ou deux semaines.

Ma question est la suivante: cela vaudra-t-il la peine de le faire? Je me pose des questions sur les performances de JOIN, l'utilisation de l'espace disque (le vidage gzip complet est de 1,25 Gio), des choses de cette nature.

Le schéma de table est:

# \d twitter_interactions
                Table "public.twitter_interactions"
         Column          |            Type             | Modifiers 
-------------------------+-----------------------------+-----------
 interaction_id          | character(36)               | not null
 status_text             | character varying(1024)     | not null
 screen_name             | character varying(40)       | not null
 twitter_user_id         | bigint                      | 
 replying_to_screen_name | character varying(40)       | 
 source                  | character varying(240)      | not null
 tweet_id                | bigint                      | not null
 created_at              | timestamp without time zone | not null
Indexes:
    "twitter_interactions_pkey" PRIMARY KEY, btree (interaction_id)
    "twitter_interactions_tweet_id_key" UNIQUE, btree (tweet_id)
    "index_twitter_interactions_on_created_at" btree (created_at)
    "index_twitter_interactions_on_screen_name" btree (screen_name)
Triggers:
    insert_twitter_interactions_trigger BEFORE INSERT ON twitter_interactions FOR EACH ROW EXECUTE PROCEDURE twitter_interactions_insert_trigger()
Number of child tables: 9 (Use \d+ to list them.)
François Beausoleil
la source

Réponses:

13

J'envisagerais de passer au type UUID. char(36)prend 40 octets, uuidprend 16, donc vous économiserez 24 octets par ligne, ce qui équivaut pour vous à 12 Mo par jour, 4 Go après un an. Index plus. Selon le matériel dont vous disposez, ce n'est pas grand-chose, mais cela pourrait l'être. Et cela s'ajoute si vous avez plus d'opportunités d'amélioration comme celle-ci.

De plus, je ne vois aucune contrainte dans votre schéma qui garantit qu'il interaction_idest réellement au bon format. Utiliser le bon type vous le donnera également.

Si vous aimez cela, cependant, l'utilisation bigintéconomiserait encore plus et aurait des performances encore meilleures. Il est très peu probable que votre application soit si volumineuse qu'une bigintcolonne pour une ID ne fonctionnera pas.

Peter Eisentraut
la source
J'ai un système distribué: plusieurs sources de données génèrent des ID pour les interactions, donc je ne peux pas utiliser un BIGINT simple sauf si j'ai réservé N bits pour l'ID du nœud.
François Beausoleil
3
@ FrançoisBeausoleil, réserver N bits pour l'ID de nœud équivaut à utiliser chaque Nième numéro dans une séquence (et donc facile à mettre en œuvre). Vous pouvez également envisager d'utiliser des clés composites.
Unreason
1
La coordination de plusieurs séquences (avec l'identifiant du nœud) est un tracas administratif en pratique et sujette aux erreurs humaines. Je ne vois aucune raison de ne pas utiliser les UUID dans ce scénario, d'autant plus que les bits sont bon marché de nos jours (à la fois la mémoire et le stockage). En effet, ce scénario est la raison même pour laquelle les UUID ont été inventés il y a des décennies: pour partager des données entre des systèmes distribués sans coordination centralisée .
Basil Bourque
6

Je ne suis pas un postgres par un effort d'imagination, mais d'après ce que je sais de SQL Server, plus vous pouvez insérer de lignes sur une page de données, meilleures sont les performances que vous allez avoir (la lecture de données à partir du disque est généralement opération la plus chère). Ainsi, le passage d'un champ large de 36 ish 1 octet à un GUID 16 octets semble être une économie de coûts directe. Moins vous pouvez effectuer de lectures, plus vite vous pouvez renvoyer des résultats. Tout cela suppose bien sûr qu'un GUID / UUID satisfait les besoins commerciaux de la table. Si un UUID le satisfait, serait-ce un bigint ? Cela réduirait davantage votre stockage coûte encore 8 octets par ligne.

Modifier 1

Pour les données de caractères dans Postgres, il y a un coût de stockage supplémentaire pour elles. Les chaînes courtes, moins de 127 octets ont une surcharge de 1 octet tandis que tout ce qui a plus de 4 octets est la façon dont le deuxième répondant a proposé un coût de 40 octets pour un champ de 36 octets. Mais il y a aussi une option pour la compression de chaîne, donc peut-être que cela ne coûtera pas le plein 40. Je ne peux pas dire quel serait le coût final mais les fondamentaux restent: tout ce qui dépasse 16 octets augmentera le coût de stockage, prendra plus de temps à lire et consommer plus de mémoire.

La mémoire requise pour une chaîne courte (jusqu'à 126 octets) est de 1 octet plus la chaîne réelle, qui inclut le remplissage d'espace dans le cas des caractères. Les chaînes plus longues ont 4 octets de surcharge au lieu de 1. Les chaînes longues sont compressées automatiquement par le système, de sorte que les exigences physiques sur le disque peuvent être moindres.

billinkc
la source
3

Outre le problème d'espace, gardez à l'esprit que vous devrez modifier chaque table pour utiliser le type de données correct, sinon vos performances de jointure seront très mauvaises.

mrdenny
la source
C'était une évidence, mais merci de me le rappeler.
François Beausoleil
3
Lorsque je fais des changements majeurs comme celui-ci, je trouve que tout noter (peu importe à quel point la chose est simple à retenir) est généralement payant.
mrdenny
3

En plus de l'économie de taille des données et des index (comme d'autres l'ont dit), ce qui se traduit par des économies d'E / S, la chose que vous devez considérer est de savoir comment allez-vous générer de nouvelles valeurs interaction_idet quel sera l'impact sur la index et conditions de requête (jointures).

Pour l'index - il sera plus petit, cependant, si un grand nombre de vos requêtes utilisent des analyses d'index, le passage aux UUID peut rendre les analyses d'index impossibles (selon la façon dont vous générerez les UUID) et bigintpeut être un bien meilleur choix.

Enfin, comme l'impact réel sur les performances dépend également de vos modèles d'utilisation et de la distribution des données, vous devez exécuter des tests et disposer d'un environnement de développement et de test dans lequel vous pouvez tester vos modifications.

Cela vous donnera une réponse beaucoup plus exacte sur l'impact sur les performances.

Déraisonnable
la source
Merci pour la contribution utile et bienvenue sur le site :)
Jack dit essayer topanswers.xyz
Mes modèles d'accès sont à travers des plages de dates, JOINing utilisant le screen_name, ou par UUID. Aucun balayage de plage sur l'ID unique n'est prévu. Merci pour votre réponse, très instructif.
François Beausoleil