2014-07-09 40 views
20

不時,我使用connection.cursor()而不是使用ORM(因爲它絕對不是一個銀彈)執行原始查詢。必要性明確cursor.close的()

我注意到,在幾個地方我不叫顯性cursor.close()我與數據庫中完成後。到目前爲止,這不會導致任何錯誤或性能問題。我想知道如果沒有明確地關閉遊標,我可能會遇到什麼樣的問題,會出現什麼問題?

據我瞭解,connectioncursor在Django遵循 「Python數據庫API規範v2.0」(PEP-249)。而且,根據它,當調用__del__()方法時,cursor會自動關閉。我想這個問題也可能是:有沒有一個用例,當它不被調用?

通知你,我使用Python 2.7和Django的1.6.5。

+0

我想這是非常相似的這個問題http://stackoverflow.com/questions/5669878/python-mysqldb-when-to-close-cursors? –

+2

@AamirAdnan非常感謝,在發帖前看到它。我知道如何關閉光標。問題是關閉它有多糟,以及後果如何。 – alecxe

回答

5

__del__/.close()

  1. __del__不能保證被稱爲
  2. 一些數據庫不叫他們__del__ cursor.close()(不好的做法,但真)
  3. 一些數據庫不實際創建的連接連接功能,但在遊標功能,而不是(例如2 & 3:pyhive的急[也許他們已經因爲修補它])

對一般

大多數服務器服務器連接有一個空閒超時配置屬性(我們稱之爲T)。如果連接閒置超過T秒,服務器將刪除連接。大多數服務器還具有設置工作程序線程池(W)大小的屬性。如果您已經有W連接到您的服務器,則嘗試新連接時可能會掛起。再想象一下,你沒有選擇顯式關閉連接。在這種情況下,您必須將超時設置爲足夠小以至於您的工作池永遠不會被完全使用,這是您具有多少併發連接的功能。但是,如果你關閉了你的遊標/連接(即使不是上面[3]的等價物,它們的行爲也是類似的),那麼你不必管理這些服務器配置屬性和線程池需要足夠大以管理所有併發連接(可選擇偶爾等待新資源)。我見過一些服務器(例如Cassandra上的Titan)無法從線程池中的工作人員中恢復,因此整個服務器都關閉,直到重新啓動。

TL/DR 如果您使用非常發達的庫,例如dano提到的,你不會有問題。如果您使用的是較少的原始庫,如果您不調用.close(),則取決於您的服務器配置和訪問速率,最終可能會阻止服務器獲取工作線程。

2

雖然這個操作系統可以正常被依靠釋放資源,它總是良好的衛生習慣,收的東西,如數據庫連接,以確保資源在需要時不再被釋放,從數據庫一點很重要的是要確保任何修改都是commit() ed。

14

Django的cursor類僅僅是底層數據庫的cursor的一個包裝,因此打開cursor的效果基本上與底層數據庫驅動程序有關。

根據psycopg2的(psycopg2是DB驅動Django使用PostgreSQL的DB的)FAQ,他們的遊標是輕量級的,但將緩存從你使用遊標對象進行查詢返回的數據,這可能會浪費內存:

遊標是輕量級的對象,並創造大量的人不應該 帶來什麼樣的問題。但請注意,用於獲取結果 集合的遊標將緩存數據並根據結果 集合大小成比例地使用內存。我們的建議是幾乎總是創建一個新的光標, 儘快處置舊如不再需要的數據(稱之爲 他們的close())。唯一的例外是緊密的循環,其中一個通常 使用相同的光標的全部插入或更新。

Django使用MySQLdb作爲後端的MySQL,它有幾種不同類型的遊標,包括一些實際的結果集存儲在服務器端。該MySQLdb documentation for Cursor.close做出一點要注意,它關閉是非常重要的服務器端遊標時,你與他們做的:

如果您正在使用服務器端遊標,這是非常重要的,關閉 光標當你完成它並在創建一個新的之前。

然而,這是不相關的Django的,因爲它使用MySQLdb提供的默認Cursor類,存儲在客戶端的結果。將使用的光標保留在開啓位置可能會浪費存儲結果集使用的內存,就像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密切的方法,而且只有在所包含的示例代碼使用斷斷續續。

你是正確的,當__del__()被稱爲cursor對象上的cursor將被關閉,所以需要顯式關閉只是,如果你保持長壽的參考cursor的問題;例如一個self.cursor對象,你保留作爲一個類的實例方法。

+0

但是,如果您爲第二個查詢重新使用遊標,則爲第一個查詢緩存的數據將不再被引用。所以從某種意義上說,不必要地創建一大堆遊標可能會起反作用,除非你特意刪除舊遊戲。 – holdenweb

+0

@holdenweb對,如果你要在短時間內完成大量查詢,使用相同的遊標可能會有意義。如果你打算做一個查詢,然後在未來的某個未知點再次創建另一個查詢,你應該關閉/刪除遊標,這樣你就不必保留它用來緩存結果集的任何內存。 – dano

+0

看來我們有暴力協議。 – holdenweb

1

cursor.close()顯式調用可能是因爲兩個原因:

  1. __del__不能保證被調用,並有一些問題,你可以閱讀herehere
  2. 明確優於隱式(Zen of Python
0

我對這個問題有點晚了。也許一個關閉出口範圍是你想要的。

from contextlib import closing 
from django.db import connection 

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

他們提到使用Django 1.6,文檔說一個上下文管理器(比如你的例子)直到1.7才被明確添加,不過,這些文檔似乎表明它在任何方式之前的工作都是因爲「魔術方法中的意外行爲查找」。 –