Sdílet prostřednictvím


Komprese dat pomocí sloupcových tabulek ve službě Azure Cosmos DB for PostgreSQL

PLATÍ PRO: Azure Cosmos DB for PostgreSQL (využívající rozšíření databáze Citus do PostgreSQL)

Azure Cosmos DB for PostgreSQL podporuje úložiště sloupcových tabulek jen pro připojení pro analytické úlohy a úlohy datových skladů. Pokud jsou sloupce (místo řádků) uložené souvisle na disku, data se zkomprimují a dotazy můžou vyžadovat podmnožinu sloupců rychleji.

Vytvoření tabulky

Pokud chcete použít sloupcové úložiště, zadejte USING columnar při vytváření tabulky:

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

Azure Cosmos DB for PostgreSQL během vkládání převádí řádky na sloupcové úložiště v pruhech. Každý pruh obsahuje data o jedné transakci nebo 15 0000 řádků, podle toho, co je menší. (Velikost pruhu a další parametry sloupcové tabulky lze změnit pomocí funkce alter_columnar_table_set .)

Následující příkaz například vloží všech pět řádků do stejného pruhu, protože všechny hodnoty jsou vloženy do jedné transakce:

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

Pokud je to možné, je nejlepší udělat velké pruhy, protože Azure Cosmos DB for PostgreSQL komprimuje sloupcová data samostatně na prokládání. Můžeme vidět fakta o naší sloupcové tabulce, jako je míra komprese, počet pruhů a průměrné řádky na pruh pomocí 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

Výstup ukazuje, že Azure Cosmos DB for PostgreSQL použil algoritmus komprese zstd k získání komprese 1.31x. Komprese porovnává a) velikost vložených dat, protože byla fázována v paměti s b) velikost těchto dat komprimovaných v konečném pruhu.

Vzhledem k tomu, jak se měří, může nebo nemusí míra komprese odpovídat rozdílu velikosti mezi řádkovým a sloupcovým úložištěm pro tabulku. Jediným způsobem, jak tento rozdíl skutečně zjistit, je vytvořit řádek a sloupcovou tabulku, která obsahuje stejná data, a porovnat.

Měření komprese

Pojďme vytvořit nový příklad s více daty, abychom mohli porovnat úspory komprese.

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

Vyplňte obě tabulky stejnou velkou datovou sadou:

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;

U těchto dat můžete vidět poměr komprese lepší než 8X ve sloupcové tabulce.

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

Příklad

Sloupcové úložiště funguje dobře s dělením tabulek. Příklad najdete v dokumentaci komunity Citus Engine, která se archivuje se sloupcovým úložištěm.

Gotchas

  • Sloupcové úložiště komprimuje na pruh. Pruhy se vytvářejí na transakci, takže vložení jednoho řádku na transakci vloží jednotlivé řádky do vlastních pruhů. Komprese a výkon pruhů s jedním řádkem bude horší než tabulka řádků. Vždy se hromadně vloží do sloupcové tabulky.
  • Pokud se pokazíte a sloupcově rozsadíte spoustu malých pruhů, zaseknete se. Jedinou opravou je vytvoření nové sloupcové tabulky a zkopírování dat z originálu v jedné transakci:
    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;
    
  • V podstatě nekompprimovatelná data můžou být problém, i když sloupcové úložiště je stále užitečné při výběru konkrétních sloupců. Ostatní sloupce není potřeba načíst do paměti.
  • V dělené tabulce s kombinací oddílů řádků a sloupců musí být aktualizace pečlivě zacílené. Vyfiltrujte je tak, aby se našly jenom oddíly řádků.
    • Pokud je operace cílená na konkrétní oddíl řádku (například UPDATE p2 SET i = i + 1), bude úspěšná. Pokud je cílem zadaného sloupcového oddílu (například UPDATE p1 SET i = i + 1), nezdaří se.
    • Pokud je operace cílená na dělenou tabulku a obsahuje klauzuli WHERE, která vylučuje všechny sloupcové oddíly (například UPDATE parent SET i = i + 1 WHERE timestamp = '2020-03-15'), bude úspěšná.
    • Pokud je operace cílená na dělenou tabulku, ale nefiltruje sloupce klíče oddílu, nezdaří se. I když existují klauzule WHERE, které odpovídají řádkům pouze ve sloupcových oddílech, nestačí – klíč oddílu musí být také filtrovaný.

Omezení

Tato funkce má stále významná omezení:

  • Komprese je na disku, ne v paměti
  • Pouze připojení (bez podpory UPDATE/DELETE)
  • Bez uvolnění místa (například vrácené transakce můžou stále spotřebovávat místo na disku)
  • Podpora indexů, prohledávání indexů ani prohledávání rastrového indexu
  • Žádné tidscans
  • Žádné ukázkové kontroly
  • Žádná podpora TOAST (vložené velké hodnoty)
  • Žádná podpora příkazů ON CONFLICT (s výjimkou akcí DO NOTHING bez zadaného cíle).
  • Žádná podpora zámků řazené kolekce členů (SELECT ... PRO SDÍLENÍ VYBERTE ... PRO AKTUALIZACI)
  • Žádná podpora pro serializovatelnou úroveň izolace
  • Podpora pouze pro server PostgreSQL verze 12 nebo novější
  • Žádná podpora pro cizí klíče, jedinečná omezení nebo omezení vyloučení
  • Žádná podpora pro logické dekódování
  • Žádná podpora paralelních kontrol uvnitř uzlu
  • Žádná podpora pro AFTER ... TRIGGERY JEDNOTLIVÝCH ŘÁDKŮ
  • Žádné sloupcové tabulky UNLOGGED
  • Žádné sloupcové tabulky TEMPORARY

Další kroky