Утечка памяти Python mysql при вставке

Я вставляю миллионы строк в MySQL с помощью Python3, но я обнаружил, что использование памяти продолжает расти и, наконец, достигло 64 ГБ. Я попытался диагностировать проблему, и вот воспроизведение проблемы: скажем, у меня есть 100 файлов CSV. Каждый файл содержит 50000 строк, и я хочу вставить их в базу данных. Вот пример кода:

import mysql.connector

insert_sql = ("INSERT INTO table (Value) VALUES (%s)")

for i in range(100):
    cnx = mysql.connector.connect(user='root', password='password', host='127.0.0.1', database='database')
    cursor = cnx.cursor()
    # Insert 50000 rows here
    for j in range(50000):
        cursor.execute(insert_sql, (j,))
    cnx.commit()
    cursor.close()
    cnx.close()
    print('Finished processing one file')

print('All done')

В базе данных всего 1 таблица с 2 столбцами:

CREATE TABLE `table` (
  `Id` int(11) NOT NULL AUTO_INCREMENT,
  `Value` int(11) NOT NULL,
  PRIMARY KEY (`Id`)
)

Окружающая среда: Mac OS Sierra; Python 3.6.x; MySQL 8.0.1; MySQL-коннектор-Python 8.0.11

Я понимаю, что перед фиксацией объем памяти должен увеличиться, потому что изменения буферизуются. Но я предполагал, что после фиксации она уменьшится. Однако это не. Поскольку в моем реальном приложении у меня есть тысячи файлов по 100 МБ каждый, моя память взорвется.

Я здесь что-то не так сделал? (Я новичок в базе данных) Как я могу контролировать использование памяти? Любое предложение будет оценено по достоинству!

Обновлено: я также пробовал следующий код в соответствии с комментариями и ответами, но он все еще не работает:

import mysql.connector    
insert_sql = ("INSERT INTO table (Value) VALUES (%s)")    
for i in range(100):
    cnx = mysql.connector.connect(user='root', password='password', host='127.0.0.1', database='database')
    cursor = cnx.cursor()
    params = [(j,) for j in range(50000)]
    # If I don't excute the following insertion, the memory is stable.
    cnx.executemany(insert_sql, params)
    cnx.commit()
    cursor.close()
    del cursor
    cnx.close()
    del cnx
    print('Finished processing one file')    
print('All done')

Вы пробовали executemany() вместо многих execute() в цикле?

Andrej Kesely 14.07.2018 18:54

Также попробуйте использовать ? вместо %s

RedEyed 14.07.2018 18:57

@AndrejKesely Да, я пытался объединить все параметры в один список и использовать executemany() для их вставки, но объем памяти все равно растет.

Ruoxi 15.07.2018 06:02

@RedEyed Спасибо, но я попробовал ?, тоже не работает.

Ruoxi 15.07.2018 06:08
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
2
4
601
1

Ответы 1

Попробуйте выполнить пакетное выполнение, проблема может быть в этом цикле вставок.

Вы можете сделать исполнение:

c.executemany("INSERT INTO table (Value) VALUES (%s)",
    [('a'),('b')])

или большой оператор вставки со всеми значениями, которые вы хотите одновременно.

Спасибо за быстрый ответ. Я попытался собрать все значения в один список и один раз использовать executemany() для файла, но это тоже не работает. После закрытия соединения рост памяти не прекращается.

Ruoxi 15.07.2018 06:12

Это проблема с объектом sql или со списком, который вы передали? Попробуйте сделать `del cnx

RedEyed 15.07.2018 12:24

@RedEyed Я думаю, что проблема в sql, потому что если я создаю список только из 50000 элементов, но не использую executemany(), использование памяти будет стабильным. Я также пробовал del cursor и del cnx после close(), но они все еще не работают.

Ruoxi 15.07.2018 16:25

Вроде есть ошибка

RedEyed 15.07.2018 19:57

@RedEyed Спасибо за ваш ответ. Я не думаю, что это связано с указанной проблемой, потому что я вставил целое число, а не строку Unicode. Но я только что сообщил об этой проблеме в MySQL, если это действительно ошибка.

Ruoxi 15.07.2018 21:35

Другие вопросы по теме