2014-07-09 16 views
20

Di tanto in tanto, eseguo query non elaborate utilizzando connection.cursor() invece di utilizzare ORM (poiché non è sicuramente un proiettile argentato).Necessità di cursor.close esplicito()

Ho notato che in diversi punti non richiamo esplicito cursor.close() dopo aver finito con il database. Finora, questo non ha comportato errori o problemi di prestazioni. Mi chiedo quale tipo di problemi potrei avere senza chiudere il cursore esplicitamente, cosa può andare storto?

Per quanto ho capito, connection e cursor in Django seguire "Python API Database Specification v2.0" (PEP-249). E, secondo esso, cursor sarebbe automaticamente chiuso ogni volta che viene chiamato il metodo __del__(). Suppongo che la domanda potrebbe essere anche: Esiste un caso d'uso quando non viene chiamato?

FYI, sto usando Python 2.7 e Django 1.6.5.

+0

Credo che sia molto simile a questa domanda http://stackoverflow.com/questions/5669878/python-mysqldb-when-to-close-cursors ?? –

+2

@AamirAdnan grazie, non proprio - visto prima di postare. So come chiudere il cursore. La domanda riguarda quanto sia grave non chiuderla e quali sono le conseguenze. – alecxe

risposta

5

__del__/.close():

  1. __del__ non è garantito di essere chiamato
  2. alcuni database non chiamare cursor.close() nella loro __del__ (cattiva pratica, ma vero)
  3. alcuni database in realtà non creano connessioni nel funzione di connessione, ma nella funzione del cursore (es per 2 & 3: di pyhive presto [Forse hanno dato patchato it])

Su connessioni server in generale

maggior parte dei server hanno una proprietà di configurazione timeout di inattività (chiamiamolo che T). Se una connessione è inattiva per più di T secondi, il server rimuoverà la connessione. La maggior parte dei server dispone anche di proprietà per impostare le dimensioni del pool di thread di lavoro (W). Se si dispone già di connessioni W al proprio server, è probabile che si blocchi quando viene tentata una nuova connessione. Per un secondo immagina di non avere la possibilità di chiudere esplicitamente le connessioni. In tale situazione, è necessario impostare il timeout in modo tale che il pool di lavoro non venga mai utilizzato completamente, il che dipende dal numero di connessioni simultanee disponibili.

Tuttavia, se si chiudono i cursori/connessioni (anche se non equivalenti da [3] sopra, si comportano in modo simile), quindi non è necessario gestire queste proprietà di configurazione del server e il pool di thread semplicemente deve essere abbastanza grande da gestire tutte le connessioni simultanee (con l'opzione per un'attesa occasionale di nuove risorse). Ho visto alcuni server (ad es. Titan su Cassandra) non in grado di eseguire il ripristino dall'esaurimento dei lavoratori nel pool di thread, quindi l'intero server si arresta fino al riavvio.

TL/DR Se stai usando le librerie molto ben sviluppati, come quelli dano cita, non si avrà un problema. Se si utilizzano librerie meno pristine, è possibile che si blocchi il server che acquisisce un thread di lavoro se non si chiama .close(), a seconda della configurazione del server e delle velocità di accesso.

2

Mentre il sistema operativo può essere normalmente utilizzato per rilasciare risorse, è sempre buona norma chiudere le connessioni del database per garantire che le risorse vengano rilasciate quando non sono più necessarie, la cosa davvero importante dal punto di vista del database è garantire che eventuali modifiche sono commit() ed.

14

classe di Django cursor è solo un involucro intorno al sottostante DB cursor, quindi l'effetto di lasciare la cursor aperto è sostanzialmente legata al driver DB sottostante.

Secondo il psycopg2 (psycopg2 è DB conducente Django utilizza per PostgreSQL DB) FAQ, i cursori sono leggeri, ma nella cache i dati che vengono restituiti dalle query eseguite utilizzando l'oggetto del cursore, che potrebbe potenzialmente sprecare memoria:

