sql >> Databáze >  >> RDS >> Database

Jak vytvořit index v Django bez výpadků

Správa migrací databází je velkou výzvou v každém softwarovém projektu. Naštěstí od verze 1.7 Django přichází s vestavěným migračním rámcem. Rámec je velmi výkonný a užitečný při řízení změn v databázích. Flexibilita poskytovaná rámcem však vyžadovala určité kompromisy. Abyste pochopili omezení migrací Django, budete se zabývat dobře známým problémem:vytvořením indexu v Django bez prostojů.

V tomto kurzu se naučíte:

  • Jak a kdy Django generuje nové migrace
  • Jak zkontrolovat příkazy, které Django generuje pro provádění migrací
  • Jak bezpečně upravit migraci tak, aby vyhovovala vašim potřebám

Tento výukový program pro střední úroveň je určen pro čtenáře, kteří již znají migrace Django. Úvod do tohoto tématu najdete v Django Migrations:A Primer.

Box zdarma: Kliknutím sem získáte bezplatný přístup k dalším výukovým programům a zdrojům Django, které můžete použít k prohloubení svých dovedností v oblasti vývoje webu v jazyce Python.


Problém s vytvářením indexu v Django Migrations

Běžnou změnou, která se obvykle stává nezbytnou, když data uložená vaší aplikací rostou, je přidání indexu. Indexy se používají k urychlení dotazů a zrychlení a odezvy aplikace.

Ve většině databází vyžaduje přidání indexu výhradní zámek tabulky. Exkluzivní zámek zabraňuje operacím úpravy dat (DML), jako je UPDATE , INSERT a DELETE při vytváření indexu.

Zámky získává databáze implicitně při provádění určitých operací. Když se například uživatel přihlásí do vaší aplikace, Django aktualizuje last_login v poli auth_user stůl. Chcete-li provést aktualizaci, databáze bude muset nejprve získat zámek na řádku. Pokud je řádek aktuálně uzamčen jiným připojením, můžete získat výjimku databáze.

Uzamčení tabulky může představovat problém, když je nutné udržovat systém dostupný během migrací. Čím větší je tabulka, tím déle může vytvoření indexu trvat. Čím déle trvá vytvoření indexu, tím déle je systém nedostupný nebo nereaguje pro uživatele.

Někteří dodavatelé databází poskytují způsob, jak vytvořit index bez zamykání tabulky. Chcete-li například vytvořit index v PostgreSQL bez zamykání tabulky, můžete použít CONCURRENTLY klíčové slovo:

CREATE INDEX CONCURRENTLY ix ON table (column);

V Oracle existuje ONLINE možnost povolit operace DML na tabulce při vytváření indexu:

CREATE INDEX ix ON table (column) ONLINE;

Při generování migrací nebude Django používat tato speciální klíčová slova. Spuštěním migrace tak, jak je, získá databáze výhradní zámek na tabulce a zabrání operacím DML při vytváření indexu.

Souběžné vytváření indexu má některá omezení. Je důležité předem porozumět problémům specifickým pro váš backend databáze. Jednou výhradou v PostgreSQL je například to, že vytváření indexu současně trvá déle, protože vyžaduje další skenování tabulky.

V tomto výukovém programu použijete migrace Django k vytvoření indexu na velké tabulce, aniž by došlo k prostojům.

Poznámka: Abyste mohli postupovat podle tohoto návodu, doporučujeme vám použít backend PostgreSQL, Django 2.xa Python 3.

Je možné sledovat i další databázové backendy. V místech, kde se používají funkce SQL jedinečné pro PostgreSQL, změňte SQL tak, aby odpovídalo vaší databázi.



Nastavení

Budete používat vymyšlený Sale model v aplikaci s názvem app . V reálném životě modely jako Sale jsou hlavní tabulky v databázi a obvykle budou velmi velké a ukládají velké množství dat:

# models.py

from django.db import models

class Sale(models.Model):
    sold_at = models.DateTimeField(
        auto_now_add=True,
    )
    charged_amount = models.PositiveIntegerField()

Chcete-li vytvořit tabulku, vygenerujte počáteční migraci a použijte ji:

$ python manage.py makemigrations
Migrations for 'app':
  app/migrations/0001_initial.py
    - Create model Sale

$ python manage migrate
Operations to perform:
  Apply all migrations: app
