web-dev-qa-db-fra.com

Pourquoi * pas * ERREUR: la taille de la ligne d'index xxxx dépasse le maximum 2712 pour l'index "foo"?

Nous avons vu à plusieurs reprises des tentatives infructueuses d'indexation de colonnes avec des valeurs dépassant une taille maximale. Postgres 10 a ce genre de message d'erreur:

ERROR:  index row size xxxx exceeds maximum 2712 for index "foo_idx"
HINT:  Values larger than 1/3 of a buffer page cannot be indexed.
       Consider a function index of an MD5 hash of the value, or use full text indexing.

Exemples:

Etc.

Maintenant, a_horse_with_no_name a démontré un cas avec des valeurs text beaucoup plus grandes (10000 caractères) qui semblent toujours fonctionner avec un index UNIQUE dans Postgres 9.6. Citant son cas de test:

create table tbl (col text);
create unique index on tbl (col);

insert into tbl
values (rpad(md5(random()::text), 10000, md5(random()::text)));

select length(val) from x;  -- 10000

Aucune erreur, et la valeur de la colonne a bien été testée avec une longueur de 10000 caractères.

Y a-t-il eu des changements récents ou comment est-ce possible?

8
Erwin Brandstetter

Réponse courte: compression.

Le type de données text permet la compression (sans perte!) Et le stockage hors ligne par défaut:

SELECT typstorage FROM pg_type WHERE typname = 'text';  -- 'x'

Le manuel sur pg_type.typstorage:

p: Value must always be stored plain.
e: Value can be stored in a “secondary” relation (if relation has one, see pg_class.reltoastrelid).
m: Value can be stored compressed inline.
x: Value can be stored compressed inline or stored in “secondary” storage.

Notez que m colonnes peuvent également être déplacées vers le stockage secondaire, mais uniquement en dernier recours (les colonnes e et x sont déplacées en premier).

Testez avec pg_column_size() au lieu de length(). Assurez-vous de tester les colonnes de table réelles (avec compression appliquée) et pas seulement les valeurs d'entrée. Voir:

CREATE TABLE tbl (id int, col text);
INSERT INTO tbl(id, col) VALUES 
   (1, rpad(md5('non_random'::text),     100, md5('non_random'::text)))
 , (2, rpad(md5('non_random'::text),    1000, md5('non_random'::text)))
 , (3, rpad(md5('non_random'::text),   10000, md5('non_random'::text)))
 , (4, rpad(md5('non_random'::text),  100000, md5('non_random'::text)))
 , (5, rpad(md5('non_random'::text),  500000, md5('non_random'::text)))
 , (6, rpad(md5('non_random'::text), 1000000, md5('non_random'::text)));
SELECT id, left(col, 10) || ' ...' AS col
     , length(col) AS char_length
     , pg_column_size(col) AS compressed
     , pg_column_size(col || '') AS uncompressed
FROM   tbl ORDER BY id;
 id | col | char_length | compressé | non compressé 
 -: | : ---------- ----------: | ---------: | -----------: 
 1 | 67ad0f29fa ... | 100 | 101 | 104 
 2 | 67ad0f29fa ... | 1000 | 1004 | 1004 
 3 | 67ad0f29fa ... | 10000 | 160 | 10004 
 4 | 67ad0f29fa ... | 100000 | 1191 | 100004 
 5 | 67ad0f29fa ... | 500000 | 5765 | 500004 
 6 | 67ad0f29fa ... | 1000000 | 11487 | 1000004 
SELECT pg_column_size(rpad(md5('non_random'::text), 1000000, md5('non_random'::text)));
 | pg_column_size | 
 | -------------: | 
 | 1000004 | 

db <> violon --- (ici

Notez comment la valeur doit être décompressée de son format de stockage avec l'expression noop: pg_column_size(col || '').

La 5ème ligne serait trop grande pour contenir le tuple d'index (même avec compression) et déclencher le message d'erreur dans le titre.

La 6ème ligne serait trop grande pour tenir même la page d'index et déclencher le message d'erreur associé:

ERREUR: la ligne d'index nécessite 11504 octets, la taille maximale est de 8191

Les valeurs de test générées avec rpad() ont des motifs répétitifs, ce qui permet une compression massive. Même les cordes très longues s'adaptent toujours facilement au max. taille après compression de cette façon.

En relation:

Longue réponse

J'ai effectué des tests plus poussés, altérant les composants internes du stockage pour vérifier ma compréhension. Uniquement à des fins de test!

  • db <> violon ici

dbfiddle n'autorise pas l'accès en écriture aux catalogues système. Mais les requêtes sont là pour essayer "à la maison".

10
Erwin Brandstetter