powered by simpleCommunicator - 2.0.61     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / PostgreSQL [игнор отключен] [закрыт для гостей] / количество повторений влияет на скорость выполнения?!
24 сообщений из 24, страница 1 из 1
количество повторений влияет на скорость выполнения?!
    #38532268
aceton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Один пользователь.
Есть анонимная функция на plpgsql (читай, дело происходит внутри одной транзакции).
Задача - перелить данные из одной базы (другого сервера) в другую.
Внутри цикл по строкам выборки на 10000 строк (т.е. за один раз зальется 10000 строк).
В процессе выполнения скорость уменьшается. При повторе этого кода всё повторяется - сначала быстро(! старые данные остались !), потом всё медленнее и медленнее.
Как так?

Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
for _r in 
			select *
			from dblink(_cs,  /* здесь быстро */
			$$
				select id, ext_id, deal_id, create_moment, amount, statement_id
				from main.deferred_transaction 
				where statement_id is not null and id > $$ || _last_trn_id::text || $$
				order by id
				limit 10000
			$$
			) as trn(id int, ext_id text, deal_id int, create_moment timestamp, amount numeric, statement_id text)
	loop
	_ma := clock_timestamp();
	perform platbox.transaction_insert(_r.id, _r.ext_id, _r.deal_id, _r.create_moment, _r.amount);
	_mb := clock_timestamp();
	perform platbox.transaction_confirm(ARRAY[_r.id], _r.statement_id);
	_i := _i + 1;
	_da := _da + (_mb - _ma);
	_db := _db + (clock_timestamp() - _mb);

	if _i % 1000 = 0 then
		raise notice '%: % (% + %)', _i, _da + _db, _da, _db;
		_da := 0; _db := 0;
		end if;
		
	end loop;
/*
первая партия 10000:
NOTICE:  start 12:39:34.369506
NOTICE:  1000: 00:00:02.846582 (00:00:02.185875 + 00:00:00.660707)
NOTICE:  2000: 00:00:04.500944 (00:00:03.475017 + 00:00:01.025927)
NOTICE:  3000: 00:00:06.143909 (00:00:04.754663 + 00:00:01.389246)
NOTICE:  4000: 00:00:07.799562 (00:00:06.039108 + 00:00:01.760454)
NOTICE:  5000: 00:00:09.518673 (00:00:07.373768 + 00:00:02.144905)
NOTICE:  6000: 00:00:11.664035 (00:00:09.073877 + 00:00:02.590158)
NOTICE:  7000: 00:00:13.533564 (00:00:10.485787 + 00:00:03.047777)
NOTICE:  8000: 00:00:15.483501 (00:00:11.951818 + 00:00:03.531683)
NOTICE:  9000: 00:00:17.455596 (00:00:13.453719 + 00:00:04.001877)
NOTICE:  10000: 00:00:19.399203 (00:00:14.943903 + 00:00:04.4553)
NOTICE:  10000, finish 12:41:22.902658
Запрос успешно выполнен без возвращаемых данных за 108939 мс.

вторая партия 10000:
NOTICE:  start 12:41:44.797815
NOTICE:  1000: 00:00:06.803871 (00:00:03.328762 + 00:00:03.475109)
NOTICE:  2000: 00:00:08.596835 (00:00:04.720047 + 00:00:03.876788)
NOTICE:  3000: 00:00:07.686614 (00:00:05.962124 + 00:00:01.72449)
NOTICE:  4000: 00:00:07.666654 (00:00:07.02673 + 00:00:00.639924)
NOTICE:  5000: 00:00:08.905079 (00:00:08.215187 + 00:00:00.689892)
NOTICE:  6000: 00:00:10.162505 (00:00:09.412222 + 00:00:00.750283)
NOTICE:  7000: 00:00:11.417373 (00:00:10.60263 + 00:00:00.814743)
NOTICE:  8000: 00:00:12.674767 (00:00:11.793804 + 00:00:00.880963)
NOTICE:  9000: 00:00:14.017387 (00:00:13.055482 + 00:00:00.961905)
NOTICE:  10000: 00:00:15.357751 (00:00:14.318372 + 00:00:01.039379)
NOTICE:  10000, finish 12:43:30.04386
Запрос успешно выполнен без возвращаемых данных за 105650 мс.

третья партия 10000:
NOTICE:  start 12:44:38.612634
NOTICE:  1000: 00:00:02.700358 (00:00:02.182794 + 00:00:00.517564)
NOTICE:  2000: 00:00:04.320822 (00:00:03.764731 + 00:00:00.556091)
NOTICE:  3000: 00:00:05.972332 (00:00:05.366343 + 00:00:00.605989)
NOTICE:  4000: 00:00:07.623809 (00:00:06.97151 + 00:00:00.652299)
NOTICE:  5000: 00:00:09.230104 (00:00:08.539776 + 00:00:00.690328)
NOTICE:  6000: 00:00:10.847671 (00:00:10.116547 + 00:00:00.731124)
NOTICE:  7000: 00:00:12.545055 (00:00:11.74776 + 00:00:00.797295)
NOTICE:  8000: 00:00:14.328765 (00:00:13.45609 + 00:00:00.872675)
NOTICE:  9000: 00:00:16.194939 (00:00:15.244556 + 00:00:00.950383)
NOTICE:  10000: 00:00:18.077916 (00:00:17.044863 + 00:00:01.033053)
NOTICE:  10000, finish 12:46:20.754079
Запрос успешно выполнен без возвращаемых данных за 102570 мс.

(выполняем VACUUM ANALYZE)

и снова...
NOTICE:  start 12:57:16.7977
NOTICE:  1000: 00:00:02.776203 (00:00:02.264289 + 00:00:00.511914)
NOTICE:  2000: 00:00:04.461631 (00:00:03.902963 + 00:00:00.558668)
NOTICE:  3000: 00:00:06.15496 (00:00:05.549169 + 00:00:00.605791)
NOTICE:  4000: 00:00:07.842916 (00:00:07.191169 + 00:00:00.651747)
NOTICE:  5000: 00:00:09.485969 (00:00:08.782869 + 00:00:00.7031)
NOTICE:  6000: 00:00:11.201791 (00:00:10.436283 + 00:00:00.765508)
NOTICE:  7000: 00:00:12.913846 (00:00:12.089559 + 00:00:00.824287)
NOTICE:  8000: 00:00:14.71476 (00:00:13.814709 + 00:00:00.900051)
NOTICE:  9000: 00:00:16.512243 (00:00:15.53318 + 00:00:00.979063)
NOTICE:  10000: 00:00:18.417055 (00:00:17.351377 + 00:00:01.065678)
NOTICE:  10000, finish 12:59:01.516679
Запрос успешно выполнен без возвращаемых данных за 105114 мс.

*/
...
Рейтинг: 0 / 0
количество повторений влияет на скорость выполнения?!
    #38532324