Running migrations:
  Applying app.0001_initial... OK

Po chvíli se prodejní tabulka velmi rozroste a uživatelé si začnou stěžovat na pomalost. Při sledování databáze jste si všimli, že mnoho dotazů používá sold_at sloupec. Chcete-li věci urychlit, rozhodnete se, že potřebujete index na sloupci.

Chcete-li přidat index na sold_at , provedete v modelu následující změnu:

# models.py

from django.db import models

class Sale(models.Model):
    sold_at = models.DateTimeField(
        auto_now_add=True,
        db_index=True,
    )
    charged_amount = models.PositiveIntegerField()

Pokud spustíte tuto migraci tak, jak je, Django vytvoří index na tabulce a bude uzamčen, dokud nebude index dokončen. Vytvoření indexu na velmi velké tabulce může chvíli trvat a vy se chcete vyhnout prostojům.

V místním vývojovém prostředí s malou datovou sadou a velmi malým počtem připojení se tato migrace může zdát okamžitá. U velkých datových sad s mnoha souběžnými připojeními však může získání zámku a vytvoření indexu chvíli trvat.

V dalších krocích upravíte migrace vytvořené Django tak, aby se vytvořil index, aniž by došlo k výpadkům.



Falešná migrace

První přístup je vytvořit index ručně. Chystáte se vygenerovat migraci, ale ve skutečnosti nenecháte Django ji použít. Místo toho spustíte SQL ručně v databázi a pak přimějete Djanga, aby si myslel, že migrace je dokončena.

Nejprve vygenerujte migraci:

$ python manage.py makemigrations --name add_index_fake
Migrations for 'app':
  app/migrations/0002_add_index_fake.py
    - Alter field sold_at on sale

Použijte sqlmigrate příkaz k zobrazení SQL, který Django použije k provedení této migrace:

$ python manage.py sqlmigrate app 0002
BEGIN;
--
-- Alter field sold_at on sale
--
CREATE INDEX "app_sale_sold_at_b9438ae4" ON "app_sale" ("sold_at");
COMMIT;

Chcete vytvořit index bez zamykání tabulky, takže musíte upravit příkaz. Přidejte CONCURRENTLY klíčové slovo a spustit v databázi:

app=# CREATE INDEX CONCURRENTLY "app_sale_sold_at_b9438ae4"
ON "app_sale" ("sold_at");

CREATE INDEX

Všimněte si, že jste provedli příkaz bez BEGIN a COMMIT díly. Vynecháním těchto klíčových slov se příkazy provedou bez databázové transakce. Databázové transakce probereme později v článku.

Pokud se po provedení příkazu pokusíte použít migraci, zobrazí se následující chyba:

$ python manage.py migrate

Operations to perform:
  Apply all migrations: app
Running migrations:
  Applying app.0002_add_index_fake...Traceback (most recent call last):
  File "venv/lib/python3.7/site-packages/django/db/backends/utils.py", line 85, in _execute
    return self.cursor.execute(sql, params)

psycopg2.ProgrammingError: relation "app_sale_sold_at_b9438ae4" already exists

Django si stěžuje, že index již existuje, takže nemůže pokračovat v migraci. Právě jste vytvořili index přímo v databázi, takže nyní musíte přimět Djanga, aby si myslel, že migrace již byla použita.

Jak předstírat migraci

Django poskytuje vestavěný způsob označení migrací jako provedených, aniž by je skutečně provedl. Chcete-li použít tuto možnost, nastavte --fake příznak při použití migrace:

$ python manage.py migrate --fake
Operations to perform:
  Apply all migrations: app
Running migrations:
  Applying app.0002_add_index_fake... FAKED

Django tentokrát neuvedl chybu. Ve skutečnosti Django ve skutečnosti žádnou migraci neaplikoval. Pouze to označilo jako spuštěné (nebo FAKED ).

