Creazione di connessioni al database: farlo una volta o per ogni query?


101

Al momento creo una connessione al database quando la mia pagina web viene caricata per la prima volta. Quindi elaboro la pagina ed eseguo qualsiasi domanda contro quella connessione. È il modo migliore per farlo o devo creare una connessione al database ogni volta che eseguo una query?

ps Per me ha più senso creare 1 connessione e usarla ma non so se questo può causare altri problemi.

Sto usando C # (ASP.NET) con MSSQL.

Risposte:


124

Se ne crei uno per query / transazione, è molto più semplice gestire la "chiusura" delle connessioni.

Posso capire perché il buon senso impone che dovresti aprirne uno e utilizzarlo dappertutto, ma incontrerai problemi con le connessioni interrotte e il multithreading. Quindi il tuo prossimo passo sarà aprire un pool, diciamo di 50, connessioni e tenerle tutte aperte, distribuendole a processi diversi. E poi scoprirai che questo è esattamente ciò che .NET Framework fa già per te .

Se apri una connessione quando ne hai bisogno e la elimini al termine, ciò non chiuderà effettivamente la connessione, ma la restituirà semplicemente al pool di connessioni per essere utilizzata nuovamente.


Stavo leggendo quell'articolo quando l'hai pubblicato :) Grazie.
webnoob,

2
La pagina Web collegata è specifica per SQL Server. .NET fornisce anche il pooling automatico durante la connessione ad altri database, ad esempio Oracle, Sqlite, MySql?
Briddums

@briddums - Penso che dipenda dal connettore. .Net, ad esempio, non fornisce un connettore MySql. È scritto e gestito da MySql. E mentre funziona, nella mia esperienza l'implementazione precedente era tutt'altro che priva di bug.
ZweiBlumen,

1
@briddums: dipende dall'assembly del provider. Sono certo che sia l'implementazione Oracle di Microsoft sia quella di Oracle supportino il pool di connessioni, perché le ho usate. Ho sentito che ce n'è uno MySql che funziona, e mi aspetto che i provider di Spring.NET supportino il pooling, ma è meglio cercare o chiedere direttamente al provider piuttosto che chiedermelo.
pdr

1
Dovrebbe essere noto che l'apertura, l'esecuzione di una query e la disposizione di una connessione, anche in un ciclo, è altrettanto veloce e talvolta più VELOCE che aprirla una volta e eseguire il ciclo della query. Smaltisci sempre e basta. È più sicuro e VELOCE. Non preoccuparti del sovraccarico di ottenere una connessione dal pool - è così banale.
smdrager,

38

Best practice per creare una connessione per query - e nel caso di visualizzazione di dati, la best practice consiste nel fare in modo che la query porti tutti i dati necessari in un'unica volta.

Informazioni di base:

In .NET, SqlConnection.Open()per impostazione predefinita la chiamata utilizzerà sempre in modo trasparente il pool di connessioni (consultare "Utilizzo del pool di connessioni con SQL Server" su MSDN). Quindi puoi semplicemente prendere una nuova connessione usando Open()e chiamare Close()quando hai finito, e .NET farà la cosa giusta.

Si noti che senza il pool di connessioni, una connessione per query sarebbe una pessima idea perché la creazione di connessioni al database reali può essere molto costosa (autenticazione, sovraccarico della rete ecc.) E il numero di connessioni aperte simultanee è generalmente molto limitato.


7
@webnoob - Poiché .NET utilizza il pool di connessioni, no, non lo è. Il motivo è che le connessioni possono essere chiuse, riallocate, ecc., Quindi riutilizzare una connessione non è una buona pratica.
Oded,

11
-1 La risposta è piuttosto fuorviante. Creazione di una connessione per ogni query è una molto cattiva idea. Quello che probabilmente intendi è "recuperare una nuova connessione per ogni query dal pool di connessioni", ma non è lo stesso che creare una connessione.
sleske,

1
@sleske - In cosa differisce dalla risposta di pdr?
Oded,

