Error function cannot execute on a qe slice because it accesses relation

Error message:

Error message:

In Greenplum database, when we use stored procedures to query data, we will report the following error information

function cannot execute on a QE slice because it accesses relation

First, let’s understand the replication table below

Replicated table

Greenplum 6 supports a new distribution strategy: copy table, that is, the whole table has a complete copy on each node.

test=# CREATE TABLE t2 (id int) DISTRIBUTED REPLICATED;
CREATE TABLE
test=# INSERT INTO t2 VALUES (1), (2), (3);
INSERT 0 3
test=# SELECT * FROM t2;
 id
----
  1
  2
  3
(3 rows)

test=# SELECT gp_segment_id, * from t2;
 gp_segment_id | id
---------------+----
         0 |  1
         0 |  2
         0 |  3

UDF cannot access any tables on segment. Due to the characteristics of MPP, any segment only contains part of the data, so the UDF executed in the segment cannot access any tables, otherwise the data calculation is wrong.

yydzero=# CREATE FUNCTION c() RETURNS bigint AS $$
yydzero$#  SELECT count(*) from t1 AS result;
yydzero$# $$ LANGUAGE SQL;
CREATE FUNCTION
yydzero=# SELECT c();
 c
---
 6
(1 row)

yydzero=# select c() from t2;
ERROR:  function cannot execute on a QE slice because it accesses relation "public.t1"  (seg0 slice1 192.168.1.107:25435 pid=76589)

If the above T1 is changed to a copy table, this problem does not exist.

Avoid distributed query plans:

If the data of a table is copied on each segment, a local connection plan can be generated to avoid data movement between different nodes of the cluster. If a replicated table is used to store a table with a small amount of data (such as thousands of rows), the performance will be significantly improved. A table with a large amount of data is not suitable for the replicated table mode.

Solution:

If the reason is found, it is simple. You only need to modify the table creation statement to solve the problem

ALTER TABLE table_name SET DISTRIBUTED REPLICATED;

Similar Posts:

Greenplum version or build

Greenplum Database 6.0.0-beta.1 build commit:067fb8e43115bb087c872ee3d2269d869430263d

OS version and uname -a

uname -a:Linux gpadmin 4.9.125-linuxkit #1 SMP Fri Sep 7 08:20:28 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux

Expected behavior

Executing the following SQL should be successful:

CREATE TABLE example(a int, b text) DISTRIBUTED by (a);

insert into example value(1,'1'),(2,'2');

CREATE FUNCTION multiset_2(a anytable) RETURNS TABLE(a int, b text) AS '/home/gpadmin/workspace/gpdb/src/test/regress/regress.so', 'multiset_example' LANGUAGE C READS SQL DATA;        --Need the regress.so file

create function scalar_3() RETURNS table(a int, b text)
as $$ select * from multiset_2( TABLE( SELECT * from example) ) order by a, b $$ LANGUAGE SQL;
create table test_b as select * from scalar_3();

Actual behavior

When executing the last sql command, we have the following error message:

NOTICE:  Table doesn't have 'DISTRIBUTED BY' clause -- Using column(s) named 'a' as the Greenplum Database data distribution key for this table.
HINT:  The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew.
ERROR:  function cannot execute on a QE slice because it accesses relation "public.example"  (entry db 127.0.0.1:15432 pid=24239)
CONTEXT:  SQL function "scalar_3" during startup

Step to reproduce the behavior

Put file regress.so to /home/gpadmin/workspace/gpdb/src/test/regress/regress.so and then
execute the following sql command:

CREATE TABLE example(a int, b text) DISTRIBUTED by (a);
insert into example value(1,'1'),(2,'2');

CREATE FUNCTION multiset_2(a anytable) RETURNS TABLE(a int, b text) AS '/home/gpadmin/workspace/gpdb/src/test/regress/regress.so', 'multiset_example' LANGUAGE C READS SQL DATA;        --Need the regress.so file

create function scalar_3() RETURNS table(a int, b text)
as $$ select * from multiset_2( TABLE( SELECT * from example) ) order by a, b $$ LANGUAGE SQL;

create table test_b as select * from scalar_3();

Others

Also, in GPText, we can successfully executing the following sql command:

gptext_test=# select * from gptext.ner_terms('gptext_test.public.ner_terms_test','content','Hong Kong','{}','rows=1');
 id | term | entity_type | frequency
----+------+-------------+-----------
(0 rows)

But if we want to create a table from the result, we will get the following error:

gptext_test=# CREATE TABLE ner_terms_test6 AS select * from gptext.ner_terms('gptext_test.public.ner_terms_test','content','Hong Kong','{}','rows=1');
NOTICE:  Table doesn't have 'DISTRIBUTED BY' clause. Creating a NULL policy entry.
ERROR:  Error on receive from entry db 172.17.0.2:5432 pid=59401: server closed the connection unexpectedly
DETAIL:
	This probably means the server terminated abnormally
	before or while processing the request.

Pilar de Teodoro

unread,

Mar 25, 2020, 3:15:53 PM3/25/20

to gpdb-…@greenplum.org

Hi all,

First of all, hope you are all safe and healthy. Things are not good in Madrid but I am happy to see the group is working as good as usual.

I have found an issue that I do not know how to workaround:

My query is a crossmatch of 2 tables having star information done using q3c extension which looks at stars that are in a certain radius:

SELECT  k.id, gdr2.source_id AS iddr2

FROM euclid.kids_dr4 AS k, 

gaiadr2.gaia_source_2M AS gdr2

WHERE q3c_join(get_ra(gdr2.source_id, ‘gaiadr2.gaia_source_2M’),get_dec(gdr2.source_id, ‘gaiadr2.gaia_source_2M’), k.raj2000::double precision, k.decj2000::double precision, 0.5/3600);

get_ra and get_dec are 2 functions needed to propagate the coordinates taking in different epochs, that means to apply another function to get the right position so both tables are in same kind of coordinates.

get_ra looks like:

create or replace function get_ra(p_source_id bigint,p_table text) 

returns double precision 

as $$ 

declare 

l_lat double precision; 

begin 

execute ‘with spunto as(select EPOCH_PROP_POS(m.ra, m.dec, m.parallax, m.pmra, m.pmdec, m.radial_velocity, 2015.5, 2000) as valor from ‘||p_table||’ m where m.source_id=’||p_source_id||’) select lat(valor) from spunto’ into l_lat; 