I cursori sono oggetti leggeri e la creazione di molti di essi non dovrebbe rappresentare alcun tipo di problema. Ma si noti che i cursori utilizzati per recuperare i risultati set memorizzeranno i dati nella cache e utilizzeranno la memoria in proporzione alla dimensione impostata del risultato . Il nostro suggerimento è quasi sempre creare un nuovo cursore e smaltire quelli vecchi non appena i dati non sono più necessari (chiamare close() su di loro.) L'unica eccezione sono i loop stretti in cui uno di solito lo utilizza lo stesso cursore per un intero mazzo di INSERT o UPDATE.

Django utilizza MySQLdb come backend per MySQL, che ha diversi tipi di cursori, tra cui alcuni che effettivamente conservano le loro risultato-set sul lato server. Il MySQLdb documentation for Cursor.close fanno un punto da notare che è molto importante chiudere il quando hai finito con loro cursore sul lato server:

Se si utilizza cursori sul lato server, è molto importante per chiudere il cursore quando hai finito con esso e prima di crearne uno nuovo.

Tuttavia, questo non è rilevante per Django, perché utilizza la classe predefinita Cursor fornito da MySQLdb, che memorizza i risultati sul lato client. Lasciare aperto un cursore usato rischia di sprecare la memoria utilizzata dal set di risultati memorizzato, proprio come psycopg2.La close method sul cursore elimina solo il riferimento interno per la connessione db ed esaurisce il set di risultati memorizzati:

def close(self): 
    """Close the cursor. No further queries will be possible.""" 
    if not self.connection: return 
    while self.nextset(): pass 
    self.connection = None 

Come meglio posso dire di guardare alla fonte, tutti i restanti backend utilizzati da Django (cx_oracle, sqlite3/pysqlite2) tutti seguono lo stesso schema; memoria libera eliminando/reimpostando i risultati memorizzati/riferimenti oggetto. Il sqlite3 docs non menziona nemmeno che la classe Cursorha un metodo di chiusura ed è usata solo sporadicamente nel codice di esempio incluso.

Sei giusto che una cursor saranno chiuse quando __del__() si chiama sull'oggetto cursor, quindi la necessità di chiudere in modo esplicito è solo un problema se si sta tenendo un riferimento longevo al cursor; per esempio. un oggetto self.cursor che stai mantenendo come metodo di istanza di una classe.

+0

Se si riutilizza un cursore per una seconda query, tuttavia, i dati memorizzati nella cache per la prima query non verranno più referenziati. Quindi, in un certo senso, creare inutilmente un intero gruppo di cursori potrebbe essere controproducente a meno che non si eliminino specificamente quelli usati. – holdenweb

+0

@holdenweb Giusto, se hai intenzione di fare un sacco di domande in un breve periodo di tempo, potrebbe essere sensato usare lo stesso cursore. Se hai intenzione di fare una query, e poi in un punto sconosciuto in futuro ne fai un altro, dovresti probabilmente chiudere/eliminare il cursore in modo da non mantenere la memoria che sta usando per memorizzare nella cache il set di risultati. – dano

+0

Sembra che abbiamo un accordo violento. – holdenweb

1

La chiamata esplicita di cursor.close() potrebbe essere causa di due motivi:

  1. __del__ non è garantito di essere chiamato e ha alcuni problemi che si possono leggere here e here
  2. Esplicito è meglio che implicita (Zen of Python)
0

Sono un po 'in ritardo a questa domanda. Forse un obiettivo close-on-exit è quello che vuoi.

from contextlib import closing 
from django.db import connection 

with closing(connection.cursor()) as cursor: 
    cursor.execute(...) 
    cursor.execute(...) 
    cursor.execute(...) 
+0

Hanno menzionato l'uso di Django 1.6 e i documenti dicono che un gestore di contesto (come il tuo esempio) non è stato aggiunto esplicitamente fino alla versione 1.7, anche se i documenti sembrano indicare che ha funzionato prima in qualsiasi modo a causa di "comportamento inaspettato nel metodo magico le ricerche". –

Problemi correlati