Search
Write a publication
Pull to refresh
54
0
Павел Лузанов @pluzanov

Пользователь

Send message

Следим. Высокая активность разработчиков увеличивает шансы на включение в 18-ю версию.

Планировщик оценит и сам решит как дешевле. В явном виде ничего указывать не нужно.

Сначала сделаем самым простым способом, который первый приходит в голову :

SELECT tmp.id,
       sq.amount
FROM tmp
LEFT JOIN
  (SELECT docid,
          SUM(amount) AS amount
   FROM line
   GROUP BY 1) sq ON sq.docid = tmp.id

А разве вот так не проще?

SELECT tmp.id,
       SUM(line.amount) AS amount
FROM tmp
     LEFT JOIN line ON line.docid = tmp.id
GROUP BY 1;

Сразу с правильным планом. Или это особенности построения запросов в ORM?

О кривом расчете n_distinct для line.docid читал здесь. Вроде ничего лучше не придумали, как вручную выставлять долю через alter table .. alter column .. set (n_disctinct= -0.xxx).

Спасибо, что подробно описали свой опыт сертификации.

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

Но небольшое уточнение внесу. Касательно последнего абзаца:

Многие вопросы составлены с подковыркой, главное не попасться. Например, в вопросе был сделан огромный select из pg_class + какой-то join из таблицы pg_database и тд. выведен результат этого запроса, а сам вопрос звучал так. С какими БД был инициализирован кластер? Если вы уже посмотрели курс, или знаете postgres, то понимаете, что этот select нужен был, только что бы вас запутать.

Я нашел этот вопрос. Вот он:

Некоторые базы данных были созданы уже после инициализации кластера:

\list
                                  List of databases
   Name    |  Owner   | Encoding |   Collate   |    Ctype    |   Access privileges   
-----------+----------+----------+-------------+-------------+-----------------------
 demo      | student  | UTF8     | en_US.UTF-8 | en_US.UTF-8 | 
 postgres  | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | 
 student   | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | 
 template0 | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/postgres          +
           |          |          |             |             | postgres=CTc/postgres
 template1 | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/postgres          +
           |          |          |             |             | postgres=CTc/postgres
(5 rows)

Какие базы данных были созданы после инициализации кластера?

В вопросе нет лишних, запутывающих, запросов. Возможно от стресса во время теста, Вы что-то не так запомнили. Но более важно, что ответили на этот вопрос правильно :-).

Кстати, посмотрел Вашу первую попытку, в ней было как минимум два вопроса (-4%), где ответы даны зеркально противоположные. Т.е. скорее всего это следствие невнимательности чтения вопроса. И Вы это учли в своих рекомендациях.

Удачи!

Внешний пользователь всё-таки подключается к серверу БД.

Настройка подключения именно к серверу выполняется в pg_hba.conf. Триггер on-login срабатывает позже.

Это только потом для него устанавливается текущая БД.

После pg_hba.conf еще проверяется разрешение на подключение к указанной базе данных и только потом сработает on-login. Такая вот цепочка.

... в другую БД можно обратиться только через расширения типа dblink или fdw. Да, поскольку любое такое расширение устанавливает отдельное соединение, вероятно, на это событие тоже срабатывает on-login триггер?

Всё верно. Когда dblink или postgres_fdw подключаются к удаленной БД с триггером on-login, то триггер сработает.

...а где именно регистрируется такой триггер, объектом чего он является? И переносится ли он в составе бэкапа отдельной базы данных?

Триггер и триггерная функция регистрируются в системном каталоге конкретной базы данных данных. И они попадут в резервную копию этой БД.

Наверное и с пулами соединений от триггера на login может быть польза. С большой корпоративной БД могут работать сразу несколько приложений, каждое со своим пулом/middleware. Тогда в on-login можно настраивать пулы сеансов каждого приложения.

Кстати по описанным в статье причинам, параметр force_parralel_mode в 16-й версии переименовали в debug_parallel_query.

Эх, OTUS, OTUS. Годы идут ничего не меняется.

Картинку с архитектурой (слайд 4) я уже видел в очень старой версии курса DBA1.

Как Егор уже написал, концептуальной проблемы не было.

А Вы вероятно писали систему, основанную на табличных триггерах? Для PostgreSQL, кстати, триггерных систем репликации общего назначения было создано несколько: Slony, Londiste, Bucardo. Последняя, судя по описанию, также поддерживает двунаправленную репликацию.

Оставлю для тех, кто дочитал до этого места. Действительно при переходе на generic план блокируются все секции секционированной таблицы. Вот пример с подготовленным запросом:

create table t (a int) partition by list (a);

select format('create table %I partition of t for values in (%s)', 'p'||x, x)
from generate_series(0,2) x
\gexec

\echo Ключ секционирования передается параметром.
prepare s (int) as select * from t where a = $1;

begin;
\echo Generic plan
set local plan_cache_mode = force_generic_plan;
explain (analyze) execute s(0);

select relation::regclass, mode
from   pg_locks 
where  pid = pg_backend_pid() and
       locktype = 'relation' and relation::regclass <> 'pg_locks'::regclass;

commit;
CREATE TABLE
CREATE TABLE
CREATE TABLE
CREATE TABLE
Ключ секционирования передается параметром.
PREPARE
BEGIN
Generic plan
SET
                                               QUERY PLAN                                               
--------------------------------------------------------------------------------------------------------
 Append  (cost=0.00..125.82 rows=39 width=4) (actual time=0.008..0.009 rows=0 loops=1)
   Subplans Removed: 2
   ->  Seq Scan on p0 t_1  (cost=0.00..41.88 rows=13 width=4) (actual time=0.007..0.007 rows=0 loops=1)
         Filter: (a = $1)
 Planning Time: 0.357 ms
 Execution Time: 0.035 ms