tadmin
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
acetonВ процессе выполнения скорость уменьшается.
Перепишите цикл на pgScript и делайте commit после каждой вставки на 10К.
У вас цена изоляции одной транзакции растет.
...
Рейтинг: 0 / 0
количество повторений влияет на скорость выполнения?!
    #38532522
aceton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
tadmin,

можно для общего развития вводную про "цену изоляции" для этого конкретного примера?
Так сказать, физику процесса (с постгресом относительно недавно занимаюсь).
...
Рейтинг: 0 / 0
количество повторений влияет на скорость выполнения?!
    #38532627
Фотография Misha Tyurin
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
aceton,

Не видно, что вы делаете. Покажите таблицы и как вы сохраняете
...
Рейтинг: 0 / 0
количество повторений влияет на скорость выполнения?!
    #38532636
Фотография Maxim Boguk
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
aceton,

Или покажите код
perform platbox.transaction_insert(_r.id, _r.ext_id, _r.deal_id, _r.create_moment, _r.amount);


или для интереса замените эти хранимки на тестовые версии которые делают ТОЛЬКО 1 insert в какую то таблицу (без индексов и FK триггеров и прочего)... и перетестируйте...
а далее сами думайте что в ваших хранимках вызывает такое поведение...

лично я бы предположил что у вас там ловля исключений в хранимках (begin/exception/end)... а это не то что в цикле стоит делать...
...
Рейтинг: 0 / 0
количество повторений влияет на скорость выполнения?!
    #38532739
aceton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Misha Tyurin,

transaction_insert() вызывает срабатывание триггера, который ведет сводные таблицы (код не слишком маленький), но(!) transaction_confirm() всего лишь прописывает значения пары полей в уже существующей записи без каких-либо доп. действий. При этом картина при выполнении обеих функций абсолютно идентична. Я сначала пытался решить проблему на irc://irc.freenode.net/postgresql. Анализ знатоками скрипта со всеми задействованными таблицами (с индексами и триггерами) и функциями криминала не выявил.
Абстрактный взгляд на вещи (с учетом единственного подключения и не упрощающихся стартовых условий для каждого повторения) указывает на какой-то ньюанс с долгой транзакцией, но мои знания работы версионника не дают ответа.

