Distribuce a úprava tabulek ve službě Azure Cosmos DB for PostgreSQL

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

Distribuce tabulek

Pokud chcete vytvořit distribuovanou tabulku, musíte nejprve definovat schéma tabulky. K tomu můžete definovat tabulku pomocí příkazu CREATE TABLE stejným způsobem jako u běžné tabulky PostgreSQL.

CREATE TABLE github_events
(
    event_id bigint,
    event_type text,
    event_public boolean,
    repo_id bigint,
    payload jsonb,
    repo jsonb,
    actor jsonb,
    org jsonb,
    created_at timestamp
);

Dále můžete pomocí funkce create_distributed_table() zadat sloupec distribuce tabulky a vytvořit horizontální oddíly pracovních procesů.

SELECT create_distributed_table('github_events', 'repo_id');

Volání funkce informuje službu Azure Cosmos DB for PostgreSQL, že github_events tabulka by měla být distribuovaná ve sloupci repo_id (hashováním hodnoty sloupce).

Ve výchozím nastavení vytvoří celkem 32 horizontálních oddílů, kde každý horizontální oddíl vlastní část prostoru hash a replikuje se na základě výchozí hodnoty konfigurace citus.shard_replication_factor. Repliky horizontálních oddílů vytvořené v pracovním procesu mají stejné definice schématu tabulky, indexu a omezení jako tabulka v koordinátoru. Po vytvoření replik uloží funkce všechna distribuovaná metadata do koordinátoru.

Každému vytvořenému horizontálnímu oddílu se přiřadí jedinečné ID horizontálního oddílu a všechny jeho repliky mají stejné ID horizontálního oddílu. Horizontální oddíly jsou na pracovním uzlu reprezentovány jako běžné tabulky PostgreSQL s názvem "tablename_shardid", kde tablename je název distribuované tabulky a ID horizontálního oddílu je jedinečné přiřazené ID. Můžete se připojit k instancím postgres pracovního procesu a zobrazit nebo spouštět příkazy v jednotlivých horizontálních oddílech.

Teď jste připraveni vložit data do distribuované tabulky a spouštět na ni dotazy. Další informace o UDF použité v této části najdete v referenčních informacích K DDL tabulek a horizontálních oddílů .

Referenční tabulky

Výše uvedená metoda distribuuje tabulky do několika horizontálních horizontálních oddílů. Další možností je distribuce tabulek do jednoho horizontálního oddílu a replikace horizontálního oddílu do každého pracovního uzlu. Takto distribuované tabulky se nazývají referenční tabulky. Slouží k ukládání dat, ke kterým musí často přistupovat více uzlů v clusteru.

Mezi běžné kandidáty pro referenční tabulky patří:

  • Menší tabulky, které je potřeba spojit s většími distribuovanými tabulkami
  • Tabulky v aplikacích s více tenanty, které nemají sloupec ID tenanta nebo které nejsou přidružené k tenantovi. (Nebo během migrace i u některých tabulek přidružených k tenantovi.)
  • Tabulky, které vyžadují jedinečná omezení napříč více sloupci a jsou dostatečně malé.

Předpokládejme například, že web elektronického obchodování s více tenanty potřebuje vypočítat dph pro transakce v některém ze svých obchodů. Daňové informace nejsou specifické pro žádného tenanta. Dává smysl ho umístit do sdílené tabulky. Referenční tabulka zaměřená na USA může vypadat takto:

-- a reference table

CREATE TABLE states (
  code char(2) PRIMARY KEY,
  full_name text NOT NULL,
  general_sales_tax numeric(4,3)
);

-- distribute it to all workers

SELECT create_reference_table('states');

Dotazy, jako je například výpočet daně z nákupního košíku, se teď můžou spojit states v tabulce bez zatížení sítě a můžou do kódu státu přidat cizí klíč pro lepší ověření.

