Search
Write a publication
Pull to refresh

Comments 28

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

Кажется на книге написана другая фамилия в графе автор.

враги подбросили не поверю, пока не прочту в очередном Постгрессо :)

да вот же первоисточник https://postgrespro.ru/education/books/sqlprimer

А это дипфейк: :)

Скандалы, интриги, расследования что дарили Моргунову?
Скандалы, интриги, расследования что дарили Моргунову?

дело о пропавшей производительности книге в PostgreSQL: руководство по поимке и обезвреживанию

Хорошо что удалось раскрыть очередной заговор. Виноватые будут награждены, а невиновные наказаны =)

За такую чудесную отсылку особенная благодарность :)

А если Роман Фролов невероятно опечален, что книгу Егора Рогова унёс с собой Евгений Моргунов (жуть как много имён в одном предложении), придётся ему продолжить традицию очно посещать конференции, чтобы у вселенной был шанс исправить сию несправедливость.

😘 Ждём вашу вторую статью на хабре про истории между строк кода 🙂

Роман хочет стать спикером, чтобы раздавать книгу самому. Для этого планирует скачать названия всех докладов с целью выявления мультимодальностей (как пишут в чате конференции)

Вселенная
Вселенная

Ну , а я в свою очередь, подожду когда на Хабре будет опубликована статья по докладу "Статистический анализ результатов бенчмарков"

Очень интересно будет сравнить комментарии с комментариями к статьям на аналогичную тему , в прошлом году.

думаете постгрес заиграет в кости 🙂

Нет. Просто удивительно - как такая простая идея - использовать математическую статистику для анализа производительности СУБД PostgreSQL так долго была абсолютно без внимания сообщества.

Просто любопытно посмотреть - как изменится реакция читателей Хабра, за прошедший год.

Всех комментаторов , я помню, все комментарии остались .

Реально интересно будет - какие комментарии получит новая работа по той же теме.

На конференции с вопросами было не очень, да вообще скучно, считай никак. Похоже никто из зрителей так и не понял о чем вообще речь какие картинки эти дети тут показывают.

Минутка конспирологии.

Вполне может быть, что столь юные создания были направлены на столь сложный и непонятный для сообщества доклад - специально.

Что бы маститые DBA не заклевали и не затроллили юношей и девушку ;-)

Поэтому и занимаю место в партере, жду реакции читателей хабра на статью по теме статистического анализа бенчмарка.

Был ещё доклад с 2 молодыми - там тоже была симпатичная девушка. В pgbench есть stddev для latency. Как вы думаете, почму не сделали доверительные интервалы для tps, их сложно считать?

доклад с 2 молодыми - там тоже была симпатичная девушка

Это именно тот доклад, который мне наиболее интересен :

Статистический анализ результатов бенчмарков
https://pgconf.ru/talk/2118738

Как вы думаете, почму не сделали доверительные интервалы для tps, их сложно считать?

Их не сложно считать, просто они не имеют особого смысла - какая польза от доверительных интервалов , если распределение не нормальное , да еще и мультимодальное ?

Я кстати поэтому , еще и жду публикации доклада - очень интересует вопрос - а почему решили , что при постоянной нагрузке при многократном выполнении одного запроса распределение результатов может быть не нормальное и даже мультимодальное (у меня была принципиально другая начальная гипотеза) ? На конференции , вопрос из онлайна не дошёл до докладчиков .

А к pgbench с tps вообще , лично у меня, большие претензии по причине использования среднего арифметического . По крайней мере , при анализе результатов нагрузочного тестирования , уже давно ( с прошлого года) результаты pgbench не смотрю вывод pgbench. Только в качестве нагрузки.