PS ловли исключений нет
...
Рейтинг: 0 / 0
количество повторений влияет на скорость выполнения?!
    #38532812
tadmin
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
acetonТак сказать, физику процесса (с постгресом относительно недавно занимаюсь).
Каждая незавершенная вставка приводит появлению записей с ID транзакции, которые не должны быть видны прочим.
Для обновления (см. ваши агрегаты) появляется минимум две записи. Если цикл повторяется N раз, то весьма вероятно появление N версий агрегатов. Соотвественно, пухнут таблицы и индексы.

Пока транзакция не завершится, не произойдет очистка от мертвых записей и обновление статистики.
Чем дальше, тем больше начинает ошибаться планировщик. Если есть параллельная нагрузка на чтение, то там будут сопутствующие проблемы.

Когда транзакция завершается, весь этот массив из N версий схлопыватся: мертвые записи оперативно выпиливаются, статистика исправляется и т.д.

Наличие у вас триггеров усугубляет проблемы. Они срабатывают на каждую из 10 0000 записей по отдельности.
Не зная вашего приложения трудно давать советы, но отказ от триггеров и пересчет в ХП должны дать хорошее ускорение.

Еще, мне кажется, что редкий цикл for loop in select func..... from table нельзя заменить на perforn func .... from table.
...
Рейтинг: 0 / 0
количество повторений влияет на скорость выполнения?!
    #38532814
Гость_0
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
aceton, какая у Вас версия сервера и операционной системы?
...
Рейтинг: 0 / 0
количество повторений влияет на скорость выполнения?!
    #38532872
aceton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
tadmin,

откуда же "появление N версий", если транзакция одна, и подключение одно? Казалось бы, сводные данные должны обновляться в рамках одной версии и не плодить новых. Или я не понял, о каких агрегатах речь.
Статистика не должна влиять при повторе эксперимента, в результате которого изменения таблиц уже относительно малы (на существующих 500000 очередные 10000 роли не сыграют).
По поводу perform func .... from table:

delete from main.transaction
where id in (select id from main.transaction limit <NNN>)

<NNN>:
10 строк изменено за 42 мс.
100 строк изменено за 91 мс.
200 строк изменено за 212 мс.
500 строк изменено за 596 мс.
1000 строк изменено за 1615 мс.
2000 строк изменено за 5225 мс.
3000 строк изменено за 9854 мс.

Та же картина.

Гость_0,
pgsql 9.3, SMP Debian 3.2.51-1 x86_64
...
Рейтинг: 0 / 0
количество повторений влияет на скорость выполнения?!
    #38533029
Фотография Maxim Boguk
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
aceton,

я бы попробовал включить
set track_functions to 'all';

далее
begin;
запуск вашей функции...
и далее изучение содержимого
select * from pg_stat_xact_user_functions ;

чтобы понять где тормозит...

возможно сделав батч на 1000
и батч на 10000

а далее сравнив
pg_stat_xact_user_functions
и вполне возможно найдется блок который начинает тормозить и тогда с ним можно будет работать системно...


PS: у вас там нигде нет constraints в режиме deferrable intially deferred ?
...
Рейтинг: 0 / 0
количество повторений влияет на скорость выполнения?!
    #38533089
Фотография Ёш
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
acetonГость_0,
pgsql 9.3Третья цифра какая?
...
Рейтинг: 0 / 0
количество повторений влияет на скорость выполнения?!
    #38533094
Фотография Misha Tyurin
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
aceton,

авторtransaction_insert() вызывает срабатывание триггера, который ведет сводные таблицы (код не слишком маленький)

так как более ничего не известно, думаю, что дело тут. что-то растет и фк/пк деградируют. или еще что-то подобное.
...
Рейтинг: 0 / 0
количество повторений влияет на скорость выполнения?!
    #38533096
Фотография Misha Tyurin
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
сводные таблицы хорошо вести асинхронно
...
Рейтинг: 0 / 0
количество повторений влияет на скорость выполнения?!
    #38533103
Фотография Misha Tyurin
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
aceton,

и да

Maxim Boguk PS: у вас там нигде нет constraints в режиме deferrable intially deferred ?
...
Рейтинг: 0 / 0
количество повторений влияет на скорость выполнения?!
    #38533121
aceton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Misha Tyurin,
deferrable есть, intially deferred - нет