return l_lat;

end;$$

LANGUAGE plpgsql;

where epoch_prop_pos is another function and lat another one from pgsphere extension.

the result I got:

ERROR: function cannot execute on a QE slice because it accesses relation «gaiadr2.gaia_source_2m» (seg5 slice2 172.25.7.122:45001 pid=14783)

CONTEXT: SQL statement «with spunto as(select EPOCH_PROP_POS(m.ra, m.dec, m.parallax, m.pmra, m.pmdec, m.radial_velocity, 2015.5, 2000) as valor from gaiadr2.gaia_source_2M m where m.source_id=1866715214893245824) select lat(valor) from spunto»
PL/pgSQL function get_ra(bigint,text) line 5 at EXECUTE statement
SQL state: 0A000

Is it not possible to execute in Greenplum a query like this?

Thank you very much,

Pilar

Heikki Linnakangas

unread,

Mar 25, 2020, 4:11:31 PM3/25/20

to Pilar de Teodoro, gpdb-…@greenplum.org

Hi!

The problem is that the function executes on the segments, but it also

tries to access the table ‘gaia_source_2m’. That’s not allowed in GPDB,

because when the function runs on one segment, it won’t see the rows

that reside on the other segments. A couple of things you could try to

work around that:

1. If the ‘gaiadr2.gaia_source_2m’ table isn’t too large, you could

change it into a replicated table.

ALTER TABLE gaiadr2.gaia_source_2m SET DISTRIBUTED REPLICATED;

Replicated tables can be accessed by functions on all segments.

2. The get_ra function takes ‘p_source_id’ and ‘p_table’ as arguments,

so that it can fetch that row from the given table. You could pass that

whole row to the function as argument, instead. That would be faster

anyway, as you already have that row available in the surrounding query.

So something like this:

create or replace function get_ra(m gaiadr2.gaia_source_2m)

returns double precision

as $$

select lat(EPOCH_PROP_POS(m.ra, m.dec, m.parallax, m.pmra, m.pmdec,

m.radial_velocity, 2015.5, 2000))

$$ language sql stable;

And formulate the query as:

SELECT k.id, gdr2.source_id AS iddr2

FROM euclid.kids_dr4 AS k,

gaiadr2.gaia_source_2M AS gdr2

WHERE q3c_join(get_ra(gdr2.*),get_dec(gdr2.*), k.raj2000::double

precision, k.decj2000::double

precision, 0.5/3600);

This is completely untested as I don’t have the full schema, so it’s

probably not quite right, but you get the idea. And I assume that

get_dec() similar refactoring as get_ra().

— Heikki

Pilar de Teodoro

unread,

Mar 25, 2020, 5:22:46 PM3/25/20

to Heikki Linnakangas, gpdb-…@greenplum.org

Thank you very much Heikki!

First option is not possible since that table is 1,6B rows and about 1,2TB in size but second option works. The only thing is that we will need to create a function for every table that needs epoch propagation. Which is better than anything. I have tested and it works fine.

Best regards,

Pilar

Report error message:

In the GreenPlum database, we will report the following information when we use the stored procedure query data.

function cannot execute on a QE slice because it accesses relation

First we understand the copy table first.

Replicated table

GreenPlum 6 supports a new distribution policy: a copy table, that is, the entire table has a complete copy on each node.

test=# CREATE TABLE t2 (id int) DISTRIBUTED REPLICATED;
CREATE TABLE
test=# INSERT INTO t2 VALUES (1), (2), (3);
INSERT 0 3
test=# SELECT * FROM t2;
 id
----
  1
  2
  3
(3 rows)

test=# SELECT gp_segment_id, * from t2;
 gp_segment_id | id
---------------+----
         0 |  1
         0 |  2
         0 |  3

UDF cannot access any table on Segment. Due to the characteristics of the MPP, any segment contains only partial data, so the UDF executed in Segment cannot access any table, otherwise the data calculates an error.

yydzero=# CREATE FUNCTION c() RETURNS bigint AS $$
yydzero$#  SELECT count(*) from t1 AS result;
yydzero$# $$ LANGUAGE SQL;
CREATE FUNCTION
yydzero=# SELECT c();
 c
---
 6
(1 row)

yydzero=# select c() from t2;
ERROR:  function cannot execute on a QE slice because it accesses relation "public.t1"  (seg0 slice1 192.168.1.107:25435 pid=76589)

If you put the aboveT1 change to a copy tableThere is no such problem.

Avoid distributed query plans:

If the data of a table has a copy on each segment, you can generate a local connection plan to avoid data between the different nodes of the cluster. If you store the amount of data (such as thousands of rows) with a copy table, you have a significant increase in performance. The table is not suitable for the replication table mode.

solution

If you find the reason, it is simple, you only need to modify the table statement to solve the problem.

ALTER TABLE table_name SET DISTRIBUTED REPLICATED;

Reference documentation

Выявление ядра распределенной базы данных Greenplum на основе PostgreSQL

Со статьей согласились 9 человек

Трансфер из:

http://www.postgres.cn/v2/news/viewone/1/453

www.postgres.cn

Во-первых, секрет ядра базы данных

Greenplum — наиболее зрелая распределенная аналитическая база данных с открытым исходным кодом (производительность OLTP Greenplum 6, который, как ожидается, будет выпущена в июне этого года, будет значительно улучшена, и она станет настоящей базой данных HTAP. Данные оценки будут опубликованы в ближайшее время). Область классического анализа данных занимает третье место в мире и четвертое в области анализа данных в реальном времени. Единственный продукт для баз данных с открытым исходным кодом, входящий в первую десятку в этих двух областях. Это означает, что если вы выберете продукт с открытым исходным кодом, из первой десятки не будет выбора, кроме этой. Оригинальный отчет Gartner.

Так как же устроена распределенная база данных Greenplum? Хорошо известно, что Greenplum основан на PostgreSQL. PostgreSQL — это самая продвинутая одноузловая база данных с множеством связанных основных документов и бумажных ресурсов. Информации о том, как преобразовать одноузловую PostgreSQL в распределенную базу данных, относительно мало. В этой статье представлена ​​основная работа, связанная с преобразованием одноузловой базы данных PostgreSQL в распределенную базу данных MPP с шести аспектов. Конечно, это всего лишь минималистский обзор: достижение производительности на уровне предприятия стоит сотни миллионов долларов.

