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 procesu roboczego.
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 miejsca 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 koordynatora.
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 na poszczególnych fragmentach.
Teraz możesz wstawić dane do tabeli rozproszonej i uruchamiać 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 pojedynczego fragmentu i replikowanie fragmentu do każdego węzła 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 usa 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 zakupów, 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 create_reference_table
funkcja 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 zatwierdzenia dwufazowe (2PC) w celu modyfikacji tabel oznaczonych w ten sposób, co zapewnia silne gwarancje spójności.
Aby zapoznać się z innym przykładem używania tabel referencyjnych, zobacz samouczek dotyczący wielodostępnej bazy danych.
Dystrybucja danych koordynatora
Jeśli istniejąca baza danych PostgreSQL jest konwertowana na węzeł koordynacji dla klastra, dane w jego tabelach mogą być dystrybuowane wydajnie 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. Będziesz wiedzieć, czy kopiuje dane według obecności komunikatu "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 zapisy są obsługiwane jako zapytania rozproszone po zatwierdzeniu funkcji. (Jeśli funkcja zakończy się niepowodzeniem, zapytania staną się ponownie 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 migracji 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 create_distributed_table
metody , a następnie skopiuj dane do tabeli.
Kopiowanie do tabel rozproszonych pozwala uniknąć wyczerpania miejsca w węźle koordynatora.
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ą przyłączone), może to mieć sens, aby jawnie je kolokować. Zyski odpowiedniej kolokacji są ważniejsze niż jakiekolwiek ponowne równoważenie obciążenia.
Aby jawnie kolokować wiele tabel, rozłóż jeden, a następnie umieść inne w swojej grupie kolokacji. Na 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 , a pg_dist_partition ujawnia, które tabele są przypisywane do których grup.
Usuwanie 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 koordynatora 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 automatycznej propagacji, 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 propaguje większość poleceń ALTER TABLE automatycznie. 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ą odpowiednie. Nie można jednak zmienić typu danych kolumny dystrybucji. Ta kolumna określa sposób dystrybucji danych tabeli za pośrednictwem klastra i modyfikowanie jego 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 kontynuowanie korzystania 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 mieć ograniczeń unikatowości odwołania krzyżowego ani integralności referencyjnej między węzłami procesu roboczego.
Aby skonfigurować klucz obcy między kolokowaną tabelą rozproszoną, zawsze uwzględnij kolumnę dystrybucji w kluczu. Dołączenie kolumny dystrybucji może obejmować tworzenie kluczowego związku.
Klucze obce mogą być tworzone w następujących sytuacjach:
- między dwiema lokalnymi (nieprostrybucyjnymi) tabelami,
- między dwiema tabelami referencyjnymi,
- między dwiema tabelami rozproszonymi, gdy klucz zawiera kolumnę dystrybucji lub
- jako tabela rozproszona odwołująca się do tabeli referencyjnej
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 zastosować do dowolnej kolumny (dystrybucji lub nie), ponieważ nie wymagają one żadnych odnośników 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 adresów innych niż 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 ograniczyć.
-- later, attempt to validate all rows
ALTER TABLE users
VALIDATE CONSTRAINT syntactic_email;
Dokumentacja bazy danych PostgreSQL zawiera więcej informacji na temat NOT VALID i VALIDATE CONSTRAINT 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, które może być niepożądane w wielodostępnej "system-of-record". Aby zminimalizować przestój aplikacji, zamiast tego utwórz indeks współbieżnie . Ta metoda wymaga większej całkowitej pracy niż kompilacja indeksu standardowego i trwa dłużej. Jednak ponieważ umożliwia ona kontynuowanie normalnych operacji podczas tworzenia 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 rozproszonymi wiąże się z kompromisami.
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 go 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ć tworzenie typu w węzłach procesu roboczego i zatwierdzić go oddzielnie podczas tworzenia tabeli rozproszonej. Umożliwiło to równoległe create_distributed_table()
kopiowanie danych. Jednak oznaczało to 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 przypadku programu Citus 11.0 domyślne zachowanie zmienia priorytety spójności schematu między węzłami koordynatora i węzłów roboczych. Nowe zachowanie ma wadę: jeśli propagacja obiektu ma miejsce po równoległym poleceniu w tej samej transakcji, nie można już ukończyć transakcji, 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:
- Użyj ustawienia ,
citus.create_object_propagation
abyautomatic
odroczyć tworzenie typu w tej sytuacji, w takim przypadku może wystąpić pewne niespójności między obiektami bazy danych w różnych węzłach. - Użyj ustawienia ,
citus.multi_shard_modify_mode
abysequential
wyłączyć równoległość poszczególnych węzłów. Ładowanie danych w tej samej transakcji może być wolniejsze.