it-swarm.com.de

Warum * nicht * FEHLER: Die Indexzeilengröße xxxx überschreitet das Maximum von 2712 für den Index "foo"?

Wir haben wiederholt fehlgeschlagene Versuche gesehen, Spalten mit Werten zu indizieren, die eine maximale Größe überschreiten. Postgres 10 hat diese Art von Fehlermeldung:

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.

Beispiele:

Usw.

Nun, a_horse_with_no_name hat einen Fall demonstriert mit viel größeren text -Werten (10000 Zeichen), die in Postgres 9.6 immer noch mit einem UNIQUE -Index zu funktionieren scheinen. Zitiert seinen Testfall:

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

Kein Fehler, und der Spaltenwert wurde tatsächlich mit einer Länge von 10000 Zeichen getestet.

Wurden kürzlich Änderungen vorgenommen oder wie ist dies möglich?

8

Kurze Antwort: Komprimierung.

Der Datentyp text ermöglicht standardmäßig (verlustfrei!) Komprimierung und Speicherung außerhalb der Zeile:

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

Das Handbuch zu 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.

Beachten Sie, dass m Spalten auch in den Sekundärspeicher verschoben werden können, jedoch nur als letzter Ausweg (e- und x-Spalten werden zuerst verschoben).

Testen Sie mit pg_column_size() anstelle von length(). Stellen Sie sicher, dass Sie die tatsächlichen Tabellenspalten (mit angewendeter Komprimierung) und nicht nur die Eingabewerte testen. Sehen:

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 | komprimiert | unkomprimiert 
 -: | : ---------- ----------: | ---------: | -----------: 
 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 <> fiddle --- (hier

Beachten Sie, wie der Wert mit dem Ausdruck noop aus seinem Speicherformat entpackt werden muss: pg_column_size(col || '').

Die 5. Zeile wäre zu groß, um in das Index-Tupel zu passen (auch bei Komprimierung) und die Fehlermeldung im Titel auszulösen.

Die 6. Zeile wäre zu groß, um auch auf die Indexseite zu passen und die zugehörige Fehlermeldung auszulösen:

FEHLER: Die Indexzeile benötigt 11504 Byte, die maximale Größe beträgt 8191

Die mit rpad() generierten Testwerte weisen sich wiederholende Muster auf, die eine massive Komprimierung ermöglichen. Selbst sehr lange Saiten passen noch problemlos in die max. Größe nach Komprimierung auf diese Weise.

Verbunden:

Lange Antwort

Ich habe umfangreichere Tests durchgeführt und Speicherinternale manipuliert, um mein Verständnis zu überprüfen. Nur zu Testzwecken!

dbfiddle erlaubt keinen Schreibzugriff auf Systemkataloge. Aber die Fragen sind da, um "zu Hause" zu versuchen.

10