Deesy
			  Май 21, 2018 14:36:38
		 	 
			
				День добрый, господа хорошие. 
Ломаю голову. подскажите куда копать.
Суть вопроса: есть python 3.3.6, есть небольшой скрипт. суть скрипта - тянет выборку (датасет1 - только чтение) с MSSQL базы (скажем 20 млн строк). для каждой строки производит сравнение по непростым условиям и в итоге может создать на каждую запись первой выборки - по 2-5 записей (датасет2 - запись), суммирует полученные и сохраняет запись в датасет3 (запись) со значением суммы.
в одном потоке на машине с ксеоном потребляет всего 10-20% ЦПУ и работает достаточно долго (за 8-10 часов). Хотелось бы распараллелить. так как в основном винда то multiprocessor вроде получается только.
Подскажите пожалуйста куда копать. для сохранения в базе, делал батчи инсерта по 500-1000 записей. т.е. готов собирать 500-1000 строк, оборачивать в лист и отсылать воркеру. но тогда получается каждый раз на инициализации/убийстве воркеров время тратится. пусть бы у каждого воркера был бы свой курсор и тянул данные по 500-1000 строк с главного процесса.
помогите пожалуйста советом как организовать.
			
		 
		
			
			  doza_and
			  Май 21, 2018 20:26:06
		 	 
			
				Deesy
так как в основном винда то multiprocessor 
Никак с виндой не связано.
Deesy
всего 10-20% ЦПУ
Это при скольких доступных ядрах?
			
 
		 
		
			
			  Deesy
			  Май 22, 2018 12:41:17
		 	 
			
				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
			  Май 23, 2018 00:25:06
		 	 
			
				1 ядро на 10% ??? Ну тут распаралелливание не поможет вы ведь не по прозводительности CPU ограничены. Надо sql запрос оптимизировать и посмотреть как диск загружен.
А оптимизацию надо начинать с профилирования. 
https://docs.python.org/3/library/debug.html
			 
		
			
			  Deesy
			  Май 23, 2018 01:10:55
		 	 
			
				примерную хрень проводил отключив выемку данных с датасета1 и заменив на идентичные значения для списка.
увы по сиквелю я больше соображаю чем по питону. выборка с оптимизирована на максимум.
фетчу из датасета по 1 записи. ибо fetchall кмк не втянет 20 млн в память. может быть есть возможность фетчить буфером по 10к?
за линк спасибо, гляну.
			
		 
		
			
			  doza_and
			  Май 23, 2018 22:33:41
		 	 
			
				Deesy
фетчу из датасета по 1 записи. 
Что? не так точно не пойдет. Вы должны получить итератор, он не приводит к загрузке всего в память.
Попробуйте привести здесь код который вы используете (упрощенный варимант).
			
 
		 
		
			
			  Deesy
			  Май 23, 2018 22:41:43
		 	 
			
				в обычный день может быть 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
			  Май 24, 2018 23:12:44
		 	 
			
				Deesy
фетчу из датасета по 1 записи. ибо fetchall кмк не втянет 20 млн в память. может быть есть возможность фетчить буфером по 10к?
Для этого есть fetchmany.
doza_and
Вы должны получить итератор, он не приводит к загрузке всего в память.
Это то-же самое по скорости, что и fetchone использовать. 
			
 
		 
		
			
			  Rodegast
			  Май 25, 2018 11:26:57
		 	 
			
				> тянет выборку (датасет1 - только чтение) с MSSQL базы (скажем 20 млн строк).
Почему пользуйтесь MSSQL? Если есть возможность используй PostgreSQL он даёт возможность делать хранимки на python-е и твой код можно будет запускать прямо на сервере что намного быстрее.
> в одном потоке на машине с ксеоном потребляет всего 10-20% ЦПУ 
Ты упираешься в IO, по этому это нормально.
> Хотелось бы распараллелить.
Это делается при помощи модуля threading
			
		 
		
			
			  Deesy
			  Май 25, 2018 11:34:34
		 	 
			
				почему MSSQL - потому, что увы не мой выбор (мне оракл больше по-душе). по-тому же почему я не особо могу (надеюсь что пока) поставить python отличный от версии 3.3.4 и т.д. увы тут не мои капризы и сделать-что-то кроме как “вылизать” код на питоне я не могу.
threading юзал, прироста не было. попробую на днях fetchmany() если оно работает с MSSQL отпишусь.