Python курсор выполняет загрузку всех данных - PullRequest
1 голос
/ 08 апреля 2020

Я пытаюсь запросить большие данные (10 миллионов строк) и пытаюсь предотвратить нехватку памяти, но не знаком с Python и путаюсь с различными мнениями относительно execute (), итератора курсора и fetchone ()

Правильно ли я предположить, что cursor.execute () не загружает все данные в память, и только когда я вызываю fetchone (), он загружает 1 строку данных

from mysql.connector import MySQLConnection


def query():
    conn = MySQLConnection(host=conf['host'],
                                conf['port'],
                                conf['user'],
                                conf['password'],
                                conf['database'])
    cursor = conn.cursor(buffered=True)
    cursor.execute('SELECT * FROM TABLE') # 10 million rows

делает этот курсор итератор делает то же самое с fetchone ()?

for row in cursor:
    print(row)

Безопасен ли мой фрагмент кода для обработки 10 миллионов строк данных? если нет, как я могу безопасно перебрать данные без нехватки памяти?

Ответы [ 2 ]

2 голосов
/ 08 апреля 2020

Мое первое предложение - использовать from mysql.connector import connect, который по умолчанию будет использовать расширение C (CMySQLConnection) вместо from mysql.connector import MySQLConnection (чистый Python).

Если вы по какой-то причине, если вы хотите использовать чистую версию Python, вы можете передать use_pure=True в connect()

Второе предложение состоит в том, чтобы разбить результаты на страницы, если вы используете буферизованный курсор, он извлечет всю набор результатов с сервера. Я не знаю, хотите ли вы этого с 10M строками.

Вот некоторые ссылки:

https://dev.mysql.com/doc/refman/8.0/en/limit-optimization.html

https://dev.mysql.com/doc/connector-python/en/connector-python-api-mysqlcursorbuffered.html

1 голос
/ 08 апреля 2020

Взято из MySQL документации :

Метод fetchone () используется fetchall () и fetchmany (). Он также используется, когда курсор используется в качестве итератора.

В следующем примере показаны два эквивалентных способа обработки результата запроса. Первый использует fetchone () некоторое время l oop, второй использует курсор в качестве итератора:

# Using a while loop
cursor.execute("SELECT * FROM employees")
row = cursor.fetchone()
while row is not None:
  print(row)
  row = cursor.fetchone()

# Using the cursor as iterator
cursor.execute("SELECT * FROM employees")
for row in cursor:
  print(row)

Также указано, что:

Вы должны получить все строки для текущего запроса перед выполнением новых операторов с использованием того же соединения.

Если вас беспокоят проблемы с производительностью, вы должны использовать fetchmany(n) через некоторое время l oop, пока не получите все результаты вот так:

'An iterator that uses fetchmany to keep memory usage down'
    while True:
        results = cursor.fetchmany(arraysize)
        if not results:
            break
        for result in results:
            yield result

Это поведение соответствует PEP249 , в котором описывается, как и какие методы должны реализовывать соединители БД. Частичный ответ дан в этом потоке .

В основном реализация fetchall vs fetchmany vs fetchone будет зависеть от разработчиков библиотеки в зависимости от возможностей базы данных, но это будет иметь смысл в случае fetchmany и fetchone, что невыполненные / оставшиеся результаты будут сохраняться на стороне сервера, пока не будет запрошен другой вызов или уничтожение объекта курсора.

Итак, в заключение я думаю, что безопасно Предположим, что вызов метода execute в этом случае (mysqldb) не выводит все данные из запроса в память.

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