Ёш,
9.3.2
...
Рейтинг: 0 / 0
количество повторений влияет на скорость выполнения?!
    #38533241
aceton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Maxim Boguk,

спасибо, воспользовался Вашим рецептом. Тормозные участки выделил, но суть пока не ясна.
Не знаю, странный ли это вопрос.. для поиска только что вставленных записей в незавершенной транзакции используется индекс?
Или такие записи индексируются по факту завершения транзакции?
...
Рейтинг: 0 / 0
количество повторений влияет на скорость выполнения?!
    #38533321
Фотография Maxim Boguk
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
acetonMaxim Boguk,

спасибо, воспользовался Вашим рецептом. Тормозные участки выделил, но суть пока не ясна.
Не знаю, странный ли это вопрос.. для поиска только что вставленных записей в незавершенной транзакции используется индекс?
Или такие записи индексируются по факту завершения транзакции?

тут есть много тонкостей... вставьте analyze вашей таблицы в вашу функцию который бы вызывался после каждой 1000 записей добавленных... (если поможет я обьясню в чем беда была)
...
Рейтинг: 0 / 0
количество повторений влияет на скорость выполнения?!
    #38533436
aceton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Maxim Boguk,

нет, не помогает. В процессе используется нежурналируемая таблица для доступа statement-level триггера к задействованным в операции записям, но там проскакивает по 3 записи, и они сразу удаляются. Может быть в нежурналируемых таблицах есть какая-то магия? :)
...
Рейтинг: 0 / 0
количество повторений влияет на скорость выполнения?!
    #38533529
tadmin
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
acetontadmin,
откуда же "появление N версий", если транзакция одна, и подключение одно? Казалось бы, сводные данные должны обновляться в рамках одной версии и не плодить новых. Или я не понял, о каких агрегатах речь.
Статистика не должна влиять при повторе эксперимента, в результате которого изменения таблиц уже относительно малы (на существующих 500000 очередные 10000 роли не сыграют).

Я предположил, что ваш триггер вычисляет агрегаты. Это так?

Вставляем 10 000 записей - 10 000 срабатываний триггера - если триггер агрегирует значения, скажем, по 100 записям, то значение агрегата за время этой вставки будет изменено 100 раз. Получаем 99 мертвых версий записи агрегата + 1 живая. Если бы агрегат вычислялся один раз после вставки всех 10 000, получилось бы две версии - одна живая, одна мертвая.

acetonПо поводу perform func .... from table:

delete from main.transaction
where id in (select id from main.transaction limit <NNN>)
А тут надо планы смотреть. Возможно, что limit <много> дороже, чем вообще без лимит.
...
Рейтинг: 0 / 0
количество повторений влияет на скорость выполнения?!
    #38533566
aceton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
tadmin,

в триггере происходит агрегирование, и одни и те же немногочисленные записи в таблице с остатками обновляются большое количество раз. Но откуда 99 мертвых версий? Разве не одна версия, соответствующая текущей транзакции, будет менять свое значение?
...
Рейтинг: 0 / 0
количество повторений влияет на скорость выполнения?!
    #38533637
tadmin
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
acetontadmin,
в триггере происходит агрегирование, и одни и те же немногочисленные записи в таблице с остатками обновляются большое количество раз. Но откуда 99 мертвых версий? Разве не одна версия, соответствующая текущей транзакции, будет менять свое значение?
Может быть вы и правы, что одна версия на всю транзакцию.
Но мне кажется, что будут выделяться новые, если не сработает HOT (heap only tuples). В противном случае индекс, как минимум, будет пухнуть. Однажды это обсуждали, но найти не могу.

Даже если мое предположение ошибочное, 100х обновление стоит много дороже однократного. Не забудьте, что оно вызывается 10К раз, а локализация для агрегата может быть хуже чем х100.

Мое предположение не трудно проверить, если выключить триггеры и обновлять агрегаты plain запросом без всяких циклов, один раз на вставку 10К.
...
Рейтинг: 0 / 0
количество повторений влияет на скорость выполнения?!
    #38533791
aceton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
tadmin,

