根據SQLAlchemy,select語句被視爲for循環中的迭代器。結果是,將返回大量行的select語句不會使用過多的內存。SELECT語句上的SQLAlchemy內存管理器
我發現,在一個MySQL表如下聲明:
for row in my_connections.execute(MyTable.__table__.select()):
yield row
似乎並沒有遵循這一點,因爲我溢出可用內存和第一行產生之前就開始抖動。我究竟做錯了什麼?
根據SQLAlchemy,select語句被視爲for循環中的迭代器。結果是,將返回大量行的select語句不會使用過多的內存。SELECT語句上的SQLAlchemy內存管理器
我發現,在一個MySQL表如下聲明:
for row in my_connections.execute(MyTable.__table__.select()):
yield row
似乎並沒有遵循這一點,因爲我溢出可用內存和第一行產生之前就開始抖動。我究竟做錯了什麼?
基本的MySQLdb
遊標一次從服務器獲取整個查詢結果。 這會消耗大量的內存和時間。 使用MySQLdb.cursors.SSCursor當你想做一個巨大的查詢和 從服務器一次拉取結果。
因此,試圖通過connect_args={'cursorclass': MySQLdb.cursors.SSCursor}
創建engine
時:
from sqlalchemy import create_engine, MetaData
import MySQLdb.cursors
engine = create_engine('mysql://root:[email protected]/e2', connect_args={'cursorclass': MySQLdb.cursors.SSCursor})
meta = MetaData(engine, reflect=True)
conn = engine.connect()
rs = s.execution_options(stream_results=True).execute()
見http://www.sqlalchemy.org/trac/ticket/1089
注意,使用SSCursor鎖定表,直到提取完成。這將影響使用相同連接的其他遊標:來自同一連接的兩個遊標無法同時從表中讀取。
但是,來自不同連接的遊標可以同時從同一張表中讀取。
下面是一些代碼演示問題:
import MySQLdb
import MySQLdb.cursors as cursors
import threading
import logging
import config
logger = logging.getLogger(__name__)
query = 'SELECT * FROM huge_table LIMIT 200'
def oursql_conn():
import oursql
conn = oursql.connect(
host=config.HOST, user=config.USER, passwd=config.PASS,
db=config.MYDB)
return conn
def mysqldb_conn():
conn = MySQLdb.connect(
host=config.HOST, user=config.USER,
passwd=config.PASS, db=config.MYDB,
cursorclass=cursors.SSCursor)
return conn
def two_cursors_one_conn():
"""Two SSCursors can not use one connection concurrently"""
def worker(conn):
cursor = conn.cursor()
cursor.execute(query)
for row in cursor:
logger.info(row)
conn = mysqldb_conn()
threads = [threading.Thread(target=worker, args=(conn,))
for n in range(2)]
for t in threads:
t.daemon = True
t.start()
# Second thread may hang or raise OperationalError:
# File "/usr/lib/pymodules/python2.7/MySQLdb/cursors.py", line 289, in _fetch_row
# return self._result.fetch_row(size, self._fetch_type)
# OperationalError: (2013, 'Lost connection to MySQL server during query')
for t in threads:
t.join()
def two_cursors_two_conn():
"""Two SSCursors from independent connections can use the same table concurrently"""
def worker():
conn = mysqldb_conn()
cursor = conn.cursor()
cursor.execute(query)
for row in cursor:
logger.info(row)
threads = [threading.Thread(target=worker) for n in range(2)]
for t in threads:
t.daemon = True
t.start()
for t in threads:
t.join()
logging.basicConfig(level=logging.DEBUG,
format='[%(asctime)s %(threadName)s] %(message)s',
datefmt='%H:%M:%S')
two_cursors_one_conn()
two_cursors_two_conn()
注意oursql是另一套的MySQL綁定的Python。 oursql遊標是真正的服務器端遊標,其中fetch rows lazily by default。在安裝oursql
,如果你改變
conn = mysqldb_conn()
到
conn = oursql_conn()
然後two_cursors_one_conn()
運行沒有懸掛或引發異常。
這解決了我的內存問題與MySQL和yield_per。任何想法爲什麼Trac的回覆說這是「近乎無用」? – bcoughlan 2013-12-10 15:34:12
@bcoughlan:我已經添加了一些代碼並討論了同時使用SSCursors的限制。 – unutbu 2013-12-10 17:35:42
這應該解決mysqldb,有沒有類似的選項爲mysqlconnector,因爲我面臨類似的問題,使用該驅動程序。 – 2014-11-07 08:42:21