(6 rows)

 relation |      mode       
----------+-----------------
 p2       | AccessShareLock
 p1       | AccessShareLock
 p0       | AccessShareLock
 t        | AccessShareLock
(4 rows)

COMMIT

Идея о переходе на generic план любопытная.

Но мне что-то не удалось повторить на своем примере. Переделал вот так и всё равно блокируется только нужная секция:

load 'auto_explain';
set auto_explain.log_min_duration=0;
set auto_explain.log_analyze=on;
set auto_explain.log_nested_statements=on;
set auto_explain.log_level='NOTICE';
\echo Ключ секционирования передается параметром.
begin;
set local plan_cache_mode = force_generic_plan;
do $$begin execute 'select * from t where a = $1' using 0; end;$$;

set auto_explain.log_min_duration=-1;
select relation::regclass, mode
from   pg_locks 
where  pid = pg_backend_pid() and
       locktype = 'relation' and relation::regclass <> 'pg_locks'::regclass;

commit;
LOAD
SET
SET
SET
SET
Ключ секционирования передается параметром.
BEGIN
SET
psql:s2.sql:26: NOTICE:  duration: 0.005 ms  plan:
Query Text: select * from t where a = $1
Query Parameters: $1 = '0'
Seq Scan on p0 t  (cost=0.00..41.88 rows=13 width=4) (actual time=0.004..0.004 rows=0 loops=1)
  Filter: (a = 0)
DO
SET
 relation |      mode       
----------+-----------------
 p0       | AccessShareLock
 t        | AccessShareLock
(2 rows)

COMMIT

Это что. Сейчас активно делают поддержку DDL команд в логической репликации!

@Kazzman,
Постгресу нужно гарантировать что секции не будут удалены/изменены во время выполнения запроса, поэтому на каждую из них накладывается AccessShareLock. Тут вряд ли что-то изменится.

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

Я использовал такой пример:

create table t (a int) partition by list (a);

select format('create table %I partition of t for values in (%s)', 'p'||x, x)
from generate_series(0,2) x
\gexec

\echo Ключ секционирования вычисляется во время выполнения запроса.
begin;
explain (costs off, analyze) select * from t where a = (select 0);

select relation::regclass, mode
from   pg_locks 
where  pid = pg_backend_pid() and
       locktype = 'relation' and relation::regclass <> 'pg_locks'::regclass;
commit;


\echo Ключ секционирования задан константой.
begin;
explain (costs off, analyze) select * from t where a = 0;

select relation::regclass, mode
from   pg_locks 
where  pid = pg_backend_pid() and
       locktype = 'relation' and relation::regclass <> 'pg_locks'::regclass;

commit;

Вывод скрипта:

DROP TABLE
CREATE TABLE
CREATE TABLE
CREATE TABLE
CREATE TABLE
Ключ секционирования вычисляется во время выполнения запроса.
BEGIN
                             QUERY PLAN                             
--------------------------------------------------------------------
 Append (actual time=0.013..0.014 rows=0 loops=1)
   InitPlan 1 (returns $0)
     ->  Result (actual time=0.001..0.001 rows=1 loops=1)
   ->  Seq Scan on p0 t_1 (actual time=0.006..0.007 rows=0 loops=1)
         Filter: (a = $0)
   ->  Seq Scan on p1 t_2 (never executed)
         Filter: (a = $0)
   ->  Seq Scan on p2 t_3 (never executed)
         Filter: (a = $0)
 Planning Time: 0.257 ms
 Execution Time: 0.039 ms
(11 rows)

 relation |      mode       
----------+-----------------
 p2       | AccessShareLock
 p1       | AccessShareLock
 p0       | AccessShareLock
 t        | AccessShareLock
(4 rows)

COMMIT
Ключ секционирования задан константой.
BEGIN
                         QUERY PLAN                         
------------------------------------------------------------
 Seq Scan on p0 t (actual time=0.003..0.003 rows=0 loops=1)
   Filter: (a = 0)
 Planning Time: 0.059 ms
 Execution Time: 0.013 ms
(4 rows)

 relation |      mode       
----------+-----------------
 p0       | AccessShareLock
 t        | AccessShareLock
(2 rows)

COMMIT

Спасибо, поправил. Заодно и настойки лс поменял ))

Как пишет автор патча Питер Эйзентраут, это изменение включено в свежий проект стандарта SQL. Вероятно многие производители СУБД будут поддерживать такой синтаксис. Постгрес уже в их числе.

Имелось ввиду, что есть таблица tenants и во всех остальных таблицах условно employees первичный ключ составной ?

Да.

Я не понимаю, как фича set null помогает в случае бд которые используются разными клиентами.

Суть патча в том, что после ON DELETE SET NULL в скобках можно прописать список столбцов, которые нужно сбросить в NULL и не включать в него tenant_id.

Будем считать, что это затишье перед ...

pg_variables хорошее расширение, но оно в ванильный постгрес не входит.

А у вас pg_variables в postgres pro используется или вы собрали для его для PostgreSQL?
С zheap понятно только то, что затихло. Конкретика и планы неизвестны.
А на merge посмотрим, может в 15 попадет.
Лично мне еще нравится патч schema variables. Эта штука, помимо прочего, даст возможность реализовать что-то вроде ораклового контекста, без чего полноценно использовать RLS сложно.

Information

Rating
Does not participate
Works in
Registered
Activity