Forbindelsesoptimering

Fuldført

Databaseforbindelser er dyre ressourcer. Hver forbindelse bruger serverhukommelse, kræver autentificeringsoverhead og tæller med servergrænser. For AI-applikationer, der laver hyppige vektorforespørgsler, er effektiv forbindelsesstyring afgørende for at opnå høj gennemstrømning uden at udtømme ressourcer.

Notat

Kodeeksempler i denne enhed demonstrerer forbindelsesstyringsmønstre for Python (psycopg) og .NET (Npgsql). Disse biblioteker opdateres ofte. Besøg psycopg-dokumentationen og Npgsql-dokumentationen for aktuelle API-detaljer.

Forbindelsesoverhead

Oprettelse af en ny PostgreSQL-forbindelse involverer flere trin, hvor hver enkelt tilføjer latenstid:

  1. TCP-håndtryk: Etablering af netværksforbindelsen (typisk 1-3 rundture)
  2. TLS-forhandling: Kryptering af forbindelsen (påkrævet for Azure Database for PostgreSQL)
  3. Autentificering: Verifikation af legitimationsoplysninger (adgangskode- eller tokenudveksling)
  4. Serverprocesallokering: PostgreSQL opretter en backend-proces for hver forbindelse
  5. Sessionsinitialisering: Indstilling af sessionsparametre og indlæsningskonfigurationer

Denne sekvens tager 50-200 millisekunder afhængigt af netværkets latenstid og serverbelastning. For en anbefalingsmotor, der håndterer tusindvis af forespørgsler per sekund, vil oprettelse af nye forbindelser pr. anmodning tage mere tid i forbindelse end i selve forespørgselsudførelsen.

Azure Database for PostgreSQL begrænser samtidige forbindelser baseret på compute-niveau. Burstable B1ms tillader 50 forbindelser, General Purpose 2 vCores tillader 859 forbindelser, General Purpose 4 vCores tillader 1.718 forbindelser, Memory Optimized 4 vCores tillader 3.437 forbindelser, og Memory Optimized 16 vCores tillader 5.000 forbindelser. Overskridelse af disse grænser forårsager forbindelsesfejl. Applikationer, der opretter forbindelser pr. anmodning, kan hurtigt ramme disse grænser under trafikspidser.

Forbindelsespooling med PgBouncer

PgBouncer er en letvægts forbindelsespooler, der sidder mellem din applikation og PostgreSQL. Den vedligeholder en pulje af databaseforbindelser og multiplexer klientforbindelser på tværs af dem, hvilket dramatisk reducerer antallet af faktiske databaseforbindelser, der er nødvendige.

Azure Database for PostgreSQL inkluderer indbygget PgBouncer-understøttelse på generelle og hukommelsesoptimerede beregningsniveauer. Burstable-tieret understøtter ikke denne funktion. Aktivér PgBouncer via Azure-portalen eller CLI. Når den er aktiveret, forbind gennem port 6432 (PgBouncer-porten) i stedet for 5432 (den direkte PostgreSQL-port). PgBouncer-forbindelsesstrengen bruger postgresql://user:password@myserver.postgres.database.azure.com:6432/mydb.

az postgres flexible-server parameter set \
    --resource-group myResourceGroup \
    --server-name myserver \
    --name pgbouncer.enabled \
    --value true

PgBouncer understøtter tre pooling-tilstande, hver med forskellige kompromiser. Sessionstilstand betyder, at en klient holder en serverforbindelse under hele sessionen (indtil forbindelsen afbrydes). Denne tilstand understøtter alle PostgreSQL-funktioner, men giver minimal reduktion af forbindelsen. Transaktionstilstand betyder, at en klient kun har en serverforbindelse under en transaktion. Mellem transaktionerne vender forbindelsen tilbage til puljen. Denne tilstand fungerer godt til de fleste applikationer og reducerer betydeligt forbindelseskravene. Statement-tilstand betyder, at en klient kun får en forbindelse for individuelle statements. Denne tilstand giver maksimal forbindelsesreduktion, men understøtter ikke transaktioner med flere udsagn. For vektorsøgningsarbejdsbelastninger er transaktionstilstand typisk det bedste valg.

PgBouncer eksponerer flere parametre, der styrer poolens adfærd, forbindelsesgrænser og timeout-håndtering. For vektorsøgningsarbejdsbelastninger med bursty trafikmønstre hjælper justering af disse parametre med at balancere forbindelsestilgængelighed mod ressourceforbrug. Konfigurér pgbouncer.default_pool_size (20-50 afhængigt af samtidighedsbehov), pgbouncer.max_client_conn (5000+ for applikationer med høj trafik), pgbouncer.pool_mode (transaktion), og pgbouncer.query_wait_timeout (30-120 sekunder).

