Was ist der Overhead für varchar (n)?

15

Ich wollte für die Bedeutung dieses Fragment aus fragen , Postgres doc in Bezug auf varchar(n)Art:

Die Speicheranforderung für eine kurze Zeichenfolge (bis zu 126 Byte) beträgt 1 Byte plus der tatsächlichen Zeichenfolge, einschließlich der Leerzeichenauffüllung bei Zeichen. Längere Zeichenfolgen haben 4 Byte Overhead anstelle von 1 Byte.

Nehmen wir an, ich habe ein varchar(255)Feld. Und nun die folgenden Aussagen:

  • Wenn dieses Feld eine Zeichenfolge von 10 Byte enthält, beträgt der Overhead 1 Byte. Die Zeichenfolge verwendet also 11 Bytes.
  • Wenn das Feld eine Zeichenfolge mit 140 Bytes enthält, beträgt der Overhead 4 Bytes. Die Zeichenfolge verwendet also 144 Byte.

Sind diese Aussagen richtig? Hier versteht jemand die Doc die gleiche Art und Weise wie ich , aber hier jemand sagt der Aufwand ist immer 4 Bytes hier ?

Tastendruck
quelle

Antworten:

19

Es überrascht nicht, dass das Handbuch richtig ist. Aber es steckt noch mehr dahinter.

Zum einen kann sich die Größe auf der Festplatte (in jeder Tabelle , auch wenn sie nicht tatsächlich auf der Festplatte gespeichert ist) von der Größe im Speicher unterscheiden . Auf der Festplatte varcharreduziert sich der Overhead für Kurzwerte bis zu 126 Byte auf 1 Byte, wie im Handbuch angegeben. Der Overhead im Speicher beträgt jedoch immer 4 Byte (sobald einzelne Werte extrahiert wurden).

Das gleiche gilt für text, varchar, varchar(n)oderchar(n) - außer , dass char(n)zu Leerzeichen aufgefüllt ist nZeichen und Sie in der Regel nicht wollen , es zu benutzen. Die effektive Größe kann bei Multibyte-Codierungen noch variieren, da nmaximal Zeichen und nicht Bytes angegeben werden:

Zeichenfolgen mit einer nLänge von bis zu Zeichen (nicht Bytes).

Alle von ihnen verwenden varlenaintern.
"char"(mit doppelten Anführungszeichen) ist eine andere Kreatur und belegt immer ein einzelnes Byte.
Untypisierte String-Literale ( 'foo') haben einen Einzelbyte- Overhead. Nicht zu verwechseln mit eingegebenen Werten!

Testen Sie mit pg_column_size().

CREATE TEMP TABLE t (id int, v_small varchar, v_big varchar);
INSERT INTO t VALUES (1, 'foo', '12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890');

SELECT pg_column_size(id)        AS id
     , pg_column_size(v_small)   AS v_small
     , pg_column_size(v_big)     AS v_big
     , pg_column_size(t)         AS t
FROM   t
UNION ALL  -- 2nd row measuring values in RAM
SELECT pg_column_size(1)
     , pg_column_size('foo'::varchar)
     , pg_column_size('12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890'::varchar)
     , pg_column_size(ROW(1, 'foo'::varchar, '12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890'::varchar));

 id | v_small | v_big |  t
----+---------+-------+-----
  4 |       4 |   144 | 176
  4 |       7 |   144 | 176

Wie du sehen kannst:

  • Die 3-Byte-Zeichenfolge 'foo' belegt 4 Byte auf der Festplatte und 7 Byte im RAM (also 1 Byte im Vergleich zu 4 Byte Overhead).
  • Die 140-Byte-Zeichenfolge '123 ...' belegt 144 Byte sowohl auf der Festplatte als auch im RAM (also immer 4 Byte Overhead).
  • Die Speicherung von integerhat keinen Overhead (aber Ausrichtungsanforderungen, die eine Polsterung erfordern können).
  • Die Zeile hat einen zusätzlichen Overhead von 24 Byte für den Tupelheader (plus zusätzliche 4 Byte pro Tupel für den Elementzeiger im Seitenkopf).
  • Und last but not least: Der Overhead des Small varcharbeträgt immer noch nur 1 Byte, solange er nicht aus der Zeile extrahiert wurde - wie aus der Zeilengröße hervorgeht. (Deshalb ist es manchmal etwas schneller, ganze Zeilen auszuwählen.)

Verbunden:

Erwin Brandstetter
quelle
1
Ist dieser 1-Byte-Overhead noch 1 Byte im Index?
dvtan
1
@dtgq: Ein Index speichert Daten genau wie eine Tabelle, also ja.
Erwin Brandstetter