Zde je několik problémů, které je třeba zvážit při předstírání migrace:

  • Ruční příkaz musí být ekvivalentní SQL generovanému Django: Musíte se ujistit, že příkaz, který spustíte, je ekvivalentní SQL generovanému Django. Použijte sqlmigrate k vytvoření příkazu SQL. Pokud se příkazy neshodují, můžete skončit s nekonzistencemi mezi databází a stavem modelů.

  • Další nepoužité migrace budou také falešné: Když máte více nepoužitých migrací, všechny budou falešné. Než použijete migrace, je důležité zajistit, aby nebyly použity pouze migrace, které chcete předstírat. V opačném případě můžete skončit s nesrovnalostmi. Další možností je zadat přesnou migraci, kterou chcete předstírat.

  • Je vyžadován přímý přístup k databázi: V databázi musíte spustit příkaz SQL. To není vždy možnost. Také provádění příkazů přímo v produkční databázi je nebezpečné a je třeba se mu vyhnout, je-li to možné.

  • Procesy automatického nasazení mohou vyžadovat úpravy: Pokud jste proces nasazení zautomatizovali (pomocí CI, CD nebo jiných automatizačních nástrojů), možná budete muset proces upravit tak, aby falešné migrace. To není vždy žádoucí.

Vyčištění

Než přejdete k další části, musíte uvést databázi zpět do jejího stavu hned po úvodní migraci. Chcete-li to provést, migrujte zpět na původní migraci:

$ python manage.py migrate 0001
Operations to perform:
  Target specific migration: 0001_initial, from app
Running migrations:
  Rendering model states... DONE
  Unapplying app.0002_add_index_fake... OK

Django zrušil použití změn provedených při druhé migraci, takže nyní je bezpečné soubor také smazat:

$ rm app/migrations/0002_add_index_fake.py

Abyste se ujistili, že jste udělali vše správně, zkontrolujte migrace:

$ python manage.py showmigrations app
app
 [X] 0001_initial

Byla použita počáteční migrace a neexistují žádné nepoužité migrace.



Spustit nezpracovaný SQL při migracích

V předchozí části jste provedli SQL přímo v databázi a předstírali migraci. Tím se práce splní, ale existuje lepší řešení.

Django poskytuje způsob, jak spouštět nezpracovaný SQL při migracích pomocí RunSQL . Zkusme to použít místo spouštění příkazu přímo v databázi.

Nejprve vygenerujte novou prázdnou migraci:

$ python manage.py makemigrations app --empty --name add_index_runsql
Migrations for 'app':
  app/migrations/0002_add_index_runsql.py

Dále upravte soubor migrace a přidejte RunSQL operace:

# migrations/0002_add_index_runsql.py

from django.db import migrations, models

class Migration(migrations.Migration):
    atomic = False

    dependencies = [
        ('app', '0001_initial'),
    ]

    operations = [
        migrations.RunSQL(
            'CREATE INDEX "app_sale_sold_at_b9438ae4" '
            'ON "app_sale" ("sold_at");',
        ),
    ]

Když spustíte migraci, získáte následující výstup:

$ python manage.py migrate
Operations to perform:
  Apply all migrations: app
Running migrations:
  Applying app.0002_add_index_runsql... OK

Vypadá to dobře, ale je tu problém. Zkusme znovu vygenerovat migraci:

$ python manage.py makemigrations --name leftover_migration
Migrations for 'app':
  app/migrations/0003_leftover_migration.py
    - Alter field sold_at on sale

Django znovu vytvořil stejnou migraci. Proč to udělal?

Vyčištění

Než budeme moci odpovědět na tuto otázku, musíte vyčistit a vrátit zpět změny, které jste v databázi provedli. Začněte odstraněním poslední migrace. Nebyl použit, takže jej můžete bezpečně smazat:

$ rm app/migrations/0003_leftover_migration.py

Dále uveďte seznam migrací pro app aplikace:

$ python manage.py showmigrations app
app
 [X] 0001_initial
 [X] 0002_add_index_runsql

Třetí migrace je pryč, ale druhá je použita. Chcete se vrátit do stavu hned po počáteční migraci. Zkuste migrovat zpět na původní migraci jako v předchozí části:

$ python manage.py migrate app 0001
Operations to perform:
  Target specific migration: 0001_initial, from app
Running migrations:
  Rendering model states... DONE
  Unapplying app.0002_add_index_runsql...Traceback (most recent call last):

NotImplementedError: You cannot reverse this operation

Django není schopen zvrátit migraci.



Operace zpětné migrace

Aby Django zvrátil migraci, provede pro každou operaci opačnou akci. V tomto případě je opak přidání indexu jeho vypuštění. Jak jste již viděli, když je migrace vratná, můžete ji zrušit. Stejně jako můžete použít checkout v Gitu můžete migraci zvrátit, pokud spustíte migrate k dřívější migraci.

