Dela via


Komprimera data med kolumntabeller i Azure Cosmos DB för PostgreSQL

GÄLLER FÖR: Azure Cosmos DB for PostgreSQL (drivs av Citus-databastillägget till PostgreSQL)

Azure Cosmos DB for PostgreSQL stöder lagring av kolumntabeller med endast tillägg för analys- och datalagerarbetsbelastningar. När kolumner (i stället för rader) lagras sammanhängande på disk blir data mer komprimerbara och frågor kan begära en delmängd av kolumnerna snabbare.

Skapa en tabell

Om du vill använda columnar Storage anger du USING columnar när du skapar en tabell:

CREATE TABLE contestant (
    handle TEXT,
    birthdate DATE,
    rating INT,
    percentile FLOAT,
    country CHAR(3),
    achievements TEXT[]
) USING columnar;

Azure Cosmos DB for PostgreSQL konverterar rader till kolumnlagring i "stripes" under infogning. Varje rand innehåller en transaktions värde av data, eller 150000 rader, beroende på vilket som är mindre. (Randstorleken och andra parametrar i en kolumntabell kan ändras med funktionen alter_columnar_table_set .)

Följande instruktion placerar till exempel alla fem raderna i samma rand eftersom alla värden infogas i en enda transaktion:

-- insert these values into a single columnar stripe

INSERT INTO contestant VALUES
  ('a','1990-01-10',2090,97.1,'XA','{a}'),
  ('b','1990-11-01',2203,98.1,'XA','{a,b}'),
  ('c','1988-11-01',2907,99.4,'XB','{w,y}'),
  ('d','1985-05-05',2314,98.3,'XB','{}'),
  ('e','1995-05-05',2236,98.2,'XC','{a}');

Det är bäst att göra stora ränder när det är möjligt, eftersom Azure Cosmos DB for PostgreSQL komprimerar kolumndata separat per rand. Vi kan se fakta om vår kolumntabell som komprimeringshastighet, antal ränder och genomsnittliga rader per rand med hjälp VACUUM VERBOSEav :

VACUUM VERBOSE contestant;
INFO:  statistics for "contestant":
storage id: 10000000000
total file size: 24576, total data size: 248
compression rate: 1.31x
total row count: 5, stripe count: 1, average rows per stripe: 5
chunk count: 6, containing data for dropped columns: 0, zstd compressed: 6

Utdata visar att Azure Cosmos DB for PostgreSQL använde zstd-komprimeringsalgoritmen för att få 1,31 x datakomprimering. Komprimeringshastigheten jämför a) storleken på infogade data när de mellanlagrades i minnet mot b) storleken på dessa data som komprimerats i dess slutliga rand.

På grund av hur den mäts kan komprimeringshastigheten matcha storleksskillnaden mellan rad- och kolumnlagring för en tabell. Det enda sättet att verkligen hitta den skillnaden är att konstruera en rad- och kolumntabell som innehåller samma data och jämföra.

Mäta komprimering

Nu ska vi skapa ett nytt exempel med mer data för att jämföra komprimeringsbesparingarna.

-- first a wide table using row storage
CREATE TABLE perf_row(
  c00 int8, c01 int8, c02 int8, c03 int8, c04 int8, c05 int8, c06 int8, c07 int8, c08 int8, c09 int8,
  c10 int8, c11 int8, c12 int8, c13 int8, c14 int8, c15 int8, c16 int8, c17 int8, c18 int8, c19 int8,
  c20 int8, c21 int8, c22 int8, c23 int8, c24 int8, c25 int8, c26 int8, c27 int8, c28 int8, c29 int8,
  c30 int8, c31 int8, c32 int8, c33 int8, c34 int8, c35 int8, c36 int8, c37 int8, c38 int8, c39 int8,
  c40 int8, c41 int8, c42 int8, c43 int8, c44 int8, c45 int8, c46 int8, c47 int8, c48 int8, c49 int8,
  c50 int8, c51 int8, c52 int8, c53 int8, c54 int8, c55 int8, c56 int8, c57 int8, c58 int8, c59 int8,
  c60 int8, c61 int8, c62 int8, c63 int8, c64 int8, c65 int8, c66 int8, c67 int8, c68 int8, c69 int8,
  c70 int8, c71 int8, c72 int8, c73 int8, c74 int8, c75 int8, c76 int8, c77 int8, c78 int8, c79 int8,
  c80 int8, c81 int8, c82 int8, c83 int8, c84 int8, c85 int8, c86 int8, c87 int8, c88 int8, c89 int8,
  c90 int8, c91 int8, c92 int8, c93 int8, c94 int8, c95 int8, c96 int8, c97 int8, c98 int8, c99 int8
);