Transaktionstilstand returnerer forbindelser til puljen efter hver transaktion, der commer eller rulles tilbage. Dette påvirker flere PostgreSQL-funktioner. Sessionsvariabler nulstilles mellem transaktioner, så indstillinger anvendt med SET ikke vedvarer på tværs af transaktioner. Brug SET LOCAL inden for transaktioner eller konfigurer standardindstillinger på serversiden. Forberedte udsagn virker måske ikke, fordi navngivne forberedte udsagn er knyttet til forbindelser. I transaktionstilstand kan en forberedt sætning, der oprettes i én transaktion, være utilgængelig i den næste, hvis en anden forbindelse tildeles. LISTEN/NOTIFY virker ikke, fordi disse funktioner kræver vedvarende forbindelser og er inkompatible med transaktionspooling. For vektorsøgningsapplikationer er disse begrænsninger sjældent problematiske, da forespørgsler typisk er simple udvælgninger uden sessionsspecifik tilstand.

Applikationsniveau forbindelsespooling

Ud over (eller i stedet for) PgBouncer kan din applikation administrere forbindelsespuljer direkte. Dette giver finere kontrol over forbindelsescyklussen og integreres med applikationsrammer.

Pakken psycopg_pool tilbyder forbindelsespooling for psycopg. Applikationsniveau-pools giver dig kontrol over forbindelsens livscyklus, idle timeout-adfærd og sundhedskontrol. De integreres også naturligt med din applikations fejlhåndtering og logning. Når applikationspools kombineres med PgBouncer, håndterer de lokal forbindelsesstyring, mens PgBouncer håndterer server-side multipleksning. Kontekstmanageren with pool.connection() returnerer automatisk forbindelsen til poolen, når blokken afsluttes, selv hvis der opstår en undtagelse.

from psycopg_pool import ConnectionPool

# Create a connection pool
pool = ConnectionPool(
    conninfo="postgresql://user:password@myserver.postgres.database.azure.com:6432/mydb",
    min_size=5,      # Minimum connections to maintain
    max_size=20,     # Maximum connections allowed
    max_idle=300,    # Close idle connections after 5 minutes
    max_lifetime=3600  # Recycle connections after 1 hour
)

# Use connections from the pool
def search_similar_products(query_embedding, limit=10):
    with pool.connection() as conn:
        with conn.cursor() as cur:
            cur.execute("""
                SELECT id, name, embedding <=> %s AS distance
                FROM products
                ORDER BY embedding <=> %s
                LIMIT %s
            """, (query_embedding, query_embedding, limit))
            return cur.fetchall()

Npgsql har indbygget connection pooling aktiveret som standard, så du behøver ikke en separat pakke. Poolen håndterer automatisk oprettelse, genbrug og bortskaffelse af forbindelser baseret på parametre, du angiver i forbindelsesstrengen. Hver unik forbindelsesstreng har sin egen pool, så konsistente forbindelsesstrenge på tværs af din applikation sikrer effektiv pooludnyttelse. Når du ringer conn.Close() eller forbindelsen bliver fjernet, vender den tilbage til poolen i stedet for at blive ødelagt. Konfigurér pooling gennem forbindelsesstrengsparametre som Minimum Pool Size=5;Maximum Pool Size=20;Connection Idle Lifetime=300;Connection Lifetime=3600.

Poolstørrelsen påvirker både ydeevne og ressourceforbrug. Hvis puljen sættes for lille, venter anmodninger på tilgængelige forbindelser, hvilket øger latenstiden. Hvis du sætter den for stor, spilder du hukommelse og kan overbelaste databaseserveren. Den rette størrelse afhænger af dine trafikmønstre, forespørgselsvarighed og antallet af applikationsinstanser, der deler databasen. Hold minimumsstørrelsen stor nok til at håndtere basistrafik uden at vente. Maks maksimal størrelse til, hvad databasen kan håndtere, divideret med antallet af applikationsinstanser. Hvis du har 10 applikationsinstanser, og din database understøtter 1.000 forbindelser, sæt maksimum til 100 pr. instans (så der er headroom). Genanvend forbindelser periodisk (hver 30.-60. minut) for at opretholde sundheden, fordi langvarige forbindelser kan opbygge hukommelseslækager eller holde forældede cache-planer.

Sessionsstyring for AI-arbejdsbelastninger

Nogle vektorforespørgsler drager fordel af sessionsniveau-indstillinger, der allokerer flere ressourcer til forespørgslen end serverdækkende standardindstillinger tillader.

Vektorlighedsforespørgsler, der sorterer store resultatsæt, drager fordel af øget work_mem. Indsæt det til specifikke sessioner eller transaktioner ved hjælp af SET LOCAL work_mem = '256MB'. SET LOCAL gælder kun inden for den nuværende transaktion. Når transaktionen slutter, vender indstillingen tilbage til standardindstillingen, hvilket er sikkert for poolede forbindelser.