Как и значения mean_exec_time - не использую, потому, что были аномалии влияющие на результат анализа. Среднее арифметическое :-(

Их не сложно считать, просто они не имеют особого смысла - какая польза от доверительных интервалов , если распределение не нормальное , да еще и мультимодальное ?

при мультимодальности тест бессмысленен. Моды появляются обычно от автоанализа, реже автовакуума, контрольной точки. Нужен показатель, чтобы понять, что тест бесполезен, надо переделывать скрипт теста или искать причину. Параметром pgbench -P можно визуально смотреть есть ли колебания tps, но для ловли наличия "мод" надо интервал менять.

при мультимодальности тест бессмысленен. 

Хорошо , с этим соглашусь. Пока оставим в стороне самый главный вопрос - а зачем вообще нужен тест ?

надо переделывать скрипт теста или искать причину.

Что можно переделать ? Один запрос вызывается много раз . Что тут переделывать ? И главное как ?

А причина очень простая и давно известна - влияние инфраструктуры . Я к этому очень быстро пришел просто сравнив результаты тестов в рабочее время и ночью. Ночью в облаке нагрузка принципиально другая. А на физических серверах я не тестировал. Даже если бы и были результаты , в продуктивном облаке оно не имеют смысла .

Параметром pgbench -P можно визуально смотреть

Вы действительно собрались 4-5 часов теста смотреть визуально?

Про tps уже было указано - среднее арифметическое не устойчиво к выбросам. А выбросы в облаках это обычное дело . Можно запустить один тест на одинаковых виртуалках и получить разные данные .

Я , повторюсь, давно уже не использую tps и mean_exec_time по причине неробастности.

Но самый главный вопрос , на который я жду ответа - вот нашли параметры распределения при выполнении одного запроса с одной нагрузкой - какой практический смысл этих цифр ? Что дальше ?

Переделывать тест - менять набор и последовательность команд.
4 часа тест - долго. Хороший тест полминуты и результат ясен :)
Если в облаке не выделенныеый сервер, то тестировать сложно.
Вот возьмем доклад Жилина. Там в проблеме #1 приведен тест:
---- number1.sql ----
\set b_id random(1,100)
\set v_delta random(-10,10)
update tabs set cnt = cnt + :v_delta where id = :b_id;
----------------
pgbench -n -T 20 -c 10 -P 5 -f number1.sql
progress: 5.0 s, 7359.8 tps, lat 1.307 ms stddev 0.704, 0 failed
progress: 10.0 s, 7500.5 tps, lat 1.294 ms stddev 0.657, 0 failed
progress: 15.0 s, 7429.2 tps, lat 1.309 ms stddev 0.723, 0 failed
tps стабильны и без всякого распределения. Дальше создается параллельно транзакция, удерживающая снэпшот или горизонт и tps с первой секунды падают:
progress: 5.0 s, 6853.2 tps, lat 1.409 ms stddev 0.791, 0 failed
progress: 10.0 s, 6276.6 tps, lat 1.555 ms stddev 0.809, 0 failed
progress: 15.0 s, 5731.5 tps, lat 1.710 ms stddev 0.832, 0 failed
progress: 20.0 s, 5236.9 tps, lat 1.873 ms stddev 0.951, 0 failed
в докладе доходят до 1000. Полминуты и всё ясно - виноват индекс 🙂

Но самый главный вопрос , на который я жду ответа - вот нашли параметры распределения при выполнении одного запроса с одной нагрузкой - какой практический смысл этих цифр ? Что дальше ?

Если нашли распределение, то не знаю зачем оно им. Доклад сделать, изучить теорию. Обычное дело при обучении

Хороший тест полминуты и результат ясен

Это шутка или вы всерьез ?

Если в облаке не выделенныеый сервер, то тестировать сложно

В общем то , ничего сложного , после года работ и исследований , конечно

https://dzen.ru/a/Z7gmHgBGAHo5m2SS

Вот возьмем доклад Жилина

...

Полминуты и всё ясно.

Ясно что ? Какой вывод из данного 100% искусственного теста в тепличных условиях ?

Доклад сделать

Это причина соглашусь.

Обычное дело при обучении

Поскольку , я уже шел этим путем , я то знаю ответ .

Просто хотелось бы услышать от специалистов идущих тем же путем. Сверить направление так сказать.

Всерьёз. Если не ориетируются в проблеме, то тесты долгие, сложные, ковровые. Когда находят причину - создают тест из нескольких строк (не обязательно с pgbench), где сразу виден результат.

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

Выводы в докладе.Единственно, при добавлении столбца, индекса и подзапроса

tps становятся сразу в ~1,5 раза ниже и падают с такой же скоростью как исходный update (без лишнего столбца, индекса, подзапроса). А при добавлении advisory lock сразу в пять раз ниже, поэтому в докладе не приведены tps решений. Предполагаю, что докладчика смутили адепты блокировок, они почему-то любят advisory locks. Тест без них корректно работает.

Но это мелочи, главное то, что приведен как пишутся хорошие тесты, с которыми не надо часами что-то измерять, результат сразу виден.

Единственно, при добавлении столбца, индекса и подзапроса. tps становятся сразу в ~1,5 раза ниже и падают с такой же скоростью как исходный update (без лишнего столбца, индекса, подзапроса). А при добавлении advisory lock сразу в пять раз ниже, поэтому в докладе не приведены tps решений.

Как показала практика, утверждение неверное. Полезно сначала проверять, и только после проверки тиражировать мысль. Обращались к Михаилу за рецензией статьи? Или, быть может, ещё к кому-нибудь, кто хорошо разбирается и мог бы посмотреть почему ваши результаты отличаются от результатов Михаила. А так, получается, что вы зря ругаете рабочие решения.

