Подтвердить что ты не робот

Необходимость явного cursor.close()

Время от времени я выполняю необработанные запросы, используя connection.cursor() вместо использования ORM (так как это определенно не серебряная пуля).

Я заметил, что в нескольких местах я не называю явным cursor.close() после того, как я закончил работу с базой данных. Пока это не приводит к ошибкам или проблемам с производительностью. Мне интересно, какие проблемы я мог бы иметь, не закрывая курсор явно, что может пойти не так?

Насколько я понимаю, connection и cursor в Django следуют "Спецификации API базы данных Python v2.0" (PEP-249). И, согласно ему, cursor будет автоматически закрываться всякий раз, когда вызывается метод __del__(). Я думаю, вопрос может быть также: есть ли прецедент, когда он не называется?

FYI, я использую Python 2.7 и Django 1.6.5.

4b9b3361

Ответ 1

__del__/.close():

  • __del__ не гарантируется называться
  • некоторые базы данных не вызывают курсор .close() в своих __del__ (неверная практика, но верно)
  • некоторые базы данных фактически не создают соединения в функции соединения, а вместо этого в функции курсора (например, для 2 и 3: pyhive presto [возможно, с тех пор они исправлены])

На общих серверах

У большинства серверов есть свойство конфигурации тайм-аута простоя (позвоните в T). Если соединение простаивает более трех секунд, сервер удалит соединение. Большинство серверов также имеют свойства для установки размера пула рабочих потоков (W). Если у вас уже есть W-соединения на вашем сервере, скорее всего, они будут зависать при попытке нового подключения. На секунду представьте, что у вас нет возможности явно закрывать соединения. В этой ситуации вы должны установить тайм-аут настолько малым, чтобы ваш рабочий пул никогда не использовался полностью, что является функцией количества параллельных соединений, которые у вас есть.

Однако, если вы закрываете свои курсоры/соединения (даже если они не эквивалентны [3] выше, они ведут себя аналогичным образом), тогда вам не нужно управлять этими настройками конфигурации сервера, а ваш пул потоков просто необходим чтобы быть достаточно большим, чтобы управлять всеми параллельными соединениями (с возможностью случайного ожидания новых ресурсов). Я видел, как некоторые серверы (например, Titan on Cassandra) не смогли восстановить работоспособность рабочих пулов, поэтому весь сервер опускается до перезапуска.

TL/DR Если вы используете очень хорошо разработанные библиотеки, например, те, что указаны dano, у вас не будет проблемы. Если вы используете менее чистые библиотеки, вы можете заблокировать сервер, получающий рабочий поток, если вы не вызываете .close(), в зависимости от конфигурации сервера и скорости доступа.

Ответ 2

Класс Django cursor - это всего лишь оболочка вокруг базового DB cursor, поэтому эффект оставления cursor open в основном связан с базовым драйвером базы данных.

Согласно psycopg2 (psycopg2 - это драйвер DB, используемый Django для DB PostgreSQL) FAQ, их курсоры легкие, но будут кэшировать данные, возвращаемые из запросов, которые вы сделанный с использованием объекта курсора, который потенциально может потерять память:

Курсоры - это легкие объекты, и их создание не должно представляют собой любую проблему. Но обратите внимание, что курсоры, используемые для получения результата наборы будут кэшировать данные и использовать память пропорционально результату комплект размер. Наше предложение состоит в том, чтобы почти всегда создавать новый курсор и распоряжаться старыми, как только данные больше не требуются (вызов close() на них.) Единственным исключением являются жесткие петли, где обычно используйте один и тот же курсор для целой группы INSERT или UPDATE.

Django использует MySQLdb как сервер для MySQL, который имеет несколько различных типов курсоров, включая некоторые, которые фактически хранят свои результирующие наборы на стороне сервера. В документации MySQLdb для Cursor.close следует отметить, что очень важно закрыть курсор на стороне сервера, когда вы закончите с ними:

Если вы используете серверные курсоры, очень важно закрыть курсор, когда вы закончите с ним, и перед созданием нового.

Однако это не относится к Django, потому что он использует класс cursor по умолчанию, предоставляемый MySQLdb, который хранит результаты на стороне клиента. Оставляя используемый курсор открытым только риски, теряющие память, используемую сохраненным результирующим набором, точно так же, как psycopg2. Метод close на курсор просто удаляет внутреннюю ссылку на соединение 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_oracle, sqlite3/pysqlite2) все следуют одному и тому же шаблону; освобождая память, удаляя/восстанавливая сохраненные результаты/ссылки на объекты. sqlite3 docs даже не упоминают, что класс cursor имеет метод close, и он использовался только случайным образом в включенном примере кода.

Вы правы, что cursor будет закрыт, когда __del__() вызывается в объекте cursor, поэтому необходимость явно закрыть это только проблема, если вы сохраняете длинную ссылку на cursor; например a self.cursor объект, который вы сохраняете как метод экземпляра класса.

Ответ 3

В то время как на операционную систему обычно можно полагаться на выпуск ресурсов, всегда хорошая гигиена для закрытия таких вещей, как соединения с базой данных, чтобы обеспечить освобождение ресурсов, когда их больше не требуется, действительно важная вещь с точки зрения базы данных заключается в обеспечении того, чтобы любые изменения commit() ed.

Ответ 4

Явный вызов cursor.close() может быть вызван двумя причинами:

  • __del__ не может быть вызван и есть некоторые проблемы, которые вы можете прочитать здесь и здесь
  • Явный лучше, чем неявный (Zen of Python)

Ответ 5

Я немного опаздываю на этот вопрос. Возможно, вы хотите, чтобы охват с закрытием-выводом.

from contextlib import closing
from django.db import connection

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