Sdílet prostřednictvím


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

Důležité

Azure Cosmos DB for PostgreSQL se už pro nové projekty nepodporuje. Tuto službu nepoužívejte pro nové projekty. Místo toho použijte jednu z těchto dvou služeb:

  • Azure Cosmos DB for NoSQL můžete použít pro distribuované databázové řešení navržené pro vysoce škálovatelné scénáře s 99,999% smlouvou o úrovni služeb (SLA), okamžitým automatickým škálováním a automatickým převzetím služeb při selhání napříč několika oblastmi.

  • Použijte funkci Elastic Clusters služby Azure Database for PostgreSQL pro horizontálně dělené PostgreSQL pomocí opensourcového rozšíření Citus.

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() určit distribuční sloupec tabulky a vytvořit pracovní fragmenty.

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

Volání funkce informuje službu Azure Cosmos DB for PostgreSQL, že by se tabulka github_events měla distribuovat na sloupci repo_id (pomocí hashování 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 úlomků vytvořené na pracovním uzlu mají stejné schéma tabulky, index a definice omezení jako tabulka na koordinátoru. Po vytvoření replik uloží funkce všechna distribuovaná metadata na koordinátora.

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 název tabulky 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 postgres instancím pracovníka a zobrazit nebo spustit příkazy v jednotlivých datových oddílech.

Teď jste připraveni vložit data do distribuované tabulky a spouštět na ní dotazy. Další informace o uživatelsky definované funkci (UDF) použité v této části najdete v referenčních informacích k DDL pro tabulky a shardy.

Referenční tabulky

Předchozí metoda distribuuje tabulky do několika horizontálních shardů. Další možností je distribuce tabulek do jednoho oddílu a replikace tohoto oddílu do každého pracovního uzlu. Tabulky distribuované tímto způsobem se nazývají referenční tabulky. Používají se k ukládání dat, ke kterým se 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 tenant ID nebo které nejsou přidružené k žádnému tenantovi. (Nebo během migrace, například u některých tabulek přidružených k tenantovi.)
  • Tabulky, které potřebují jedinečná omezení ve více sloupcích a jsou dostatečně malé.

Předpokládejme například, že e-commerce platforma pro více nájemců musí vypočítat daň z prodeje pro transakce v kterémkoli z jejích obchodů. Daňové údaje nejsou specifické pro žádného tenanta. Dává smysl ji 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');

Teď se dotazy, například výpočet daně pro nákupní košík, mohou připojit k tabulce states bez síťové režie a mohou přidat cizí klíč ke kódu státu pro lepší ověření.

Kromě distribuce tabulky jako jediného replikovaného fragmentu ji create_reference_table funkce definovaná uživatelem označí jako referenční tabulku v tabulkách metadat Azure Cosmos DB pro 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 databáze s více tenanty.

Distribuce koordinátorských dat

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

Funkce create_distributed_table popsaná dříve funguje na prázdných i neprázdných tabulkách a u druhé funkce automaticky distribuuje řádky tabulky v celém clusteru. Víte, jestli kopíruje data podle přítomnosti zprávy, "NOTICE: Copying data from local table..." (OZNÁMENÍ: Kopírování dat z místní tabulky... Napří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 v tabulce se během migrace dat zablokují a čekající zápisy se zpracovávají jako distribuované dotazy po potvrzení funkce. (Pokud funkce selže, dotazy se znovu stanou místními.) Čtení může pokračovat normálně a po potvrzení funkce se stanou distribuovanými dotazy.

Při rozdělování tabulek A a B, kde A obsahuje cizí klíč k B, nejprve rozdělte cílovou tabulku klíče B. Když to uděláte v nesprávné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í, zahoď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 Azure Cosmos DB for PostgreSQL, nejprve vytvořte distribuované tabulky Azure Cosmos DB for PostgreSQL prostřednictvím create_distributed_tablea zkopírujte data do tabulky. Kopírování do distribuovaných tabulek zabraňuje výpadku místa na koordinačním uzlu.

Společné umístění tabulek

Kolokace znamená umísťování a uchovávání propojený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 umístěny společně ve skupinách. Chcete-li ručně řídit přiřazení tabulky do skupiny umístění, použijte volitelný parametr colocate_withcreate_distributed_table. Pokud vás nezajímá kolokace tabulky, vyloučíte tento parametr. Výchozí hodnota je hodnota 'default', která seskupí tabulku s jakoukoli jinou výchozí kolokací tabulkou se stejným typem distribučního sloupce, počtem horizontálních oddílů a faktorem replikace. Pokud chcete přerušit nebo aktualizovat tuto implicitní kolokaci, 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 vlastních skupin kolokace zlepšuje 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 jsou tabulky skutečně související (například když jsou spojené), může mít smysl je explicitně umístit. Zisky správné kolokace jsou důležitější než jakákoli režie vyplývající z přerozdělování.

Pokud chcete explicitně umístit více tabulek, umístěte jednu a potom ostatní do její skupiny kolokace. 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 skupinách kolokace jsou uloženy v tabulce pg_dist_colocation, zatímco pg_dist_partition odhalí, které tabulky jsou přiřazeny ke skupinám.

Odstraně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 funkce DROP TABLE odebere všechny indexy, pravidla, triggery a omezení, které existují pro cílovou tabulku. Také zahodí shardy na pracovních uzlech a vyčistí jejich metadata.

DROP TABLE github_events;

Úprava tabulek

Azure Cosmos DB for PostgreSQL automaticky šíří mnoho druhů příkazů DDL. Úprava distribuované tabulky na koordinačním uzlu aktualizuje shardy i na pracovních uzlech. Jiné příkazy DDL vyžadují ruční šíření a některé jiné jsou zakázány, například všechny, které by upravily distribuční sloupec. Pokus o spuštění DDL, který nemá nárok na automatické šíření, vyvolá chybu a ponechá tabulky v koordinačním uzlu beze změny.

Tady je přehled kategorií příkazů DDL, které se šíří.

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

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

-- 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 ale nelze změnit. Tento sloupec určuje, jak se data tabulky distribuují prostřednictvím clusteru, a úprava jejich datového typu by vyžadovala přesunutí dat.

Při pokusu o to dojde k chybě:

-- 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ání nebo odebrání omezení

Použití služby Azure Cosmos DB for PostgreSQL umožňuje 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 nezohledňuje konzistenci omezujících podmínek jedinečnosti ani referenční integritu mezi pracovními uzly.

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

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

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

Poznámka:

Primární klíče a omezení unikátnosti musí obsahovat distribuční sloupec. Přidání těchto prvků do sloupce, který není distribuční, způsobí chybu.

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 distribuční sloupec 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í typu not-null se dají použít u libovolného sloupce (ať už distribuovaného, nebo nedistribuovaného), protože nevyžadují žádné vyhledávání mezi pracovníky.

ALTER TABLE ads ALTER COLUMN image_url SET NOT NULL;

Použití NEPLATNÝch omezení

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í neodpovídající řádky zůstaly beze změny. Azure Cosmos DB for PostgreSQL podporuje tuto funkci pro CHECK omezení a cizí klíče pomocí označení omezení „NOT VALID“ v PostgreSQL.

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

V průběhu času si představte, že se do tabulky dostane několik neplatných 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. Povoluje však 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 nyní 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 se správce databáze může mimo špičku pokusit opravit chybné řádky a znovu ověřit omezení.

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

Dokumentace k PostgreSQL obsahuje další informace o NOT VALID a VALIDATE CONSTRAINT v části ALTER TABLE .

Přidávání a 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 přebírá zámek zápisu, což může být nežádoucí v systému záznamů s více tenanty. Pro minimalizaci výpadků aplikací vytvořte index souběžně. Tato metoda vyžaduje větší celkovou práci než standardní sestavení indexu a dokončení trvá déle. Vzhledem k tomu, že umožňuje normální operace pokračovat během sestavování indexu, je tato metoda užitečná pro přidání nových indexů do produkčního 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živatelsky definovaných funkcí se propaguje na pracovní uzly. Vytvoření takových databázových objektů v transakci s distribuovanými operacemi však zahrnuje kompromisy.

Azure Cosmos DB for PostgreSQL paralelizuje operace, jako je create_distributed_table(), napříč šardy pomocí více připojení na každého pracovníka. Když se vytváří databázový objekt, Azure Cosmos DB pro PostgreSQL ho propaguje do pracovních uzlů s použitím jednoho připojení na každý pracovní uzel. Kombinace dvou operací v jedné transakci může způsobit problémy, protože paralelní připojení nebudou moci vidět objekt, který byl vytvořen v rámci jednoho připojení, ale dosud nebyl potvrzen.

Zvažte blok transakcí, který vytvoří typ, tabulku, 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 Citus 11.0 odkládal Citus vytvoření typu na pracovních uzlech a potvrzoval ho samostatně při vytváření distribuované tabulky. To umožnilo, aby kopírování dat v create_distributed_table() probíhalo paralelně. To však zároveň znamenalo, že typ nebyl vždy k dispozici na pracovních uzlech Citus – nebo pokud došlo ke zpětvzetí transakce, typ by na pracovních uzlech zůstal.

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

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. Pomocí příkazu citus.create_object_propagation na automatic odložíte vytvoření typu v této situaci, kdy může docházet k určité nekonzistenci v tom, které databázové objekty existují na různých uzlech.
  2. Pomocí nastavení citus.multi_shard_modify_modesequential můžete zakázat paralelismus pro jednotlivé uzly. Načtení dat ve stejné transakci může být pomalejší.

Další kroky