Distribuera och ändra tabeller i Azure Cosmos DB for PostgreSQL

GÄLLER FÖR: Azure Cosmos DB for PostgreSQL (drivs av Citus-databastillägget till PostgreSQL)

Distribuera tabeller

Om du vill skapa en distribuerad tabell måste du först definiera tabellschemat. För att göra det kan du definiera en tabell med hjälp av CREATE TABLE-instruktionen på samma sätt som med en vanlig PostgreSQL-tabell.

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

Sedan kan du använda funktionen create_distributed_table() för att ange tabelldistributionskolumnen och skapa arbetsshards.

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

Funktionsanropet informerar Azure Cosmos DB for PostgreSQL om att den github_events tabellen ska distribueras i kolumnen repo_id (genom att hasha kolumnvärdet).

Den skapar som standard totalt 32 shards, där varje shard äger en del av ett hash-utrymme och replikeras baserat på standardkonfigurationsvärdet citus.shard_replication_factor. De shardrepliker som skapas i arbetaren har samma tabellschema, index och villkorsdefinitioner som tabellen på koordinatorn. När replikerna har skapats sparar funktionen alla distribuerade metadata på koordinatorn.

Varje skapad shard tilldelas ett unikt shard-ID och alla dess repliker har samma fragment-ID. Shards representeras på arbetsnoden som vanliga PostgreSQL-tabeller med namnet "tablename_shardid" där tablename är namnet på den distribuerade tabellen och shard-ID är det unika ID som tilldelats. Du kan ansluta till postgres-instanserna för arbetare för att visa eller köra kommandon på enskilda shards.

Nu är du redo att infoga data i den distribuerade tabellen och köra frågor på den. Du kan också lära dig mer om den UDF som används i det här avsnittet i tabellen och SHARD DDL-referensen .

Referenstabeller

Metoden ovan distribuerar tabeller till flera vågräta shards. En annan möjlighet är att distribuera tabeller till en enda shard och replikera fragmentet till varje arbetsnod. Tabeller som distribueras på det här sättet kallas referenstabeller. De används för att lagra data som måste användas ofta av flera noder i ett kluster.

Vanliga kandidater för referenstabeller är:

  • Mindre tabeller som behöver kopplas till större distribuerade tabeller.
  • Tabeller i appar med flera klientorganisationer som saknar en kolumn med klient-ID eller som inte är associerade med en klientorganisation. (Eller, under migreringen, även för vissa tabeller som är associerade med en klientorganisation.)
  • Tabeller som behöver unika begränsningar i flera kolumner och som är tillräckligt små.

Anta till exempel att en e-handelswebbplats för flera innehavare behöver beräkna moms för transaktioner i någon av sina butiker. Skatteinformation är inte specifik för någon klientorganisation. Det är klokt att lägga den i en delad tabell. En usa-centrerad referenstabell kan se ut så här:

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

Nu kan frågor som en beräkning av skatt för en kundvagn anslutas states till tabellen utan nätverksomkostnader och kan lägga till en sekundärnyckel i delstatskoden för bättre validering.

Förutom att distribuera en tabell som en enda replikerad shard markerar create_reference_table UDF den som en referenstabell i metadatatabellerna för Azure Cosmos DB for PostgreSQL. Azure Cosmos DB for PostgreSQL utför automatiskt tvåfasincheckningar (2PC) för ändringar i tabeller som markerats på det här sättet, vilket ger starka konsekvensgarantier.

Ett annat exempel på hur du använder referenstabeller finns i självstudien om databas för flera klientorganisationer.

Distribuera koordinatordata

Om en befintlig PostgreSQL-databas konverteras till koordinatornoden för ett kluster kan data i dess tabeller distribueras effektivt och med minimalt avbrott i ett program.

Funktionen create_distributed_table som beskrevs tidigare fungerar på både tomma och icke-tomma tabeller, och för det senare distribuerar den automatiskt tabellrader i hela klustret. Du vet om den kopierar data genom att meddelandet "OBS! Kopierar data från lokal tabell..." Till exempel:

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)

Skrivningar i tabellen blockeras medan data migreras och väntande skrivningar hanteras som distribuerade frågor när funktionen checkas in. (Om funktionen misslyckas blir frågorna lokala igen.) Läsningar kan fortsätta som vanligt och distribueras frågor när funktionen checkas in.

När du distribuerar tabellerna A och B, där A har en sekundärnyckel till B, distribuerar du nyckelmåltabellen B först. Om du gör det i fel ordning uppstår ett fel:

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

Om det inte går att distribuera i rätt ordning släpper du sekundärnycklarna, distribuerar tabellerna och återskapar sekundärnycklarna.