Juster hnsw.ef_search eller ivfflat.probes for forespørgsler med forskellige nøjagtighedskrav. Brug SET LOCAL hnsw.ef_search = 200 for højere genkaldelse i forespørgsler, hvor nøjagtighed er kritisk, eller SET LOCAL hnsw.ef_search = 20 for hurtigere forespørgsler, hvor omtrentlige resultater er acceptable. Dette mønster lader dig balancere nøjagtighed og hastighed baseret på den specifikke anvendelse uden at påvirke andre forespørgsler.

Effektive SDK-brugsmønstre

Ud over forbindelsesstyring påvirker måden, du strukturerer databaseinteraktioner på, ydeevnen.

Netværks-rundrejser tilføjer latenstid til hver databaseoperation. Når du har brug for flere datastykker, eliminerer hentning af dem i én forespørgsel overhead ved netværkstransmission, forespørgselsparsning og resultatserialisering. For AI-applikationer, der henter embeddings for flere elementer, kan batching reducere den samlede latenstid fra hundreder af millisekunder til enkeltcifrede værdier. I stedet for at lave flere rundture med individuelle forespørgsler, brug en enkelt forespørgsel med WHERE id = ANY(%s) og send en liste af ID'er.

Til indlæsning af store mængder vektorer er PostgreSQL-kommandoen COPY dramatisk hurtigere end individuelle INSERT sætninger. COPY strømmer data direkte ind i tabellen i binært eller tekstformat og omgår overhead ved at parse individuelle SQL-sætninger. Når man indlæser embedding-data fra batchbehandlingspipelines eller indledende datamigrationer, COPY kan indlæsningstiderne reduceres fra timer til minutter. COPY kan indlæse hundredtusindvis af rækker per sekund, mens individuelle indsættelser er begrænset til tusindvis af rækker per sekund.

Når din applikation kan parallelisere arbejdet, forbedrer asynkrone databaseoperationer gennemstrømningen ved at udføre flere forespørgsler samtidig uden at blokere tråde. Dette mønster er værdifuldt for AI-arbejdsbelastninger, der skal søge i flere vektorsamlinger samtidig eller kombinere vektorsøgning med anden datahentning. Asynkrone pools håndterer forbindelser effektivt på tværs af samtidige operationer, samtidig med at de respekterer poolstørrelsesgrænser. Brug AsyncConnectionPool fra psycopg_pool og asyncio.gather til at udføre flere søgninger samtidig.

Forbindelsesrobusthed

Netværksproblemer, servergenstarter og failovers kan afbryde databaseforbindelser. Robuste applikationer håndterer disse med ynde.

Midlertidige fejl som netværksfejl, forbindelsesnulstillinger og kortvarig serverutilgængelighed under vedligeholdelse er uundgåelige i cloud-miljøer. Implementering af retry-logik med eksponentiel backoff hjælper din applikation med at komme sig elegant efter disse midlertidige problemer uden at overbelaste serveren med øjeblikkelige genprøver. Tilføj tilfældig jitter for at forhindre, at flere applikationsinstanser prøver igen samtidig. Fang OperationalError undtagelser, beregn ventetiden som (2 ** attempt) + random.uniform(0, 1), og prøv op til et maksimalt antal forsøg.

Timeouts forhindrer, at din applikation venter uendeligt, når databasen er langsom eller utilgængelig. Forbindelsestimeouts begrænser, hvor længe man skal vente, når nye forbindelser etableres, mens sætningstimeouts begrænser forespørgselseksekveringstiden. For vektorsøgningsapplikationer skal du vælge timeouts, der imødekommer dine langsomste legitime forespørgsler, samtidig med at du fejler hurtigt på forespørgsler, der overstiger acceptabel latenstid. Konfigurer timeouts i din forbindelsesstreng ved hjælp af parametre som connect_timeout=10 og options=-c statement_timeout=30000. For vektorforespørgsler, sæt sætnings-timeouts, der tilpasser dig dine langsomst acceptable forespørgsler. En 30-sekunders timeout er rimelig for komplekse vektorsøgninger; Interaktive applikationer kan bruge lavere værdier.

Når alle poolforbindelser er i brug, og nye forespørgsler ankommer, skal poolen enten sætte forespørgsler i kø (hvilket øger latenstiden) eller afvise dem straks. Ingen af mulighederne er ideelle, så overvågning af pooludnyttelsen hjælper dig med at skalere, før udmattelse bliver hyppig. Når udmattelse opstår, hjælper det klienterne med at implementere deres egen genprøvningslogik ved at returnere en klar fejlmeddelelse i stedet for at gå uforudsigeligt i timeout. Håndter PoolTimeout undtagelser ved at returnere en elegant fejl som {"error": "Service temporarily busy, please retry"}. Overvåg pooludnyttelsen og skalér, hvis udmattelse sker ofte.

Yderligere ressourcer