2010-09-13 2 views
9

SQLAlchemy'a göre, ifadeler seçim döngüleri için yinelenen olarak kabul edilir. Etki, büyük miktarda satır döndüren seçik bir ifadenin aşırı bellek kullanmamasıdır. Belirli bir deyimdeki SQLAlchemy bellek resmi belirtme

for row in my_connections.execute(MyTable.__table__.select()): 
    yield row 

Ben kullanılabilir belleği taşması gibi bu takip etmek ve ilk satır vermiştir önce dayak başlamak için görünmüyor:

Ben MySQL masaya aşağıdaki deyimi olduğunu bulma yaşıyorum. Neyi yanlış yapıyorum?

cevap

12

Temel MySQLdb imleci, tüm sorgu sonucunu sunucudan bir defada getirir. Bu, çok fazla bellek ve zaman tüketebilir. Büyük bir sorgu oluşturmak istediğinizde ve sunucudan sonuçları bir kerede çekerken, MySQLdb.cursors.SSCursor kullanın.

Dolayısıyla engine oluştururken connect_args={'cursorclass': MySQLdb.cursors.SSCursor} geçen deneyin:

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() 

getirme tamamlanana kadar kullanarak SSCursor tabloyu kilitler olduğunu http://www.sqlalchemy.org/trac/ticket/1089


Not bakınız. Bu, aynı bağlantıyı kullanarak diğer imleçleri etkiler: Aynı bağlantıdan iki imleç, aynı anda tablodan okuyamaz. Bununla birlikte, farklı bağlantılardan gelen imleçler aynı tablodan eşzamanlı olarak okuyabilirler. oursql Python için MySQL bağlamaları alternatif setinden oluşması

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() 

Not:

İşte sorunu gösteren bazı kodudur. oursql imleçleri, fetch rows lazily by default olan gerçek sunucu tarafı imleçlerdir. oursql yüklü ile, asılı ya da bir istisna yükseltmeden

conn = mysqldb_conn() 

için
conn = oursql_conn() 

sonra two_cursors_one_conn() çalışır değiştirirseniz.

+0

Bu, bellek sorunlarımı MySQL ve yield_per ile çözdü. Trac’in cevabının niçin "işe yaramamış" olduğuna dair bir fikri var mı? – bcoughlan

+1

@bcoughlan: Bazı kodları ekledim ve aynı anda SSCursors kullanmanın sınırlamalarını tartıştım. – unutbu

+0

Bu mysqldb için çözülmelidir, mysqlconnector için benzer bir seçenek var, bu sürücüyü kullanarak benzer bir sorunla karşılaşıyorum. –