När du migrerar data från en extern databas, till exempel från Amazon RDS till Azure Cosmos DB for PostgreSQL, skapar du först de distribuerade tabellerna i Azure Cosmos DB for PostgreSQL via create_distributed_tableoch kopierar sedan data till tabellen. Om du kopierar till distribuerade tabeller får du inte slut på utrymme på koordinatornoden.

Samordna tabeller

Samlokalisering innebär att man placerar relaterad information på samma datorer. Det möjliggör effektiva frågor samtidigt som du drar nytta av den horisontella skalbarheten för hela datauppsättningen. Mer information finns i samlokalisering.

Tabeller samplaceras i grupper. Om du vill styra en tabells samlokaliseringsgruppstilldelning manuellt använder du den valfria colocate_with parametern create_distributed_tableför . Om du inte bryr dig om en tabells samlokalisering utelämnar du den här parametern. Standardvärdet 'default'är , som grupperar tabellen med andra standardsamlokaliseringstabeller med samma distributionskolumntyp, shardantal och replikeringsfaktor. Om du vill avbryta eller uppdatera den här implicita samlokaliseringen kan du använda 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');

När en ny tabell inte är relaterad till andra i den implicita samlokaliseringsgruppen anger du colocated_with => 'none'.

-- not co-located with other tables

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

Genom att dela upp orelaterade tabeller i egna samlokaliseringsgrupper förbättras prestandan för horisontell ombalansering , eftersom shards i samma grupp måste flyttas tillsammans.

När tabeller verkligen är relaterade (till exempel när de ska kopplas) kan det vara klokt att uttryckligen samplacera dem. Vinsterna av lämplig samlokalisering är viktigare än eventuella ombalanseringskostnader.

Om du uttryckligen vill samplacera flera tabeller distribuerar du en och placerar sedan de andra i dess samlokaliseringsgrupp. Exempel:

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

Information om samlokaliseringsgrupper lagras i tabellen pg_dist_colocation medan pg_dist_partition visar vilka tabeller som har tilldelats till vilka grupper.

Ta bort tabeller

Du kan använda standardkommandot PostgreSQL DROP TABLE för att ta bort dina distribuerade tabeller. Precis som med vanliga tabeller tar DROP TABLE bort alla index, regler, utlösare och begränsningar som finns för måltabellen. Dessutom tar den bort fragmenten på arbetsnoderna och rensar deras metadata.

DROP TABLE github_events;

Ändra tabeller

Azure Cosmos DB for PostgreSQL sprider automatiskt många typer av DDL-instruktioner. Om du ändrar en distribuerad tabell på koordinatornoden uppdateras även shards på arbetarna. Andra DDL-instruktioner kräver manuell spridning och vissa andra är förbjudna, till exempel alla som skulle ändra en distributionskolumn. Om du försöker köra DDL som inte är berättigat till automatisk spridning genereras ett fel och tabellerna på koordinatornoden lämnas oförändrade.

Här är en referens till de kategorier av DDL-instruktioner som sprids.

Lägga till/ändra kolumner

Azure Cosmos DB for PostgreSQL sprider de flesta ALTER TABLE-kommandon automatiskt. Att lägga till kolumner eller ändra deras standardvärden fungerar som i en PostgreSQL-databas för en enskild dator:

-- Adding a column

ALTER TABLE products ADD COLUMN description text;

-- Changing default value

ALTER TABLE products ALTER COLUMN price SET DEFAULT 7.77;

Betydande ändringar i en befintlig kolumn som att byta namn på den eller ändra dess datatyp är också bra. Det går dock inte att ändra datatypen för distributionskolumnen . Den här kolumnen bestämmer hur tabelldata distribueras via klustret, och om datatypen ändras måste data flyttas.

Om du försöker göra det uppstår ett fel:

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

Lägga till/ta bort begränsningar

Med Azure Cosmos DB for PostgreSQL kan du fortsätta att ha nytta av säkerheten för en relationsdatabas, inklusive databasbegränsningar (se PostgreSQL-dokumenten). På grund av de distribuerade systemens natur kommer Azure Cosmos DB for PostgreSQL inte att korsreferenser för unikhetsbegränsningar eller referensintegritet mellan arbetsnoder.

Om du vill konfigurera en sekundärnyckel mellan samlokaliserade distribuerade tabeller ska du alltid inkludera distributionskolumnen i nyckeln. Att inkludera distributionskolumnen kan innebära att göra nyckelsammansättningen.

Sekundärnycklar kan skapas i dessa situationer:

  • mellan två lokala (icke-distribuerade) tabeller,
  • mellan två referenstabeller,
  • mellan två samlokaliserade distribuerade tabeller när nyckeln innehåller distributionskolumnen, eller
  • som en distribuerad tabell som refererar till en referenstabell

