Dystrybuowanie i modyfikowanie tabel w usłudze Azure Cosmos DB for PostgreSQL

DOTYCZY: Usługa Azure Cosmos DB for PostgreSQL (obsługiwana przez rozszerzenie bazy danych Citus do bazy danych PostgreSQL)

Dystrybucja tabel

Aby utworzyć tabelę rozproszoną, należy najpierw zdefiniować schemat tabeli. W tym celu można zdefiniować tabelę przy użyciu instrukcji CREATE TABLE w taki sam sposób, jak w przypadku regularnej tabeli 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
);

Następnie możesz użyć funkcji create_distributed_table(), aby określić kolumnę rozkładu tabel i utworzyć fragmenty procesów roboczych.

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

Wywołanie funkcji informuje usługę Azure Cosmos DB for PostgreSQL, że tabela github_events powinna być dystrybuowana w kolumnie repo_id (przez skrót wartości kolumny).

Domyślnie tworzy on 32 fragmenty, gdzie każdy fragment jest właścicielem części obszaru skrótu i jest replikowany na podstawie domyślnej wartości konfiguracji citus.shard_replication_factor. Repliki fragmentów utworzone w ramach procesu roboczego mają te same definicje schematu, indeksu i ograniczenia tabeli co tabela w koordynatorze. Po utworzeniu replik funkcja zapisuje wszystkie rozproszone metadane w koordynatorze.

Każdy utworzony fragment ma przypisany unikatowy identyfikator fragmentu, a wszystkie jego repliki mają ten sam identyfikator fragmentu. Fragmenty są reprezentowane w węźle procesu roboczego jako zwykłe tabele PostgreSQL o nazwie "tablename_shardid", gdzie nazwa tabeli jest nazwą tabeli rozproszonej, a identyfikator fragmentu jest unikatowym przypisanym identyfikatorem. Możesz nawiązać połączenie z wystąpieniami procesu roboczego postgres, aby wyświetlić lub uruchomić polecenia dla poszczególnych fragmentów.

Teraz możesz wstawić dane do tabeli rozproszonej i uruchomić na niej zapytania. Więcej informacji na temat funkcji zdefiniowanej przez użytkownika można również uzyskać w tej sekcji w tabeli i dokumentacji DDL fragmentu .

Tabele odwołań

Powyższa metoda dystrybuuje tabele do wielu poziomych fragmentów. Inną możliwością jest dystrybucja tabel do jednego fragmentu i replikowanie fragmentu do każdego węzła procesu roboczego. Tabele rozproszone w ten sposób są nazywane tabelami referencyjnymi. Są one używane do przechowywania danych, które muszą być często używane przez wiele węzłów w klastrze.

Typowe kandydaty do tabel referencyjnych to:

  • Mniejsze tabele, które muszą łączyć się z większymi tabelami rozproszonymi.
  • Tabele w aplikacjach wielodostępnych, które nie mają kolumny identyfikatora dzierżawy lub które nie są skojarzone z dzierżawą. (Lub, podczas migracji, nawet w przypadku niektórych tabel skojarzonych z dzierżawą).
  • Tabele, które wymagają unikatowych ograniczeń w wielu kolumnach i są wystarczająco małe.

Załóżmy na przykład, że witryna handlu elektronicznego z wieloma dzierżawami musi obliczyć podatek od sprzedaży dla transakcji w dowolnym z jej sklepów. Informacje podatkowe nie są specyficzne dla żadnej dzierżawy. Warto umieścić go w udostępnionej tabeli. Tabela referencyjna skoncentrowana na Stanach Zjednoczonych może wyglądać następująco:

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

Teraz zapytania, takie jak obliczanie podatku dla koszyka zakupowego, mogą łączyć się z states tabelą bez narzutów sieciowych i może dodać klucz obcy do kodu stanu w celu uzyskania lepszej weryfikacji.

Oprócz dystrybucji tabeli jako pojedynczego replikowanego fragmentu funkcja create_reference_table UDF oznacza ją jako tabelę referencyjną w tabelach metadanych usługi Azure Cosmos DB for PostgreSQL. Usługa Azure Cosmos DB for PostgreSQL automatycznie wykonuje dwufazowe zatwierdzenia (2PC) w celu modyfikacji tabel oznaczonych w ten sposób, co zapewnia gwarancje silnej spójności.

