RE:NODE

Hosting aplikací10 min čtení

.NET s PostgreSQL, MySQL nebo SQL Serverem: ovladače a providery

Jak vybrat a zapojit databázi pro aplikaci v .NET: providery EF Core, Npgsql, MySqlConnector a SqlClient, connection stringy, pooling a rozdíly, na kterých se zakopneš.

3 čtenáři

.NET funguje dobře se všemi třemi. SQL Server má nejhlubší nástroje a provider, který píše sám Microsoft, PostgreSQL má Npgsql, který je výborný a stejně rychlý jako cokoli v ekosystému, a MySQL má MySqlConnector pod komunitním providerem Pomelo pro Entity Framework Core. Volba se týká spíš databáze než ovladače: SQL Server Express je zdarma, ale omezený na 10 GB na databázi a zhruba 1,4 GB paměti pro buffer; PostgreSQL nemá žádná omezení edic a nabízí nejbohatší sadu funkcí; MySQL je všude a je nejlehčí na provoz. Jakmile si vybereš, věci, které dělají potíže, jsou u všech stejné - connection string, velikost poolu, chování dat a řetězců a udržování migrací specifických pro daný provider. Tento článek je probírá všechny vedle sebe.

Providery a ovladače#

Každý databázový stack v .NET má dvě vrstvy. Dole je ovladač ADO.NET, který mluví síťovým protokolem databáze a implementuje DbConnection a DbCommand. Nad ním volitelně stojí provider Entity Framework Core, který překládá LINQ do SQL dialektu dané databáze. Dapper a ručně psané SQL používají ovladač přímo.

DatabázeOvladač ADO.NETProvider EF CoreUdržuje
PostgreSQLNpgsqlNpgsql.EntityFrameworkCore.PostgreSQLProjekt Npgsql
MySQLMySqlConnectorPomelo.EntityFrameworkCore.MySqlKomunita (Pomelo, MySqlConnector)
MySQL (alternativa)MySql.DataMySql.EntityFrameworkCoreOracle
SQL ServerMicrosoft.Data.SqlClientMicrosoft.EntityFrameworkCore.SqlServerMicrosoft

Pár poznámek k té tabulce:

  • `System.Data.SqlClient` je starý ovladač SQL Serveru a je zastaralý. Nový kód i každá současná verze EF Core používají Microsoft.Data.SqlClient. Zkopírovaný příklad se starým jmenným prostorem se pořád zkompiluje a dostaneš ovladač se staršími výchozími hodnotami a bez nových oprav.
  • Pro MySQL dej přednost MySqlConnector. Má licenci MIT, je skutečně asynchronní (MySql.Data od Oracle své asynchronní metody historicky implementoval synchronně) a používá ho Pomelo. Balíčky Oracle jsou pod GPL s výjimkou pro FOSS, což u některých komerčních projektů hraje roli.
  • Vydání Pomelo následují major verze EF Core, někdy s měsíčním zpožděním. Než povýšíš EF Core na novou major verzi, ověř, že odpovídající vydání Pomelo existuje. Npgsql svůj provider pro EF Core obvykle vydává blízko vydání Microsoftu.

Zapojení každého z nich s EF Core#

Registrace vypadá u všech tří téměř stejně, což je smysl EF Core:

csharp
// PostgreSQLbuilder.Services.AddDbContext<AppDbContext>(o =>    o.UseNpgsql(builder.Configuration.GetConnectionString("Default")));// MySQL via Pomelovar mysql = builder.Configuration.GetConnectionString("Default");builder.Services.AddDbContext<AppDbContext>(o =>    o.UseMySql(mysql, ServerVersion.AutoDetect(mysql)));// SQL Serverbuilder.Services.AddDbContext<AppDbContext>(o =>    o.UseSqlServer(builder.Configuration.GetConnectionString("Default")));

Pomelo potřebuje znát verzi serveru, protože MySQL a MariaDB se liší v SQL, které přijímají. ServerVersion.AutoDetect se při startu připojí a zeptá se, což shodí start aplikace, pokud je databáze v tu chvíli nedostupná; new MySqlServerVersion(new Version(8, 4, 0)) se tomu zbytečnému volání vyhne a v produkci je lepší volba.

U Npgsql doporučují verze 7 a novější vytvořit NpgsqlDataSource jednou a předat ho EF Core nebo ho používat přímo; tam se nastavují mapování typů, enumy a logování:

csharp
var dataSource = new NpgsqlDataSourceBuilder(    builder.Configuration.GetConnectionString("Default")).Build();builder.Services.AddDbContext<AppDbContext>(o => o.UseNpgsql(dataSource));