Kromě distribuce tabulky jako jednoho replikovaného horizontálního oddílu create_reference_table ji uživatelská funkce označí jako referenční tabulku v tabulkách metadat Azure Cosmos DB for PostgreSQL. Azure Cosmos DB for PostgreSQL automaticky provádí dvoufázová potvrzení (2PC) pro úpravy tabulek označených tímto způsobem, což poskytuje záruky silné konzistence.

Další příklad použití referenčních tabulek najdete v kurzu k databázi s více tenanty.

Distribuce dat koordinátoru

Pokud se existující databáze PostgreSQL převede na koordinační uzel clusteru, data v jejích tabulkách se můžou distribuovat efektivně a s minimálním přerušením do aplikace.

Funkce popsaná create_distributed_table výše funguje u prázdných i neprázdných tabulek a u druhé tabulky automaticky distribuuje řádky tabulky v celém clusteru. To, jestli kopíruje data, poznáte podle zprávy "OZNÁMENÍ: Kopírování dat z místní tabulky..." Příklad:

CREATE TABLE series AS SELECT i FROM generate_series(1,1000000) i;
SELECT create_distributed_table('series', 'i');
NOTICE:  Copying data from local table...
 create_distributed_table
 --------------------------

 (1 row)

Zápisy do tabulky jsou při migraci dat blokované a čekající zápisy se po potvrzení funkce zpracovávají jako distribuované dotazy. (Pokud funkce selže, dotazy se znovu stanou místními.) Čtení může pokračovat jako obvykle a po potvrzení funkce se stanou distribuovanými dotazy.

Při distribuci tabulek A a B, kde A má cizí klíč do B, distribuujte nejprve cílovou tabulku klíče B. Když to uděláte ve špatném pořadí, dojde k chybě:

ERROR:  cannot create foreign key constraint
DETAIL:  Referenced table must be a distributed table or a reference table.

Pokud není možné distribuovat ve správném pořadí, vyřaďte cizí klíče, distribuujte tabulky a vytvořte cizí klíče znovu.

Při migraci dat z externí databáze, například z Amazon RDS do služby Azure Cosmos DB for PostgreSQL, nejprve vytvořte distribuované tabulky Azure Cosmos DB for PostgreSQL prostřednictvím create_distributed_tablea pak zkopírujte data do tabulky. Kopírováním do distribuovaných tabulek dochází místo na koordinačním uzlu.

Společné umístění tabulek

Kolokace znamená umístění souvisejících informací na stejných strojích. Umožňuje efektivní dotazy a současně využívá horizontální škálovatelnost celé datové sady. Další informace najdete v tématu kolokace.

Tabulky jsou společně umístěné ve skupinách. Pokud chcete ručně řídit přiřazení kolokační skupiny tabulky, použijte volitelný colocate_with parametr .create_distributed_table Pokud vás nezajímá kolokace tabulky, vypněte tento parametr. Výchozí hodnotou je hodnota 'default', která seskupí tabulku s jakoukoli jinou výchozí tabulkou kolokace, která má stejný typ distribučního sloupce, počet horizontálních oddílů a faktor replikace. Pokud chcete tuto implicitní kolokaci přerušit nebo aktualizovat, můžete použít update_distributed_table_colocation().

-- these tables are implicitly co-located by using the same
-- distribution column type and shard count with the default
-- co-location group

SELECT create_distributed_table('A', 'some_int_col');
SELECT create_distributed_table('B', 'other_int_col');

Pokud nová tabulka nesouvisí s ostatními v její implicitní kolokační skupině, zadejte colocated_with => 'none'.

-- not co-located with other tables

SELECT create_distributed_table('A', 'foo', colocate_with => 'none');

Rozdělení nesouvisejících tabulek do jejich vlastních skupin kolokace zlepší výkon vyrovnávání horizontálních oddílů, protože horizontální oddíly ve stejné skupině se musí přesouvat společně.