Mnoho vestavěných operací migrace již definuje zpětnou akci. Opačným postupem pro přidání pole je například vypuštění odpovídajícího sloupce. Opačným postupem pro vytvoření modelu je vypuštění odpovídající tabulky.

Některé operace migrace nejsou vratné. Například neexistuje žádná zpětná akce pro odstranění pole nebo smazání modelu, protože jakmile byla migrace aplikována, data jsou pryč.

V předchozí části jste použili RunSQL úkon. Když jste se pokusili zvrátit migraci, došlo k chybě. Podle chyby nelze jednu z operací při migraci vrátit zpět. Django ve výchozím nastavení nedokáže zvrátit nezpracovaný SQL. Protože Django neví, co bylo operací provedeno, nemůže automaticky vygenerovat opačnou akci.

Jak zajistit, aby byla migrace vratná

Aby byla migrace vratná, musí být vratné všechny operace v ní. Část migrace není možné vrátit zpět, takže jediná nevratná operace učiní celou migraci nevratnou.

Chcete-li vytvořit RunSQL operace reverzibilní, musíte zadat SQL, aby se provedl, když je operace obrácena. Reverzní SQL je k dispozici v reverse_sql argument.

Opačnou akcí k přidání indexu je jeho vypuštění. Aby byla migrace vratná, zadejte reverse_sql pro zrušení indexu:

# migrations/0002_add_index_runsql.py

from django.db import migrations, models

class Migration(migrations.Migration):
    atomic = False

    dependencies = [
        ('app', '0001_initial'),
    ]

    operations = [
        migrations.RunSQL(
            'CREATE INDEX "app_sale_sold_at_b9438ae4" '
            'ON "app_sale" ("sold_at");',

            reverse_sql='DROP INDEX "app_sale_sold_at_b9438ae4";',
        ),
    ]

Nyní zkuste zvrátit migraci:

$ python manage.py showmigrations app
app
 [X] 0001_initial
 [X] 0002_add_index_runsql

$ python manage.py migrate app 0001
Operations to perform:
  Target specific migration: 0001_initial, from app
Running migrations:
  Rendering model states... DONE
 Unapplying app.0002_add_index_runsql... OK

$ python manage.py showmigrations app
app
 [X] 0001_initial
 [ ] 0002_add_index_runsql

Druhá migrace byla obrácena a index byl zrušen Django. Nyní je bezpečné odstranit migrační soubor:

$ rm app/migrations/0002_add_index_runsql.py

Vždy je dobré poskytnout reverse_sql . V situacích, kdy stornování nezpracované operace SQL nevyžaduje žádnou akci, můžete operaci označit jako vratnou pomocí speciální kontroly migrations.RunSQL.noop :

migrations.RunSQL(
    sql='...',  # Your forward SQL here
    reverse_sql=migrations.RunSQL.noop,
),


Porozumění stavu modelu a stavu databáze

Při vašem předchozím pokusu vytvořit index ručně pomocí RunSQL Django generoval stejnou migraci znovu a znovu, i když byl index vytvořen v databázi. Abyste pochopili, proč to Django udělal, musíte nejprve pochopit, jak se Django rozhoduje, kdy generovat nové migrace.


Když Django generuje novou migraci

V procesu generování a aplikace migrací se Django synchronizuje mezi stavem databáze a stavem modelů. Když například přidáte pole do modelu, Django přidá sloupec do tabulky. Když z modelu odeberete pole, Django odstraní sloupec z tabulky.

Pro synchronizaci mezi modely a databází udržuje Django stav, který představuje modely. Pro synchronizaci databáze s modely Django generuje operace migrace. Operace migrace se překládají do SQL specifického pro dodavatele, které lze spustit v databázi. Po provedení všech operací migrace se očekává, že databáze a modely budou konzistentní.

Chcete-li získat stav databáze, Django agreguje operace ze všech minulých migrací. Když agregovaný stav migrací není konzistentní se stavem modelů, Django vygeneruje novou migraci.

V předchozím příkladu jste index vytvořili pomocí nezpracovaného SQL. Django nevěděl, že jste vytvořili index, protože jste nepoužili známou operaci migrace.

Když Django agregoval všechny migrace a porovnal je se stavem modelů, zjistil, že chybí index. To je důvod, proč, i když jste index vytvořili ručně, Django si stále myslel, že chybí, a vygeneroval pro něj novou migraci.