Хотя в этом нет необходимости, но понимание базовых знаний ядра PostgreSQL полезно для понимания некоторых деталей в этой статье. PPT Брюса Момджяна — отличный вводный материал.

Во-вторых, обзор кластеризации Greenplum

PostgreSQL — самая продвинутая автономная база данных с открытым исходным кодом в мире. Greenplum основан на PostgreSQL и является самой передовой базой данных MPP с открытым исходным кодом в мире (для получения дополнительной информации о Greenplum посетите китайское сообщество Greenplum). С точки зрения пользователя Greenplum представляет собой полную систему управления реляционными базами данных (СУБД). На физическом уровне он содержит несколько экземпляров PostgreSQL, к которым можно получить доступ индивидуально. Чтобы реализовать разделение труда и сотрудничество между несколькими независимыми экземплярами PostgreSQL и предоставить пользователям логическую базу данных, Greenplum реализует распределенную кластеризацию хранения данных, вычислений, связи и управления на разных уровнях. Хотя Greenplum является кластером, для пользователей он инкапсулирует все распределенные детали и предоставляет пользователям единую логическую базу данных. Такая упаковка значительно освобождает разработчиков и обслуживающий персонал.

Преобразование PostgreSQL с одним узлом в кластер включает в себя несколько аспектов работы.Эта статья в основном представляет 6 аспектов, таких как распределение данных, распараллеливание плана запроса, распараллеливание выполнения, распределенные транзакции, перетасовка данных и распараллеливание управления.

Greenplum также добавляет большое количество других функций поверх PostgreSQL, таких как таблицы, оптимизированные для добавления, таблицы с хранением в столбцах, внешние таблицы, многоуровневые таблицы разделов, детализированные менеджеры ресурсов, оптимизаторы запросов ORCA, резервное копирование и восстановление, высокая доступность, обнаружение ошибок и Восстановление после сбоев, миграция данных кластера, расширение емкости, библиотека алгоритмов машинного обучения MADlib, UDF контейнерного исполнения, расширение PostGIS, набор GPText, управление мониторингом, интегрированный Kubernetes и т. Д.

На следующем рисунке показан кластер Greenplum с высоты птичьего полета.Один главный узел, два узла сегмента и 4 экземпляра сегмента развернуты на каждом узле сегмента, чтобы улучшить использование ресурсов. Каждый экземпляр, будь то главный экземпляр или экземпляр сегмента, является физически независимой базой данных PostgreSQL.

Три, распределенное хранилище данных

Распределение хранилища данных — первая проблема, которую решают распределенные базы данных. Базовый принцип распределенного хранения данных относительно прост и прост в реализации.Многие промежуточные программы баз данных также могут обеспечивать базовое распределенное хранилище данных. В этом отношении Greenplum не только обеспечивает базовое распределенное хранилище данных, но также предоставляет множество более продвинутых и гибких функций, таких как многоуровневое разделение и полиморфное хранилище. Greenplum 6 еще больше расширяет эту область, реализуя согласованные таблицы хеширования и репликации и позволяя пользователям вмешиваться в методы распределения данных на основе приложений.

Как показано на рисунке ниже, пользователь видит логическую базу данных.В каждой базе данных есть системные таблицы (например, pg_class, pg_proc и т. Д. В pg_catalog) и пользовательские таблицы (таблица продаж и таблица клиентов в следующем примере). На физическом уровне он состоит из множества независимых баз данных. Каждая база данных имеет свою собственную системную таблицу и пользовательскую таблицу. Основная база данных содержит только метаданные и не сохраняет пользовательские данные. На главном устройстве все еще есть таблицы пользовательских данных, и все эти таблицы пользовательских данных являются пустыми таблицами без данных. Оптимизатору необходимо использовать эти пустые таблицы для оптимизации запросов и создания планов. Большинство системных таблиц в базе данных сегментов (за исключением нескольких таблиц, таких как таблицы, связанные со статистикой) аналогичны системным таблицам на главном сервере.Каждый сегмент хранит часть таблицы пользовательских данных.

В Greenplum пользовательские данные распределяются по разным экземплярам сегментов разных узлов в соответствии с определенной стратегией. У каждого экземпляра есть свой собственный независимый каталог данных, в котором пользовательские данные сохраняются в виде файлов на диске. Использование стандартных операторов INSERT SQL позволяет автоматически распределять данные по соответствующим узлам в соответствии с заданными пользователем стратегиями, однако INSERT имеет низкую производительность и подходит только для вставки небольшого количества данных. Greenplum предоставляет специальный инструмент параллельной загрузки данных для эффективного импорта данных. Подробнее см. Официальные документы gpfdist и gpload. Кроме того, Greenplum также поддерживает параллельное КОПИРОВАНИЕ, если данные были сохранены в каждом сегменте, это самый быстрый метод загрузки данных. На следующем рисунке наглядно показано, что данные таблицы продаж пользователя распределены по разным экземплярам сегмента.

Помимо поддержки горизонтального распределения данных между различными узлами, Greenplum также поддерживает разделение в соответствии с различными стандартами на одном узле и поддерживает многоуровневое разделение. Greenplum поддерживает следующие методы разделения:

● Разделение по диапазону: разделение данных в соответствии с диапазоном времени или диапазоном значений столбца. Например, следующий SQL создаст многораздельную таблицу, которая разделена по дням. С 01.01.2016 по 01.01.2017 данные за весь год делятся на 366 секций по дням:

CREATE TABLE sales (id int, date date, amt decimal(10,2))
DISTRIBUTED BY (id)
PARTITION BY RANGE (date)
( START (date '2016-01-01') INCLUSIVE
  END (date '2017-01-01') EXCLUSIVE
  EVERY (INTERVAL '1 day') );

● Раздел списка: согласно списку значений данных определенного столбца данные не разделяются на разные разделы. Например, следующий SQL создает таблицу разделов на основе пола.Таблица имеет три раздела: в одном разделе хранятся данные о женщинах, в одном разделе хранятся данные о мужчинах, а другие значения, такие как NULL, хранятся в отдельном другом разделе.

CREATE TABLE rank (id int, rank int, year int, gender char(1), count int ) 
DISTRIBUTED BY (id)
PARTITION BY LIST (gender)
( PARTITION girls VALUES ('F'), 
 PARTITION boys VALUES ('M'), 
 DEFAULT PARTITION oth er );

