Mysql
 sql >> Teknologi Basis Data >  >> RDS >> Mysql

Kapan harus menutup kursor menggunakan MySQLdb

Daripada menanyakan apa itu praktik standar, karena sering kali tidak jelas dan subjektif, Anda dapat mencoba melihat modul itu sendiri untuk panduan. Secara umum, menggunakan with kata kunci seperti yang disarankan pengguna lain adalah ide bagus, tetapi dalam situasi khusus ini mungkin tidak memberikan fungsionalitas yang Anda harapkan.

Pada modul versi 1.2.5, MySQLdb.Connection mengimplementasikan protokol pengelola konteks dengan kode berikut (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()

Ada beberapa T&J yang ada tentang with sudah, atau Anda dapat membaca Memahami pernyataan "dengan" Python , tetapi pada dasarnya yang terjadi adalah __enter__ dijalankan di awal with blokir, dan __exit__ dijalankan setelah meninggalkan with memblokir. Anda dapat menggunakan sintaks opsional with EXPR as VAR untuk mengikat objek yang dikembalikan oleh __enter__ ke nama jika Anda berniat untuk merujuk objek itu nanti. Jadi, dengan penerapan di atas, berikut adalah cara sederhana untuk mengkueri database Anda:

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,),)"

Pertanyaannya sekarang adalah, bagaimana status koneksi dan kursor setelah keluar dari with memblokir? __exit__ metode yang ditampilkan di atas hanya memanggil self.rollback() atau self.commit() , dan tidak satu pun dari metode tersebut yang memanggil close() metode. Kursor itu sendiri tidak memiliki __exit__ metode yang ditentukan – dan tidak masalah jika itu berhasil, karena with hanya mengelola koneksi. Oleh karena itu, koneksi dan kursor tetap terbuka setelah keluar dari with memblokir. Ini mudah dikonfirmasi dengan menambahkan kode berikut ke contoh di atas:

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'

Anda akan melihat output "kursor terbuka; koneksi terbuka" dicetak ke stdout.

Saya yakin Anda perlu menutup kursor sebelum melakukan koneksi.

Mengapa? MySQL C API , yang merupakan dasar untuk MySQLdb , tidak mengimplementasikan objek kursor apa pun, seperti yang tersirat dalam dokumentasi modul:"MySQL tidak mendukung kursor; namun, kursor mudah ditiru." Memang, MySQLdb.cursors.BaseCursor kelas mewarisi langsung dari object dan tidak memberlakukan batasan seperti itu pada kursor sehubungan dengan komit/kembalikan. Pengembang Oracle mengatakan ini :

cnx.commit() sebelum cur.close() terdengar paling logis bagi saya. Mungkin Anda bisa mengikuti aturan:"Tutup kursor jika Anda tidak membutuhkannya lagi." Jadi komit() sebelum menutup kursor. Pada akhirnya, untuk Connector/Python, itu tidak membuat banyak perbedaan, tetapi atau database lain mungkin.

Saya berharap itu sedekat Anda akan mendapatkan "praktik standar" tentang hal ini.

Apakah ada keuntungan signifikan untuk menemukan kumpulan transaksi yang tidak memerlukan komitmen perantara sehingga Anda tidak perlu mendapatkan kursor baru untuk setiap transaksi?

Saya sangat meragukannya, dan dalam mencoba melakukannya, Anda mungkin menambahkan kesalahan manusia. Lebih baik memutuskan konvensi dan menaatinya.

Apakah ada banyak biaya tambahan untuk mendapatkan kursor baru, atau ini bukan masalah besar?

Overhead dapat diabaikan, dan tidak menyentuh server database sama sekali; itu sepenuhnya dalam implementasi MySQLdb. Anda dapat melihat BaseCursor.__init__ di github jika Anda benar-benar ingin tahu apa yang terjadi saat Anda membuat kursor baru.

Kembali ke awal ketika kita mendiskusikan with , mungkin sekarang Anda dapat memahami mengapa MySQLdb.Connection kelas __enter__ dan __exit__ metode memberi Anda objek kursor baru di setiap with blokir dan jangan repot-repot melacaknya atau menutupnya di ujung blok. Ini cukup ringan dan ada murni untuk kenyamanan Anda.

Jika benar-benar penting bagi Anda untuk mengelola objek kursor secara mikro, Anda dapat menggunakan contextlib.closing untuk menebus fakta bahwa objek kursor tidak memiliki __exit__ yang ditentukan metode. Dalam hal ini, Anda juga dapat menggunakannya untuk memaksa objek koneksi menutup sendiri setelah keluar dari with memblokir. Ini akan menampilkan "my_curs ditutup; my_conn ditutup":

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'

Perhatikan bahwa with closing(arg_obj) tidak akan memanggil __enter__ objek argumen dan __exit__ metode; itu akan hanya panggil close objek argumen metode di akhir with memblokir. (Untuk melihat ini beraksi, cukup tentukan kelas Foo dengan __enter__ , __exit__ , dan close metode yang berisi print simple sederhana pernyataan, dan bandingkan apa yang terjadi ketika Anda melakukan with Foo(): pass apa yang terjadi ketika Anda melakukan with closing(Foo()): pass .) Ini memiliki dua implikasi signifikan:

Pertama, jika mode komit otomatis diaktifkan, MySQLdb akan BEGIN transaksi eksplisit di server saat Anda menggunakan with connection dan komit atau kembalikan transaksi di akhir blok. Ini adalah perilaku default MySQLdb, yang dimaksudkan untuk melindungi Anda dari perilaku default MySQL yang segera melakukan setiap dan semua pernyataan DML. MySQLdb mengasumsikan bahwa ketika Anda menggunakan manajer konteks, Anda menginginkan transaksi, dan menggunakan BEGIN eksplisit untuk melewati pengaturan autocommit di server. Jika Anda terbiasa menggunakan with connection , Anda mungkin mengira komit otomatis dinonaktifkan padahal sebenarnya itu hanya dilewati. Anda mungkin mendapatkan kejutan yang tidak menyenangkan jika Anda menambahkan closing ke kode Anda dan kehilangan integritas transaksional; Anda tidak akan dapat mengembalikan perubahan, Anda mungkin mulai melihat bug konkurensi dan mungkin tidak segera jelas alasannya.

Kedua, with closing(MySQLdb.connect(user, pass)) as VAR mengikat objek koneksi ke VAR , berbeda dengan with MySQLdb.connect(user, pass) as VAR , yang mengikat objek kursor baru ke VAR . Dalam kasus terakhir, Anda tidak akan memiliki akses langsung ke objek koneksi! Sebagai gantinya, Anda harus menggunakan connection kursor atribut, yang menyediakan akses proxy ke koneksi asli. Saat kursor ditutup, connection its atribut disetel ke None . Ini menghasilkan koneksi yang ditinggalkan yang akan bertahan sampai salah satu hal berikut terjadi:

  • Semua referensi kursor dihapus
  • kursor keluar dari ruang lingkup
  • Waktu koneksi habis
  • Koneksi ditutup secara manual melalui alat administrasi server

Anda dapat mengujinya dengan memantau koneksi terbuka (di Workbench atau dengan menggunakan SHOW PROCESSLIST ) saat menjalankan baris berikut satu per satu:

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. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Membandingkan Solusi Replikasi Dari Oracle dan MySQL

  2. Cara Membuat dan Memanipulasi Database SQL dengan Python

  3. Menyetel ulang kata sandi ROOT di MySQL 5.6

  4. Ubah batas untuk ukuran Baris Mysql terlalu besar

  5. PDO mengambil satu kolom dari tabel ke dalam array 1 dimensi