Jak oddělit databázi a stát při migracích

Vzhledem k tomu, že Django nedokáže vytvořit index tak, jak chcete, chcete poskytnout svůj vlastní SQL, ale přesto Django upozornit, že jste jej vytvořili.

Jinými slovy, musíte provést něco v databázi a poskytnout Django operaci migrace, aby se synchronizoval její vnitřní stav. Za tímto účelem nám Django poskytuje speciální operaci migrace nazvanou SeparateDatabaseAndState . Tato operace není dobře známá a měla by být vyhrazena pro speciální případy, jako je tento.

Je mnohem snazší upravovat migrace, než je psát od začátku, takže začněte vygenerováním migrace obvyklým způsobem:

$ python manage.py makemigrations --name add_index_separate_database_and_state

Migrations for 'app':
  app/migrations/0002_add_index_separate_database_and_state.py
    - Alter field sold_at on sale

Toto je obsah migrace generovaný Django, stejný jako předtím:

# migrations/0002_add_index_separate_database_and_state.py

from django.db import migrations, models

class Migration(migrations.Migration):

    dependencies = [
        ('app', '0001_initial'),
    ]

    operations = [
        migrations.AlterField(
            model_name='sale',
            name='sold_at',
            field=models.DateTimeField(
                auto_now_add=True,
                db_index=True,
            ),
        ),
    ]

Django vygeneroval AlterField operace na poli sold_at . Operace vytvoří index a aktualizuje stav. Chceme tuto operaci ponechat, ale poskytnout jiný příkaz k provedení v databázi.

Pro získání příkazu znovu použijte SQL generovaný Django:

$ python manage.py sqlmigrate app 0002
BEGIN;
--
-- Alter field sold_at on sale
--
CREATE INDEX "app_sale_sold_at_b9438ae4" ON "app_sale" ("sold_at");
COMMIT;

Přidejte CONCURRENTLY klíčové slovo na příslušném místě:

CREATE INDEX CONCURRENTLY "app_sale_sold_at_b9438ae4"
ON "app_sale" ("sold_at");

Dále upravte soubor migrace a použijte SeparateDatabaseAndState k poskytnutí upraveného příkazu SQL ke spuštění:

# migrations/0002_add_index_separate_database_and_state.py

from django.db import migrations, models

class Migration(migrations.Migration):

    dependencies = [
        ('app', '0001_initial'),
    ]

    operations = [

        migrations.SeparateDatabaseAndState(

            state_operations=[
                migrations.AlterField(
                    model_name='sale',
                    name='sold_at',
                    field=models.DateTimeField(
                        auto_now_add=True,
                        db_index=True,
                    ),
                ),
            ],

            database_operations=[
                migrations.RunSQL(sql="""
                    CREATE INDEX CONCURRENTLY "app_sale_sold_at_b9438ae4"
                    ON "app_sale" ("sold_at");
                """, reverse_sql="""
                    DROP INDEX "app_sale_sold_at_b9438ae4";
                """),
            ],
        ),

    ],

Operace migrace SeparateDatabaseAndState přijímá 2 seznamy operací:

  1. state_operations jsou operace, které se mají aplikovat na stav interního modelu. Nemají vliv na databázi.
  2. databázové_operace jsou operace, které se mají aplikovat na databázi.

Původní operaci vygenerovanou Django jste ponechali v state_operations . Při použití SeparateDatabaseAndState , to je to, co obvykle budete chtít udělat. Všimněte si, že db_index=True argument je poskytnut do pole. Tato operace migrace dá Django vědět, že v poli je index.

Použili jste SQL generovaný Django a přidali jste CONCURRENTLY klíčové slovo. Použili jste speciální akci RunSQL k provedení nezpracovaného SQL při migraci.

Pokud se pokusíte spustit migraci, získáte následující výstup:

$ python manage.py migrate app
Operations to perform:
  Apply all migrations: app
Running migrations:
  Applying app.0002_add_index_separate_database_and_state...Traceback (most recent call last):
  File "/venv/lib/python3.7/site-packages/django/db/backends/utils.py", line 83, in _execute
    return self.cursor.execute(sql)
psycopg2.InternalError: CREATE INDEX CONCURRENTLY cannot run inside a transaction block



Neatomové migrace