На следующем рисунке показано, что таблица продаж пользователя сначала распределяется по двум узлам, а затем каждый узел разбивается на разделы в соответствии с определенным стандартом. Основная цель разделения — обеспечить адаптацию разделов для повышения производительности за счет сокращения доступа к данным. Адаптация разделов означает, что в соответствии с условиями запроса оптимизатор автоматически отфильтровывает разделы, к которым не требуется доступ, чтобы уменьшить объем сканирования данных во время выполнения запроса. PostgreSQL поддерживает статическое условное сокращение разделов, а Greenplum реализует динамическое сокращение разделов с помощью оптимизатора ORCA. Динамическая резка перегородок может повысить производительность от десяти до сотен раз.

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

● Таблица кучи: Таблица кучи — это метод хранения по умолчанию для Greenplum, а также метод хранения для PostgreSQL. Поддерживает эффективные операции обновления и удаления, работает быстро при доступе к нескольким столбцам. Обычно используется для запросов OLTP.

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

● Таблица AOCO (оптимизированная для добавления, ориентированная на столбцы): таблица AOCO представляет собой список с лучшей степенью сжатия, поддерживает различные алгоритмы сжатия и подходит для сценариев запросов с меньшим количеством столбцов.

● Внешняя таблица: данные внешней таблицы хранятся извне (данные не обрабатываются Greenplum), и в Greenplum есть только метаданные внешней таблицы. Greenplum поддерживает множество внешних источников данных, таких как S3, HDFS, файлы, Gemfire, различные реляционные базы данных и т. Д., А также множество форматов данных, таких как Text, CSV, Avro, Parquet и т. Д.

Как показано на рисунке ниже, если предположить, что вышеупомянутая таблица продаж разделена по месяцам, тогда для хранения данных в разное время могут использоваться разные стратегии хранения. Например, данные за последние три месяца хранятся в таблице кучи (Heap), а более старые данные используют столбцы. Хранилище, данные годичной давности хранятся в S3 или HDFS с использованием внешних таблиц.

Распределение данных — это основа любой базы данных MPP и один из ключей к эффективности базы данных MPP. Распределение огромных объемов данных по нескольким узлам, с одной стороны, значительно сокращает объем данных, обрабатываемых одним узлом, а с другой стороны, это также закладывает основу для параллелизма обработки. Комбинация этих двух факторов может значительно улучшить производительность всей системы. Например, в кластере из ста узлов каждый узел экономит только один процент от общего объема данных, а сто узлов обрабатываются параллельно в одно и то же время, и производительность будет в десятки раз выше, чем у одного узла с более надежной конфигурацией. Если данные распределены неравномерно и данные искажены, что ограничивается эффектом короткой платы, производительность всей системы будет такой же, как и у самого медленного узла. Следовательно, разумное распределение данных имеет большое влияние на общую производительность Greenplum.

Greenplum 6 предлагает следующие стратегии распределения данных.

● Распределение хэшей

● Случайное распределение

● Реплицируемая таблица (Replicated Table)

Распределение хэшей

Распределение хэшей — наиболее часто используемый метод распределения данных в Greenlum. Вычислите хеш-значение пользовательских данных в соответствии с предопределенным ключом распределения, а затем сопоставьте хеш-значение с определенным сегментом. Ключ распределения может содержать несколько полей. Правильный выбор ключей распределения — главный фактор производительности Greenplum. Хороший ключ распределения равномерно распределяет данные по каждому сегменту, чтобы избежать перекоса данных.

Код для Greenplum для вычисления хеш-значения распределенного ключа находится в cdbhash.c. Структура CdbHash — это основная структура данных для обработки распределенных хешей ключей. Логика вычисления хеш-значения распределенного ключа:

● Используйте makeCdbHash (int segnum) для создания структуры CdbHash.

● Затем выполните следующие операции с каждым кортежем, вычислите значение хеш-функции, соответствующее кортежу, и определите, в какой сегмент следует распределить кортеж:

○ cdbhashinit (): выполнить операцию инициализации

○ cdbhash (), эта функция вызывает hashDatum () для выполнения различной предварительной обработки для разных типов, и, наконец, addToCdbHash () добавляет обработанное значение столбца к вычислению хэша.

○ cdbhashreduce () отображает хеш-значение в сегмент

Структура CdbHash:

typedef struct CdbHash
{
    uint32 hash; / * значение результата хеширования * /
         int numsegs; / * количество сегментов * /
         CdbHashReduce reducealg; / * Алгоритм, используемый для сокращения сегментов * /
         uint32 rrindex; / * индекс цикла * /
} CdbHash;

Основная функция

● makeCdbHash (int numsegs): создайте структуру CdbHash, которая поддерживает следующую информацию:

○ Количество сегментов

○ Метод сокращения

■ Если количество сегментов является степенью двойки, используйте REDUCE_BITMASK, в противном случае используйте REDUCE_LAZYMOD.

○ Значение хеш-функции в структуре будет инициализировано для каждого кортежа. Эта операция выполняется в cdbhashinit ().

●void cdbhashinit(CdbHash *h)

h-> hash = FNV1_32_INIT; сбросить значение хеш-функции на исходное смещение

● void cdbhash (CdbHash * h, Datum datum, Oid type): добавить атрибут к вычислению CdbHash, то есть добавить атрибут, учитываемый при вычислении хэша. Эта функция передаст указатель на функцию: addToCdbHash.

● void addToCdbHash (void * cdbHash, void * buf, size_t len); реализует функцию datumHashFunction

h-> hash = fnv1_32_buf (buf, len, h-> hash); // Выполняем 32-битный хеш FNV 1 в буфере

Обычный путь вызова: evalHashKey -> cdbhash -> hashDatum -> addToCdbHash

● unsigned int cdbhashreduce (CdbHash * h): чтобы отобразить хеш-значение на сегмент, основная логика состоит в том, чтобы взять модуль, как показано ниже:

switch (h->reducealg)
{
  case REDUCE_BITMASK:
     result = FASTMOD(h->hash, (uint32) h->numsegs);       /* fast mod (bitmask) */
     break;

case REDUCE_LAZYMOD:
result = (h->hash) % (h->numsegs); /* simple mod */
break;
}

Для каждого кортежа выполнить следующий поток:

●void cdbhashinit(CdbHash *h)
●void cdbhash(CdbHash *h, Datum datum, Oid type)
●void addToCdbHash(void *cdbHash, void *buf, size_t len)
●unsigned int cdbhashreduce(CdbHash *h)