Pokud spolu tabulky skutečně souvisejí (například když budou spojeny), může mít smysl je explicitně umístit společně. Zisky z odpovídajícího kolokace jsou důležitější než jakákoli režie na vyrovnávání.

Pokud chcete explicitně umístit více tabulek do společné polohy, rozdělte jednu z nich a pak umístěte ostatní do jejich společné skupiny. Příklad:

-- distribute stores
SELECT create_distributed_table('stores', 'store_id');

-- add to the same group as stores
SELECT create_distributed_table('orders', 'store_id', colocate_with => 'stores');
SELECT create_distributed_table('products', 'store_id', colocate_with => 'stores');

Informace o kolokaačních skupinách jsou uloženy v tabulce pg_dist_colocation , zatímco pg_dist_partition odhalí, které tabulky jsou přiřazené ke kterým skupinám.

Vyřazování tabulek

K odebrání distribuovaných tabulek můžete použít standardní příkaz PostgreSQL DROP TABLE. Stejně jako u běžných tabulek i drop TABLE odebere všechny indexy, pravidla, triggery a omezení, které existují pro cílovou tabulku. Kromě toho také zahodí horizontální oddíly na pracovních uzlech a vyčistí jejich metadata.

DROP TABLE github_events;

Úpravy tabulek

Azure Cosmos DB for PostgreSQL automaticky šíří mnoho druhů příkazů DDL. Úprava distribuované tabulky na koordinačním uzlu aktualizuje také horizontální oddíly pracovních procesů. Jiné příkazy DDL vyžadují ruční šíření a některé jiné jsou zakázány, například ty, které by změnily distribuční sloupec. Při pokusu o spuštění DDL, který není způsobilý pro automatické šíření, dojde k chybě a tabulky na koordinačním uzlu zůstanou beze změny.

Tady je odkaz na kategorie příkazů DDL, které se šíří.

Přidávání a úpravy sloupců

Azure Cosmos DB for PostgreSQL šíří většinu příkazů ALTER TABLE automaticky. Přidání sloupců nebo změna jejich výchozích hodnot funguje stejně jako v databázi PostgreSQL na jednom počítači:

-- Adding a column

ALTER TABLE products ADD COLUMN description text;

-- Changing default value

ALTER TABLE products ALTER COLUMN price SET DEFAULT 7.77;

Významné změny existujícího sloupce, jako je přejmenování nebo změna jeho datového typu, jsou také v pořádku. Datový typ distribučního sloupce však nelze změnit. Tento sloupec určuje, jak se data tabulky distribuují v clusteru, a změna jejího datového typu by vyžadovala přesunutí dat.

Pokus o to způsobí chybu:

-- assumining store_id is the distribution column
-- for products, and that it has type integer

ALTER TABLE products
ALTER COLUMN store_id TYPE text;

/*
ERROR:  XX000: cannot execute ALTER TABLE command involving partition column
LOCATION:  ErrorIfUnsupportedAlterTableStmt, multi_utility.c:2150
*/

Přidávání a odebírání omezení

Použití služby Azure Cosmos DB for PostgreSQL vám umožní dál využívat bezpečnost relační databáze, včetně omezení databáze (viz dokumentace k PostgreSQL). Vzhledem k povaze distribuovaných systémů azure Cosmos DB for PostgreSQL nebude mezi pracovními uzly obsahovat omezení jedinečnosti křížového odkazu ani referenční integritu.

Pokud chcete nastavit cizí klíč mezi společně distribuovanými tabulkami, vždy do klíče zahrňte sloupec distribuce. Zahrnutí distribučního sloupce může zahrnovat vytvoření složeného klíče.

Cizí klíče mohou být vytvořeny v těchto situacích:

  • mezi dvěma místními (nedistribuovanými) tabulkami,
  • mezi dvěma referenčními tabulkami,
  • mezi dvěma distribuovanými tabulkami, pokud klíč obsahuje distribuční sloupec, nebo
  • jako distribuovanou tabulku odkazující na referenční tabulku

