web-dev-qa-db-ja.com

明示的なcursor.close()の必要性

時々、ORMを使用する代わりに connection.cursor() を使用して生のクエリを実行しています(これは間違いなく特効薬ではないため)。

データベースを使い終わった後、いくつかの場所で明示的なcursor.close()を呼び出さないことに気づきました。これまでのところ、これによってエラーやパフォーマンスの問題が発生することはありません。カーソルを明示的に閉じないと、どのような問題が発生する可能性があるのでしょうか。

私が理解している限り、connectionおよびcursor in Django follow "Python Database API Specification v2.0"( PEP-249 =)そして、それによると、cursor__del__()メソッドが呼び出されるたびに自動的に閉じられます。

参考までに、Python 2.7およびDjango 1.6.5。

34
alecxe

___del___/.close()

  1. ___del___の呼び出しは保証されていません
  2. 一部のデータベースは、___del___でcursor.close()を呼び出さない(不適切な方法ですが、true)
  3. 一部のデータベースは、実際には接続関数で接続を作成せず、カーソル関数で接続を作成します(例:2&3の場合:pyhiveのプレスト[たぶん、パッチを適用してから])

サーバー接続全般

ほとんどのサーバーには、アイドルタイムアウト構成プロパティがあります(Tと呼びましょう)。接続がT秒以上アイドル状態の場合、サーバーは接続を削除します。ほとんどのサーバーには、ワーカースレッドプール(W)のサイズを設定するプロパティもあります。サーバーへのW接続がすでにある場合、新しい接続が試行されるとハングする可能性があります。次に、明示的に接続を閉じるオプションがないことを想像してみてください。そのような状況では、ワーカープールが完全に使用されることがないようにタイムアウトを十分に小さく設定する必要があります。これは、同時接続数の関数です。

ただし、カーソル/接続を閉じた場合(上記の[3]で同等ではない場合でも、それらは同様に動作します)、これらのサーバー構成プロパティを管理する必要はなく、スレッドプールは単純に大きくする必要があります。すべての同時接続を管理するのに十分です(新しいリソースをときどき待機するオプションがあります)。一部のサーバー(CassandraのTitanなど)で、スレッドプールのワーカーが不足して回復できないため、サーバー全体が再起動するまで停止します。

TL/DRdanoで言及されているライブラリなど、非常によく開発されたライブラリを使用している場合は、問題は発生しません。より少ない初期のライブラリーを使用している場合、サーバーの構成とアクセス率によっては、.close()を呼び出さないと、サーバーでワーカースレッドを取得できなくなる可能性があります。

8
metaperture

Djangoのcursorクラスは、基盤となるDBのcursorのラッパーにすぎないため、cursorを開いたままにした場合の影響は、基本的には基盤となるDBドライバーに関連付けられます。

Psycopg2's(psycopg2 is DB driver Django Uses for PostgreSQL DB's) [〜#〜] faq [〜#〜] によると、カーソルは軽量ですが、データをキャッシュしますカーソルオブジェクトを使用して行ったクエリから返された、メモリを浪費する可能性があります。

カーソルは軽量のオブジェクトであり、それらの多くを作成することは、いかなる種類の問題も引き起こすべきではありません。ただし、結果セットのフェッチに使用されるカーソルはデータをキャッシュし、結果セットのサイズに比例してメモリを使用することに注意してください。私たちの提案は、ほとんどの場合、新しいカーソルを作成し、データが不要になったらすぐに古いカーソルを破棄することです(それらに対してclose()を呼び出します)。 INSERTまたはUPDATE。

Djangoは、MySQLのバックエンドとしてMySQLdbを使用します。これには、いくつかの異なるタイプのカーソルがあり、その中には実際に結果セットをサーバー側に格納するものもあります。 Cursor.closeMySQLdbドキュメンテーション)= 使い終わったら、サーバー側カーソルを閉じることが非常に重要であることに注意してください。

サーバー側カーソルを使用している場合は、カーソルを使い終わったら、新しいカーソルを作成する前に、カーソルを閉じることが非常に重要です。

ただし、クライアント側で結果を保存するCursorによって提供されるデフォルトのMySQLdbクラスを使用するため、これはDjangoには関係ありません。使用済みのカーソルを開いたままにしておくと、psycopg2と同じように、保存された結果セットが使用するメモリを浪費するおそれがあります。カーソルの close method は、db接続への内部参照を削除し、格納された結果セットを使い果たします。

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

ソースを見るとわかるように、Django( cx_Oraclesqlite3 / pysqlite2 )が使用する残りのバックエンドはすべて、同じパターン;保存された結果/オブジェクト参照を削除/リセットすることでメモリを解放します。 sqlite3 docsCursorクラスのcloseメソッドがあることさえ言及していません。コード例。

cursorオブジェクトで__del__()が呼び出されると、cursorが閉じられることは正しいので、明示的に閉じる必要があるのは、cursorへの長期にわたる参照を保持している場合のみです。例えばクラスのインスタンスメソッドとして保持しているself.cursorオブジェクト。

19
dano

cursor.close()の明示的な呼び出しには、次の2つの理由が考えられます。

  1. __del__の呼び出しは保証されておらず、 here および here で読み取ることができる問題があります。
  2. 明示的は暗黙的よりも優れている( Zen of Python
1
Aamir Adnan

この質問には少し遅れます。たぶん、exit-on-exit-scopeが必要です。

from contextlib import closing
from Django.db import connection

with closing(connection.cursor()) as cursor:
    cursor.execute(...)
    cursor.execute(...)
    cursor.execute(...)
1
stanleyxu2005

通常、オペレーティングシステムはリソースの解放に依存できますが、データベース接続などを閉じて、リソースが不要になったときに確実に解放されるようにすることは常に良い衛生状態です。データベースの観点からの本当に重要なことは、変更がcommit() ed。

1
holdenweb