Случайное распределение

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

test=# create table t1 (id int) DISTRIBUTED RANDOMLY;
CREATE TABLE
test=# INSERT INTO t1 VALUES (1);
INSERT 0 1
test=# INSERT INTO t1 VALUES (2);
INSERT 0 1
test=# INSERT INTO t1 VALUES (3);
INSERT 0 1
test=# SELECT gp_segment_id, * from t1;
gp_segment_id | id
---------------±—
1 | 1
1 | 2
1 | 3

Некоторые инструменты используют случайное распределение для реализации управления данными.Например, инструменту расширения gpexpand необходимо перераспределить данные после добавления узлов. Во время инициализации gpexpand пометит все таблицы как случайно распределенные, а затем выполнит операцию перераспределения, так что операция перераспределения не повлияет на нормальную работу бизнеса. (Greenplum 6 переработал gpexpand, больше не нужно изменять стратегию распределения на случайное распределение)

Реплицированная таблица

Greenplum 6 поддерживает новую стратегию распространения: реплицировать таблицы, то есть вся таблица имеет полную копию на каждом узле.

test=# CREATE TABLE t2 (id int) DISTRIBUTED REPLICATED;
CREATE TABLE
test=# INSERT INTO t2 VALUES (1), (2), (3);
INSERT 0 3
test=# SELECT * FROM t2;
id

1
2
3
(3 rows)

test=# SELECT gp_segment_id, * from t2;
gp_segment_id | id
—————±—
0 | 1
0 | 2
0 | 3

Копирование таблицы решает две проблемы:

● UDF не может получить доступ ни к одной таблице в сегменте. Из-за характеристик MPP любой сегмент содержит только часть данных, поэтому UDF, выполняемая в сегменте, не может получить доступ к какой-либо таблице, иначе расчет данных будет неправильным.

yydzero=# CREATE FUNCTION c() RETURNS bigint AS KaTeX parse error: Can't use function '$' in math mode at position 9: 
yydzero$̲#  SELECT count… LANGUAGE SQL;
CREATE FUNCTION
yydzero=# SELECT c();
c

6
(1 row)

yydzero=# select c() from t2;
ERROR: function cannot execute on a QE slice because it accesses relation “public.t1” (seg0 slice1 192.168.1.107:25435 pid=76589)

Если вы измените указанный выше t1 на таблицу копирования, этой проблемы не будет.

Существует множество сценариев приложений для копирования таблиц, например, PostGIS space_ref_sys (PostGIS имеет большое количество UDF, которым необходим доступ к этой таблице) и plr_modules в PLR могут использовать метод копирования таблицы. До поддержки этой функции Greenplum мог поддерживать такие таблицы, как Space_ref_sys, только с помощью нескольких приемов.

● Избегайте распределенных планов запросов: если данные таблицы копируются в каждом сегменте, то можно сгенерировать локальный план подключения, чтобы избежать перемещения данных между различными узлами в кластере. Если вы используете реплицированную таблицу для хранения таблицы с относительно небольшим объемом данных (например, тысячами строк), то производительность будет значительно улучшена. Таблицы с большим объемом данных не подходят для использования режима реплицированной таблицы.

Четвертый, распараллеливание плана запроса

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

Greenplum представляет операторы движения (операторы) для распараллеливания планов запросов. Оператор движения реализует передачу данных между разными узлами. Он скрывает разницу между архитектурой MPP и автономной машиной для других операторов, так что большинству других операторов не важно, выполняются ли они в кластере или на автономной машине. У каждого оператора движения есть отправитель и получатель. Кроме того, Greenplum также выполняет распределенную оптимизацию некоторых операторов, например агрегацию. (В этом разделе необходимо понять базовые знания оптимизатора PostgreSQL, обратитесь к src / backend / optimizer / README)

Пример оптимизации

Прежде чем вводить технические подробности, давайте рассмотрим несколько примеров.

В следующем примере создаются две таблицы t1 и t2, каждая из которых имеет два столбца c1, c2, оба из которых используют c1 в качестве ключа распределения.

CREATE table t1 AS SELECT g c1, g + 1 as c2 FROM generate_series(1, 10) g DISTRIBUTED BY (c1);
CREATE table t2 AS SELECT g c1, g + 1 as c2 FROM generate_series(5, 15) g DISTRIBUTED BY (c1);

SQL1:

SELECT * from t1, t2 where t1.c1 = t2.c1;
c1 | c2 | c1 | c2
—-±—±—±—
5 | 6 | 5 | 6
6 | 7 | 6 | 7
7 | 8 | 7 | 8
8 | 9 | 8 | 9
9 | 10 | 9 | 10
10 | 11 | 10 | 11
(6 rows)

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

Gather Motion 3:1 (slice1; segments: 3) (cost=3.23…6.48 rows=10 width=16)
-> Hash Join (cost=3.23…6.48 rows=4 width=16)
Hash Cond: t2.c1 = t1.c1
-> Seq Scan on t2 (cost=0.00…3.11 rows=4 width=8)
-> Hash (cost=3.10…3.10 rows=4 width=8)
-> Seq Scan on t1 (cost=0.00…3.10 rows=4 width=8)
Optimizer: legacy query optimizer

SQL2:

SELECT * from t1, t2 where t1.c1 = t2.c2;
c1 | c2 | c1 | c2
----±—±---±—
9 | 10 | 8 | 9
10 | 11 | 9 | 10
8 | 9 | 7 | 8
6 | 7 | 5 | 6
7 | 8 | 6 | 7
(5 rows)

План запроса SQL2 выглядит следующим образом. Связанный ключ c1 таблицы t1 также является его ключом распределения, а связанный ключ c2 таблицы t2 не является ключом распределения, поэтому данные необходимо перераспределить в соответствии с t2.c2, чтобы все строки t1.c1 = t2.c2 Все выполняют связанные операции с одним и тем же сегментом.

Gather Motion 3:1 (slice2; segments: 3) (cost=3.23…6.70 rows=10 width=16)
-> Hash Join (cost=3.23…6.70 rows=4 width=16)
Hash Cond: t2.c2 = t1.c1
-> Redistribute Motion 3:3 (slice1; segments: 3) (cost=0.00…3.33 rows=4 width=8)
Hash Key: t2.c2
-> Seq Scan on t2 (cost=0.00…3.11 rows=4 width=8)
-> Hash (cost=3.10…3.10 rows=4 width=8)
-> Seq Scan on t1 (cost=0.00…3.10 rows=4 width=8)
Optimizer: legacy query optimizer