Cizí klíče z referenčních tabulek do distribuovaných tabulek se nepodporují.

Poznámka

Primární klíče a omezení jedinečnosti musí obsahovat sloupec distribuce. Když je přidáte do nedistribuovaného sloupce, vygeneruje se chyba.

Tento příklad ukazuje, jak vytvořit primární a cizí klíče v distribuovaných tabulkách:

--
-- Adding a primary key
-- --------------------

-- We'll distribute these tables on the account_id. The ads and clicks
-- tables must use compound keys that include account_id.

ALTER TABLE accounts ADD PRIMARY KEY (id);
ALTER TABLE ads ADD PRIMARY KEY (account_id, id);
ALTER TABLE clicks ADD PRIMARY KEY (account_id, id);

-- Next distribute the tables

SELECT create_distributed_table('accounts', 'id');
SELECT create_distributed_table('ads',      'account_id');
SELECT create_distributed_table('clicks',   'account_id');

--
-- Adding foreign keys
-- -------------------

-- Note that this can happen before or after distribution, as long as
-- there exists a uniqueness constraint on the target column(s) which
-- can only be enforced before distribution.

ALTER TABLE ads ADD CONSTRAINT ads_account_fk
  FOREIGN KEY (account_id) REFERENCES accounts (id);
ALTER TABLE clicks ADD CONSTRAINT clicks_ad_fk
  FOREIGN KEY (account_id, ad_id) REFERENCES ads (account_id, id);

Podobně zahrňte sloupec distribuce do omezení jedinečnosti:

-- Suppose we want every ad to use a unique image. Notice we can
-- enforce it only per account when we distribute by account id.

ALTER TABLE ads ADD CONSTRAINT ads_unique_image
  UNIQUE (account_id, image_url);

Omezení not-null se dají použít u libovolného sloupce (distribuce nebo ne), protože nevyžadují žádné vyhledávání mezi pracovními procesy.

ALTER TABLE ads ALTER COLUMN image_url SET NOT NULL;

Použití OMEZENÍ, KTERÁ NEJSOU PLATNÁ

V některých situacích může být užitečné vynutit omezení pro nové řádky a zároveň umožnit, aby stávající nevyhovující řádky zůstaly beze změny. Azure Cosmos DB for PostgreSQL podporuje tuto funkci pro omezení CHECK a cizí klíče a používá označení omezení PostgreSQL "NENÍ PLATNÉ".

Představte si například aplikaci, která ukládá profily uživatelů do referenční tabulky.

-- we're using the "text" column type here, but a real application
-- might use "citext" which is available in a postgres contrib module

CREATE TABLE users ( email text PRIMARY KEY );
SELECT create_reference_table('users');

Časem si představte, že se do tabulky dostane několik ne adres.

INSERT INTO users VALUES
   ('foo@example.com'), ('hacker12@aol.com'), ('lol');

Chtěli bychom ověřit adresy, ale PostgreSQL nám obvykle neumožňuje přidat omezení CHECK, které selže pro existující řádky. Umožňuje ale omezení označené jako neplatné:

ALTER TABLE users
ADD CONSTRAINT syntactic_email
CHECK (email ~
   '^[a-zA-Z0-9.!#$%&''*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$'
) NOT VALID;

Nové řádky jsou teď chráněné.

INSERT INTO users VALUES ('fake');

/*
ERROR:  new row for relation "users_102010" violates
        check constraint "syntactic_email_102010"
DETAIL:  Failing row contains (fake).
*/

Později, během období mimo špičku, se správce databáze může pokusit opravit chybné řádky a znovu obnovit omezení.

-- later, attempt to validate all rows
ALTER TABLE users
VALIDATE CONSTRAINT syntactic_email;

Další informace o funkci NOT VALID a VALIDATE CONSTRAINT najdete v dokumentaci k PostgreSQL v části ALTER TABLE .

