Mysql
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Mysql

Khi nào đóng con trỏ bằng MySQLdb

Thay vì hỏi thực hành tiêu chuẩn là gì, vì điều đó thường không rõ ràng và chủ quan, bạn có thể thử tìm kiếm chính mô-đun để được hướng dẫn. Nói chung, sử dụng with từ khóa như một người dùng khác đề xuất là một ý tưởng tuyệt vời, nhưng trong trường hợp cụ thể này, nó có thể không cung cấp cho bạn đầy đủ chức năng mà bạn mong đợi.

Kể từ phiên bản 1.2.5 của mô-đun, MySQLdb.Connection triển khai giao thức trình quản lý ngữ cảnh với mã sau ( 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()

Có một số Hỏi và Đáp hiện tại về with rồi, hoặc bạn có thể đọc Hiểu câu lệnh "with" của Python , nhưng về cơ bản những gì xảy ra là __enter__ thực thi ở đầu with khối và __exit__ thực thi khi rời with khối. Bạn có thể sử dụng cú pháp tùy chọn with EXPR as VAR để liên kết đối tượng được trả về bởi __enter__ đến một tên nếu bạn định tham chiếu đến đối tượng đó sau này. Vì vậy, với cách triển khai ở trên, đây là một cách đơn giản để truy vấn cơ sở dữ liệu của bạn:

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

Câu hỏi bây giờ là trạng thái của kết nối và con trỏ sau khi thoát khỏi with khối? __exit__ phương thức hiển thị ở trên chỉ gọi self.rollback() hoặc self.commit() và không có phương thức nào trong số đó tiếp tục gọi close() phương pháp. Bản thân con trỏ không có __exit__ phương thức được xác định - và sẽ không thành vấn đề nếu có, vì with chỉ quản lý kết nối. Do đó, cả kết nối và con trỏ vẫn mở sau khi thoát khỏi with khối. Điều này dễ dàng được xác nhận bằng cách thêm mã sau vào ví dụ trên:

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'

Bạn sẽ thấy đầu ra "con trỏ đang mở; kết nối đang mở" được in ra stdout.

Tôi tin rằng bạn cần đóng con trỏ trước khi thực hiện kết nối.

Tại sao? MySQL C API , là cơ sở cho MySQLdb , không triển khai bất kỳ đối tượng con trỏ nào, như được ngụ ý trong tài liệu mô-đun: "MySQL không hỗ trợ con trỏ; tuy nhiên, con trỏ dễ dàng được mô phỏng. " Thật vậy, MySQLdb.cursors.BaseCursor lớp kế thừa trực tiếp từ object và không áp đặt hạn chế như vậy đối với con trỏ liên quan đến cam kết / khôi phục. Một nhà phát triển Oracle đã nói điều này :

cnx.commit () trước cur.close () nghe có vẻ hợp lý nhất đối với tôi. Có thể bạn có thể thực hiện theo quy tắc:"Đóng con trỏ nếu bạn không cần nó nữa." Do đó, commit () trước khi đóng con trỏ. Cuối cùng, forConnector / Python, nó không tạo ra nhiều khác biệt, nhưng hoặc các cơ sở dữ liệu khác thì có thể.

Tôi hy vọng rằng bạn sắp đạt được "thực hành tiêu chuẩn" về chủ đề này.

Có lợi thế đáng kể nào khi tìm các tập hợp giao dịch không yêu cầu cam kết trung gian để bạn không phải nhận con trỏ mới cho mỗi giao dịch không?

Tôi rất nghi ngờ điều đó, và khi cố gắng làm như vậy, bạn có thể mắc thêm lỗi do con người gây ra. Tốt hơn bạn nên quyết định một quy ước và gắn bó với nó.

Có rất nhiều chi phí để có được con trỏ mới hay đó không phải là vấn đề lớn?

Chi phí không đáng kể và hoàn toàn không liên quan đến máy chủ cơ sở dữ liệu; nó hoàn toàn nằm trong việc triển khai MySQLdb. Bạn có thể xem BaseCursor.__init__ trên github nếu bạn thực sự tò mò muốn biết điều gì đang xảy ra khi bạn tạo một con trỏ mới.

Quay lại lúc trước khi chúng ta thảo luận về with , có lẽ bây giờ bạn có thể hiểu tại sao MySQLdb.Connection lớp __enter____exit__ các phương thức cung cấp cho bạn một đối tượng con trỏ hoàn toàn mới trong mỗi with chặn và không bận tâm theo dõi nó hoặc đóng nó ở cuối khối. Nó khá nhẹ và tồn tại hoàn toàn để thuận tiện cho bạn.

Nếu việc quản lý vi mô đối tượng con trỏ thực sự quan trọng đối với bạn, bạn có thể sử dụng contextlib.closing để bù đắp cho thực tế là đối tượng con trỏ không có __exit__ được xác định phương pháp. Đối với vấn đề đó, bạn cũng có thể sử dụng nó để buộc đối tượng kết nối tự đóng khi thoát khỏi with khối. Điều này sẽ xuất ra "my_curs đã đóng; my_conn đã đóng":

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'

Lưu ý rằng with closing(arg_obj) sẽ không gọi __enter__ của đối tượng và __exit__ các phương pháp; nó sẽ chỉ gọi close của đối tượng đối số ở cuối with khối. (Để xem điều này hoạt động, chỉ cần xác định một lớp Foo với __enter__ , __exit__close các phương thức chứa print đơn giản và so sánh những gì sẽ xảy ra khi bạn thực hiện with Foo(): pass điều gì sẽ xảy ra khi bạn thực hiện with closing(Foo()): pass .) Điều này có hai ý nghĩa quan trọng:

Đầu tiên, nếu chế độ tự động gửi được bật, MySQLdb sẽ BEGIN một giao dịch rõ ràng trên máy chủ khi bạn sử dụng with connection và cam kết hoặc khôi phục giao dịch ở cuối khối. Đây là các hành vi mặc định của MySQLdb, nhằm bảo vệ bạn khỏi hành vi mặc định của MySQL là thực hiện ngay lập tức bất kỳ và tất cả các câu lệnh DML. MySQLdb giả định rằng khi bạn sử dụng trình quản lý ngữ cảnh, bạn muốn một giao dịch và sử dụng BEGIN rõ ràng để bỏ qua cài đặt tự động gửi trên máy chủ. Nếu bạn quen sử dụng with connection , bạn có thể nghĩ rằng tính năng autocommit bị vô hiệu hóa trong khi thực sự nó chỉ bị bỏ qua. Bạn có thể nhận được một bất ngờ khó chịu nếu bạn thêm close mã của bạn và mất tính toàn vẹn của giao dịch; bạn sẽ không thể khôi phục các thay đổi, bạn có thể bắt đầu thấy các lỗi đồng thời và có thể không rõ ràng ngay lập tức tại sao.

Thứ hai, with closing(MySQLdb.connect(user, pass)) as VAR liên kết với đối tượng kết nối thành VAR , ngược lại với with MySQLdb.connect(user, pass) as VAR , liên kết với một đối tượng con trỏ mới thành VAR . Trong trường hợp sau, bạn sẽ không có quyền truy cập trực tiếp vào đối tượng kết nối! Thay vào đó, bạn sẽ phải sử dụng connection của con trỏ thuộc tính này, cung cấp quyền truy cập proxy vào kết nối ban đầu. Khi con trỏ được đóng, kết nối connection thuộc tính được đặt thành None . Điều này dẫn đến một kết nối bị bỏ rơi sẽ tồn tại cho đến khi một trong những điều sau xảy ra:

  • Tất cả các tham chiếu đến con trỏ đều bị xóa
  • Con trỏ đi ra khỏi phạm vi
  • Kết nối hết thời gian chờ
  • Kết nối được đóng theo cách thủ công thông qua các công cụ quản trị máy chủ

Bạn có thể kiểm tra điều này bằng cách theo dõi các kết nối đang mở (trong Workbench hoặc bằng sử dụng SHOW PROCESSLIST ) trong khi thực hiện từng dòng sau:

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. Làm thế nào để có được kích thước cơ sở dữ liệu MySQL cho cơ sở dữ liệu của bạn?

  2. bbPress:Cách tìm ánh xạ các tệp đính kèm đến các bài đăng tương ứng của chúng

  3. Bảng được chỉ định hai lần, vừa là mục tiêu cho 'CẬP NHẬT' và là nguồn riêng cho dữ liệu trong mysql

  4. mysql sau khi chèn trình kích hoạt sẽ cập nhật cột của bảng khác

  5. MySQL - MariaDB - Viết thủ tục được lưu trữ đầu tiên