SQL3:

SELECT * from t1, t2 where t1.c2 = t2.c2;
c1 | c2 | c1 | c2
----±—±---±—
8 | 9 | 8 | 9
9 | 10 | 9 | 10
10 | 11 | 10 | 11
5 | 6 | 5 | 6
6 | 7 | 6 | 7
7 | 8 | 7 | 8
(6 rows)

План запроса SQL3 показан ниже. Ключ ассоциации c2 для t1 не является ключом распределения, а ключ ассоциации c2 для t2 не является ключом распределения. Таким образом, широковещательное движение используется для широковещательной передачи данных одной таблицы всем узлам для обеспечения правильной связи. Секс. План, созданный последним мастер-кодом для этого запроса, выберет перераспределение двух таблиц. Почему это может быть вопрос для размышлений :)

Gather Motion 3:1 (slice2; segments: 3) (cost=3.25…6.96 rows=10 width=16)
-> Hash Join (cost=3.25…6.96 rows=4 width=16)
Hash Cond: t1.c2 = t2.c2
-> Broadcast Motion 3:3 (slice1; segments: 3) (cost=0.00…3.50 rows=10 width=8)
-> Seq Scan on t1 (cost=0.00…3.10 rows=4 width=8)
-> Hash (cost=3.11…3.11 rows=4 width=8)
-> Seq Scan on t2 (cost=0.00…3.11 rows=4 width=8)
Optimizer: legacy query optimizer

SELECT * from t1 LEFT JOIN t2 on t1.c2 = t2.c2 ;
c1 | c2 | c1 | c2
—-±—±—±—
1 | 2 | |
2 | 3 | |
3 | 4 | |
4 | 5 | |
5 | 6 | 5 | 6
6 | 7 | 6 | 7
7 | 8 | 7 | 8
8 | 9 | 8 | 9
9 | 10 | 9 | 10
10 | 11 | 10 | 11
(10 rows)

План запроса SQL4 показан ниже. Хотя связанный ключ такой же, как и у SQL3, метод широковещательной передачи t1 нельзя использовать из-за левого соединения. В противном случае данные будут дублироваться. Таким образом, план запроса перераспределяет обе таблицы. . В зависимости от стоимости пути оптимизатор SQL4 может также выбрать метод широковещательной передачи t2. (Если объем данных одинаков, стоимость трансляции одной таблицы выше, чем перераспределение двойной таблицы. Для перераспределения двойной таблицы каждый кортеж каждой таблицы передается один раз, что эквивалентно двукратной передаче каждого кортежа одной таблицы во время трансляции Каждый кортеж одной таблицы необходимо передать nSegments раз.)

