Co to jest narzut dla varchar (n)?

15

Chciałem zapytać o znaczenie tego fragmentu z dokumentu Postgres w odniesieniu do varchar(n)typu:

Wymaganie dotyczące przechowywania krótkiego ciągu (do 126 bajtów) wynosi 1 bajt plus rzeczywisty ciąg, który obejmuje dopełnianie spacji w przypadku znaku. Dłuższe łańcuchy mają narzut 4 bajty zamiast 1.

Załóżmy, że mam varchar(255)pole. A teraz następujące oświadczenia:

  • Jeśli to pole zawiera ciąg 10 bajtów, wówczas narzut wynosi 1 bajt. Więc ciąg będzie używał 11 bajtów.
  • Jeśli pole zawiera ciąg znaków o długości 140 bajtów, wówczas narzut wynosi 4 bajty. Zatem łańcuch będzie używał 144 bajtów.

Czy powyższe stwierdzenia są prawdziwe? Tutaj ktoś rozumie dokument w taki sam sposób jak ja, ale tutaj ktoś twierdzi, że narzut zawsze ma tutaj 4 bajty ?

naciśnięcie klawisza
źródło

Odpowiedzi:

19

Nic dziwnego, że instrukcja ma rację. Ale jest coś więcej.

Po pierwsze, rozmiar na dysku (w dowolnej tabeli , nawet jeśli faktycznie nie jest przechowywany na dysku) może różnić się od rozmiaru w pamięci . Na dysku narzut dla krótkich varcharwartości do 126 bajtów jest redukowany do 1 bajtu, jak podano w instrukcji. Ale narzut w pamięci wynosi zawsze 4 bajty (po wyodrębnieniu poszczególnych wartości).

To samo odnosi się do text, varchar, varchar(n)lubchar(n) - oprócz tego, że char(n)jest puste, wypełnione do nznaków i zwykle nie chcą z niego korzystać. Jego efektywny rozmiar może nadal różnić się w kodowaniu wielobajtowym, ponieważ noznacza maksymalną liczbę znaków, a nie bajty:

ciągi znaków do długości n(nie bajtów).

Wszystkie z nich używają varlenawewnętrznie.
"char"(z podwójnymi cudzysłowami) jest innym stworzeniem i zawsze zajmuje jeden bajt.
Nieodpisane literały łańcuchowe ( 'foo') mają narzut na jeden bajt. Nie należy mylić ich z wpisanymi wartościami!

Przetestuj za pomocą 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

Jak widzisz:

  • 3-bajtowy ciąg „foo” zajmuje 4 bajty na dysku i 7 bajtów w pamięci RAM (więc 1 bajt vs. 4 bajty narzutu).
  • 140-bajtowy ciąg „123 ...” zajmuje 144 bajty zarówno na dysku, jak i w pamięci RAM (czyli zawsze 4 bajty narzutu).
  • Przechowywanie integernie ma narzutu (ale ma wymagania dotyczące wyrównania, które mogą nałożyć wypełnienie).
  • Wiersz ma dodatkowy narzut 24 bajtów dla nagłówka krotki (plus dodatkowe 4 bajty na krotkę dla wskaźnika pozycji w nagłówku strony).
  • I na koniec: narzut małego varcharma wciąż tylko 1 bajt, podczas gdy nie został wyodrębniony z wiersza - co widać na podstawie wielkości wiersza. (Dlatego czasami zaznaczanie całych wierszy jest nieco szybsze).

Związane z:

Erwin Brandstetter
źródło
1
Czy to 1 bajt narzutu nadal 1 bajt w indeksie?
dvtan
1
@dtgq: Indeks przechowuje dane tak jak tabela, więc tak.
Erwin Brandstetter