3
@Oded: Ah, capisco. In .NET, la chiamata SqlConnection.Open()utilizzerà sempre in modo trasparente il pool di connessioni. Quindi la differenza tra "aprire una connessione" e "recuperare una connessione da un pool" non esiste. Il mio fraintendimento. Mi sono preso la libertà di pubblicare una piccola spiegazione nella domanda e ho ripreso il voto.
sleske,

2
@ eaglei22 - sicuramente dovrebbe farlo (consultare docs.microsoft.com/en-us/dotnet/framework/data/adonet/… ). In generale, si desidera restituire la connessione al pool il più presto possibile, tuttavia, se si stanno eseguendo un numero di query in sequenza, potrebbe essere meglio riutilizzare una connessione come suggerito. Dovresti testare e vedere quale approccio è meglio per te (non so quali criteri utilizzi: controlla in entrambi i modi e vedi l'effetto sulle metriche scelte).
Oded,

0

Ricorda che tutto ciò è nel contesto dell'ecosistema .Net.

Gli sviluppatori a volte vogliono "ottimizzare" il loro codice per riutilizzare i loro oggetti di connessione. Dato il contesto di questa domanda, questo è quasi sempre un errore.

ADO.Net ha una funzione chiamata Pool di connessioni . Quando si crea e si apre un nuovo oggetto connessione, ciò che si sta realmente facendo è richiedere una connessione da un pool. Quando si chiude una connessione, la si restituisce al pool.

È importante comprendere gli oggetti che utilizziamo direttamente nel codice: SqlConnection, MySqlConnection, OleDbConnectio, ecc., Sono tutti semplicemente avvolgenti attorno a una vera connessione sottostante gestita da ADO.Net e le connessioni reali ADO.Net sono molto più "pesanti" e più costose dal punto di vista delle prestazioni. Sono questi oggetti sottostanti che hanno preoccupazioni come l'autenticazione, il transito in rete, la crittografia e quelle cose superano di gran lunga la piccola quantità di memoria nell'oggetto che vedi effettivamente nel tuo codice.

Quando si tenta di riutilizzare l'oggetto connessione, si interrompe la capacità di ADO.Net di gestire efficacemente le connessioni sottostanti importanti. Ottieni efficienza nella piccola cosa a spese della cosa molto più grande.

Il riutilizzo di una connessione attraverso un'applicazione o una richiesta http può anche costringere a serializzare accidentalmente qualcosa che altrimenti potrebbe essere in grado di funzionare in parallelo e diventare un collo di bottiglia delle prestazioni. L'ho visto accadere in applicazioni reali.

Nel caso dell'esempio di una pagina Web qui, in cui mantieni almeno la piccola connessione per la durata di una singola richiesta / risposta http, potresti ottenere ancora più efficienza valutando quali query esegui nella tua pipeline di richieste e prova a ottenere fino al minor numero possibile di richieste separate al database (suggerimento: è possibile inviare più di una query in una singola stringa SQL e utilizzare DataReader.NextResult()o controllare tabelle diverse in a DataSetper spostarsi tra di esse).

In altre parole, piuttosto che pensare in termini di riutilizzo di una connessione per un'applicazione o di una richiesta http rispetto a una connessione per query, pensa in termini di una connessione ogni volta che chiami al database ... ogni round trip. Quindi provare a ridurre al minimo il numero di connessioni riducendo al minimo il numero di tali viaggi. In questo modo puoi soddisfare entrambi gli obiettivi.


Ma questo è solo un tipo di ottimizzazione. C'è anche l'ottimizzazione del tempo del programmatore e l'ottenimento di un riutilizzo efficace del codice. Gli sviluppatori non vogliono scrivere più volte lo stesso codice boilerplate solo per ottenere un oggetto di connessione che sia aperto e pronto per l'uso. Non è solo noioso, è un modo per introdurre bug in un programma.

Anche qui, tuttavia, è generalmente meglio avere una connessione per query (o andata e ritorno). Esistono altri schemi che è possibile utilizzare per evitare di riscrivere lo stesso codice di boilerplate. Ecco un esempio che mi piace, ma ce ne sono molti altri.


Sono in ritardo a questa festa, ma penso che questa risposta copra alcuni punti importanti :)
Joel Coehoorn,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.