Gather Motion 3:1 (slice3; segments: 3) (cost=3.47…6.91 rows=10 width=16)
-> Hash Left Join (cost=3.47…6.91 rows=4 width=16)
Hash Cond: t1.c2 = t2.c2
-> Redistribute Motion 3:3 (slice1; segments: 3) (cost=0.00…3.30 rows=4 width=8)
Hash Key: t1.c2
-> Seq Scan on t1 (cost=0.00…3.10 rows=4 width=8)
-> Hash (cost=3.33…3.33 rows=4 width=8)
-> Redistribute Motion 3:3 (slice2; segments: 3) (cost=0.00…3.33 …
Hash Key: t2.c2
-> Seq Scan on t2 (cost=0.00…3.11 rows=4 width=8)
Optimizer: legacy query optimizer

SELECT c2, count(1) from t1 group by c2;
c2 | count
—-±——
5 | 1
6 | 1
7 | 1
4 | 1
3 | 1
10 | 1
11 | 1
8 | 1
9 | 1
2 | 1
(10 rows)

Вышеупомянутые четыре SQL-запроса показывают влияние различных типов JOIN на тип перемещения данных (тип движения). SQL5 демонстрирует оптимизацию агрегирования Greenplum: двухэтапное агрегирование. Первый этап агрегации выполняется для каждого сегмента локальных данных, а затем второй этап агрегации выполняется путем перераспределения в каждый сегмент. Наконец, мастер резюмирует это с помощью Gather Motion. Greenplum также использует трехэтапную агрегацию для некоторых SQL, таких как DISTINCT GROUP BY.

Gather Motion 3:1 (slice2; segments: 3) (cost=3.55…3.70 rows=10 width=12)
-> HashAggregate (cost=3.55…3.70 rows=4 width=12)
Group Key: t1.c2
-> Redistribute Motion 3:3 (slice1; segments: 3) (cost=3.17…3.38 rows=4 width=12)
Hash Key: t1.c2
-> HashAggregate (cost=3.17…3.17 rows=4 width=12)
Group Key: t1.c2
-> Seq Scan on t1 (cost=0.00…3.10 rows=4 width=4)
Optimizer: legacy query optimizer
(9 rows)

Greenplum представляет новые структуры данных и концепции для оптимизации запросов

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

Чтобы превратить план запроса для одной машины в параллельный план, Greenplum вводит некоторые новые концепции для улучшения структур Node, Path и Plan в PostgreSQL:

● Добавьте новый тип узла: Flow.

● Добавьте новый тип пути (Path): CdbMotionPath.

● Добавьте новый оператор плана запроса (Plan): Motion (первое поле Motion — это Plan, а первое поле структуры Plan — это тип NodeTag. Первый узел Flow также имеет тип NodeTag, а RangeVar, (IntoClause, Expr, RangeTableRef — это концепция уровня)

● Добавлено поле локуса CdbPathLocus в структуру Path, чтобы указать стратегию перераспределения результирующего кортежа по этому пути.

● Добавьте поле Flow в структуру Plan, чтобы указать направление потока кортежа этого оператора;

Новый тип узла: Flow

Новый тип узла Flow описывает поток кортежей в параллельном плане. Каждый узел плана запроса (структура плана) имеет поле потока, указывающее направление потока выходного кортежа текущего узла. Поток — это новый тип узла, но не узел плана запроса. Кроме того, структура потока также включает некоторые поля-элементы для планирования распараллеливания.

У потока есть три основных поля:

● FlowType, который представляет тип потока.

● НЕОПРЕДЕЛЕННЫЙ: неопределенный поток

● SINGLETON: это означает GatherMotion.

● REPLICATED: означает трансляцию движения.

● РАЗДЕЛЕННЫЕ: представляет перераспределение движения.

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

● Нет: движение не требуется.

● ФОКУС: сосредоточиться на одном сегменте, что эквивалентно GatherMotion.

● ТРАНСЛЯЦИЯ: трансляция движения.

● REPARTITION: перераспределение хэшей.

● EXPLICIT: переместить кортеж в сегменты, отмеченные полем segid.

● CdbLocusType: Тип локуса. Оптимизатор использует эту информацию для выбора наиболее подходящего узла для наиболее подходящей обработки потока данных и определения соответствующего движения.

● CdbLocusType_Null: не использовать Locus.

● CdbLocusType_Entry: указывает на единственный внутренний процесс для записи db (т.е. мастер), который может быть QD (диспетчер запросов) или QE (исполнитель запросов) на entrydb.

● CdbLocusType_SingleQE: единый внутренний процесс на любом узле, который может быть QD или любым процессом QE.

● CdbLocusType_General: совместим с любым локусом

● CdbLocusType_Replicated: есть реплики во всех QE.

● CdbLocusType_Hashed: распределение хешей для всех QE.

● CdbLocusType_Strewn: данные распределяются и хранятся, но ключ распределения неизвестен.

Новый тип пути: CdbMotionPath

Путь представляет собой возможный путь вычисления (например, последовательное сканирование или корреляция хэшей). Более сложные пути наследуют структуру пути и записывают дополнительную информацию для оптимизации. Greenplum добавляет поле локуса CdbPathLocus в структуру Path, чтобы указать стратегию перераспределения и выполнения результирующего кортежа по текущему пути.

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

Greenplum также представил новый путь: CdbMotionPath, который используется для указания того, как результаты подпути передаются от процесса-отправителя к процессу-получателю.

Оператор нового плана: движение

Как упоминалось выше, Motion — это узел дерева плана запроса, который реализует перемешивание данных (Shuffle), чтобы его родительский оператор мог получить необходимые данные от своего дочернего оператора. Есть три типа движения:

● MOTIONTYPE_HASH: используйте алгоритм хеширования для перераспределения данных в соответствии с ключом перераспределения и отправьте каждый кортеж после оператора в целевой сегмент. Целевой сегмент определяется хеш-значением ключа перераспределения.

● MOTIONTYPE_FIXED: отправка кортежей в фиксированный набор сегментов, который может быть широковещательным движением (отправляется во все сегменты) или сбором движения (отправляется в фиксированный сегмент).

● MOTIONTYPE_EXPLICIT: отправить кортеж в сегменты, указанные в его поле segid, что соответствует явному перераспределению движения. Отличие от MOTIONTYPE_HASH в том, что нет необходимости вычислять хеш-значение.

Как упоминалось ранее, Greenplum ввел поле потока Flow * в структуру Plan, чтобы указать направление потока результирующего кортежа. Кроме того, в структуре Plan также представлены несколько других полей, связанных с оптимизацией и выполнением, например, поле отправки DispatchMethod, которое указывает, требуется ли планирование MPP, поле directDispatch, которое может быть отправлено напрямую (отправка напрямую в сегмент, обычно используется для запроса первичного ключа) , SliceTable для распределенных планов, которые облегчают выполнение MPP, motionNode для записи родительского узла движения текущего узла плана и т. Д.

Создать план распределенного запроса

На следующем рисунке показан процесс оптимизации традиционного оптимизатора (оптимизатор ORCA другой) в Greenplum.В этом разделе подчеркиваются отличия от автономного оптимизатора PostgreSQL.

standard_planner — это оптимизатор PostgreSQL по умолчанию, он в основном вызывает subquery_planner и set_plan_references. В Greenplum cdbparallelize вызывается после set_plan_references для окончательного распараллеливания дерева запросов.

subquery_planner оптимизирует подзапрос, как указано в названии, и генерирует дерево плана запроса. Он имеет два основных этапа выполнения:

● Оптимизация основных функций запросов (также известных как SPJ: Select / Projection / Join) осуществляется с помощью query_planner ().

● Оптимизация расширенных функций запросов (без SPJ), таких как агрегирование, реализуется с помощью grouping_planner (), grouping_planner () вызывает query_planner () для базовой оптимизации, а затем оптимизирует расширенные функции.

Распределенная обработка автономных планов Greenplum в основном происходит в двух местах:

● Единый подзапрос: дерево плана подзапроса, возвращаемое функцией subquery_planner () Greenplum, подверглось некоторой распределенной обработке, такой как добавление операторов движения в HashJoin, двухэтапное агрегирование и т. Д.

Множественные подзапросы: Greenplum необходимо установить соответствующий поток данных между несколькими подзапросами, чтобы результаты определенного подзапроса могли использоваться верхним деревом запросов. Эта операция реализуется функцией cdbparallelize.

Распараллеливание отдельного подзапроса

Процесс оптимизации отдельного подзапроса в Greenplum аналогичен процессу PostgreSQL. Основные отличия:

● Ассоциация: определите, нужно ли добавлять узел «Движение», какой тип движения и т. Д. В соответствии с распределением данных в левой и правой подтаблицах оператора ассоциации.

● Оптимизация расширенных операций, таких как агрегирование, например, двухэтапное агрегирование, упомянутое ранее.

Ниже кратко представлен основной процесс:

Сначала используйте build_simple_rel () для построения информации простой таблицы. build_simple_rel получает основную информацию о таблице, например, сколько кортежей в таблице, сколько страниц занято и т. д. Одна из наиболее важных сведений — это информация о распределении данных: GpPolicy описывает тип распределения данных и ключ распределения базовой таблицы.

Затем используйте set_base_rel_pathlists (), чтобы установить путь доступа к базовой таблице. set_base_rel_pathlists вызывает разные функции в соответствии с разными типами таблиц:

●RTE_FUNCTION: create_functionscan_path()
●RTE_RELATION: create_external_path()/create_aocs_path()/create_seqscan_path()/create_index_paths()
●RTE_VALUES: create_valuesscan_path

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

Как определить локус?

Для обычных таблиц кучи (Heap) путь последовательного сканирования create_seqscan_path () использует следующий метод для определения информации о местоположении пути:

● Если таблица хешируется, тип локуса — CdbLocusType_Hashed.

● Если он распределен случайным образом, тип локуса — CdbLocusType_Strewn.

● Если это системная таблица, тип локуса — CdbLocusType_Entry.

Для функций create_function_path () использует следующий метод для определения местоположения пути:

● Если функция неизменяема, используйте: CdbLocusType_General.

● Если функция является изменяемой, используйте: CdbLocusType_Entry.

● Если функцию необходимо выполнить на главном сервере, используйте: CdbLocusType_Entry

● Если функцию необходимо выполнить для всех сегментов, используйте CdbLocusType_Strewn.

Если оператор SQL содержит ассоциации, используйте make_rel_from_joinlist (), чтобы сгенерировать путь доступа для дерева ассоциаций. Соответствующие функции:

create_nestloop_path / create_mergejoin_path / create_hashjoin_path. Самым важным моментом этого процесса является определение того, нужно ли вам добавить узел движения и какой тип узла движения. Например, предыдущий ключ ассоциации SQL1 является ключом распределения двух таблиц t1 / t2, поэтому нет необходимости добавлять движение; в то время как SQL2 должен перераспределять t2, так что для любого кортежа t1 условие ассоциации (t1.c1 = t2) .c2) Все кортежи t2 находятся в одном сегменте.

Если SQL содержит расширенные функции, такие как агрегирование и оконные функции, вызовите cdb_grouping_planner () для оптимизации обработки, такой как преобразование агрегирования в двухэтапное или трехэтапное агрегирование.

Последний шаг — выбрать самый дешевый путь из всех возможных путей и вызвать create_plan (), чтобы преобразовать оптимальное дерево путей в оптимальное дерево запросов.

На этом этапе Locus of Path влияет на тип Flow сгенерированного плана. Поток связан с Группой в секции исполнительного механизма. Поток позволяет исполнительному механизму не заботиться о форме распределения данных и о том, что такое ключ распределения, а заботиться только о том, находятся ли данные в нескольких сегментах или в одном сегменте. Соответствие между Locus и Flow:

●FLOW_SINGLETON: Locus_Entry/Locus_SingleQE/Locus_General
●FLOW_PARTITIONED: Locus_Hash/Locus_Strewn/Locus_Replicated

Распараллеливание между несколькими подзапросами

Основная цель cdbparallelize () — решить поток данных между несколькими подзапросами и сгенерировать окончательный план распараллеливания запроса. Он состоит из двух основных шагов: предварительного сканирования и apply_motion.

● Предварительное сканирование имеет две цели: одна цель — пометить определенные типы узлов плана (например, поток) для последующей обработки apply_motion, вторая цель — пометить или деформировать узлы подплана (подплан). SubPlan на самом деле не является узлом плана запроса, а узлом выражения, который содержит узел плана и его таблицу диапазонов (Таблица диапазонов). SubPlan соответствует SubLink (выражение подзапроса SQL) в дереве запроса и может появиться в выражении. prescan выполняет следующую обработку в дереве плана, содержащемся в SubPlan:

● Если Subplan является Initplan, сделайте отметку в корневом узле дерева запроса, что означает, что вам нужно вызвать apply_motion, чтобы добавить узел движения позже.

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

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

Result

_Material

_Broadcast (or Gather)

_SeqScan

● apply_motion: добавьте узел движения в дерево запросов верхнего уровня в соответствии с узлом потока в плане. Добавьте разные узлы движения в соответствии с различными типами SubPlan (например, InitPlan, нерелевантный многострочный подзапрос, связанный подзапрос).

Например, SELECT * FROM tbl WHERE id = 1, prescan () отметит корневой узел, когда он переходит к корневому узлу дерева запросов, и добавит GatherMotion поверх корневого узла, когда apply_motion ().

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

Опубликовано 2020-01-02

Распределенная база данных

Согласен 9

добавить комментарий

нравиться

Избранное

I am writing a simple plpgsql function to test an idea I have to use the information_schema.columns table to dynamically run metrics on various tables. The function works fine, but when I use info in the information_schema table to generate the table name to pass to my function, I get the error message in the title:

ERROR:  function cannot execute on segment because it accesses relation "my_table"

Here is the simple function (a proof-of-principle):

create or replace function count_rows(table_name text, column_name text)
returns bigint as $$
declare
    n bigint;
BEGIN
    execute 'select count(*) from (select ' || column_name || ' from ' || table_name || ') as t' into n;
    return n;
END;
$$ language 'plpgsql';

This query (and therefore the function) works fine:

select * from count_rows('my_table','my_column');  -- works correctly!

But this query using inputs from the information_schema.columns table fails with the error above:

select table_name, column_name, count_rows(table_name, column_name) as num_rows
from information_schema.columns where table_name = 'my_table'; -- doesnt work

What does this error message mean? Why can’t it query the table listed in information_schema in this way?

Actually, I do not think this is a bug, but at least Greenplum should throw an error when try to scan replicated table on master segment.

Greenplum is an MPP database, we do have some limitations from single instance Postgres.

UDF that invokes SQL is subtle in Greenplum because:

  • only QD can dispatch, create gang and build interconnect
  • Greenplum can only have one dispatch state at a time, means we cannot dispatch within a dispatch context.

Q1. why not replicated table, we hit the issue?
A1: function cannot execute on a QE slice because it accesses relation: it is going to execute a distributed SQL on a QE, but QE cannot dispatch and create Gang

Q2: why replicated table no data?
A2:

  1. Replicated Table does not store data on Master
  2. the function secure_Test is defaultly violation and execute anywhere (select provolatile, proexeclocation from pg_proc where proname = ‘secure_Test’), thus this function in current gpdb planner’s code, will schedule on master to execute, (this seems can be changed, but that is a feature request)

Work around:

To work around such case, DBA or users of Greenplum need to have a good knowledge .

Following is a workaround from me:

Method : execute on initplan

When create a UDF, refer to the doc: , read EXECUTE ON intplan related doc. In this method, distributed replicated, hash, or random all can work.

create OR replace function secure_Test() returns TABLE(testNumberReturn integer)
language plpgsql as
$$ BEGIN return query SELECT testNumber from testNumbers; END $$
execute on initplan;

Понравилась статья? Поделить с друзьями:
  • Error func no 8 hifi image
  • Error func no 11 recovery image error no 2 load failed
  • Error full join is only supported with merge joinable or hash joinable join conditions
  • Error ftp login failed
  • Error ftdiun2k ini not found press finish to exit