Aby zapoznać się z innym przykładem użycia tabel referencyjnych, zobacz samouczek dotyczący bazy danych z wieloma dzierżawami.

Dystrybucja danych koordynatora

Jeśli istniejąca baza danych PostgreSQL jest konwertowana na węzeł koordynacji dla klastra, dane w jej tabelach mogą być efektywnie dystrybuowane i z minimalnymi przerwami w działaniu aplikacji.

Opisana create_distributed_table wcześniej funkcja działa zarówno na pustych, jak i niepustych tabelach, a druga automatycznie dystrybuuje wiersze tabeli w całym klastrze. Wiesz, czy kopiuje dane według obecności komunikatu "NOTICE: Copying data from local table..." (UWAGA: Kopiowanie danych z tabeli lokalnej..." Na przykład:

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)

Zapisy w tabeli są blokowane podczas migrowania danych, a oczekujące operacje zapisu są obsługiwane jako zapytania rozproszone po zatwierdzeniu funkcji. (Jeśli funkcja zakończy się niepowodzeniem, zapytania ponownie staną się lokalne). Odczyty mogą być kontynuowane w normalny sposób i staną się zapytaniami rozproszonymi po zatwierdzeniu funkcji.

Podczas dystrybucji tabel A i B, gdzie A ma klucz obcy do B, najpierw dystrybuuj tabelę docelową klucza B. Wykonanie tej czynności w niewłaściwej kolejności spowoduje błąd:

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

Jeśli nie można dystrybuować w odpowiedniej kolejności, porzucaj klucze obce, dystrybuuj tabele i utwórz ponownie klucze obce.

Podczas migrowania danych z zewnętrznej bazy danych, takiej jak z usługi Amazon RDS do usługi Azure Cosmos DB for PostgreSQL, najpierw utwórz tabele rozproszone usługi Azure Cosmos DB for PostgreSQL za pośrednictwem usługi create_distributed_table, a następnie skopiuj dane do tabeli. Kopiowanie do tabel rozproszonych pozwala uniknąć wyczerpania miejsca w węźle koordynacji.

Kolokowanie tabel

Kolokacja oznacza umieszczenie powiązanych informacji na tych samych maszynach. Umożliwia wydajne wykonywanie zapytań przy jednoczesnym wykorzystaniu skalowalności poziomej dla całego zestawu danych. Aby uzyskać więcej informacji, zobacz kolokacja.

Tabele są kolokowane w grupach. Aby ręcznie kontrolować przypisanie grupy kolokacji tabeli, użyj opcjonalnego colocate_with parametru .create_distributed_table Jeśli nie interesuje Cię kolokacja tabeli, pomiń ten parametr. Domyślnie jest to wartość 'default', która grupuje tabelę z dowolną inną domyślną tabelą kolokacji o tym samym typie kolumny dystrybucji, liczbie fragmentów i współczynniku replikacji. Jeśli chcesz przerwać lub zaktualizować tę niejawną kolokację, możesz użyć polecenia 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');

Jeśli nowa tabela nie jest powiązana z innymi w niejawnej grupie kolokacji, określ wartość colocated_with => 'none'.

-- not co-located with other tables

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

Podzielenie niepowiązanych tabel na własne grupy kolokacji poprawi wydajność ponownego równoważenia fragmentów , ponieważ fragmenty w tej samej grupie muszą zostać przeniesione razem.

Gdy tabele są rzeczywiście powiązane (na przykład kiedy zostaną połączone), warto jawnie je kolokować. Korzyści z odpowiedniej kolokacji są ważniejsze niż jakiekolwiek ponowne równoważenie obciążenia.

Aby jawnie kolokować wiele tabel, dystrybuuj jedną, a następnie umieść pozostałe w swojej grupie kolokacji. Przykład:

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

Informacje o grupach kolokacji są przechowywane w tabeli pg_dist_colocation , podczas gdy pg_dist_partition ujawnia, które tabele są przypisane do których grup.

Porzucanie tabel