использовать хинты - порочная практика 🙂 Расширений много, нужны стабильные и работающие во всех случаях. Патч Синаева поразил докладчика - специалиста в 1С. Патч очень красив мощью и минимализмом. Я больше скажу - патч не делает хуже, он делает только лучше, так как патч не удаляет ни строчки кода 😉 он перед древним вычислением добавляет своё. Если статистики для своего вычисления нет, то отрабатывает древняя формула. Патч даже ревьювить не надо, сразу принимать.

И, как обычно бывает, в таких случаях притянул "в оракле переставляет" (и там, конечно же, не переставляет)

Проверяйте, пожалуйста, факты при написании статьи. Ссылайтесь на документацию, подтверждающие ваши слова. Хотя бы в случаях, когда вы пишете, что кто-то "прав или не прав".

Документация Oracle напрямую говорит, что база данных строит хэш-таблицу по наименьшему входному множеству. В Oracle это работает и для inner join, и для outer join, и для anti join и для semi join. Хинтом SWAP_JOIN_INPUTS можно повлиять на то, по какому источнику будет строиться хэш-таблица.

The optimizer uses the smaller of two data sets to build a hash table on the join key in memory

Более того, ответ Фёдора про "реляционную алгебру" был явно мимо. Разумеется, реляционная алгебра ни коем образом не мешает реализовать два оба варианта hash join inner (именно про inner join говорилось в докладе!). В inner случае задача hash join полностью симметрична, и то, по какому из входов строить хэш-таблицу полностью отдаётся на откуп оптимизатору.

Может показаться, что в outer join уж точно невозможно переставить таблицы местами, но это не так. Ещё раз повторю, что OracleDB это давным-давно делает (см. HASH JOIN OUTER vs HASH JOIN RIGHT OUTER), да и из общих соображений понятно, что, скажем, left outer join можно реализовать двумя путями.

Например, для select c.id, o.id from clients c left outer join orders o on (c.id = o.client_id)

а) Хэшируем orders. Пробегаемся по таблице clients, и либо находим запись в хэш-таблице и выводим в результат данные (c.id, o.id), либо не находим запись в хэш-таблице и выводим только часть про clients (c.id, null)

a) Хэшируем clients. Идём по orders. Для каждой строки из второго источника либо находим запись в хэш-таблице (и тогда выводим её как результат c.id, o.id), либо не находим (и тогда игнорируем). В конце пробегаемся по тем записям хэш-таблицы, которые ни разу не использовались и дополняем их к результатам outer (c.id, null). Да, при таком подходе нужно запоминать какие записи в хэш-таблице мы встречались, а какие нет, но никакая реляционная алгебра не запрещает подобный подход. Более того, если таблица clients гораздо меньше orders, то это намного эффективнее, нежели строить хэш-таблицу по огромной таблице orders

Фёдор моментально ответил: планировщик не смог переставить порядок, наверняка было левое соединение

Вы неверно поняли. Моментальным ответом было "патч решал только замену nested loops на hash join для конкретного запроса, а уж какой там hash join получался Фёдор вообще не смотрел".

политкорректным вопросом "Когда в PGpro Enterprise появится патч?"

Вопрос "когда что-либо появится в PGpro Enterprise?" некорректен. Единственным ответом может быть лишь "обращайтесь в пресс-службу". Разработчики далеко не единственные, кто влияют на даты, поэтому вопросы про сроки в закрытых проектах почти лишены смысла.

Корректным был бы вопрос "а можно где-нибудь посмотреть на патч?"

Проверяйте, пожалуйста, факты при написании статьи. Ссылайтесь на документацию, подтверждающие ваши слова.

На OUTER JOIN таблица, по которой строится хэш, не меняется (при изменении порядка таблицы LEFT меняется на RIGHT):

select /*+ USE_HASH(D L) SWAP_JOIN_INPUTS(d) */ department_name, d.location_id, l.location_id, l.street_address
from departments d, locations l
where d.location_id = l.location_id (+)
/
SELECT * FROM table (dbms_xplan.display_cursor);

----------------------------------------------------------------------------------
| Id  | Operation          | Name        | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------

PLAN_TABLE_OUTPUT                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |             |       |       |     6 (100)|          |
|*  1 |  HASH JOIN OUTER   |             |    27 |  1053 |     6   (0)| 00:00:01 |
|   2 |   TABLE ACCESS FULL| DEPARTMENTS |    27 |   405 |     3   (0)| 00:00:01 |
|   3 |   TABLE ACCESS FULL| LOCATIONS   |    23 |   552 |     3   (0)| 00:00:01 |
----------------------------------------------------------------------------------

select /*+ USE_HASH(D L) SWAP_JOIN_INPUTS(l) */ department_name, d.location_id, l.location_id, l.street_address
from departments d, locations l
where d.location_id = l.location_id (+)
/
SELECT * FROM table (dbms_xplan.display_cursor);

-------------------------------------------------------------------------------------
| Id  | Operation             | Name        | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------