V SQL CREATE , DROP , ALTER a TRUNCATE operace se označují jako Jazyk definic dat (DDL). V databázích, které podporují transakční DDL, jako je PostgreSQL, Django ve výchozím nastavení provádí migraci uvnitř databázové transakce. Podle výše uvedené chyby však PostgreSQL nemůže vytvořit index současně v rámci transakčního bloku.

Abyste mohli vytvořit index souběžně v rámci migrace, musíte sdělit Django, aby neprováděl migraci v databázové transakci. Chcete-li to provést, označte migraci jako neatomickou nastavením atomic na False :

# migrations/0002_add_index_separate_database_and_state.py

from django.db import migrations, models

class Migration(migrations.Migration):
    atomic = False

    dependencies = [
        ('app', '0001_initial'),
    ]

    operations = [

        migrations.SeparateDatabaseAndState(

            state_operations=[
                migrations.AlterField(
                    model_name='sale',
                    name='sold_at',
                    field=models.DateTimeField(
                        auto_now_add=True,
                        db_index=True,
                    ),
                ),
            ],

            database_operations=[
                migrations.RunSQL(sql="""
                    CREATE INDEX CONCURRENTLY "app_sale_sold_at_b9438ae4"
                    ON "app_sale" ("sold_at");
                """,
                reverse_sql="""
                    DROP INDEX "app_sale_sold_at_b9438ae4";
                """),
            ],
        ),

    ],

Poté, co označíte migraci jako neatomickou, můžete migraci spustit:

$ python manage.py migrate app
Operations to perform:
  Apply all migrations: app
Running migrations:
  Applying app.0002_add_index_separate_database_and_state... OK

Právě jste provedli migraci bez jakéhokoli výpadku.

Zde je několik problémů, které je třeba zvážit, když používáte SeparateDatabaseAndState :

  • Databázové operace musí být ekvivalentní stavovým operacím: Nesrovnalosti mezi databází a stavem modelu mohou způsobit spoustu problémů. Dobrým výchozím bodem je ponechat operace generované Django v state_operations a upravte výstup sqlmigrate použít v database_operations .

  • Neatomové migrace nelze vrátit zpět v případě chyby: Pokud během migrace dojde k chybě, nebudete se moci vrátit zpět. Migraci byste museli buď vrátit zpět, nebo ji dokončit ručně. Je dobré omezit operace prováděné v rámci neatomové migrace na minimum. Pokud máte v migraci další operace, přesuňte je do nové migrace.

  • Migrace může být specifická pro dodavatele: SQL generovaný Django je specifický pro backend databáze použitý v projektu. Může fungovat s jinými databázovými backendy, ale to není zaručeno. Pokud potřebujete podporovat více databázových backendů, musíte tento přístup trochu upravit.



Závěr

Tento tutoriál jste začali s velkým stolem a problémem. Chtěli jste svou aplikaci zrychlit pro své uživatele a chtěli jste to udělat, aniž byste jim způsobili jakékoli prostoje.

Na konci tutoriálu se vám podařilo vygenerovat a bezpečně upravit migraci Django, abyste dosáhli tohoto cíle. Po cestě jste řešili různé problémy a podařilo se vám je překonat pomocí vestavěných nástrojů poskytovaných migračním rámcem.

V tomto tutoriálu jste se naučili následující:

  • Jak migrace Django fungují interně pomocí modelu a stavu databáze a kdy jsou generovány nové migrace
  • Jak spouštět vlastní SQL při migracích pomocí RunSQL akce
  • Co jsou reverzibilní migrace a jak vytvořit RunSQL akce vratná
  • Co jsou atomové migrace a jak změnit výchozí chování podle vašich potřeb
  • Jak bezpečně provádět složité migrace v Django

Oddělení mezi stavem modelu a databáze je důležitý koncept. Jakmile to pochopíte a pochopíte, jak je používat, můžete překonat mnohá omezení vestavěných operací migrace. Některé případy použití, které přicházejí na mysl, zahrnují přidání indexu, který již byl vytvořen v databázi, a poskytnutí argumentů specifických pro dodavatele příkazům DDL.



  1. Průvodce Pgpool pro PostgreSQL:Část první

  2. Vložte obrázek do databáze postgresql

  3. Jak používat pgBackRest k zálohování PostgreSQL a TimescaleDB

  4. Začínáme s ProxySQL – Výukový program pro vyrovnávání zátěže MySQL a MariaDB