-- next a table with identical columns using columnar storage
CREATE TABLE perf_columnar(LIKE perf_row) USING COLUMNAR;

Fyll båda tabellerna med samma stora datauppsättning:

INSERT INTO perf_row
  SELECT
    g % 00500, g % 01000, g % 01500, g % 02000, g % 02500, g % 03000, g % 03500, g % 04000, g % 04500, g % 05000,
    g % 05500, g % 06000, g % 06500, g % 07000, g % 07500, g % 08000, g % 08500, g % 09000, g % 09500, g % 10000,
    g % 10500, g % 11000, g % 11500, g % 12000, g % 12500, g % 13000, g % 13500, g % 14000, g % 14500, g % 15000,
    g % 15500, g % 16000, g % 16500, g % 17000, g % 17500, g % 18000, g % 18500, g % 19000, g % 19500, g % 20000,
    g % 20500, g % 21000, g % 21500, g % 22000, g % 22500, g % 23000, g % 23500, g % 24000, g % 24500, g % 25000,
    g % 25500, g % 26000, g % 26500, g % 27000, g % 27500, g % 28000, g % 28500, g % 29000, g % 29500, g % 30000,
    g % 30500, g % 31000, g % 31500, g % 32000, g % 32500, g % 33000, g % 33500, g % 34000, g % 34500, g % 35000,
    g % 35500, g % 36000, g % 36500, g % 37000, g % 37500, g % 38000, g % 38500, g % 39000, g % 39500, g % 40000,
    g % 40500, g % 41000, g % 41500, g % 42000, g % 42500, g % 43000, g % 43500, g % 44000, g % 44500, g % 45000,
    g % 45500, g % 46000, g % 46500, g % 47000, g % 47500, g % 48000, g % 48500, g % 49000, g % 49500, g % 50000
  FROM generate_series(1,50000000) g;

INSERT INTO perf_columnar
  SELECT
    g % 00500, g % 01000, g % 01500, g % 02000, g % 02500, g % 03000, g % 03500, g % 04000, g % 04500, g % 05000,
    g % 05500, g % 06000, g % 06500, g % 07000, g % 07500, g % 08000, g % 08500, g % 09000, g % 09500, g % 10000,
    g % 10500, g % 11000, g % 11500, g % 12000, g % 12500, g % 13000, g % 13500, g % 14000, g % 14500, g % 15000,
    g % 15500, g % 16000, g % 16500, g % 17000, g % 17500, g % 18000, g % 18500, g % 19000, g % 19500, g % 20000,
    g % 20500, g % 21000, g % 21500, g % 22000, g % 22500, g % 23000, g % 23500, g % 24000, g % 24500, g % 25000,
    g % 25500, g % 26000, g % 26500, g % 27000, g % 27500, g % 28000, g % 28500, g % 29000, g % 29500, g % 30000,
    g % 30500, g % 31000, g % 31500, g % 32000, g % 32500, g % 33000, g % 33500, g % 34000, g % 34500, g % 35000,
    g % 35500, g % 36000, g % 36500, g % 37000, g % 37500, g % 38000, g % 38500, g % 39000, g % 39500, g % 40000,
    g % 40500, g % 41000, g % 41500, g % 42000, g % 42500, g % 43000, g % 43500, g % 44000, g % 44500, g % 45000,
    g % 45500, g % 46000, g % 46500, g % 47000, g % 47500, g % 48000, g % 48500, g % 49000, g % 49500, g % 50000
  FROM generate_series(1,50000000) g;