GetConnectionString("Default") čte z konfigurace ConnectionStrings:Default, což v produkci nastavíš proměnnou prostředí ConnectionStrings__Default. Connection string je tajemství; do appsettings.json v repozitáři nepatří. Konfigurace a tajemství v ASP.NET Core popisuje vrstvení konfigurace.

Connection stringy vedle sebe#

code
PostgreSQL (Npgsql)Host=db.example.net;Port=5432;Database=app;Username=app;Password=...;SSL Mode=PreferMySQL (MySqlConnector)Server=db.example.net;Port=3306;Database=app;User ID=app;Password=...;SslMode=PreferredSQL Server (Microsoft.Data.SqlClient)Server=tcp:db.example.net,1433;Database=app;User ID=app;Password=...;Encrypt=True;TrustServerCertificate=True

Rozdíly, na kterých lidé klopýtají:

  • SQL Server píše port za čárku, ne za dvojtečku, a nemá samostatné klíčové slovo Port. db.example.net:1433 se přečte jako název hostitele a selže. Stejná čárka se objevuje v poli s názvem serveru v SQL Server Management Studiu.
  • SQL Server šifruje ve výchozím stavu. Od Microsoft.Data.SqlClient 4.0 (a tedy od EF Core 7) je Encrypt ve výchozím stavu True a ovladač ověřuje certifikát serveru. Server s self-signed certifikátem - jaký si SQL Server vygeneruje sám, když žádný nenastavíš - selže s hláškou „The certificate chain was issued by an authority that is not trusted“, dokud nepřidáš TrustServerCertificate=True nebo nenainstaluješ důvěryhodný certifikát. Connection stringy SQL Serveru projde variantu pro každý ovladač.
  • MySQL 8.4 se ověřuje přes `caching_sha2_password`. Přes nešifrované spojení potřebuje první přihlášení veřejný klíč RSA serveru a MySqlConnector odmítne s hláškou „Retrieval of the RSA public key is not enabled for insecure connections“, pokud nepoužiješ TLS (SslMode=Required) nebo nepřidáš AllowPublicKeyRetrieval=True. Dej přednost TLS.
  • Výchozí hodnota Npgsql je `SSL Mode=Prefer`, která použije TLS, když ho server nabízí, ale certifikát neověřuje. Použij Require, aby se odmítl nešifrovaný provoz, a VerifyFull s důvěryhodnou CA, když potřebuješ certifikát kontrolovat.

Hesla obsahující ; nebo = se v connection stringech ADO.NET musí dát do uvozovek - Password="a;b=c" - nebo se musí sestavit třídou ConnectionStringBuilder daného ovladače, která escapuje za tebe.

Connection pooling a timeouty#

Všechny tři ovladače poolují spojení ve výchozím stavu, jeden pool na každý odlišný connection string v procesu. Otevření spojení si jedno z poolu vezme, uvolnění ho vrátí. Proto je standardní vzor - DbContext na požadavek, uvolněný na jeho konci - levný.

NastaveníNpgsqlMySqlConnectorSqlClient
Max. velikost pooluMaximum Pool Size=100MaximumPoolSize=100Max Pool Size=100
Min. velikost pooluMinimum Pool Size=0MinimumPoolSize=0Min Pool Size=0
Timeout připojeníTimeout=15ConnectionTimeout=15Connect Timeout=15
Timeout příkazuCommand Timeout=30DefaultCommandTimeout=3030 s (na příkaz)
Odstraňování nečinných spojeníConnection Idle Lifetime=300ConnectionIdleTimeout=180Řídí ovladač

Hodnoty jsou v sekundách. Sto spojení na proces je mnohem víc, než kolik malý databázový server dokáže dobře využít. Zvlášť PostgreSQL utrácí za každé spojení skutečnou paměť a databáze na plánu s 1 nebo 2 GB má max_connections hluboko pod tím, co by mohly otevřít tři instance aplikace s výchozími pooly. Dimenzuj pool podle databáze, ne naopak: dvacet až třicet spojení je pro většinu aplikací na malém serveru víc než dost a aplikace, která potřebuje víc, obvykle drží spojení příliš dlouho. Connection pooly a limity vysvětluje, proč bývá menší pool rychlejší.

Když se pool vyčerpá, požadavky čekají na timeout připojení a pak selžou s chybou timeoutu poolu. Ta chyba téměř nikdy neznamená, že je pool příliš malý. Znamená, že spojení unikají (nejsou uvolněná) nebo se drží přes pomalou práci - například HTTP volání uprostřed transakce.

