我不时使用connection.cursor()
而不是使用ORM执行原始查询(因为它绝对不是银弹)。
我注意到在我完成数据库后,我在几个地方都没有打电话给cursor.close()
。到目前为止,这还没有导致任何错误或性能问题。我想知道如果不明确地关闭光标我可能会遇到什么样的问题,哪些可能出错?
据我所知,Django中的connection
和cursor
遵循" Python数据库API规范v2.0" (PEP-249)。并且,根据它,只要调用cursor
方法,__del__()
就会自动关闭。我想这个问题也可能是:在没有被调用时是否存在用例?
仅供参考,我使用的是Python 2.7和Django 1.6.5。
答案 0 :(得分:17)
Django的cursor
类只是基础数据库cursor
的包装器,因此保持cursor
开放的效果基本上与基础相关联DB驱动程序。
根据psycopg2(psycopg2是Django用于PostgreSQL DB的数据库驱动程序)FAQ,它们的游标是轻量级的,但会缓存从使用游标进行的查询返回的数据对象,可能会浪费内存:
游标是轻量级对象,不应创建大量游标 造成任何问题。但请注意,游标用于获取结果 sets将缓存数据并使用与结果成比例的内存 设定尺寸。我们的建议是几乎总是创建一个新的游标和 一旦不再需要数据就立即处理旧的(呼叫 close()on。)唯一的例外是紧密循环,通常是一个 对于一大堆INSERT或UPDATE使用相同的游标。
Django使用MySQLdb
作为MySQL的后端,它有几种不同类型的游标,包括一些实际在服务器端存储结果集的游标。 MySQLdb
documentation for Cursor.close
请注意,关闭服务器端光标非常重要:
如果您使用服务器端游标,则关闭非常重要 完成后和创建新光标之前的光标。
但是,这与Django无关,因为它使用Cursor
提供的默认MySQLdb
类,它在客户端存储结果。将已使用的游标保持打开只会浪费存储结果集使用的内存,就像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_oracle,sqlite3 / pysqlite2)都遵循相同的模式;通过删除/重置存储的结果/对象引用来释放内存。 sqlite3 docs甚至没有提及Cursor
类具有一种关闭方法,并且它仅在所包含的示例代码中偶尔使用。
在cursor
对象上调用__del__()
时,cursor
将被关闭是正确的,因此如果您要保留,则明确关闭的需要只是一个问题对cursor
的长期参考;例如一个self.cursor
对象,您将其作为类的实例方法保留。
答案 1 :(得分:7)
<强> __del__
/ .close()
强>
__del__
无法保证被称为__del__
中调用cursor.close()(不好的做法,但都是如此)一般在服务器连接上
大多数服务器都有空闲超时配置属性(让我们称之为T)。如果连接空闲超过T秒,服务器将删除该连接。大多数服务器还具有用于设置工作线程池(W)大小的属性。如果您已经与服务器建立了W连接,则在尝试新连接时可能会挂起。第二个想象,您没有明确关闭连接的选项。在这种情况下,您必须将超时设置得足够小,以至于您的工作池永远不会被完全使用,这是您拥有多少并发连接的函数。
但是,如果你关闭游标/连接(即使上面的[3]不相同,它们的行为方式类似),那么你不必管理这些服务器配置属性,而你的线程池只需要足够大以管理所有并发连接(偶尔等待新资源的选项)。我已经看到一些服务器(例如Cassandra上的Titan)无法从线程池中的工作程序用完中恢复,因此整个服务器都会关闭直到重新启动。
<强> TL / DR 强>
如果您使用的是非常完善的库,例如dano
提到的库,那么您将不会遇到任何问题。如果您使用较少的原始库,如果您不调用.close()
,则最终可能会阻止服务器获取工作线程,具体取决于您的服务器配置和访问速率。
答案 2 :(得分:1)
虽然通常可以依赖操作系统来释放资源,但是关闭数据库连接以确保资源在不再需要时被释放时总是保持良好的卫生,从数据库的角度来看,这是非常重要的事情是为了确保任何更改都是commit()
。
答案 3 :(得分:1)
明确调用cursor.close()
可能是因为两个原因:
__del__
无法保证被调用,但您可以阅读here和here 答案 4 :(得分:1)
我对这个问题有点迟了。也许你想要的是近距离退出范围。
from contextlib import closing
from django.db import connection
with closing(connection.cursor()) as cursor:
cursor.execute(...)
cursor.execute(...)
cursor.execute(...)