Ошибка памяти SQLAlchemy в операторе select - PullRequest
9 голосов
/ 13 сентября 2010

Согласно SQLAlchemy, операторы select обрабатываются как итерируемые в циклах for.В результате оператор select, который будет возвращать огромное количество строк, не использует избыточную память.

Я обнаружил, что следующий оператор в таблице MySQL:

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

Некажется, следуют этому, поскольку я переполняю доступную память и начинаю побеждать прежде, чем будет получен первый ряд.Что я делаю не так?

1 Ответ

13 голосов
/ 13 сентября 2010

Базовый курсор 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:zenoss@localhost/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 - это настоящие серверные курсоры, которые по умолчанию лениво извлекают строки .При установленном oursql, если вы измените

conn = mysqldb_conn()

на

conn = oursql_conn()

, тогда two_cursors_one_conn() будет работать без зависания или возникновения исключения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...