Aby usunąć tabele rozproszone, możesz użyć standardowego polecenia PostgreSQL DROP TABLE. Podobnie jak w przypadku zwykłych tabel, funkcja DROP TABLE usuwa wszystkie indeksy, reguły, wyzwalacze i ograniczenia, które istnieją dla tabeli docelowej. Ponadto usuwa również fragmenty w węzłach procesu roboczego i czyści metadane.

DROP TABLE github_events;

Modyfikowanie tabel

Usługa Azure Cosmos DB for PostgreSQL automatycznie propaguje wiele rodzajów instrukcji DDL. Zmodyfikowanie tabeli rozproszonej w węźle koordynacji spowoduje również zaktualizowanie fragmentów procesów roboczych. Inne instrukcje DDL wymagają ręcznego propagacji, a niektóre inne są zabronione, takie jak każda, która zmodyfikuje kolumnę dystrybucji. Próba uruchomienia języka DDL, który nie kwalifikuje się do propagacji automatycznej, spowoduje wystąpienie błędu i pozostawienie tabel w węźle koordynacji bez zmian.

Oto odwołanie do kategorii instrukcji DDL, które propagują.

Dodawanie/modyfikowanie kolumn

Usługa Azure Cosmos DB for PostgreSQL automatycznie propaguje większość poleceń ALTER TABLE . Dodawanie kolumn lub zmienianie ich wartości domyślnych działa tak, jak w przypadku pojedynczej bazy danych PostgreSQL:

-- Adding a column

ALTER TABLE products ADD COLUMN description text;

-- Changing default value

ALTER TABLE products ALTER COLUMN price SET DEFAULT 7.77;

Istotne zmiany w istniejącej kolumnie, takie jak zmiana jej nazwy lub zmiana typu danych również są poprawne. Nie można jednak zmienić typu danych kolumny dystrybucji . Ta kolumna określa, w jaki sposób dane tabeli są dystrybuowane przez klaster, a modyfikowanie ich typu danych wymagałoby przeniesienia danych.

Próba wykonania tej czynności powoduje błąd:

-- 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
*/

Dodawanie/usuwanie ograniczeń

Korzystanie z usługi Azure Cosmos DB for PostgreSQL umożliwia dalsze korzystanie z bezpieczeństwa relacyjnej bazy danych, w tym ograniczeń bazy danych (zobacz dokumentację bazy danych PostgreSQL). Ze względu na charakter systemów rozproszonych usługa Azure Cosmos DB for PostgreSQL nie będzie zawierać ograniczeń unikatowości między odwołaniami ani więzów integralności między węzłami procesu roboczego.

Aby skonfigurować klucz obcy między kolokowanymi tabelami rozproszonymi, zawsze uwzględnij kolumnę dystrybucji w kluczu. Dołączenie kolumny dystrybucji może obejmować tworzenie kluczowego związku.

Klucze obce mogą być tworzone w takich sytuacjach:

Klucze obce z tabel odwołań do tabel rozproszonych nie są obsługiwane.

Uwaga

Klucze podstawowe i ograniczenia unikatowości muszą zawierać kolumnę dystrybucji. Dodanie ich do kolumny bez dystrybucji spowoduje wygenerowanie błędu

W tym przykładzie pokazano, jak utworzyć klucze podstawowe i obce w tabelach rozproszonych:

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

Podobnie uwzględnij kolumnę dystrybucji w ograniczeniach unikatowości:

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

Ograniczenia niepuste można stosować do dowolnej kolumny (dystrybucji lub nie), ponieważ nie wymagają wyszukiwania między procesami roboczymi.

ALTER TABLE ads ALTER COLUMN image_url SET NOT NULL;

Używanie NIEPRAWIDŁOWYch ograniczeń

W niektórych sytuacjach może być przydatne wymuszenie ograniczeń dla nowych wierszy, umożliwiając jednocześnie zachowanie niezmienionych istniejących wierszy niezgodnych. Usługa Azure Cosmos DB for PostgreSQL obsługuje tę funkcję w przypadku ograniczeń CHECK i kluczy obcych przy użyciu oznaczenia ograniczenia "NOT VALID" bazy danych PostgreSQL.