EF Core má vlastní, samostatný pool: AddDbContextPool<AppDbContext>() opakovaně používá instance DbContext (ve výchozím stavu až 1024), aby ušetřil alokace. Nemění, kolik databázových spojení existuje, a vyžaduje, aby tvůj kontext nedržel žádný stav vázaný na požadavek.

Opakování a přechodné chyby#

Sítě ztrácejí pakety a databáze se restartují. EF Core umí neúspěšné operace automaticky opakovat:

csharp
o.UseSqlServer(conn, sql => sql.EnableRetryOnFailure(    maxRetryCount: 5, maxRetryDelay: TimeSpan.FromSeconds(10), errorNumbersToAdd: null));o.UseNpgsql(conn, pg => pg.EnableRetryOnFailure());o.UseMySql(conn, version, my => my.EnableRetryOnFailure());

Se zapnutými opakováními vyhodí transakce, kterou si otevřeš sám přes BeginTransactionAsync, výjimku, protože EF Core nedokáže přehrát půlku transakce. Celou jednotku práce obal místo toho do execution strategy:

csharp
var strategy = db.Database.CreateExecutionStrategy();await strategy.ExecuteAsync(async () =>{    await using var tx = await db.Database.BeginTransactionAsync();    // ... several SaveChangesAsync calls    await tx.CommitAsync();});

Opakování skryjí krátké výpadky. Databázi, která opravdu nejede, ale po dobu opakování nechají vypadat jako velmi pomalou aplikaci, takže drž počty skromné a každé opakování loguj.

Rozdíly, které bolí při přechodu#

EF Core skrývá SQL dialekt. Neskrývá chování databáze a na tyto rozdíly lidé narazí, když přesouvají aplikaci z jednoho enginu na druhý nebo píšou testy proti SQLite a produkci provozují na něčem jiném:

  • Porovnávání řetězců a velikost písmen. Výchozí collation SQL Serveru (SQL_Latin1_General_CP1_CI_AS) a výchozí collation MySQL 8 (utf8mb4_0900_ai_ci) porovnávají bez ohledu na velikost písmen, takže WHERE Email = 'Bob@x.com' najde bob@x.com. PostgreSQL porovnává s ohledem na velikost písmen. Vyhledání uživatele při přihlášení, které fungovalo na SQL Serveru, na PostgreSQL potichu selže. Normalizuj e-maily při zápisu, nebo na PostgreSQL použij collation bez ohledu na velikost písmen či typ citext.
  • Data a časová pásma. Npgsql 6 a novější mapují DateTime na timestamp with time zone a odmítnou zapsat DateTime, jehož Kind není Utc, s hláškou „Cannot write DateTime with Kind=Unspecified to PostgreSQL type 'timestamp with time zone'“. Ukládej všude UTC, nebo použij DateTimeOffset. datetime2 v SQL Serveru a datetime v MySQL neukládají pásmo vůbec, takže stejný kód zapíše místní časy bez stížností a chyba se projeví až později.
  • Unicode. Na SQL Serveru mapuje EF Core string na nvarchar, což je Unicode. Ručně psané tabulky se sloupci varchar mohou ztratit znaky mimo znakovou stránku collation. Na MySQL používej utf8mb4 - starší alias utf8 je tříbajtový a emoji uložit neumí. Collation a Unicode v SQL Serveru a MySQL utf8mb4 a collation jdou víc do hloubky.
  • Výchozí délky řetězců. Neohraničená vlastnost typu string se stane nvarchar(max) na SQL Serveru, longtext na MySQL a text na PostgreSQL. Na SQL Serveru nemohou být sloupce nvarchar(max) klíči indexu; vlastnostem, podle kterých budeš filtrovat, dej [MaxLength].
  • Hodnoty identity. Všechny tři generují klíče přes IDENTITY, AUTO_INCREMENT nebo sloupce identity opřené o sekvence. Mezery jsou po rollbacích a restartech normální u všech; nikdy nepoužívej klíč jako počet.
  • Migrace jsou specifické pro provider. Migrace vygenerovaná proti SQL Serveru obsahuje typy sloupců a anotace SQL Serveru. Změna enginu znamená vygenerovat migrace pro nový provider od nuly, ne znovu použít původní složku.

Výběr databáze pro aplikaci v .NET#

Podle funkcí zvládne typickou webovou aplikaci kterákoli ze tří. Volba obvykle záleží na limitech, nástrojích a na tom, co už znáš.

SQL Server Express se hodí pro týmy, které žijí v SQL Server Management Studiu, pro aplikace využívající funkce specifické pro SQL Server (temporal tables, uložené procedury v T-SQL, plné nástroje SQL Serveru) a pro cokoli, co se může později přesunout na placenou edici SQL Serveru nebo Azure SQL. Jeho limity jsou skutečné: každá databáze je omezená na 10 GB dat, buffer pool zhruba na 1 410 MB a výpočetní výkon na menší z jednoho socketu nebo čtyř jader, bez SQL Server Agenta pro plánované úlohy. Hosting SQL Server Express popisuje, kdy to stačí.

PostgreSQL je výchozí doporučení pro nový projekt bez dalších omezení: žádné limity edic, výborná podpora JSON, bohaté indexování a Npgsql je prvotřídní. Vzdálená připojení k PostgreSQL popisuje první připojení.

MySQL je z těch tří nejlehčí na provoz a téměř každému povědomé. Je to dobrá volba, když na ni aplikace už míří nebo sdílí databázi se softwarem v PHP.

Delší srovnání, včetně MongoDB a Valkey, najdeš v článku kterou databázi použít.

Na RE:NODE je každá z nich samostatný databázový plán s přihlašovacími údaji generovanými pro každý server, dostupný na hostiteli a portu plánu: PostgreSQL od 6 $ měsíčně, MySQL 8.4 LTS od 4 $ s vygenerovaným heslem root, aplikační databází a aplikačním uživatelem a SQL Server 2022 Express od 6 $ s heslem sa a databází vytvořenou a nastavenou jako výchozí pro sa. Databázové plány nemají slot pro proxy - aplikace se připojuje přímo na hostitele a port. Plány aplikací C# / .NET zahrnují také vlastní sloty pro databáze, vytvářené v panelu s vygenerovaným hostitelem, uživatelem a heslem.

Řešení potíží#

`A network-related or instance-specific error occurred` (SQL Server). Špatný hostitel nebo port, dvojtečka místo čárky před portem, nebo firewall. Nejdřív otestuj port.

`The certificate chain was issued by an authority that is not trusted` (SQL Server). Šifrování je zapnuté ve výchozím stavu a certifikát serveru je self-signed. Přidej TrustServerCertificate=True nebo nainstaluj certifikát od důvěryhodné CA.

`Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool`. Spojení unikají nebo se drží příliš dlouho. Hledej kontexty nebo spojení vytvořená mimo using a práci prováděnou uvnitř otevřených transakcí.

`53300: sorry, too many clients already` (PostgreSQL) nebo `Too many connections` (MySQL). Pooly všech instancí tvé aplikace dohromady překračují limit serveru. Sniž Maximum Pool Size.

`Unable to connect to any of the specified MySQL hosts`. Hostitel, port nebo firewall. Pokud je hostitel správně, zkontroluj, jestli se uživatel smí připojit z tvé adresy.

FAQ#

Je Entity Framework Core pomalejší než Dapper?

U jednoduchých dotazů je moderní EF Core blízko, zejména s AsNoTracking() a projekcemi. Dapper pořád vyhrává v režii mapování a dává ti přesnou kontrolu nad SQL. Mnoho aplikací používá EF Core pro zápisy a migrace a Dapper pro pár náročných čtecích dotazů, nad stejným connection stringem.

Můžu v testech používat SQLite a v produkci SQL Server?

Můžeš a skryje to chyby: velikost písmen, práce s daty, chování transakcí i překlad SQL se liší. Spouštěj integrační testy proti stejnému enginu jako produkce, v případě potřeby v kontejneru.

Který provider pro MySQL mám s EF Core použít?

Pomelo nad MySqlConnector. Je nejrozšířenější a nejúplnější. Před povýšením EF Core ověř, že jeho vydání pro tvou major verzi EF Core existuje.

Potřebuju TLS mezi aplikací a databází?

Pokud spojení vede přes jakoukoli síť, kterou neovládáš - což zahrnuje internet mezi aplikačním a databázovým serverem - ano. Podporují ho všechny tři ovladače; ovladač SQL Serveru ho zapíná ve výchozím stavu.

Kolik spojení má můj pool povolit?

Méně, než si myslíš. Začni na dvaceti až třiceti na instanci aplikace u malé databáze, dohlédni, aby součet přes instance zůstal pod limitem databáze, a zvyšuj ho, jen když dokážeš, že požadavky čekají na pool, zatímco databáze sama nic nedělá.


Komentáře

Zcela anonymně: žádný účet, žádný e-mail, žádné cookie. Ukládáme jen jméno, které napíšeš, text a čas - nic dalšího. Počet odkazů je omezený a značkování se nevykresluje.

0/2000