Přidávání nebo odebírání indexů

Azure Cosmos DB for PostgreSQL podporuje přidávání a odebírání indexů:

-- Adding an index

CREATE INDEX clicked_at_idx ON clicks USING BRIN (clicked_at);

-- Removing an index

DROP INDEX clicked_at_idx;

Přidání indexu vyžaduje zámek zápisu, což může být nežádoucí v systému záznamů s více tenanty. Pokud chcete minimalizovat výpadky aplikací, vytvořte místo toho index současně . Tato metoda vyžaduje více celkové práce než standardní sestavení indexu a trvá déle. Vzhledem k tomu, že umožňuje pokračovat v normálních operacích, zatímco je index sestaven, je tato metoda užitečná pro přidání nových indexů v produkčním prostředí.

-- Adding an index without locking table writes

CREATE INDEX CONCURRENTLY clicked_at_idx ON clicks USING BRIN (clicked_at);

Typy a funkce

Vytváření vlastních typů SQL a uživatelem definovaných funkcí pro pracovní uzly Vytváření takových databázových objektů v transakci s distribuovanými operacemi však zahrnuje kompromisy.

Azure Cosmos DB for PostgreSQL paralelizuje operace, například create_distributed_table() napříč horizontálními oddíly, pomocí více připojení na pracovní proces. Zatímco při vytváření databázového objektu ho Azure Cosmos DB for PostgreSQL šíří do pracovních uzlů pomocí jednoho připojení na pracovní proces. Kombinace těchto dvou operací v jedné transakci může způsobit problémy, protože paralelní připojení nebudou moci zobrazit objekt, který byl vytvořen přes jedno připojení, ale ještě nebyl potvrzen.

Představte si blok transakce, který vytvoří typ tabulky, načte data a distribuuje tabulku:

BEGIN;

-- type creation over a single connection:
CREATE TYPE coordinates AS (x int, y int);
CREATE TABLE positions (object_id text primary key, position coordinates);

-- data loading thus goes over a single connection:
SELECT create_distributed_table(‘positions’, ‘object_id’);

SET client_encoding TO 'UTF8';
\COPY positions FROM ‘positions.csv’

COMMIT;

Před verzí Citus 11.0 by Citus odložil vytvoření typu na pracovních uzlech a při vytváření distribuované tabulky ho potvrzoval samostatně. To umožnilo paralelní kopírování create_distributed_table() dat. Také to ale znamenalo, že typ nebyl vždy přítomen na pracovních uzlech Citus – nebo pokud by se transakce vrátila zpět, zůstal typ na pracovních uzlech.

U Citus 11.0 se výchozí chování změní tak, aby se upřednostňují konzistenci schématu mezi koordinačními a pracovními uzly. Nové chování má nevýhodu: Pokud k šíření objektu dojde po paralelním příkazu ve stejné transakci, pak již nelze transakci dokončit, jak je zvýrazněno chybou v následujícím bloku kódu:

BEGIN;
CREATE TABLE items (key text, value text);
-- parallel data loading:
SELECT create_distributed_table(‘items’, ‘key’);
SET client_encoding TO 'UTF8';
\COPY items FROM ‘items.csv’
CREATE TYPE coordinates AS (x int, y int);

ERROR:  cannot run type command because there was a parallel operation on a distributed table in the transaction

Pokud narazíte na tento problém, existují dvě jednoduchá alternativní řešení:

  1. Pokud chcete v této situaci odložit vytvoření typu, použijte set citus.create_object_propagation na automatic . V takovém případě může existovat určitá nekonzistence mezi tím, které databázové objekty existují na různých uzlech.
  2. Pokud chcete zakázat paralelismus pro jednotlivé uzly, použijte nastavit citus.multi_shard_modify_mode na sequential . Načítání dat ve stejné transakci může být pomalejší.

Další kroky