Rozważmy na przykład aplikację, która przechowuje profile użytkowników w tabeli referencyjnej.

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

W czasie wyobraź sobie, że kilka nie-adresów dostać się do tabeli.

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

Chcemy zweryfikować adresy, ale usługa PostgreSQL zwykle nie zezwala na dodanie ograniczenia CHECK, które kończy się niepowodzeniem dla istniejących wierszy. Jednak zezwala na ograniczenie oznaczone jako nieprawidłowe:

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;

Nowe wiersze są teraz chronione.

INSERT INTO users VALUES ('fake');

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

Później, w godzinach innych niż szczyt, administrator bazy danych może spróbować naprawić nieprawidłowe wiersze i ponownie usunąć ograniczenie.

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

Dokumentacja bazy danych PostgreSQL zawiera więcej informacji na temat NIEPRAWIDŁOWEgo i WERYFIKOWANIA OGRANICZEŃ w sekcji ALTER TABLE .

Dodawanie/usuwanie indeksów

Usługa Azure Cosmos DB for PostgreSQL obsługuje dodawanie i usuwanie indeksów:

-- Adding an index

CREATE INDEX clicked_at_idx ON clicks USING BRIN (clicked_at);

-- Removing an index

DROP INDEX clicked_at_idx;

Dodanie indeksu powoduje zablokowanie zapisu, co może być niepożądane w wielodostępnej "system-of-record". Aby zminimalizować przestoje aplikacji, utwórz indeks jednocześnie . Ta metoda wymaga większej całkowitej pracy niż standardowa kompilacja indeksu i trwa dłużej. Jednak ponieważ pozwala to na kontynuowanie normalnych operacji podczas kompilowania indeksu, ta metoda jest przydatna do dodawania nowych indeksów w środowisku produkcyjnym.

-- Adding an index without locking table writes

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

Typy i funkcje

Tworzenie niestandardowych typów SQL i funkcji zdefiniowanych przez użytkownika propogate do węzłów roboczych. Jednak utworzenie takich obiektów bazy danych w transakcji z operacjami rozproszonym obejmuje kompromisy.

Usługa Azure Cosmos DB for PostgreSQL równoległie wykonuje operacje, takie jak create_distributed_table() między fragmentami przy użyciu wielu połączeń na proces roboczy. Podczas tworzenia obiektu bazy danych usługa Azure Cosmos DB for PostgreSQL propaguje ją do węzłów roboczych przy użyciu jednego połączenia na proces roboczy. Połączenie dwóch operacji w jednej transakcji może powodować problemy, ponieważ połączenia równoległe nie będą mogły zobaczyć obiektu, który został utworzony za pośrednictwem jednego połączenia, ale nie został jeszcze zatwierdzony.

Rozważ blok transakcji, który tworzy typ, tabelę, ładuje dane i dystrybuuje tabelę:

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;

Przed citus 11.0 Citus odroczyć utworzenie typu w węzłach procesu roboczego i zatwierdzić go oddzielnie podczas tworzenia tabeli rozproszonej. Umożliwiło to równoległe kopiowanie create_distributed_table() danych. Oznaczało to jednak również, że typ nie zawsze był obecny w węzłach procesu roboczego Citus — lub jeśli transakcja została wycofana, typ pozostanie w węzłach roboczych.

W systemie Citus 11.0 domyślne zachowanie zmienia priorytety spójności schematu między węzłami koordynatora i procesu roboczego. Nowe zachowanie ma wadę: jeśli propagacja obiektu występuje po równoległym poleceniu w tej samej transakcji, transakcja nie może być już ukończona, co zostało wyróżnione przez błąd w bloku kodu poniżej:

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

Jeśli wystąpi ten problem, istnieją dwa proste obejścia:

  1. Użyj zestawu citus.create_object_propagation , aby automatic odroczyć tworzenie typu w tej sytuacji, w takim przypadku może istnieć pewne niespójności między obiektami bazy danych w różnych węzłach.
  2. Użyj ustawienia , citus.multi_shard_modify_mode aby sequential wyłączyć równoległość dla węzła. Ładowanie danych w tej samej transakcji może być wolniejsze.

Następne kroki