У меня очень большой Pandas Dataframe ~9 миллионов записей, 56 столбцов, который я пытаюсь загрузить в таблицу MSSQL, используя Dataframe.to_sql(). Импорт всего Dataframe в одном операторе часто приводит к ошибкам, связанным с памятью.
Чтобы справиться с этим, я просматриваю Dataframe пакетами по 100 тысяч строк и импортирую пакет за раз. Таким образом, я больше не получаю никаких ошибок, но код резко замедляется примерно после 5,8 миллиона записей. Код, который я использую:
maxrow = df.shape[0]
stepsize = 100000
for i in range(0, maxrow, stepsize):
batchstart = datetime.datetime.now()
if i == 0:
if_exists = 'replace'
else:
if_exists = 'append'
df_import = df.iloc[i:i+stepsize]
df_import.to_sql('tablename',
engine,
schema='tmp',
if_exists=if_exists,
index=False,
dtype=dtypes
)
Я рассчитал время выполнения партий и обнаружил явный переломный момент в скорости: Эти результаты в основном одинаковы для пакетов по 50 000, 100 000 и 200 000 строк. загрузка 6 миллионов записей занимает около 40 минут, а загрузка следующих 3 миллионов записей — еще 2 часа 20 минут.
Я думал, что это произошло либо из-за размера таблицы MSSQL, либо из-за того, что что-то кэшировалось/сохранялось после каждой загрузки. Из-за этого я попытался поместить Dataframe в две разные таблицы. Я также пробовал что-то вроде expunge_all() на SQLALchemy session после каждой загрузки. И то и другое безрезультатно.
Остановка импорта вручную после 5 миллионов записей и перезапуск с 5 миллионов с новым объектом движка также не помогли.
У меня нет идей, что может быть причиной столь резкого замедления процесса, и я был бы очень признателен за помощь.
ОБНОВЛЯТЬ
В крайнем случае я изменил цикл, загрузив части Dataframe, начиная с самого высокого индекса, и заканчивая циклом вниз.
Это фактически изменило время на партию. Кажется, что сами данные отличаются/больше в дальнейшем в Dataframe. Не перегрузка соединения или слишком большая таблица SQL.
Спасибо всем, кто пытается помочь, но, похоже, мне нужно просмотреть данные, чтобы понять, что является причиной этого.
можете ли вы добавить некоторые подробности о SQL Server? (выберите @@version, это локальная версия, база данных Azure sql и т. д.) Схема целевой таблицы, включая все определения индексов. при разных запусках вы удаляете/перезагружаете одну и ту же таблицу или разные таблицы?
Каковы типы данных в DataFrame и каковы типы данных столбцов, которые он создает в целевой таблице SQL Server? Худшим результатом будет создание столбцов varchar(max), nvarchar(max) или varbinary(max) в таблице из-за дополнительных накладных расходов на управление хранилищем LOB (Large OBject).
@GordThompson: Да, это было первое, что я попробовал, прежде чем создать свой собственный цикл. Однако в большинстве случаев это не удавалось из-за ошибок памяти или отключения моего VPN-соединения (которое я использую для подключения к серверу MSSQL). Один раз это сработало, но на это ушло почти семь часов.
@AlwaysLearning Создает довольно много столбцов varchar(max). Я явно указал ему типы данных SQLAlchemy и не добавил ограничений на длину столбцов, максимальная длина которых мне неизвестна.
@StrayCatDBA Я думаю, что это локальный сервер, к которому я могу подключиться после активации VPN-соединения. @@version дает мне Microsoft SQL Server 2019 (RTM-CU22-GDR) (KB5029378) - 15.0.4326.1 (X64) . В таблице нет определений индексов. Первая итерация цикла заменяет существующую таблицу и выполняется после этого добавления к вновь созданной таблице.
@AlwaysLearning: Теперь я попробовал ограничить размер каждого столбца varchar, задав для него максимальный размер. Это не повлияло на время, необходимое для каждой партии.






Я нашел решение, которое может быть полезно для тех, кто хочет ускорить медленную Dataframe.to_sql() операцию, и уже пробовал такие вещи, как chunksizes и настройку SQLALchemy соединения с fast_executemany=True.
Не знаю, какой механизм вызывает такое изменение производительности, но у меня это сработало, и я больше нигде не упоминал об этом. Поэтому я надеюсь, что это поможет кому-то еще.
Сортировка исходного Dataframe с помощью DataFrame.sort_values() сократила необходимое время примерно на 2/3.
Однако кажется очень важным, по каким столбцам сортируются значения. Скорость оптимальна, когда Dataframe сортируется по уникальному столбцу и действует как вложенная/кластеризованная сортировка. Сортировка по этому столбцу неявно сортирует примерно по 20 столбцам.
Это сортировка адресов, которая также сортирует улицу, почтовый индекс, город, регион и т. д. Это также означает, что многие строки в пакете имеют много общих значений внутри строк. Когда Dataframe сортируется по другому столбцу, который не имеет связи с другими столбцами, это на самом деле существенно замедляет работу Dataframe.to_sql().
Возможно, это связано: github.com/mkleehammer/pyodbc/issues/741
@GordThompson Я еще не сталкивался с этой проблемой, но кажется похожей. Я использую pyodbc версию 5.0.1. Как вы думаете, обновление до версии 5.1 устранило бы проблему автоматически?
AFAIK, нет, проблема остается в последней версии pyodbc.
Пробовали ли вы просто использовать аргумент
chunksize=для.to_sql(), чтобы увидеть, имеет ли это какое-либо значение?