VACUUM (FREEZE, ANALYZE) perf_row;
VACUUM (FREEZE, ANALYZE) perf_columnar;

För dessa data kan du se ett komprimeringsförhållande på bättre än 8X i kolumntabellen.

SELECT pg_total_relation_size('perf_row')::numeric/
       pg_total_relation_size('perf_columnar') AS compression_ratio;
 compression_ratio
--------------------
 8.0196135873627944
(1 row)

Exempel

Columnar Storage fungerar bra med tabellpartitionering. Ett exempel finns i citusmotorns communitydokumentation, arkivering med columnar storage.

Gotchas

  • Columnar Storage komprimerar per rand. Ränder skapas per transaktion, så om du infogar en rad per transaktion placeras enskilda rader i sina egna ränder. Komprimering och prestanda för enstaka radränder blir sämre än en radtabell. Infoga alltid massvis i en kolumntabell.
  • Om du strular till det och kolumnariserar ett gäng små ränder, är du fast. Den enda korrigeringen är att skapa en ny kolumntabell och kopiera data från originalet i en transaktion:
    BEGIN;
    CREATE TABLE foo_compacted (LIKE foo) USING columnar;
    INSERT INTO foo_compacted SELECT * FROM foo;
    DROP TABLE foo;
    ALTER TABLE foo_compacted RENAME TO foo;
    COMMIT;
    
  • I grunden kan icke-komprimerbara data vara ett problem, även om kolumnlagring fortfarande är användbart när du väljer specifika kolumner. Den behöver inte läsa in de andra kolumnerna i minnet.
  • I en partitionerad tabell med en blandning av rad- och kolumnpartitioner måste uppdateringar vara noggrant riktade. Filtrera dem så att de bara träffar radpartitionerna.
    • Om åtgärden är riktad mot en specifik radpartition (till exempel UPDATE p2 SET i = i + 1), kommer den att lyckas. Om den riktas mot en angiven kolumnpartition (till exempel UPDATE p1 SET i = i + 1), misslyckas den.
    • Om åtgärden är riktad mot den partitionerade tabellen och har en WHERE-sats som exkluderar alla kolumnpartitioner (till exempel UPDATE parent SET i = i + 1 WHERE timestamp = '2020-03-15') kommer den att lyckas.
    • Om åtgärden är riktad mot den partitionerade tabellen, men inte filtrerar på partitionsnyckelkolumnerna, misslyckas den. Även om det finns WHERE-satser som endast matchar rader i kolumnpartitioner räcker det inte – partitionsnyckeln måste också filtreras.

Begränsningar

Den här funktionen har fortfarande betydande begränsningar:

  • Komprimering finns på disken, inte i minnet
  • Endast tillägg (inget stöd för UPPDATERING/TA BORT)
  • Ingen utrymmesåtertagning (till exempel kan återställda transaktioner fortfarande förbruka diskutrymme)
  • Inget indexstöd, indexgenomsökningar eller bitmappsindexgenomsökningar
  • Inga tidscans
  • Inga exempelgenomsökningar
  • Inget TOAST-stöd (stora värden stöds infogade)
  • Inget stöd för ON CONFLICT-instruktioner (förutom GÖR INGENTING-åtgärder utan angivet mål).
  • Inget stöd för tuppeln lås (SELECT ... FÖR DELA VÄLJER DU ... FÖR UPPDATERING)
  • Inget stöd för serialiserbar isoleringsnivå
  • Endast stöd för PostgreSQL-serverversioner 12+
  • Inget stöd för sekundärnycklar, unika begränsningar eller undantagsbegränsningar
  • Inget stöd för logisk avkodning
  • Inget stöd för parallella genomsökningar inom noden
  • Inget stöd för AFTER ... FÖR VARJE RADutlösare
  • Inga ologgade kolumntabeller
  • Inga temporära kolumntabeller

Nästa steg