спору нет - одно обновление гораздо лучше тысячи.
Здесь transaction_insert() вставляет документ, делает проводки, триггеры на которые обновляют остатки по задействованным счетам (помимо разных проверок), поэтому завернуть цикл вставки таких документов в простой запрос, приводящий к одному изменению вместо множественных, не получится (разные счета, даты, условия). А проводок по каждому документу несколько. Избыточная работа по агрегированию итогов - печальное следствие недоступности в statement-триггере задействованных в операции записей. В эквивалентном функционале на ms sql server любое изменение таблицы проводок приводит к однократному выполнению триггера, и одним быстрым запросом обновляются остатки и обороты по всем счетам в любом дне из задействованных. Мера вынужденная - выбор средств для реализации такого поведения мал.
HOT сработать должен, т.к. в таблице со сводной информацией по счетам (несколько десятков записей) эти самые многократные обновления затрагивают только неиндексированные поля.
Строго говоря, при штатной работе проблема никак себя не проявляет. Выкрутиться можно (по Вашей рекомендации открыл для себя pgScript), однако разобраться в причинах было бы интересно и полезно.
Завтра попробую поэкспериментировать с временными таблицами. Unlogged-таблицы были реализованы заметно позже, и, быть может, описываемый эффект пропадет. Просто unlogged-таблица идеально вписалась в задачу: заполнить данными внутри транзакции, использовать их в расчетах и в итоге удалить (минус оверхед на создание временной таблицы). А с временной таблицей придется городить еще один триггер before all для ее создания. Пока думается, что выигрыша такой вариант не даст, а в штатном режиме окажется медленнее.
...
Рейтинг: 0 / 0
количество повторений влияет на скорость выполнения?!
    #38533867
Фотография Maxim Boguk
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
acetontadmin,

спору нет - одно обновление гораздо лучше тысячи.
Здесь transaction_insert() вставляет документ, делает проводки, триггеры на которые обновляют остатки по задействованным счетам (помимо разных проверок), поэтому завернуть цикл вставки таких документов в простой запрос, приводящий к одному изменению вместо множественных, не получится (разные счета, даты, условия). А проводок по каждому документу несколько. Избыточная работа по агрегированию итогов - печальное следствие недоступности в statement-триггере задействованных в операции записей. В эквивалентном функционале на ms sql server любое изменение таблицы проводок приводит к однократному выполнению триггера, и одним быстрым запросом обновляются остатки и обороты по всем счетам в любом дне из задействованных. Мера вынужденная - выбор средств для реализации такого поведения мал.
HOT сработать должен, т.к. в таблице со сводной информацией по счетам (несколько десятков записей) эти самые многократные обновления затрагивают только неиндексированные поля.
Строго говоря, при штатной работе проблема никак себя не проявляет. Выкрутиться можно (по Вашей рекомендации открыл для себя pgScript), однако разобраться в причинах было бы интересно и полезно.
Завтра попробую поэкспериментировать с временными таблицами. Unlogged-таблицы были реализованы заметно позже, и, быть может, описываемый эффект пропадет. Просто unlogged-таблица идеально вписалась в задачу: заполнить данными внутри транзакции, использовать их в расчетах и в итоге удалить (минус оверхед на создание временной таблицы). А с временной таблицей придется городить еще один триггер before all для ее создания. Пока думается, что выигрыша такой вариант не даст, а в штатном режиме окажется медленнее.

Даже если сработает HOT у вас всеравно при каждом обновлени в цепочке HOT будет фиксироваться новая версия строки... т.е. 100 обновлений одной и той же записи в транзакции создадут всеравно вам 100 версий записи (только плюс что индексы не будут затронуты). Поэтому циклическое/многократное обновление одной и той же записи внутри хранимки - будет замедлятся по мере работы (и с этим вы ничего не сделаете). HOT помогает избежать изменения индексов на каждой операции но совершенно не спасает от появления новой версии строки на каждом update.
Так что совет переливать батчами по 100-1000 записей и коммитится после каждого батча... (а заодно возможно принудительно вызывать vacuum analyze нужных таблиц в промежутках). Скорее всего так будет быстрее...
...
Рейтинг: 0 / 0
количество повторений влияет на скорость выполнения?!
    #38533973
aceton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Maxim Boguk,

похоже, всё так и есть. Простой эксперимент:
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
do
$$
declare _i int;
begin

	create temporary table hot_test(num int, amount numeric) on commit drop;
	
	insert into hot_test values (1, 1234), (2, 2345), (3, 3456), (4, 4567);
	for _i in 1..1000 loop
		update hot_test set num = num, amount = amount;
		end loop;
	
end
$$
/*
1000 - 102 ms
5000 - 1778 ms
10000 - 6844 ms
*/


Буду учитывать эту особенность. Всем спасибо.
...
Рейтинг: 0 / 0
24 сообщений из 24, страница 1 из 1
Форумы / PostgreSQL [игнор отключен] [закрыт для гостей] / количество повторений влияет на скорость выполнения?!
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]