Sekundärnycklar från referenstabeller till distribuerade tabeller stöds inte.

Anteckning

Primära nycklar och unikhetsbegränsningar måste innehålla distributionskolumnen. Om du lägger till dem i en icke-distributionskolumn genereras ett fel

Det här exemplet visar hur du skapar primära och externa nycklar i distribuerade tabeller:

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

På samma sätt kan du inkludera distributionskolumnen i unikhetsbegränsningar:

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

Begränsningar som inte är null kan tillämpas på valfri kolumn (distribution eller inte) eftersom de inte kräver några sökningar mellan arbetare.

ALTER TABLE ads ALTER COLUMN image_url SET NOT NULL;

Använda OGILTIGA villkor

I vissa situationer kan det vara användbart att framtvinga begränsningar för nya rader, samtidigt som befintliga icke-överensstämmande rader kan förbli oförändrade. Azure Cosmos DB for PostgreSQL stöder den här funktionen för CHECK-begränsningar och sekundärnycklar, med postgreSQL-villkorsbeteckningen "NOT VALID".

Tänk dig till exempel ett program som lagrar användarprofiler i en referenstabell.

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

Tänk dig att några icke-adresser hamnar i tabellen under tiden.

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

Vi vill verifiera adresserna, men PostgreSQL tillåter normalt inte att vi lägger till en CHECK-begränsning som misslyckas för befintliga rader. Det tillåter dock en begränsning som är markerad som ogiltig:

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;

Nya rader är nu skyddade.

INSERT INTO users VALUES ('fake');

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

Senare, under tider med låg belastning, kan en databasadministratör försöka åtgärda de felaktiga raderna och återuppta villkoret.

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

PostgreSQL-dokumentationen innehåller mer information om INTE GILTIG och VALIDERA BEGRÄNSNING i avsnittet ALTER TABLE .

Lägga till/ta bort index

Azure Cosmos DB for PostgreSQL stöder tillägg och borttagning av index:

-- Adding an index

CREATE INDEX clicked_at_idx ON clicks USING BRIN (clicked_at);

-- Removing an index

DROP INDEX clicked_at_idx;

Att lägga till ett index tar ett skrivlås, vilket kan vara oönskat i en "system-of-record" för flera klientorganisationer. Om du vill minimera programavbrotten skapar du indexet samtidigt i stället. Den här metoden kräver mer totalt arbete än en standardindexversion och tar längre tid att slutföra. Men eftersom det gör att normala åtgärder kan fortsätta medan indexet skapas är den här metoden användbar för att lägga till nya index i en produktionsmiljö.

-- Adding an index without locking table writes

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

Typer och funktioner

Skapa anpassade SQL-typer och användardefinierade funktioner propogates till arbetsnoder. Men att skapa sådana databasobjekt i en transaktion med distribuerade åtgärder innebär kompromisser.

Azure Cosmos DB for PostgreSQL parallelliserar åtgärder, till exempel create_distributed_table() mellan shards med flera anslutningar per arbetare. När du skapar ett databasobjekt sprider Azure Cosmos DB for PostgreSQL det till arbetsnoder med en enda anslutning per arbetare. Att kombinera de två åtgärderna i en enda transaktion kan orsaka problem, eftersom de parallella anslutningarna inte kan se objektet som skapades över en enda anslutning men som ännu inte har checkats in.

Överväg ett transaktionsblock som skapar en typ, en tabell, läser in data och distribuerar tabellen:

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;

Innan Citus 11.0 skulle Citus skjuta upp skapandet av typen på arbetsnoderna och checka in den separat när den distribuerade tabellen skapas. Detta gjorde det möjligt att kopiera in create_distributed_table() data parallellt. Men det innebar också att typen inte alltid fanns på Citus-arbetsnoderna – eller om transaktionen återställdes skulle typen finnas kvar på arbetsnoderna.

Med Citus 11.0 ändras standardbeteendet för att prioritera schemakonsekvens mellan koordinator- och arbetsnoder. Det nya beteendet har en nackdel: om objektspridning sker efter ett parallellt kommando i samma transaktion kan transaktionen inte längre slutföras, vilket markeras av FELET i kodblocket nedan:

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

Om du stöter på det här problemet finns det två enkla lösningar:

  1. Använd inställd citus.create_object_propagation på för att automatic skjuta upp skapandet av typen i den här situationen, i vilket fall det kan finnas viss inkonsekvens mellan vilka databasobjekt som finns på olika noder.
  2. Använd inställd citus.multi_shard_modify_mode på för att sequential inaktivera parallellitet per nod. Datainläsningen i samma transaktion kan vara långsammare.

Nästa steg