Найти - Пользователи
Полная версия: Python 3 + параллелизация работы с базой данных
Начало » Python для экспертов » Python 3 + параллелизация работы с базой данных
1 2
Deesy
День добрый, господа хорошие.
Ломаю голову. подскажите куда копать.
Суть вопроса: есть python 3.3.6, есть небольшой скрипт. суть скрипта - тянет выборку (датасет1 - только чтение) с MSSQL базы (скажем 20 млн строк). для каждой строки производит сравнение по непростым условиям и в итоге может создать на каждую запись первой выборки - по 2-5 записей (датасет2 - запись), суммирует полученные и сохраняет запись в датасет3 (запись) со значением суммы.
в одном потоке на машине с ксеоном потребляет всего 10-20% ЦПУ и работает достаточно долго (за 8-10 часов). Хотелось бы распараллелить. так как в основном винда то multiprocessor вроде получается только.

Подскажите пожалуйста куда копать. для сохранения в базе, делал батчи инсерта по 500-1000 записей. т.е. готов собирать 500-1000 строк, оборачивать в лист и отсылать воркеру. но тогда получается каждый раз на инициализации/убийстве воркеров время тратится. пусть бы у каждого воркера был бы свой курсор и тянул данные по 500-1000 строк с главного процесса.

помогите пожалуйста советом как организовать.
doza_and
Deesy
так как в основном винда то multiprocessor
Никак с виндой не связано.
Deesy
всего 10-20% ЦПУ
Это при скольких доступных ядрах?
Deesy
doza_and
Никак с виндой не связано.
читал что в cpython multithreading работает по-другому из-за реализации GIL. но увы доступны только машины под виндой и только стандартный cpython. сейчас установлен 3.3.4 (пардон, не 3.3.6) но подал заявку на установку последнего 3.6.5 ибо читал что с (насколько я помню) 3.4.Х добавили асинкио. возможно будет полезным для скидывания данных в базу.

doza_and
Это при скольких доступных ядрах?
пардон не дописал. на моей рабочей машине стоит зион с 6-ю ядрами (дополнительно х2 хайпертрединг итого 12) но при работе используется только 1 ядро на 10-20%. но в целом планировалось потом скрипт запускать на отдельной машине.
doza_and
1 ядро на 10% ??? Ну тут распаралелливание не поможет вы ведь не по прозводительности CPU ограничены. Надо sql запрос оптимизировать и посмотреть как диск загружен.

А оптимизацию надо начинать с профилирования.
https://docs.python.org/3/library/debug.html
Deesy
примерную хрень проводил отключив выемку данных с датасета1 и заменив на идентичные значения для списка.
увы по сиквелю я больше соображаю чем по питону. выборка с оптимизирована на максимум.

фетчу из датасета по 1 записи. ибо fetchall кмк не втянет 20 млн в память. может быть есть возможность фетчить буфером по 10к?

за линк спасибо, гляну.
doza_and
Deesy
фетчу из датасета по 1 записи.
Что? не так точно не пойдет. Вы должны получить итератор, он не приводит к загрузке всего в память.

Попробуйте привести здесь код который вы используете (упрощенный варимант).
Deesy
в обычный день может быть 20млн записей. в необычный в 3-4 раза больше.

 def load_and_process_date(day):
    global process_start
    batch_total_started = time.perf_counter()
    batch_total = 0
    batch_current = 0
    row_list = []
    batch_id = 0
    with get_db_connection() as con:
        with get_db_cursor(con) as cur:
            sql = settings.sql_query['LOAD_ICF_DATA']
            sql = sql % day
            cur.execute(sql)
            process_start = time.perf_counter()
            column_names = [col[0] for col in cur.description]
            batch_current_started = time.perf_counter()
            while True:
                row = cur.fetchone()
                if not row:
                    break
                batch_total += 1
                add_txn_data_row(row, day)
                # new_row = dict(zip(column_names, row))
                # add_txn_data_row(new_row, day)
                if batch_current < settings.database['RowsInList']:
                    batch_current += 1
                if batch_current == settings.database['RowsInList']:
                    commit_inserts(day)
                    batch_id += 1
                    print("Avg/1M: {}, {}r/{:.4f} sec. TT: {:.4f} sec.".format(int(
                        (time.perf_counter() - batch_total_started) * 1000000 / 60 / batch_total), batch_total,
                        time.perf_counter() - batch_current_started, time.perf_counter() - batch_total_started))
                    batch_current = 0
                    batch_current_started = time.perf_counter()
    commit_inserts(day)
    return
Kudria

Deesy
фетчу из датасета по 1 записи. ибо fetchall кмк не втянет 20 млн в память. может быть есть возможность фетчить буфером по 10к?
Для этого есть fetchmany.

doza_and
Вы должны получить итератор, он не приводит к загрузке всего в память.
Это то-же самое по скорости, что и fetchone использовать.
Rodegast
> тянет выборку (датасет1 - только чтение) с MSSQL базы (скажем 20 млн строк).

Почему пользуйтесь MSSQL? Если есть возможность используй PostgreSQL он даёт возможность делать хранимки на python-е и твой код можно будет запускать прямо на сервере что намного быстрее.

> в одном потоке на машине с ксеоном потребляет всего 10-20% ЦПУ

Ты упираешься в IO, по этому это нормально.

> Хотелось бы распараллелить.

Это делается при помощи модуля threading
Deesy
почему MSSQL - потому, что увы не мой выбор (мне оракл больше по-душе). по-тому же почему я не особо могу (надеюсь что пока) поставить python отличный от версии 3.3.4 и т.д. увы тут не мои капризы и сделать-что-то кроме как “вылизать” код на питоне я не могу.

threading юзал, прироста не было. попробую на днях fetchmany() если оно работает с MSSQL отпишусь.
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB