"Kolumn Tetris"
Du kan faktiskt göra något , men detta kräver djupare förståelse. Nyckelordet är alignment padding . Varje datatyp har specifika anpassningskrav.
Du kan minimera utrymmet som går förlorat till utfyllnad mellan kolumner genom att beställa dem positivt. Följande (extremt) exempel skulle slösa mycket fysiskt diskutrymme:
CREATE TABLE t (
e int2 -- 6 bytes of padding after int2
, a int8
, f int2 -- 6 bytes of padding after int2
, b int8
, g int2 -- 6 bytes of padding after int2
, c int8
, h int2 -- 6 bytes of padding after int2
, d int8)
För att spara 24 byte per rad, använd istället:
CREATE TABLE t (
a int8
, b int8
, c int8
, d int8
, e int2
, f int2
, g int2
, h int2) -- 4 int2 occupy 8 byte (MAXALIGN), no padding at the end
db<>spela här
Gammal sqlfiddle
Som en tumregel, om du sätter 8-byte kolumner först, sedan 4-byte, 2-byte och 1-byte kolumner sist kan du inte gå fel.
boolean
, uuid
(!) och några andra typer behöver ingen justering vaddering. text
, varchar
och andra typer av "varlena" (variabel längd) nominellt kräver "int"-justering (4 byte på de flesta maskiner). Men jag observerade ingen anpassningsutfyllnad i diskformat (till skillnad från i RAM). Så småningom hittade jag förklaringen i en anteckning i källkoden:
Observera också att vi tillåter att den nominella inriktningen överträds vid lagring av "packade" varlenor; TOAST-mekanismen tar hand om att dölja det från de flesta koder.
Så "int"-justering upprätthålls endast när den (eventuellt komprimerade) datum inklusive en enda ledande längd-byte överstiger 127 byte. Sedan växlar varlena storage till fyra ledande byte och kräver "int"-justering.
Normalt kan du spara ett par byte per rad i bästa fall genom att spela "kolumntetris" . Inget av detta är nödvändigt i de flesta fall. Men med miljarder rader kan det lätt betyda ett par gigabyte.
Du kan testa den faktiska kolumn-/radstorleken med funktionen pg_column_size()
.
Vissa typer tar upp mer utrymme i RAM än på disk (komprimerat eller "packat" format). Du kan få större resultat för konstanter (RAM-format) än för tabellkolumner när du testar samma värde (eller rad med värden jämfört med tabellrad) med pg_column_size()
.
Slutligen kan vissa typer komprimeras eller "rostas" (lagras utanför raden) eller båda.
Overhead per tuppel (rad)
4 byte per rad för objektidentifieraren - inte föremål för ovanstående överväganden.
Och minst 24 byte (23 + utfyllnad) för tuppelhuvudet. Manualen om databassidalayout:
Det finns en rubrik med fast storlek (upptar 23 byte på de flesta maskiner), följt av en valfri noll-bitmapp, ett valfritt objekt-ID-fält och användardata.
För utfyllnad mellan rubrik och användardata behöver du känna till MAXALIGN
på din server - vanligtvis 8 byte på ett 64-bitars operativsystem (eller 4 byte på ett 32-bitars operativsystem). Om du inte är säker, kolla in pg_controldata
.
Kör följande i din Postgres binära katalog för att få ett definitivt svar:
./pg_controldata /path/to/my/dbcluster
Manualen:
Den faktiska användardatan (kolumnerna i raden) börjar vid förskjutningen som anges av
t_hoff
, som alltid måste vara en multipel avMAXALIGN
avstånd för plattformen.
Så du får vanligtvis lagringsutrymmet optimalt genom att packa data i multipler av 8 byte.
Det finns inget att vinna på exemplet du postade . Det är redan tätt packat. 2 byte utfyllnad efter den sista int2
, 4 byte i slutet. Du kan konsolidera utfyllnaden till 6 byte i slutet, vilket inte skulle förändra någonting.
Overhead per datasida
Datasidans storlek är vanligtvis 8 KB. Lite overhead/uppsvällning på denna nivå också:Rester som inte är tillräckligt stora för att passa en annan tuppel, och ännu viktigare döda rader eller en procentandel reserverad med FILLFACTOR
inställning.
Det finns ett par andra faktorer för storleken på disken att ta hänsyn till:
- Hur många poster kan jag lagra i 5 MB PostgreSQL på Heroku?
- Använder inte NULL i PostgreSQL fortfarande en NULL-bitmapp i rubriken?
- Konfigurera PostgreSQL för läsprestanda
Arraytyper?
Med en array typ som du utvärderade, skulle du lägga till 24 byte av overhead för typen. Dessutom tar arrayelement plats som vanligt. Inget att vinna där.