sql >> Databáze >  >> RDS >> Mysql

Kdy zavřít kurzory pomocí MySQLdb

Namísto toho, abyste se ptali, co je standardní postup, protože to je často nejasné a subjektivní, můžete zkusit hledat pokyny v samotném modulu. Obecně platí, že pomocí with klíčové slovo, jak navrhl jiný uživatel, je skvělý nápad, ale za těchto konkrétních okolností vám nemusí poskytnout takovou funkčnost, jakou očekáváte.

Od verze 1.2.5 modulu MySQLdb.Connection implementuje protokol správce kontextu s následujícím kódem (github ):

def __enter__(self):
    if self.get_autocommit():
        self.query("BEGIN")
    return self.cursor()

def __exit__(self, exc, value, tb):
    if exc:
        self.rollback()
    else:
        self.commit()

Existuje několik existujících otázek a odpovědí o with již nebo si můžete přečíst Porozumění příkazu Python "with" , ale v podstatě se stane to, že __enter__ provede se na začátku with blok a __exit__ provede se po opuštění with blok. Můžete použít volitelnou syntaxi with EXPR as VAR svázat objekt vrácený __enter__ na jméno, chcete-li později na tento objekt odkazovat. Vzhledem k výše uvedené implementaci je zde jednoduchý způsob dotazování databáze:

connection = MySQLdb.connect(...)
with connection as cursor:            # connection.__enter__ executes at this line
    cursor.execute('select 1;')
    result = cursor.fetchall()        # connection.__exit__ executes after this line
print result                          # prints "((1L,),)"

Otázkou nyní je, jaké jsou stavy připojení a kurzoru po opuštění with blok? __exit__ výše uvedená metoda volá pouze self.rollback() nebo self.commit() a žádná z těchto metod nepokračuje ve volání close() metoda. Samotný kurzor nemá žádný __exit__ metoda definovaná – a na tom by nezáleželo, protože with pouze spravuje připojení. Po opuštění with tedy spojení i kurzor zůstanou otevřené blok. To lze snadno potvrdit přidáním následujícího kódu do výše uvedeného příkladu:

try:
    cursor.execute('select 1;')
    print 'cursor is open;',
except MySQLdb.ProgrammingError:
    print 'cursor is closed;',
if connection.open:
    print 'connection is open'
else:
    print 'connection is closed'

Měli byste vidět výstup „kurzor je otevřený; připojení je otevřené“ vytištěné na stdout.

Domnívám se, že před provedením připojení musíte zavřít kurzor.

Proč? MySQL C API , což je základ pro MySQLdb , neimplementuje žádný objekt kurzoru, jak vyplývá z dokumentace modulu:"MySQL nepodporuje kurzory, ale kurzory lze snadno emulovat." Opravdu, MySQLdb.cursors.BaseCursor třída dědí přímo z object a neukládá žádná taková omezení na kurzory s ohledem na potvrzení/vrácení zpět. Vývojář Oracle to řekl :

cnx.commit() před cur.close() mi přijde nejlogičtější. Možná se můžete řídit pravidlem:"Zavřete kurzor, pokud jej již nepotřebujete." Před zavřením kurzoru tedy commit(). V případě Connectoru/Pythonu to nakonec příliš nemění, ale v jiných databázích ano.

Očekávám, že je to tak blízko, jak se dostanete ke „standardní praxi“ na toto téma.

Existuje nějaká významná výhoda při hledání sad transakcí, které nevyžadují přechodné potvrzení, takže pro každou transakci nemusíte získávat nové kurzory?

Velmi o tom pochybuji a při pokusu o to můžete způsobit další lidskou chybu. Je lepší rozhodnout se pro konvenci a držet se jí.

Je na získávání nových kurzorů spousta režie, nebo to prostě není velký problém?

Režie je zanedbatelná a vůbec se nedotýká databázového serveru; je to zcela v rámci implementace MySQLdb. Můžete se podívat na BaseCursor.__init__ na github pokud jste opravdu zvědaví, co se děje, když vytvoříte nový kurzor.

