read_sql() из MySQL работает очень медленно

#python #mysql #python-3.x #pandas #sqlalchemy

#python #mysql #python-3.x #панды #sqlalchemy

Вопрос:

Я использую MySQL с pandas и sqlalchemy. Однако он работает чрезвычайно медленно. Выполнение такого простого запроса, как этот, занимает более 11 минут для таблицы с 11 миллионами строк. Какие действия могли бы улучшить эту производительность? Упомянутая таблица не имеет первичного ключа и была проиндексирована только по одному столбцу.

 from sqlalchemy import create_engine
import pandas as pd
sql_engine_access = 'mysql pymysql://root:[password]@localhost')
sql_engine = create_engine(sql_engine_access, echo=False)
script = 'select * from my_database.my_table'
df = pd.read_sql(script, con=self.sql_engine)
 

Комментарии:

1. Вы извлекаете эти строки из облачного экземпляра или из-за относительно медленного сетевого подключения? Один миллион строк в минуту — это 16,7 тысячи строк в секунду, что не так уж и медленно (примерно половина скорости моего локального подключения к локальной сети).

2. все записи были извлечены с помощью команды выполнения sqlachemy из python. Для этого потребовалось более 12 часов (для извлечения около 11 миллионов строк и 58 столбцов). Сервер установлен на том же компьютере, на котором был запущен код python. Нет доступа к локальной сети или облаку. Этот компьютер имеет 32 ГБ памяти и процессор AMD с 12 ядрами, использующий MS Windows 10 pro.

3. Я также заметил, что диск интенсивно используется во время этих процессов

Ответ №1:

Вы можете попробовать наш инструмент connectorx ( pip install -U connectorx ). Он реализован в Rust и нацелен на повышение производительности pandas.read_sql . API в основном такой же, как у pandas . Например, в вашем случае код будет выглядеть так:

 import connectorx as cx
conn_url = "mysql://root:[password]@localhost:port/my_database"
query = "select * from my_table"
df = cx.read_sql(conn_url, query)
 

Если в результате вашего запроса числовой столбец равномерно распределен, как ID, вы также можете еще больше ускорить процесс, используя несколько ядер, подобных этому:

 df = cx.read_sql(conn_url, query, partition_on="ID", partition_num=4)
 

Это разделило бы весь запрос на четыре небольших запроса путем фильтрации по ID столбцу и connectorx выполняло бы их параллельно. Вы можете проверить здесь для получения дополнительной информации об использовании и примерах.

Вот результат теста, загружающий 60 миллионов строк x 16 столбцов из MySQL в фрейм данных pandas с использованием 4 ядер: время mysql
память MySQL

Ответ №2:

Хотя, возможно, это не единственная причина низкой производительности, одним из факторов, способствующих этому, является то, что PyMySQL ( mysql pymysql:// ) может быть значительно медленнее, чем mysqlclient ( mysql mysqldb:// ) при больших нагрузках. В очень неформальном тесте (без многократных запусков, без усреднения, без перезапуска сервера) Я видел следующие результаты, используя df.read_sql_query() локальную базу данных MySQL:

извлеченные строки mysql mysqldb (секунды) mysql pymysql (секунды)
1_000_000 13.6 54.0
2_000_000 25.9 114.1
3_000_000 38.9 171.5
4_000_000 62.8 217.0
5_000_000 78.3 277.4

chart.png

Комментарии:

1. Спасибо, Томпсон, за ваш ответ и за улучшение моего вопроса. На самом деле, я изменил на «mysql mysqldb //», и мой запрос перешел к 4 mind вместо 11. Это намного лучше, хотя я считаю, что еще есть возможности для повышения производительности с помощью других действий.