Delen via


Gegevens comprimeren met kolomtabellen in Azure Cosmos DB for PostgreSQL

VAN TOEPASSING OP: Azure Cosmos DB for PostgreSQL (mogelijk gemaakt door de Citus-database-extensie naar PostgreSQL)

Azure Cosmos DB for PostgreSQL biedt ondersteuning voor kolomopslag met alleen toevoeggegevens voor analytische en datawarehousingworkloads. Wanneer kolommen (in plaats van rijen) aaneengesloten op schijf worden opgeslagen, worden gegevens gecomprimeerder en kunnen query's een subset van kolommen sneller aanvragen.

Een tabel maken

Als u kolomopslag wilt gebruiken, geeft u USING columnar op wanneer u een tabel maakt:

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

Azure Cosmos DB for PostgreSQL converteert rijen naar kolomopslag in 'strepen' tijdens het invoegen. Elke stripe bevat de waarde van één transactie of 150000 rijen, afhankelijk van wat minder is. (De streepgrootte en andere parameters van een kolomtabel kunnen worden gewijzigd met de functie alter_columnar_table_set .)

Met de volgende instructie worden bijvoorbeeld alle vijf rijen in dezelfde streep geplaatst, omdat alle waarden in één transactie worden ingevoegd:

-- 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}');

Het is raadzaam om zo mogelijk grote strepen te maken, omdat Azure Cosmos DB for PostgreSQL kolomgegevens afzonderlijk per stripe comprimeert. We kunnen feiten over onze kolomtabel zien, zoals compressiesnelheid, aantal strepen en gemiddelde rijen per stripe met behulp van VACUUM VERBOSE:

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

De uitvoer laat zien dat Azure Cosmos DB for PostgreSQL het zstd-compressiealgoritme heeft gebruikt om 1,31x-gegevenscompressie te verkrijgen. De compressiesnelheid vergelijkt a) de grootte van ingevoegde gegevens zoals deze in het geheugen zijn gefaseerd tegen b) de grootte van die gegevens die in de uiteindelijke stripe zijn gecomprimeerd.

Vanwege hoe deze wordt gemeten, komt de compressiesnelheid mogelijk wel of niet overeen met het grootteverschil tussen rij- en kolomopslag voor een tabel. De enige manier om dat verschil echt te vinden, is door een rij- en kolomtabel te maken die dezelfde gegevens bevatten en vergelijken.

Compressie meten

Laten we een nieuw voorbeeld maken met meer gegevens om de compressiebesparingen te benchmarken.

-- 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;

Vul beide tabellen in met dezelfde grote gegevensset:

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;

Voor deze gegevens ziet u een compressieverhouding van beter dan 8X in de kolomtabel.

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

Opmerking

Columnar Storage werkt goed met tabelpartitionering. Zie de documentatie van de Citus Engine-community voor archivering met kolomopslag voor een voorbeeld.

Gotcha's

  • Kolomopslag comprimeert per streep. Strepen worden per transactie gemaakt, dus als u één rij per transactie invoegt, worden afzonderlijke rijen in hun eigen strepen geplaatst. Compressie en prestaties van stripes met één rij zijn slechter dan een rijtabel. Voeg altijd bulksgewijs toe aan een kolomtabel.
  • Als je een heleboel kleine strepen verpest en columnariseert, zit je vast. De enige oplossing is het maken van een nieuwe kolomtabel en het kopiëren van gegevens uit het origineel in één transactie:
    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;
    
  • Niet-gecomprimeerde gegevens kunnen een probleem zijn, hoewel kolomopslag nog steeds nuttig is bij het selecteren van specifieke kolommen. De andere kolommen hoeven niet in het geheugen te worden geladen.
  • Op een gepartitioneerde tabel met een combinatie van rij- en kolompartities moeten updates zorgvuldig worden toegepast. Filter ze om alleen op de rijpartities te drukken.
    • Als de bewerking is gericht op een specifieke rijpartitie (bijvoorbeeld UPDATE p2 SET i = i + 1), slaagt deze; als deze is gericht op een opgegeven kolompartitie (bijvoorbeeld UPDATE p1 SET i = i + 1), mislukt deze.
    • Als de bewerking is gericht op de gepartitioneerde tabel en een WHERE-component heeft die alle kolompartities (bijvoorbeeld UPDATE parent SET i = i + 1 WHERE timestamp = '2020-03-15') uitsluit, slaagt deze.
    • Als de bewerking is gericht op de gepartitioneerde tabel, maar niet filtert op de kolommen met partitiesleutels, mislukt deze. Zelfs als er WHERE-componenten zijn die overeenkomen met rijen in alleen kolompartities, is dit niet voldoende. De partitiesleutel moet ook worden gefilterd.

Beperkingen

Deze functie heeft nog steeds aanzienlijke beperkingen:

  • Compressie bevindt zich op schijf, niet in het geheugen
  • Alleen toevoegen (geen UPDATE/DELETE-ondersteuning)
  • Geen ruimteherstel (bijvoorbeeld teruggedraaide transacties kunnen nog steeds schijfruimte verbruiken)
  • Geen indexondersteuning, indexscans of bitmapindexscans
  • Geen tidscans
  • Geen voorbeeldscans
  • Geen TOAST-ondersteuning (grote waarden die inline worden ondersteund)
  • Geen ondersteuning voor ON CONFLICT-instructies (behalve NIETS-acties zonder opgegeven doel).
  • Geen ondersteuning voor tuple-vergrendelingen (SELECT ... VOOR DELEN SELECTEERT U ... VOOR UPDATE)
  • Geen ondersteuning voor serialiseerbare isolatieniveau
  • Alleen ondersteuning voor PostgreSQL-serverversies 12+
  • Geen ondersteuning voor refererende sleutels, unieke beperkingen of uitsluitingsbeperkingen
  • Geen ondersteuning voor logische decodering
  • Geen ondersteuning voor parallelle scans binnen knooppunten
  • Geen ondersteuning voor AFTER ... VOOR ELKE RIJ-triggers
  • Geen niet-VASTGELEGDE kolomtabellen
  • Geen TIJDELIJKE kolomtabellen

Volgende stappen