PLAN_TABLE_OUTPUT                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |             |       |       |     6 (100)|          |
|*  1 |  HASH JOIN RIGHT OUTER|             |    27 |  1053 |     6   (0)| 00:00:01 |
|   2 |   TABLE ACCESS FULL   | LOCATIONS   |    23 |   552 |     3   (0)| 00:00:01 |
|   3 |   TABLE ACCESS FULL   | DEPARTMENTS |    27 |   405 |     3   (0)| 00:00:01 |
-------------------------------------------------------------------------------------

Для INNER JOIN таблица, по которой строится хэш, может меняться:

Для INNER JOIN таблица по которой строится хэш может меняться:

select /*+ USE_HASH(D L) SWAP_JOIN_INPUTS(l) */ department_name, 
d.location_id, l.location_id, l.street_address from departments d, 
locations l where d.location_id = l.location_id
 
----------------------------------------------------------------------------------
| Id  | Operation          | Name        | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------

PLAN_TABLE_OUTPUT                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |             |       |       |     6 (100)|          |
|*  1 |  HASH JOIN         |             |    27 |  1053 |     6   (0)| 00:00:01 |
|   2 |   TABLE ACCESS FULL| LOCATIONS   |    23 |   552 |     3   (0)| 00:00:01 |
|   3 |   TABLE ACCESS FULL| DEPARTMENTS |    27 |   405 |     3   (0)| 00:00:01 |
----------------------------------------------------------------------------------


select /*+ USE_HASH(D L) SWAP_JOIN_INPUTS(d) */ department_name, 
d.location_id, l.location_id, l.street_address from departments d, 
locations l where d.location_id = l.location_id
 
----------------------------------------------------------------------------------
| Id  | Operation          | Name        | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------

PLAN_TABLE_OUTPUT                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |             |       |       |     6 (100)|          |
|*  1 |  HASH JOIN         |             |    27 |  1053 |     6   (0)| 00:00:01 |
|   2 |   TABLE ACCESS FULL| DEPARTMENTS |    27 |   405 |     3   (0)| 00:00:01 |
|   3 |   TABLE ACCESS FULL| LOCATIONS   |    23 |   552 |     3   (0)| 00:00:01 |
----------------------------------------------------------------------------------

Хэш строится по внешней таблице:

На OUTER JOIN таблица, по которой строится хэш, не меняется (при изменении порядка таблицы LEFT меняется на RIGHT):

Переформулируйте, пожалуйста. Я несколько раз прочитал и не понял что вы имеете ввиду. Если вы имеете ввиду, что я всё верно сказал, так и скажите. Если вы до сих пор думаете, что я неправ, то подумайте ещё раз. Я серьёзно.

Хинт SWAP_JOIN_INPUTS не влияет на результат, поэтому ваша фраза "LEFT меняется на RIGHT" крайне странная. Разумеется, результат запроса сохраняется, и как было в вашем случае departments d left outer join locations , так и остаётся. А таблица, по которой строится хэш как раз меняется. При любом типе HASH JOIN в OracleDB хэш-таблица строится по первой таблице (верхней в плане). По планам, которые вы показываете как раз видно, что в первом случае хэш-таблица строится по departments, а во втором — по locations.

Вы правы! Я проверил на соединении большой и мальенькой таблицы, хэш может строиться по любой из таблиц:

select /*+ USE_HASH(P S) SWAP_JOIN_INPUTS(P) */ p.prod_name , s.cust_id 
 from  sales1 s, products p where s.prod_id (+) = p.prod_id
 
-------------------------------------------------------------------------------
| Id  | Operation          | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |          |       |       |  1236 (100)|          |
|*  1 |  HASH JOIN OUTER   |          |   918K|    34M|  1236   (1)| 00:00:01 |
|   2 |   TABLE ACCESS FULL| PRODUCTS |    72 |  2160 |     3   (0)| 00:00:01 |
|   3 |   TABLE ACCESS FULL| SALES1   |   918K|  8075K|  1230   (1)| 00:00:01 |
-------------------------------------------------------------------------------


select /*+ USE_HASH(P S) SWAP_JOIN_INPUTS(S) */ p.prod_name , s.cust_id 
 from  sales1 s, products p where s.prod_id (+) = p.prod_id
 
------------------------------------------------------------------------------------------
| Id  | Operation             | Name     | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |          |       |       |       |  2154 (100)|          |
|*  1 |  HASH JOIN RIGHT OUTER|          |   918K|    34M|    18M|  2154   (1)| 00:00:01 |
|   2 |   TABLE ACCESS FULL   | SALES1   |   918K|  8075K|       |  1230   (1)| 00:00:01 |
|   3 |   TABLE ACCESS FULL   | PRODUCTS |    72 |  2160 |       |     3   (0)| 00:00:01 |
------------------------------------------------------------------------------------------
Sign up to leave a comment.

Articles