Vraťme se k dřívější době, kdy jsme diskutovali with , možná nyní chápete, proč MySQLdb.Connection třídy __enter__ a __exit__ metody vám poskytnou zcela nový objekt kurzoru v každém with blok a neobtěžujte se jeho sledováním nebo zavíráním na konci bloku. Je poměrně lehký a existuje pouze pro vaše pohodlí.

Pokud je pro vás opravdu tak důležité mikrospravovat objekt kurzoru, můžete použít contextlib.closing aby se vynahradil fakt, že objekt kurzoru nemá definovaný __exit__ metoda. V tomto případě jej můžete také použít k vynucení toho, aby se objekt spojení uzavřel při ukončení with blok. To by mělo vypsat "my_curs is closed; my_conn is closed":

from contextlib import closing
import MySQLdb

with closing(MySQLdb.connect(...)) as my_conn:
    with closing(my_conn.cursor()) as my_curs:
        my_curs.execute('select 1;')
        result = my_curs.fetchall()
try:
    my_curs.execute('select 1;')
    print 'my_curs is open;',
except MySQLdb.ProgrammingError:
    print 'my_curs is closed;',
if my_conn.open:
    print 'my_conn is open'
else:
    print 'my_conn is closed'

Všimněte si, že with closing(arg_obj) nebude volat objekt argumentu __enter__ a __exit__ metody; bude pouze zavolejte objekt argumentu close metoda na konci with blok. (Chcete-li to vidět v akci, jednoduše definujte třídu Foo pomocí __enter__ , __exit__ a close metody obsahující jednoduchý print a porovnejte, co se stane, když provedete with Foo(): pass k tomu, co se stane, když provedete with closing(Foo()): pass .) To má dva významné důsledky:

Za prvé, pokud je povolen režim automatického potvrzení, MySQLdb BEGIN explicitní transakce na serveru, když použijete with connection a potvrzení nebo vrácení transakce na konci bloku. Toto jsou výchozí chování MySQLdb, která vás mají chránit před výchozím chováním MySQL, kterým je okamžité provedení všech příkazů DML. MySQLdb předpokládá, že když používáte správce kontextu, chcete transakci, a používá explicitní BEGIN obejít nastavení automatického potvrzení na serveru. Pokud jste zvyklí používat with connection , můžete si myslet, že automatické potvrzení je zakázáno, i když ve skutečnosti bylo pouze obejito. Pokud přidáte closing, můžete být nepříjemně překvapeni k vašemu kódu a ztratíte transakční integritu; nebudete moci vrátit změny, můžete začít vidět chyby souběžnosti a nemusí být hned zřejmé proč.

Za druhé, with closing(MySQLdb.connect(user, pass)) as VAR sváže objekt připojení na VAR , na rozdíl od with MySQLdb.connect(user, pass) as VAR , který sváže nový objekt kurzoru na VAR . V druhém případě byste neměli přímý přístup k objektu připojení! Místo toho byste museli použít connection kurzoru atribut, který poskytuje proxy přístup k původnímu připojení. Když je kurzor zavřený, jeho connection atribut je nastaven na None . Výsledkem je opuštěné připojení, které bude přetrvávat, dokud nenastane jedna z následujících situací:

  • Všechny odkazy na kurzor jsou odstraněny
  • Kurzor je mimo rozsah
  • Vypršel časový limit připojení
  • Připojení je ukončeno ručně pomocí nástrojů pro správu serveru

Můžete to otestovat sledováním otevřených připojení (ve Workbench nebo pomocí pomocí SHOW PROCESSLIST ) při provádění následujících řádků jeden po druhém:

with MySQLdb.connect(...) as my_curs:
    pass
my_curs.close()
my_curs.connection          # None
my_curs.connection.close()  # throws AttributeError, but connection still open
del my_curs                 # connection will close here


  1. Tipy, jak přesunout databázi SQL Server z jednoho serveru na druhý - SQL výuka od Rajana Singha

  2. Instalace SQL Server 2017 krok za krokem -2

  3. Hibernate:Automatické vytváření/aktualizace db tabulek na základě tříd entit

  4. Jak může vaše malá firma těžit z cloud computingu