Важно: Данный раздел актуален для Платформы данных On-Premise.
В этом разделе рассматривается язык определения данных (DDL) в базе данных RT.Warehouse, а также способы создания и управления объектами базы данных.
Создание объектов в базе данных RT.Warehouse включает предварительный выбор параметров распределения данных, параметров хранения, загрузки данных и других функций RT.Warehouse, которые будут влиять на текущую производительность вашей системы баз данных. Понимание доступных вариантов и того, как будет использоваться база данных, поможет вам принять правильное решение.
Большинство расширенных функций RT.Warehouse доступны с расширениями операторов SQL CREATE DDL.
Система базы данных RT.Warehouse представляет собой отдельный экземпляр базы данных RT.Warehouse. Может быть установлено несколько отдельных систем базы данных RT.Warehouse, но обычно только одна выбирается настройками переменных среды.
В системе баз данных RT.Warehouse может быть несколько баз данных. Это отличается от некоторых систем управления базами данных (таких как Oracle), где экземпляром базы данных является сама база данных. Хотя вы можете создать множество баз данных в системе RT.Warehouse, клиентские программы могут одновременно подключаться и получать доступ только к одной базе данных — вы не можете выполнять перекрестные запросы между базами данных.
База данных RT.Warehouse предоставляет несколько шаблонов баз данных и базу данных по умолчанию, template1, template0 и postgres.
По умолчанию каждая новая база данных, которую вы создаете, основана на базе данных шаблона. База данных RT.Warehouse использует template1 для создания баз данных, если вы не укажете другой шаблон. Создание объектов в template1 не рекомендуется. Объекты будут в каждой базе данных, которую вы создаете, используя базу данных шаблона по умолчанию.
База данных RT.Warehouse использует другой шаблон базы данных, template0, для внутреннего использования. Не удаляйте и не изменяйте template0. Вы можете использовать template0 для создания полностью чистой базы данных, содержащей только стандартные объекты, предварительно определенные базой данных RT.Warehouse при инициализации.
Вы можете использовать базу данных postgres для первого подключения к базе данных RT.Warehouse. База данных RT.Warehouse использует postgres в качестве базы данных по умолчанию для административных подключений. Например, postgres используется процессами запуска, процессом глобального обнаружения взаимоблокировок и процессом FTS (Fault Tolerance Server) для доступа к каталогу.
Команда CREATE DATABASE создает новую базу данных. Например:
=> CREATE DATABASE new_dbname;
Чтобы создать базу данных, вы должны иметь права на создание базы данных или быть суперпользователем базы данных RT.Warehouse. Если у вас нет необходимых прав, вы не сможете создать базу данных. Обратитесь к администратору базы данных RT.Warehouse, чтобы он предоставил вам необходимые права или создал для вас базу данных.
Вы также можете использовать клиентскую программу createdb для создания базы данных. Например, выполнение следующей команды в терминале командной строки подключается к базе данных RT.Warehouse, используя предоставленное имя хоста и порт, и создает базу данных с именем mydatabase:
$ createdb -h masterhost -p 5432 mydatabase
Имя хоста и порт должны совпадать с именем хоста и портом установленной системы базы данных RT.Warehouse.
Некоторые объекты, такие как роли, являются общими для всех баз данных в системе баз данных RT.Warehouse. Другие объекты, такие как создаваемые вами таблицы, известны только в базе данных, в которой вы их создаете.
Внимание. Команда CREATE DATABASE не является транзакционной. |
По умолчанию новая база данных создается путем клонирования стандартного шаблона системной базы данных template1. Любую базу данных можно использовать в качестве шаблона при создании новой базы данных, тем самым обеспечивая возможность «клонирования» или копирования существующей базы данных и всех объектов и данных в этой базе данных. Например:
=> CREATE DATABASE new_dbname TEMPLATE old_dbname;
Другого владельца базы данных можно назначить при создании базы данных:
=> CREATE DATABASE new_dbname WITH owner=new_user;
Если вы работаете в клиентской программе psql, вы можете использовать мета-команду \l для отображения списка баз данных и шаблонов в вашей системе баз данных RT.Warehouse. Если вы используете другую клиентскую программу и являетесь суперпользователем, вы можете запросить список баз данных из таблицы системного каталога pg_database. Например:
=> SELECT datname from pg_database;
Команда ALTER DATABASE изменяет атрибуты базы данных, такие как владелец, имя или атрибуты конфигурации по умолчанию. Например, следующая команда изменяет базу данных, устанавливая путь поиска схемы по умолчанию (параметр конфигурации search_path):
=> ALTER DATABASE mydatabase SET search_path TO myschema, public, pg_catalog;
Чтобы изменить базу данных, вы должны быть владельцем базы данных или суперпользователем.
Команда DROP DATABASE или удаляет базу данных. Он удаляет записи системного каталога для базы данных и удаляет каталог базы данных на диске, содержащий данные. Вы должны быть владельцем базы данных или суперпользователем, чтобы удалить базу данных, и вы не можете удалить базу данных, пока вы или кто-либо еще подключен к ней. Подключитесь к postgres (или другой базе данных) перед удалением базы данных. Например:
=> \c postgres
=> DROP DATABASE mydatabase;
Вы также можете использовать клиентскую программу dropdb для удаления базы данных. Например, следующая команда подключается к базе данных RT.Warehouse, используя предоставленное имя хоста и порт, и удаляет базу данных mydatabase:
$ dropdb -h masterhost -p 5432 mydatabase
Внимание. Удаление базы данных нельзя отменить. Команда DROP DATABASE не является транзакционной. |
Табличные пространства позволяют администраторам баз данных иметь несколько файловых систем на машине и решать, как лучше всего использовать физическое хранилище для хранения объектов базы данных. Табличные пространства позволяют назначать различное хранилище для часто и редко используемых объектов базы данных или контролировать производительность ввода-вывода для определенных объектов базы данных. Например, поместите часто используемые таблицы в файловые системы, использующие высокопроизводительные твердотельные накопители (SSD), а другие таблицы — на стандартные жесткие диски.
Для табличного пространства требуется местоположение файловой системы хоста для хранения файлов базы данных. В базе данных RT.Warehouse расположение файловой системы должно существовать на всех хостах, включая хосты, на которых работает мастер, резервный мастер, каждый основной сегмент и каждый зеркальный сегмент.
Табличное пространство — это системный объект базы данных RT.Warehouse (глобальный объект), вы можете использовать табличное пространство из любой базы данных, если у вас есть соответствующие привилегии.
Примечание. База данных RT.Warehouse не поддерживает разные расположения табличных пространств для пары основное-зеркало с одинаковым идентификатором контента. Можно настроить только разные местоположения для разных идентификаторов контента. Не изменяйте символические ссылки в каталоге pg_tblspc, чтобы пары основное-зеркало указывали на разные расположения файлов; это приведет к ошибочному поведению. |
Команда CREATE TABLESPACE определяет табличное пространство. Например:
CREATE TABLESPACE fastspace LOCATION '/fastdisk/gpdb';
Суперпользователи базы данных определяют табличные пространства и предоставляют доступ пользователям базы данных с помощью команды GRANTCREATE. Например:
GRANT CREATE ON TABLESPACE fastspace TO admin;
Пользователи с привилегией CREATE для табличного пространства могут создавать объекты базы данных в этом табличном пространстве, такие как таблицы, индексы и базы данных. Команда:
CREATE TABLE tablename(options) TABLESPACE spacename
Например, следующая команда создает таблицу в табличном пространстве space1:
CREATE TABLE foo(i int) TABLESPACE space1;
Вы также можете использовать параметр default_tablespace, чтобы указать табличное пространство по умолчанию для команд CREATE TABLE и CREATE INDEX, которые не указывают табличное пространство:
SET default_tablespace = space1;
CREATE TABLE foo(i int);
Существует также параметр конфигурации temp_tablespaces, который определяет размещение временных таблиц и индексов, а также временных файлов, используемых для таких целей, как сортировка больших наборов данных. Это может быть список имен табличных пространств, разделенных запятыми, а не только одно, чтобы нагрузка, связанная с временными объектами, могла быть распределена по нескольким табличным пространствам. Каждый раз при создании временного объекта выбирается случайный элемент списка.
Табличное пространство, связанное с базой данных, хранит системные каталоги этой базы данных, временные файлы, созданные серверными процессами, использующими эту базу данных, и является табличным пространством по умолчанию, выбранным для таблиц и индексов, созданных в базе данных, если при создании объектов не указано TABLESPACE. Если вы не укажете табличное пространство при создании базы данных, база данных будет использовать то же самое табличное пространство, что и ее шаблонная база данных.
Вы можете использовать табличное пространство из любой базы данных в системе базы данных RT.Warehouse, если у вас есть соответствующие привилегии.
Каждая система базы данных RT.Warehouse имеет следующие табличные пространства по умолчанию.
Эти табличные пространства используют расположение системы по умолчанию, расположение каталогов данных, созданное при инициализации системы.
Чтобы просмотреть информацию о табличном пространстве, используйте таблицу каталогов pg_tablespace, чтобы получить идентификатор объекта (OID) табличного пространства, а затем используйте функцию gp_tablespace_location() для отображения каталогов табличного пространства. Это пример, в котором указано одно определяемое пользователем табличное пространство, myspace:
SELECT oid, * FROM pg_tablespace ;
oid | spcname | spcowner | spcacl | spcoptions
-------+------------+----------+--------+------------
1663 | pg_default | 10 | |
1664 | pg_global | 10 | |
16391 | myspace | 10 | |
(3 rows)
OID для табличного пространства myspace — 16391. Запустите gp_tablespace_location(), чтобы отобразить расположения табличных пространств для системы, состоящей из двух экземпляров сегментов и мастера.
# SELECT * FROM gp_tablespace_location(16391);
gp_segment_id | tblspc_loc
---------------+------------------
0 | /data/mytblspace
1 | /data/mytblspace
-1 | /data/mytblspace
(3 rows)
Этот запрос использует gp_tablespace_location() таблицу каталога gp_segment_configuration для отображения информации об экземпляре сегмента с расположением в файловой системе для табличного пространства myspace.
WITH spc AS (SELECT * FROM gp_tablespace_location(16391))
SELECT seg.role, spc.gp_segment_id as seg_id, seg.hostname, seg.datadir, tblspc_loc
FROM spc, gp_segment_configuration AS seg
WHERE spc.gp_segment_id = seg.content ORDER BY seg_id;
Это информация для тестовой системы, состоящей из двух экземпляров сегмента и мастера на одном хосте.
role | seg_id | hostname | datadir | tblspc_loc
------+--------+----------+----------------------+------------------
p | -1 | testhost | /data/master/gpseg-1 | /data/mytblspace
p | 0 | testhost | /data/data1/gpseg0 | /data/mytblspace
p | 1 | testhost | /data/data2/gpseg1 | /data/mytblspace
(3 rows)
Чтобы удалить табличное пространство, вы должны быть владельцем табличного пространства или суперпользователем. Вы не можете удалить табличное пространство, пока не будут удалены все объекты во всех базах данных, использующих это табличное пространство.
Команда DROP TABLESPACE удаляет пустое табличное пространство.
Примечание. Вы не можете удалить табличное пространство, если оно не пусто или в нем хранятся временные файлы или файлы транзакций. |
Вы можете переместить временные файлы или файлы транзакций в определенное табличное пространство, чтобы улучшить производительность базы данных при выполнении запросов, создании резервных копий и более последовательном хранении данных.
Параметр конфигурации сервера базы данных RT.Warehouse temp_tablespaces управляет расположением как временных таблиц, так и временных spill - файлов для запросов хеш-агрегации и хэш-соединения. (Временные файлы для таких целей, как сортировка больших наборов данных, хранятся вместе с общими данными сегмента в <data_dir>/<seg_ID>/base/pgsql_tmp.)
temp_tablespaces задает табличные пространства, в которых создаются временные объекты (временные таблицы и индексы для временных таблиц), когда команда CREATE явно не указывает табличное пространство.
Также обратите внимание на следующую информацию о временных файлах или файлах транзакций:
Схемы логически организуют объекты и данные в базе данных. Схемы позволяют иметь в базе данных более одного объекта (например, таблицы) с одинаковым именем без конфликтов, если объекты находятся в разных схемах.
Каждая база данных имеет схему по умолчанию с именем public. Если вы не создаете никаких схем, объекты создаются в общедоступной схеме. Все роли базы данных (пользователи) имеют привилегии CREATE и USAGE в общедоступной схеме. Когда вы создаете схему, вы предоставляете своим пользователям права доступа к схеме.
Используйте команду CREATE SCHEMA для создания новой схемы. Например:
=> CREATE SCHEMA myschema;
Чтобы создать или получить доступ к объектам в схеме, напишите полное имя, состоящее из имени схемы и имени таблицы, разделенных точкой. Например:
myschema.table
Вы можете создать схему, принадлежащую кому-то другому, например, чтобы ограничить действия ваших пользователей четко определенными пространствами имен. Синтаксис:
=> CREATE SCHEMA schemaname AUTHORIZATION username;
Чтобы указать расположение объекта в базе данных, используйте имя с указанием схемы. Например:
=> SELECT * FROM myschema.mytable;
Вы можете установить параметр конфигурации search_path, чтобы указать порядок поиска объектов в доступных схемах. Схема, указанная первой в пути поиска, становится схемой по умолчанию. Если схема не указана, объекты создаются в схеме по умолчанию.
Параметр конфигурации search_path задает порядок поиска схемы. Команда ALTER DATABASE устанавливает путь поиска. Например:
=> ALTER DATABASE mydatabase SET search_path TO myschema,
public, pg_catalog;
Вы также можете установить search_path для конкретной роли (пользователя) с помощью команды ALTER ROLE. Например:
=> ALTER ROLE sally SET search_path TO myschema, public,
pg_catalog;
Используйте функцию current_schema() для просмотра текущей схемы. Например:
=> SELECT current_schema();
Используйте команду SHOW для просмотра текущего пути поиска. Например:
=> SHOW search_path;
Используйте команду DROP SCHEMA, чтобы удалить схему. Например:
=> DROP SCHEMA myschema;
По умолчанию схема должна быть пустой, прежде чем ее можно будет удалить. Чтобы удалить схему и все ее объекты (таблицы, данные, функции и т. д.), используйте:
=> DROP SCHEMA myschema CASCADE;
В каждой базе данных существуют следующие схемы системного уровня:
Таблицы базы данных RT.Warehouse аналогичны таблицам любой реляционной базы данных, за исключением того, что строки таблицы распределены по разным сегментам системы. При создании таблицы вы указываете политику распределения таблицы.
Команда CREATE TABLE создает таблицу и определяет ее структуру. При создании таблицы вы определяете:
Тип данных столбца определяет типы значений данных, которые может содержать столбец. Выберите тип данных, который занимает наименьшее возможное пространство, но при этом может вместить ваши данные и наилучшим образом ограничивает данные. Например, используйте символьные типы данных для строк, типы данных даты или отметки времени для дат и числовые типы данных для чисел.
Для столбцов таблицы, содержащих текстовые данные, укажите тип данных VARCHAR или TEXT. Не рекомендуется указывать тип данных CHAR. В базе данных RT.Warehouse типы данных VARCHAR или TEXT обрабатывают отступы, добавляемые к данным (символы пробела добавляются после последнего символа, отличного от пробела) как значащие символы, а тип данных CHAR — нет.
Используйте наименьший числовой тип данных, который будет вмещать ваши числовые данные и позволит в будущем расширяться. Например, использование BIGINT для данных, которые умещаются в INT или SMALLINT, приводит к пустой трате места для хранения. Если вы ожидаете, что ваши значения данных со временем будут увеличиваться, учтите, что переход от меньшего типа данных к большему типу данных после загрузки больших объемов данных является дорогостоящим. Например, если ваши текущие значения данных укладываются в SMALLINT, но есть вероятность, что значения будут расширяться, INT — лучший выбор в долгосрочной перспективе.
Используйте те же типы данных для столбцов, которые вы планируете использовать в соединениях между таблицами. Соединения между таблицами обычно используют первичный ключ в одной таблице и внешний ключ в другой таблице. Когда типы данных различаются, база данных должна преобразовать один из них, чтобы можно было правильно сравнивать значения данных, что добавляет ненужные накладные расходы.
База данных RT.Warehouse имеет богатый набор собственных типов данных, доступных пользователям.
Вы можете определить ограничения для столбцов и таблиц, чтобы ограничить данные в ваших таблицах. База данных RT.Warehouse поддерживает ограничения так же, как PostgreSQL, с некоторыми ограничениями, в том числе:
Примечание. Ограничения UNIQUE и PRIMARY KEY не разрешены для таблиц, оптимизированных для добавления, поскольку индексы UNIQUE, созданные ограничениями, не разрешены для таблиц, оптимизированных для добавления. |
Проверочные ограничения позволяют указать, что значение в определенном столбце должно удовлетворять логическому выражению (значение истинности). Например, требуется, чтобы цены на товары были положительными:
=> CREATE TABLE products
( product_no integer,
name text,
price numeric CHECK (price > 0) );
Ограничения Not-NULL указывают, что столбец не должен принимать нулевое значение. Непустое ограничение всегда записывается как ограничение столбца. Например:
=> CREATE TABLE products
( product_no integer NOT NULL,
name text NOT NULL,
price numeric );
Ограничения по уникальности гарантируют, что данные, содержащиеся в столбце или группе столбцов, уникальны по отношению ко всем строкам таблицы. Таблица должна быть распределена по хешу или реплицирована (не РАСПРОСТРАНЕНА СЛУЧАЙНО). Если таблица распределена по хэшу, столбцы ограничения должны совпадать со столбцами ключа распределения таблицы (или являться их надмножеством). Например:
=> CREATE TABLE products
( product_no integer UNIQUE,
name text,
price numeric)
DISTRIBUTED BY (product_no);
Ограничение первичного ключа представляет собой комбинацию ограничения UNIQUE и ограничения NOT NULL. Таблица должна быть распределена по хэшу (не РАСПРОСТРАНЕНО СЛУЧАЙНО), а столбцы первичного ключа должны быть такими же (или надмножеством) столбцов ключа распределения таблицы. Если у таблицы есть первичный ключ, этот столбец (или группа столбцов) по умолчанию выбирается в качестве ключа распределения для таблицы. Например:
=> CREATE TABLE products
( product_no integer PRIMARY KEY,
name text,
price numeric)
DISTRIBUTED BY (product_no);
Ограничения внешнего ключа указывают, что значения в столбце или группе столбцов должны совпадать со значениями, отображаемыми в некоторой строке другой таблицы, чтобы поддерживать ссылочную целостность между двумя связанными таблицами. Проверки ссылочной целостности не могут быть применены между сегментами распределенных таблиц базы данных RT.Warehouse.
Внешние ключи не поддерживаются. Вы можете объявить их, но ссылочная целостность не обеспечивается.
Все таблицы базы данных RT.Warehouse распределены. Когда вы создаете или изменяете таблицу, вы дополнительно указываете DISTRIBUTED BY (хеш-распределение), DISTRIBUTED RANDOMLY (циклическое распределение) или DISTRIBUTED REPLICATED (полностью распределенное), чтобы определить распределение строк в таблице.
Примечание. Параметр конфигурации сервера базы данных RT.Warehouse gp_create_table_random_default_distribution управляет политикой распределения таблиц, если предложение DISTRIBUTED BY не указано при создании таблицы. |
При выборе политики распределения таблиц учитывайте следующие моменты:
Политику распределения реплицированных таблиц (DISTRIBUTED REPLICATED) следует использовать только для небольших таблиц. Репликация данных в каждый сегмент требует больших затрат как с точки зрения хранения, так и с точки зрения обслуживания, а также непозволительна для больших таблиц фактов. Основные варианты использования реплицированных таблиц:
Примечание. На скрытые системные столбцы (ctid, cmin, cmax, xmin, xmax и gp_segment_id) нельзя ссылаться в пользовательских запросах к реплицированным таблицам, поскольку они не имеют единого однозначного значения. База данных RT.Warehouse возвращает для запроса ошибку “столбца не существует”. |
Необязательные предложения REATE TABLE DISTRIBUTED BY, DISTRIBUTED RANDOMLY и DISTRIBUTED REPLICATED определяют политику распределения для таблицы. По умолчанию используется политика распределения хэшей, в которой в качестве ключа распределения используется либо ПЕРВИЧНЫЙ КЛЮЧ (если он есть в таблице), либо первый столбец таблицы. Столбцы с геометрическими или определяемыми пользователем типами данных не подходят в качестве столбцов ключа распределения базы данных RT.Warehouse. Если в таблице нет подходящего столбца, база данных RT.Warehouse распределяет строки случайным образом или циклически.
Реплицированные таблицы не имеют ключа распределения, поскольку каждая строка распространяется на каждый экземпляр сегмента базы данных RT.Warehouse.
Чтобы обеспечить равномерное распределение хэш-данных, выберите ключ распределения, уникальный для каждой записи. Если это невозможно, выберите DISTRIBUTED RANDOMLY (случайное распределение). Например:
=> CREATE TABLE products
(name varchar(40),
prod_id integer,
supplier_id integer)
DISTRIBUTED BY (prod_id);
=> CREATE TABLE random_stuff
(things text,
doodads text,
etc text)
DISTRIBUTED RANDOMLY;
Примечание. Если первичный ключ существует, он является ключом распределения по умолчанию для таблицы. Если первичный ключ не существует, но существует уникальный ключ, это ключ распределения по умолчанию для таблицы. |
Хеш-функция, используемая для политики распределения хэшей, определяется классом хеш-операторов для типа данных столбца. По умолчанию база данных RT.Warehouse использует по умолчанию класс хеш-операторов для типа данных, тот же класс операторов, который используется для хэш-соединений и хэш-агрегатов, что подходит для большинства случаев использования. Однако вы можете объявить класс хэш-операторов не по умолчанию в предложении DISTRIBUTED BY.
Использование пользовательского класса хеш-операторов может быть полезно для поддержки совмещенных соединений с оператором, отличным от оператора равенства по умолчанию (=).
В этом примере создается пользовательский класс хэш-операторов для целочисленного типа данных, который используется для повышения производительности запросов. Класс операторов сравнивает абсолютные значения целых чисел.
Создайте функцию и оператор равенства, который возвращает true, если абсолютные значения двух целых чисел равны.
CREATE FUNCTION abseq(int, int) RETURNS BOOL AS
$$
begin return abs($1) = abs($2); end;
$$ LANGUAGE plpgsql STRICT IMMUTABLE;
CREATE OPERATOR |=| (
PROCEDURE = abseq,
LEFTARG = int,
RIGHTARG = int,
COMMUTATOR = |=|,
hashes, merges);
Теперь создайте хэш-функцию и класс оператора, который использует этот оператор.
CREATE FUNCTION abshashfunc(int) RETURNS int AS
$$
begin return hashint4(abs($1)); end;
$$ LANGUAGE plpgsql STRICT IMMUTABLE;
CREATE OPERATOR CLASS abs_int_hash_ops FOR TYPE int4
USING hash AS
OPERATOR 1 |=|,
FUNCTION 1 abshashfunc(int);
Кроме того, создайте операторы меньше и больше, а также класс операторов btree для них. Нам они не нужны для наших запросов, но Postgres Planner не будет рассматривать совместное размещение объединений без них.
CREATE FUNCTION abslt(int, int) RETURNS BOOL AS
$$
begin return abs($1) < abs($2); end;
$$ LANGUAGE plpgsql STRICT IMMUTABLE;
CREATE OPERATOR |<| (
PROCEDURE = abslt,
LEFTARG = int,
RIGHTARG = int);
CREATE FUNCTION absgt(int, int) RETURNS BOOL AS
$$
begin return abs($1) > abs($2); end;
$$ LANGUAGE plpgsql STRICT IMMUTABLE;
CREATE OPERATOR |>| (
PROCEDURE = absgt,
LEFTARG = int,
RIGHTARG = int);
CREATE FUNCTION abscmp(int, int) RETURNS int AS
$$
begin return btint4cmp(abs($1),abs($2)); end;
$$ LANGUAGE plpgsql STRICT IMMUTABLE;
CREATE OPERATOR CLASS abs_int_btree_ops FOR TYPE int4
USING btree AS
OPERATOR 1 |<|,
OPERATOR 3 |=|,
OPERATOR 5 |>|,
FUNCTION 1 abscmp(int, int);
Теперь вы можете использовать собственный класс хеш-операторов в таблицах.
CREATE TABLE atab (a int) DISTRIBUTED BY (a abs_int_hash_ops);
CREATE TABLE btab (b int) DISTRIBUTED BY (b abs_int_hash_ops);
INSERT INTO atab VALUES (-1), (0), (1);
INSERT INTO btab VALUES (-1), (0), (1), (2);
Запросы, выполняющие соединение с использованием пользовательского оператора равенства |=| можно воспользоваться совместным размещением.
С целочисленным операционным классом по умолчанию для этого запроса требуются узлы Redistribute Motion.
EXPLAIN (COSTS OFF) SELECT a, b FROM atab, btab WHERE a = b;
QUERY PLAN
------------------------------------------------------------------
Gather Motion 4:1 (slice3; segments: 4)
-> Hash Join
Hash Cond: (atab.a = btab.b)
-> Redistribute Motion 4:4 (slice1; segments: 4)
Hash Key: atab.a
-> Seq Scan on atab
-> Hash
-> Redistribute Motion 4:4 (slice2; segments: 4)
Hash Key: btab.b
-> Seq Scan on btab
Optimizer: Postgres query optimizer
(11 rows)
С пользовательским операционным классом возможен более эффективный план.
EXPLAIN (COSTS OFF) SELECT a, b FROM atab, btab WHERE a |=| b;
QUERY PLAN
------------------------------------------------------------------
Gather Motion 4:1 (slice1; segments: 4)
-> Hash Join
Hash Cond: (atab.a |=| btab.b)
-> Seq Scan on atab
-> Hash
-> Seq Scan on btab
Optimizer: Postgres query optimizer
(7 rows)
База данных RT.Warehouse поддерживает несколько моделей хранения и сочетание моделей хранения. Когда вы создаете таблицу, вы выбираете, как хранить ее данные. В этом разделе объясняются варианты хранения таблиц и способы выбора лучшей модели хранилища для вашей рабочей нагрузки.
Примечание. Чтобы упростить создание таблиц базы данных, вы можете указать значения по умолчанию для некоторых параметров хранения таблиц с помощью параметра конфигурации сервера базы данных RT.Warehouse gp_default_storage_options. |
По умолчанию база данных RT.Warehouse использует ту же модель хранения heap (куча), что и PostgreSQL. Хранилище таблиц в heap лучше всего работает с рабочими нагрузками типа OLTP, когда данные часто изменяются после первоначальной загрузки. Операции UPDATE и DELETE требуют хранения информации о версиях на уровне строк для обеспечения надежной обработки транзакций базы данных. Таблицы кучи лучше всего подходят для небольших таблиц, таких как таблицы измерений, которые часто обновляются после первоначальной загрузки.
Таблицы heap (кучи), ориентированные на строки, являются типом хранилища по умолчанию.
=> CREATE TABLE foo (a int, b text) DISTRIBUTED BY (a);
Используйте предложение WITH команды CREATE TABLE, чтобы объявить параметры хранения таблицы. По умолчанию таблица создается как обычная таблица хранения кучи, ориентированная на строки. Например, чтобы создать таблицу, оптимизированную для добавления, без сжатия:
=> CREATE TABLE bar (a int, b text)
WITH (appendoptimized=true)
DISTRIBUTED BY (a);
Примечание. Используйте синтаксис appendoptimized=value, чтобы указать тип хранения таблицы, оптимизированный для добавления.append optimized - это упрощенный алиас для устаревшего варианта хранения только для добавления. База данных RT.Warehouse сохраняет только добавление в каталоге и отображает то же самое при перечислении вариантов хранения для таблиц, оптимизированных для добавления. |
UPDATE и DELETE не разрешены для таблиц, оптимизированных для добавления в повторяющихся транзакциях чтения или сериализуемых транзакциях, это приведет к прерыванию транзакции. CLUSTER, DECLARE...FOR UPDATE и триггеры не поддерживаются в таблицах, оптимизированных для добавления.
Хранилище таблиц, оптимизированное для добавления, лучше всего работает с денормализованными таблицами фактов в среде хранилища данных. Денормализованные таблицы фактов обычно являются самыми большими таблицами в системе. Таблицы фактов обычно загружаются пакетами, и доступ к ним осуществляется с помощью запросов только для чтения. Перемещение больших таблиц фактов в модель хранения, оптимизированную для добавления, устраняет накладные расходы на хранение информации о видимости обновления для каждой строки, экономя около 20 байтов на строку. Это позволяет сделать структуру страницы более компактной и легкой для оптимизации. Модель хранения таблиц, оптимизированных для добавления, оптимизирована для массовой загрузки данных. Однострочные операторы INSERT не рекомендуются.
RT.Warehouse предоставляет выбор модели ориентации хранилища: строка, столбец или их комбинация. В этом разделе приведены общие рекомендации по выбору оптимальной ориентации таблицы при хранении. Оценивайте производительность, используя собственные рабочие нагрузки данных и запросов.
Для большинства общих или смешанных рабочих нагрузок построчное хранилище предлагает наилучшее сочетание гибкости и производительности. Однако есть случаи использования, когда модель хранения, ориентированная на столбцы, обеспечивает более эффективный ввод-вывод и хранение. При выборе модели ориентации хранения для таблицы учитывайте следующие требования:
SELECT SUM(salary)...
SELECT AVG(salary)... WHERE salary > 10000
Или когда предикат WHERE относится к одному столбцу и возвращает относительно небольшое количество строк. Например:
SELECT salary, dept ... WHERE state='CA'
Предложение WITH команды CREATE TABLE указывает параметры хранения таблицы. По умолчанию используется таблица кучи, ориентированная на строки. Таблицы, использующие хранилище, ориентированное на столбцы, должны быть таблицами, оптимизированными для добавления. Например, чтобы создать таблицу, ориентированную на столбцы:
=> CREATE TABLE bar (a int, b text)
WITH (appendoptimized=true, orientation=column)
DISTRIBUTED BY (a);
В базе данных RT.Warehouse доступны два типа сжатия в базе данных для таблиц, оптимизированных для добавления:
В следующей таблице приведены доступные алгоритмы сжатия.
Таблица 112. Алгоритмы сжатия для таблиц, оптимизированных для добавления
Ориентация таблицы | Доступные типы сжатия | Поддерживаемые алгоритмы |
---|---|---|
Строка (row) | Таблица | ZLIB, ZSTD и QUICKLZ |
Столбец (column) | Таблица и Столбец | RLE_TYPE, ZLIB, ZSTD и QUICKLZ |
Примечание. Сжатие QUICKLZ недоступно в версии базы данных RT.Warehouse с открытым исходным кодом. |
При выборе типа и уровня сжатия для таблиц, оптимизированных для добавления, учитывайте следующие факторы:
Примечание. Не создавайте сжатые таблицы, оптимизированные для добавления, в файловых системах, использующих сжатие. Если файловая система, в которой находится ваш каталог данных сегмента, является сжатой файловой системой, ваша таблица, оптимизированная для добавления, не должна использовать сжатие. |
Производительность со сжатыми таблицами, оптимизированными для добавления, зависит от оборудования, параметров настройки запросов и других факторов. Вы должны выполнить сравнительное тестирование, чтобы определить фактическую производительность в вашей среде.
Примечание. Уровень сжатия Zstd может быть установлен на значения от 1 до 19. Уровень сжатия QuickLZ может быть установлен только на уровень 1; другие значения недоступны. Уровень сжатия с zlib может быть установлен на значения от 1 до 9. Уровень сжатия с RLE может быть установлен на значения от 1 до 4. Предложение ENCODING указывает тип и уровень сжатия для отдельных столбцов. Когда предложение ENCODING конфликтует с предложением WITH, предложение ENCODING имеет более высокий приоритет, чем предложение WITH. |
Предложение WITH команды CREATE TABLE объявляет параметры хранения таблицы. Таблицы, использующие сжатие, должны быть таблицами, оптимизированными для добавления. Например, чтобы создать оптимизированную для добавления таблицу со сжатием zlib при уровне сжатия 5:
=> CREATE TABLE foo (a int, b text)
WITH (appendoptimized=true, compresstype=zlib, compresslevel=5);
RT.Warehouse предоставляет встроенные функции для проверки степени сжатия и распределения таблицы, оптимизированной для добавления. Функции принимают либо идентификатор объекта, либо имя таблицы. Вы можете уточнить имя таблицы с помощью имени схемы.
Таблица 113. Функции для сжатых метаданных таблиц, оптимизированных для добавления
Функция | Возвращаемый тип | Описание |
---|---|---|
get_ao_distribution(name) get_ao_distribution(oid) |
Набор строк (dbid, tuplecount) | Показывает распределение строк таблицы, оптимизированной для добавления, в массиве. Возвращает набор строк, каждая из которых включает сегмент dbid и количество кортежей, хранящихся в сегменте. |
get_ao_compression_ratio(name) get_ao_compression_ratio(oid) |
float8 | Вычисляет коэффициент сжатия для сжатой таблицы, оптимизированной для добавления. Если информация недоступна, эта функция возвращает значение -1. |
Коэффициент сжатия возвращается как общий коэффициент. Например, возвращаемое значение 3,19 или 3,19:1 означает, что несжатая таблица немного больше, чем в три раза превышает размер сжатой таблицы.
Распределение таблицы возвращается в виде набора строк, указывающих, сколько кортежей хранится в каждом сегменте. Например, в системе с четырьмя основными сегментами со значениями dbid в диапазоне от 0 до 3 функция возвращает четыре строки, подобные следующим:
=# SELECT get_ao_distribution('lineitem_comp');
get_ao_distribution
---------------------
(0,7500721)
(1,7501365)
(2,7499978)
(3,7497731)
(4 rows)
База данных RT.Warehouse поддерживает кодирование длин серий (RLE) для сжатия на уровне столбцов. Сжатие данных RLE сохраняет повторяющиеся данные как одно значение данных и количество. Например, в таблице с двумя столбцами, датой и описанием, которая содержит 200 000 записей, содержащих значение date1, и 400 000 записей, содержащих значение date2, сжатие RLE для поля даты аналогично date1 200000 date2 400000. RLE бесполезен. с файлами, которые не содержат больших наборов повторяющихся данных, так как это может значительно увеличить размер файла.
Доступны четыре уровня сжатия RLE. Уровни постепенно увеличивают степень сжатия, но уменьшают скорость сжатия.
База данных RT.Warehouse сочетает дельта-сжатие со сжатием RLE для данных в столбцах типа BIGINT, INTEGER, DATE, TIME или TIMESTAMP. Алгоритм дельта-сжатия основан на изменении между последовательными значениями столбцов и предназначен для улучшения сжатия, когда данные загружаются в отсортированном порядке или когда сжатие применяется к данным в отсортированном порядке.
Вы можете добавить следующие директивы хранения в столбец для таблиц, оптимизированных для добавления, с ориентацией столбца:
Добавьте директивы хранилища с помощью команд CREATE TABLE, ALTER TABLE и CREATE TYPE.
В следующей таблице подробно описаны типы директив хранения и возможные значения для каждого из них.
Таблица 114. Директивы хранилища для сжатия на уровне столбцов
Название | Определение | Значение | Комментарий |
---|---|---|---|
compresstype | Тип сжатия |
zstd: алгоритм ZStandard zlib: алгоритм выкачки quicklz: быстрое сжатие RLE_TYPE: непрерывное кодирование нет: без сжатия |
Значения не чувствительны к регистру. |
compresslevel | Уровень сжатия | zlib сжатие: 1-9 |
1 — самый быстрый метод с наименьшим сжатием. 1 по умолчанию. 9 — самый медленный метод с наибольшим сжатием. |
zstd сжатие: 1-19 |
1 — самый быстрый метод с наименьшим сжатием. 1 по умолчанию. 19 — самый медленный метод с наибольшим сжатием. |
||
Сжатие QuickLZ: 1 — использовать сжатие |
1 по умолчанию. | ||
Сжатие RLE_TYPE: 1–4 1 - применять только RLE
2 - применить RLE, затем применить уровень сжатия zlib 1
3 - применить RLE, затем применить уровень сжатия zlib 5
4 - применить RLE, затем применить уровень сжатия zlib 9 |
1 — самый быстрый метод с наименьшим сжатием. 4 — самый медленный метод с наибольшим сжатием. 1 по умолчанию. |
||
blocksize | Размер в байтах для каждого блока в таблице | 8192 – 2097152 | Значение должно быть кратно 8192. |
Ниже приведен формат добавления директив хранилища.
[ ENCODING ( storage_directive [,…] ) ]
Формат, где требуется слово ENCODING, а директива хранения состоит из трех частей:
Разделите несколько директив хранилища запятой. Примените директиву хранилища к одному столбцу или назначьте ее директивой по умолчанию для всех столбцов, как показано в следующих предложениях CREATE TABLE.
Общее использование:
column_name data_type ENCODING ( storage_directive [, … ] ), …
COLUMN column_name ENCODING ( storage_directive [, … ] ), …
DEFAULT COLUMN ENCODING ( storage_directive [, … ] )
Пример:
C1 char ENCODING (compresstype=quicklz, blocksize=65536)
COLUMN C1 ENCODING (compresstype=zlib, compresslevel=6, blocksize=65536)
DEFAULT COLUMN ENCODING (compresstype=quicklz)
Если тип сжатия, уровень сжатия и размер блока не определены, по умолчанию сжатие отсутствует, а размер блока устанавливается равным параметру конфигурации сервера block_size.
Параметры сжатия столбцов наследуются от уровня типа до уровня таблицы, от уровня раздела до уровня подраздела. Настройки самого низкого уровня имеют приоритет.
Примечание. Предложение INHERITS не допускается в таблице, содержащей директиву хранения или директиву хранения ссылок на столбцы. Таблицы, созданные с использованием предложения LIKE, игнорируют директивы хранения и директивы хранения ссылок на столбцы. |
Лучше всего установить параметры сжатия столбца на том уровне, на котором находятся данные. См. пример 5, в котором показана таблица с глубиной раздела 2. Сжатие RLE_TYPE добавляется к столбцу на уровне подраздела.
В следующих примерах показано использование директив хранения в операторах CREATE TABLE.
В этом примере столбец c1 сжат с помощью zstd и использует размер блока, определенный системой. Столбец c2 сжат с помощью quicklz и использует размер блока 65 536. Столбец c3 не сжат и использует размер блока, определенный системой.
CREATE TABLE T1 (c1 int ENCODING (compresstype=zstd),
c2 char ENCODING (compresstype=quicklz, blocksize=65536),
c3 char) WITH (appendoptimized=true, orientation=column);
В этом примере столбец c1 сжат с помощью zlib и использует размер блока, определенный системой. Столбец c2 сжат с помощью quicklz и использует размер блока 65 536. Столбец c3 сжат с использованием RLE_TYPE и использует размер блока, определенный системой.
CREATE TABLE T2 (c1 int ENCODING (compresstype=zlib),
c2 char ENCODING (compresstype=quicklz, blocksize=65536),
c3 char,
COLUMN c3 ENCODING (compresstype=RLE_TYPE)
)
WITH (appendoptimized=true, orientation=column);
В этом примере столбец c1 сжат с помощью zlib и использует размер блока, определенный системой. Столбец c2 сжат с помощью quicklz и использует размер блока 65536. Столбец c3 сжат с помощью zlib и использует размер блока, определенный системой. Обратите внимание, что столбец c3 использует zlib (не RLE_TYPE) в разделах, потому что хранилище столбцов в предложении раздела имеет приоритет над директивой хранилища в определении столбца для таблицы.
CREATE TABLE T3 (c1 int ENCODING (compresstype=zlib),
c2 char ENCODING (compresstype=quicklz, blocksize=65536),
c3 text, COLUMN c3 ENCODING (compresstype=RLE_TYPE) )
WITH (appendoptimized=true, orientation=column)
PARTITION BY RANGE (c3) (START ('1900-01-01'::DATE)
END ('2100-12-31'::DATE),
COLUMN c3 ENCODING (compresstype=zlib));
В этом примере CREATE TABLE присваивает директиве хранилища zlib compresstype значение c1. Столбец c2 не имеет директивы хранения и наследует тип сжатия (quicklz) и размер блока (65536) из предложения DEFAULT COLUMN ENCODING.
Предложение ENCODING столбца c3 определяет его тип сжатия, RLE_TYPE. Предложение ENCODING, определенное для определенного столбца, переопределяет предложение DEFAULT ENCODING, поэтому в столбце c3 используется размер блока по умолчанию, 32768.
Столбец c4 имеет тип сжатия none и использует размер блока по умолчанию.
CREATE TABLE T4 (c1 int ENCODING (compresstype=zlib),
c2 char,
c3 text,
c4 smallint ENCODING (compresstype=none),
DEFAULT COLUMN ENCODING (compresstype=quicklz,
blocksize=65536),
COLUMN c3 ENCODING (compresstype=RLE_TYPE)
)
WITH (appendoptimized=true, orientation=column);
В этом примере создается оптимизированная для добавления столбцов таблица T5. T5 имеет два раздела, p1 и p2, каждый из которых имеет подразделы. Каждый подраздел имеет предложения ENCODING:
CREATE TABLE T5(i int, j int, k int, l int)
WITH (appendoptimized=true, orientation=column)
PARTITION BY range(i) SUBPARTITION BY range(j)
(
partition p1 start(1) end(2)
( subpartition sp1 start(1) end(2)
column i encoding(compresstype=zlib, blocksize=65536)
),
partition p2 start(2) end(3)
( subpartition sp1 start(1) end(2)
column i encoding(compresstype=rle_type)
column k encoding(blocksize=8192)
)
);
Когда вы создаете новый тип, вы можете определить для него атрибуты сжатия по умолчанию. Например, следующая команда CREATE TYPE определяет тип с именем int33, который задает сжатие quicklz:
CREATE TYPE int33 (
internallength = 4,
input = int33_in,
output = int33_out,
alignment = int4,
default = 123,
passedbyvalue,
compresstype="quicklz",
blocksize=65536,
compresslevel=1
);
Когда вы указываете int33 в качестве типа столбца в команде CREATE TABLE, столбец создается с директивами хранения, которые вы указали для типа:
CREATE TABLE t2 (c1 int33)
WITH (appendoptimized=true, orientation=column);
Атрибуты хранения на уровне таблицы или столбца, указанные в определении таблицы, переопределяют атрибуты хранения на уровне типа.
Размер блока — это размер в байтах для каждого блока в таблице. Размер блока должен быть от 8192 до 2097152 байт и быть кратным 8192. Значение по умолчанию — 32768.
Указание больших размеров блоков может потреблять большие объемы памяти. Размер блока определяет буферизацию на уровне хранения. RT.Warehouse поддерживает буфер для каждого раздела и для каждого столбца в таблицах, ориентированных на столбцы. Таблицы с большим количеством разделов или столбцов потребляют большой объем памяти.
Команда ALTER TABLE изменяет определение таблицы. Используйте ALTER TABLE для изменения атрибутов таблицы, таких как определения столбцов, политика распределения, модель хранения и структура разделов. Например, чтобы добавить ненулевое ограничение в столбец таблицы:
=> ALTER TABLE address ALTER COLUMN street SET NOT NULL;
ALTER TABLE предоставляет опции для изменения политики распределения таблицы. При изменении параметров распределения таблицы данные таблицы могут быть перераспределены на диске, что может потребовать значительных ресурсов. Вы также можете перераспределять данные таблицы, используя существующую политику распространения.
Для многораздельных таблиц изменения политики распределения рекурсивно применяются к дочерним разделам. Эта операция сохраняет право собственности и все другие атрибуты таблицы. Например, следующая команда перераспределяет таблицу продаж по всем сегментам, используя столбец customer_id в качестве ключа распределения:
ALTER TABLE sales SET DISTRIBUTED BY (customer_id);
Когда вы изменяете распределение хэша таблицы, данные таблицы автоматически перераспределяются. Изменение политики распределения на случайное распределение не приводит к перераспределению данных. Например, следующая команда ALTER TABLE не имеет немедленного эффекта:
ALTER TABLE sales SET DISTRIBUTED RANDOMLY;
Изменение политики распределения таблицы на DISTRIBUTED REPLICATED или с DISTRIBUTED REPLICATED автоматически перераспределяет данные таблицы.
Чтобы перераспределить табличные данные для таблиц с политикой случайного распределения (или когда политика распределения хэшей не изменилась), используйте REORGANIZE=TRUE. Реорганизация данных может потребоваться для исправления проблемы перекоса данных или при добавлении в систему ресурсов сегмента. Например, следующая команда перераспределяет данные таблицы по всем сегментам, используя текущую политику распределения, включая случайное распределение.
ALTER TABLE sales SET WITH (REORGANIZE=TRUE);
Изменение политики распределения таблицы на DISTRIBUTED REPLICATED или с DISTRIBUTED REPLICATED всегда приводит к перераспределению данных таблицы, даже если вы используете REORGANIZE=FALSE.
Хранение таблицы, сжатие и ориентация могут быть объявлены только при создании. Чтобы изменить модель хранения, необходимо создать таблицу с правильными параметрами хранения, загрузить данные исходной таблицы в новую таблицу, удалить исходную таблицу и переименовать новую таблицу, используя имя исходной таблицы. Вы также должны повторно предоставить любые разрешения для таблиц. Например:
CREATE TABLE sales2 (LIKE sales)
WITH (appendoptimized=true, compresstype=quicklz,
compresslevel=1, orientation=column);
INSERT INTO sales2 SELECT * FROM sales;
DROP TABLE sales;
ALTER TABLE sales2 RENAME TO sales;
GRANT ALL PRIVILEGES ON sales TO admin;
GRANT SELECT ON sales TO guest;
Используйте команду ALTER TABLE, чтобы добавить сжатый столбец в таблицу.
В следующем примере показано, как добавить столбец со сжатием zlib в таблицу T1.
ALTER TABLE T1
ADD COLUMN c4 int DEFAULT 0
ENCODING (compresstype=zlib);
Раздел, добавленный в таблицу, в которой есть подразделы, определенные с параметрами сжатия, наследует параметры сжатия из подраздела. В следующем примере показано, как создать таблицу с кодировками подразделов, а затем изменить ее, чтобы добавить раздел.
CREATE TABLE ccddl (i int, j int, k int, l int)
WITH
(appendoptimized = TRUE, orientation=COLUMN)
PARTITION BY range(j)
SUBPARTITION BY list (k)
SUBPARTITION template(
SUBPARTITION sp1 values(1, 2, 3, 4, 5),
COLUMN i ENCODING(compresstype=ZLIB),
COLUMN j ENCODING(compresstype=QUICKLZ),
COLUMN k ENCODING(compresstype=ZLIB),
COLUMN l ENCODING(compresstype=ZLIB))
(PARTITION p1 START(1) END(10),
PARTITION p2 START(10) END(20))
;
ALTER TABLE ccddl
ADD PARTITION p3 START(20) END(30)
;
Выполнение команды ALTER TABLE создает разделы таблицы ccddl с именами ccddl_1_prt_p3 и ccddl_1_prt_p3_2_prt_sp1. Раздел ccddl_1_prt_p3 наследует различные кодировки сжатия подраздела sp1.
Команда DROP TABLE удаляет таблицы из базы данных. Например:
DROP TABLE mytable;
Чтобы очистить таблицу от строк без удаления определения таблицы, используйте DELETE или TRUNCATE. Например:
DELETE FROM mytable;
TRUNCATE mytable;
Команда DROP TABLE всегда удаляет все индексы, правила, триггеры и ограничения, существующие для целевой таблицы. Укажите CASCADE, чтобы удалить таблицу, на которую ссылается представление. CASCADE удаляет зависимые представления.
Партиционирование таблиц позволяет поддерживать очень большие таблицы, такие как таблицы фактов, путем их логического разделения на более мелкие и более управляемые части. Партиционированные таблицы могут повысить производительность запросов, позволяя оптимизатору запросов базы данных RT.Warehouse сканировать только те данные, которые необходимы для выполнения заданного запроса, а не сканировать все содержимое большой таблицы.
Партиционирование не меняет физического распределения данных таблицы по сегментам. Распределение таблиц является физическим: база данных RT.Warehouse физически разделяет многораздельные и неразделенные таблицы на сегменты, чтобы обеспечить параллельную обработку запросов. Разбиение таблиц на разделы логично: база данных RT.Warehouse логически разделяет большие таблицы для повышения производительности запросов и облегчения задач обслуживания хранилища данных, таких как удаление старых данных из хранилища данных.
База данных RT.Warehouse поддерживает:
База данных RT.Warehouse делит таблицы на части (также называемые партициями), чтобы обеспечить массовую параллельную обработку. Таблицы партиционируются во время CREATE TABLE с использованием предложения PARTITION BY (и, возможно, SUBPARTITION BY). Партиционирование создает таблицу верхнего уровня (или родительскую) с одним или несколькими уровнями подтаблиц (или дочерних таблиц). Внутренне база данных RT.Warehouse создает отношение наследования между таблицей верхнего уровня и ее базовыми разделами, подобно функциональности предложения INHERITS в PostgreSQL.
RT.Warehouse использует критерии раздела, определенные во время создания таблицы, для создания каждого раздела с отдельным ограничением CHECK, которое ограничивает данные, которые может содержать таблица. Оптимизатор запросов использует ограничения CHECK, чтобы определить, какие разделы таблицы сканировать, чтобы удовлетворить заданному предикату запроса.
В системном каталоге RT.Warehouse хранится информация об иерархии разделов, чтобы строки, вставленные в родительскую таблицу верхнего уровня, правильно распространялись на разделы дочерней таблицы. Чтобы изменить структуру раздела или структуру таблицы, измените родительскую таблицу, используя ALTER TABLE с предложением PARTITION.
Чтобы вставить данные в многораздельную таблицу, вы указываете корневую многораздельную таблицу, таблицу, созданную с помощью команды CREATE TABLE. Вы также можете указать конечную дочернюю таблицу многораздельной таблицы в команде INSERT. Возвращается ошибка, если данные недопустимы для указанной конечной дочерней таблицы. Указание неконечной или некорневой таблицы разделов в команде DML не поддерживается.
База данных RT.Warehouse не поддерживает партиционирование реплицированных таблиц (DISTRIBUTED REPLICATED). Не все таблицы с хеш-распределением или случайным распределением являются хорошими кандидатами на партиционирование. Если ответ положительный на все или большинство следующих вопросов, партиционирование таблиц является жизнеспособной стратегией проектирования базы данных для повышения производительности запросов. Если на большинство следующих вопросов ответ отрицательный, партиционирование таблицы не является правильным решением для этой таблицы. Проверьте свою стратегию проектирования, чтобы убедиться, что производительность запросов повышается, как и ожидалось.
Не создавайте больше партиций, чем необходимо. Создание слишком большого количества партиций может замедлить выполнение задач управления и обслуживания, таких как очистка, восстановление сегментов, расширение кластера, проверка использования диска и другие.
Партиционирование не повышает производительность запросов, если только оптимизатор запросов не может устранить партиционирование на основе предикатов запроса. Запросы, которые сканируют каждую партицию, выполняются медленнее, чем если бы таблица не была партицирована, поэтому избегайте партиционирования, если несколько ваших запросов достигают устранения партиций. Проверьте план объяснения для запросов, чтобы убедиться, что партиции устранены.
Внимание. Будьте очень осторожны с многоуровневым партиционированием, потому что количество файлов разделов может расти очень быстро. Например, если таблица партиционирована как по дням, так и по городам, и имеется 1000 данных по дням и 1000 городов, общее количество партиций равно одному миллиону. Таблицы, ориентированные на столбцы, хранят каждый столбец в физической таблице, поэтому, если в этой таблице 100 столбцов, системе потребуется управлять 100 миллионами файлов для таблицы для каждого сегмента. |
Прежде чем остановиться на стратегии многоуровневого партиционирования, рассмотрите одноуровневое партиционирование с битовыми индексами. Индексы замедляют загрузку данных, поэтому рекомендуется провести тестирование производительности с вашими данными и схемой, чтобы выбрать наилучшую стратегию.
Вы разбиваете таблицы на партиции, когда создаете их с помощью CREATE TABLE. В этом разделе приведены примеры синтаксиса SQL для создания таблицы с различными схемами разделов.
Чтобы разбить таблицу:
Партиционированная таблица диапазона дат использует один столбец даты или времени в качестве столбца ключа партиции. Вы можете использовать один и тот же столбец ключа партиции для создания подразделов, если это необходимо, например, для разделения по месяцам, а затем для разделения по дням. Рассмотрите возможность партиционирования на самом детальном уровне. Например, для таблицы, партиционированной по дате, вы можете разбить по дням и иметь 365 ежедневных партиций, а не партиционировать по годам, затем подразделять по месяцам, а затем подразделять по дням. Многоуровневая структура может сократить время планирования запросов, но структура с плоским партиционированием работает быстрее.
Вы можете сделать так, чтобы база данных RT.Warehouse автоматически создавала партиции, задавая значение START, значение END и предложение EVERY, которое определяет значение приращения раздела. По умолчанию значения START всегда включают, а значения END всегда исключают. Например:
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') );
Вы также можете объявить и назвать каждую партицию отдельно. Например:
CREATE TABLE sales (id int, date date, amt decimal(10,2))
DISTRIBUTED BY (id)
PARTITION BY RANGE (date)
( PARTITION Jan16 START (date '2016-01-01') INCLUSIVE ,
PARTITION Feb16 START (date '2016-02-01') INCLUSIVE ,
PARTITION Mar16 START (date '2016-03-01') INCLUSIVE ,
PARTITION Apr16 START (date '2016-04-01') INCLUSIVE ,
PARTITION May16 START (date '2016-05-01') INCLUSIVE ,
PARTITION Jun16 START (date '2016-06-01') INCLUSIVE ,
PARTITION Jul16 START (date '2016-07-01') INCLUSIVE ,
PARTITION Aug16 START (date '2016-08-01') INCLUSIVE ,
PARTITION Sep16 START (date '2016-09-01') INCLUSIVE ,
PARTITION Oct16 START (date '2016-10-01') INCLUSIVE ,
PARTITION Nov16 START (date '2016-11-01') INCLUSIVE ,
PARTITION Dec16 START (date '2016-12-01') INCLUSIVE
END (date '2017-01-01') EXCLUSIVE );
Вам не нужно объявлять значение END для каждой партиции, только для последней. В этом примере 16 января заканчивается там, где начинается 16 февраля.
Партиционировання таблица с числовым диапазоном использует один столбец с числовым типом данных в качестве столбца ключа партиции. Например:
CREATE TABLE rank (id int, rank int, year int, gender
char(1), count int)
DISTRIBUTED BY (id)
PARTITION BY RANGE (year)
( START (2006) END (2016) EVERY (1),
DEFAULT PARTITION extra );
Партиционированная таблица со списком может использовать столбец любого типа данных, допускающий сравнение на равенство, в качестве столбца ключа партиции. Партиция списка также может иметь многостолбцовый (составной) ключ партиции, тогда как партиция диапазона позволяет использовать только один столбец в качестве ключа партиции. Для партиций списка вы должны объявить спецификацию партиции для каждой партиции (значение списка), которую вы хотите создать. Например:
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 other );
Примечание. Текущий планировщик Postgres позволяет создавать списки партиций с многостолбцовыми (составными) ключами партиций. Партиция диапазона позволяет использовать только один столбец в качестве ключа партиции. Оптимизатор запросов RT.Warehouse не поддерживает составные ключи, поэтому не следует использовать составные ключи партиций. |
Вы можете создать многоуровневую структуру партиций с подразделами партиций. Использование шаблона подраздела гарантирует, что каждая партиция будет иметь одинаковую структуру подраздела, включая разделы, которые вы добавите позже. Например, следующий SQL-код создает двухуровневую структуру партиции:
CREATE TABLE sales (trans_id int, date date, amount
decimal(9,2), region text)
DISTRIBUTED BY (trans_id)
PARTITION BY RANGE (date)
SUBPARTITION BY LIST (region)
SUBPARTITION TEMPLATE
( SUBPARTITION usa VALUES ('usa'),
SUBPARTITION asia VALUES ('asia'),
SUBPARTITION europe VALUES ('europe'),
DEFAULT SUBPARTITION other_regions)
(START (date '2011-01-01') INCLUSIVE
END (date '2012-01-01') EXCLUSIVE
EVERY (INTERVAL '1 month'),
DEFAULT PARTITION outlying_dates );
В следующем примере показан трехуровневая партиция, в которой таблица продаж разделена по годам, месяцам и регионам. Предложения SUBPARTITION TEMPLATE гарантируют, что каждый годовой раздел имеет одинаковую структуру подразделов. В примере объявляется партиция DEFAULT на каждом уровне иерархии.
CREATE TABLE p3_sales (id int, year int, month int, day int,
region text)
DISTRIBUTED BY (id)
PARTITION BY RANGE (year)
SUBPARTITION BY RANGE (month)
SUBPARTITION TEMPLATE (
START (1) END (13) EVERY (1),
DEFAULT SUBPARTITION other_months )
SUBPARTITION BY LIST (region)
SUBPARTITION TEMPLATE (
SUBPARTITION usa VALUES ('usa'),
SUBPARTITION europe VALUES ('europe'),
SUBPARTITION asia VALUES ('asia'),
DEFAULT SUBPARTITION other_regions )
( START (2002) END (2012) EVERY (1),
DEFAULT PARTITION outlying_years );
Внимание. Когда вы создаете многоуровневые партиции на диапазонах, легко создать большое количество подразделов, некоторые из которых содержат мало данных или вообще не содержат их. Это может добавить много записей в системные таблицы, что увеличивает время и память, необходимые для оптимизации и выполнения запросов. Увеличьте интервал диапазона или выберите другую стратегию разделения, чтобы уменьшить количество создаваемых подразделов. |
Таблицы можно партиционировать только при создании. Если у вас есть таблица, которую вы хотите партиционировать, вы должны создать партиционированную таблицу, загрузить данные из исходной таблицы в новую таблицу, удалить исходную таблицу и переименовать партиционированную таблицу, используя имя исходной таблицы. Вы также должны повторно предоставить любые разрешения для таблиц. Например:
CREATE TABLE sales2 (LIKE sales)
PARTITION BY RANGE (date)
( START (date 2016-01-01') INCLUSIVE
END (date '2017-01-01') EXCLUSIVE
EVERY (INTERVAL '1 month') );
INSERT INTO sales2 SELECT * FROM sales;
DROP TABLE sales;
ALTER TABLE sales2 RENAME TO sales;
GRANT ALL PRIVILEGES ON sales TO admin;
GRANT SELECT ON sales TO guest;
Для каждого уровня партиций партиционированная таблица может иметь максимум 32 767 партиций.
Первичный ключ или уникальное ограничение для партиционированной таблицы должны содержать все столбцы партиционирования. В уникальном индексе столбцы разделения могут отсутствовать; однако он применяется только к частям многораздельной таблицы, а не к многораздельной таблице в целом.
Таблицы, созданные с помощью политики распределения DISTRIBUTED REPLICATED, не могут быть партиционированы.
GPORCA, оптимизатор запросов нового поколения RT.Warehouse, поддерживает унифицированные многоуровневые партиционированные таблицы. Если GPORCA включена (по умолчанию) и многоуровневая партиционированная таблица неоднородна, база данных RT.Warehouse выполняет запросы к таблице с помощью планировщика Postgres.
Далее описаны ограничения для многораздельных таблиц, когда конечный дочерний раздел таблицы является внешней таблицей:
COPY (SELECT * from my_sales ) TO stdout
После создания структуры партиционированных таблиц родительские таблицы верхнего уровня пусты. Данные направляются в партиции дочерней таблицы нижнего уровня. В многоуровневой структуре партиций данные могут содержаться только в нижних разделах иерархии.
Строки, которые не могут быть сопоставлены с партицией дочерней таблицы, отклоняются, и загрузка завершается ошибкой. Чтобы избежать отклонения несопоставленных строк во время загрузки, определите иерархию партиций с партицией ПО УМОЛЧАНИЮ (DEFAULT). Любые строки, которые не соответствуют ограничениям CHECK раздела, загружаются в партицию DEFAULT.
Во время выполнения оптимизатор запросов сканирует всю иерархию наследования таблиц и использует ограничения таблицы CHECK, чтобы определить, какую из партиций дочерней таблицы следует сканировать, чтобы удовлетворить условиям запроса. Раздел ПО УМОЛЧАНИЮ (если он есть в вашей иерархии) всегда сканируется. Разделы ПО УМОЛЧАНИЮ, содержащие данные, замедляют общее время сканирования.
Когда вы используете COPY или INSERT для загрузки данных в родительскую таблицу, данные автоматически перенаправляются в правильную партицию, как и обычная таблица.
Передовой практикой для загрузки данных в партиционированные таблицы является создание промежуточной таблицы, ее загрузка, а затем обмен данными с вашей схемой партиционирования.
Когда таблица партиционируется на основе предиката запроса, вы можете использовать EXPLAIN, чтобы убедиться, что оптимизатор запросов сканирует только релевантные данные для проверки плана запроса.
Например, предположим, что таблица продаж разделена по диапазонам дат по месяцам и подразделена по регионам. Для следующего запроса:
EXPLAIN SELECT * FROM sales WHERE date='01-07-12' AND
region='usa';
План запроса для этого запроса должен отображать сканирование только следующих таблиц:
В следующем примере показана соответствующая часть плана запроса:
-> Seq Scan onsales_1_prt_1 sales (cost=0.00..0.00 rows=0
width=0)
Filter: "date"=01-07-12::date AND region='USA'::text
-> Seq Scan onsales_1_2_prt_usa sales (cost=0.00..9.87
rows=20
width=40)
Убедитесь, что оптимизатор запросов не сканирует ненужные разделы или подразделы (например, сканирование месяцев или регионов, не указанных в предикате запроса), и что сканирование таблиц верхнего уровня возвращает строки 0-1.
Следующие ограничения могут привести к тому, что план запроса будет показывать неизбирательное сканирование иерархии партиций.
Вы можете найти информацию о структуре вашей партиции, используя системное представление pg_partitions. Например, чтобы увидеть структуру партиции таблицы продаж:
SELECT partitionboundary, partitiontablename, partitionname,
partitionlevel, partitionrank
FROM pg_partitions
WHERE tablename='sales';
Следующие представления также содержат информацию о партиционированных таблицах.
Чтобы поддерживать партиционированную таблицу, используйте команду ALTER TABLE для родительской таблицы верхнего уровня. Наиболее распространенным сценарием является удаление старых разделов и добавление новых, чтобы сохранить скользящее окно данных в структуре партиций диапазона. Вы можете преобразовать (обменять) старые партиции в формат сжатого хранилища с оптимизированным добавлением для экономии места. Если у вас есть партциия по умолчанию в вашей структуре партиций, вы добавляете партицию, разделяя партицию по умолчанию.
Внимание. При определении и изменении структуры партиций используйте заданное имя партиции, а не имя объекта таблицы. Данное имя раздела является значением столбца partitionname в системном представлении pg_partitions. Хотя вы можете запрашивать и загружать любую таблицу (включая многораздельной таблицы) непосредственно с помощью команд SQL, вы можете изменить структуру многораздельной таблицы только с помощью предложений ALTER TABLE...PARTITION. Партиции не обязаны иметь имена. Если у партиции нет имени, используйте одно из следующих выражений, чтобы указать партицию: PARTITION FOR (value) или PARTITION FOR (RANK(number)). |
Для многоуровневой партиционированной таблицы вы определяете конкретную партицию, которую нужно изменить, с помощью предложений ALTER PARTITION. Для каждого уровня партиции в иерархии таблиц, который находится выше целевой партиции, укажите партицию, связанную с целевой партицией, в предложении ALTER PARTITION. Например, если у вас есть партиционированная таблица, состоящая из трех уровней, года, квартала и региона, эта команда ALTER TABLE меняет конечную партицию region на таблицу region_new.
ALTER TABLE sales ALTER PARTITION year_1 ALTER PARTITION quarter_4 EXCHANGE PARTITION region WITH TABLE region_new ;
Два предложения ALTER PARTITION определяют, какой партицию region следует обменять. Оба пункта необходимы для идентификации конкретного листовой партиции для обмена.
Вы можете добавить партицию в структуру партиций с помощью команды ALTER TABLE. Если первоначальный проект партиции включал в себя подразделы, определенные шаблоном подраздела, вновь добавленная партиция подразделяется в соответствии с этим шаблоном. Например:
ALTER TABLE sales ADD PARTITION
START (date '2017-02-01') INCLUSIVE
END (date '2017-03-01') EXCLUSIVE;
Если вы не использовали шаблон подраздела при создании таблицы, вы определяете подразделы при добавлении партиции:
ALTER TABLE sales ADD PARTITION
START (date '2017-02-01') INCLUSIVE
END (date '2017-03-01') EXCLUSIVE
( SUBPARTITION usa VALUES ('usa'),
SUBPARTITION asia VALUES ('asia'),
SUBPARTITION europe VALUES ('europe') );
Когда вы добавляете подраздел к существующей партиции, вы можете указать партицию, которую нужно изменить. Например:
ALTER TABLE sales ALTER PARTITION FOR (RANK(12))
ADD PARTITION africa VALUES ('africa');
Примечание. Вы не можете добавить партицию в структуру партиций, в которой есть партиция по умолчанию. Вы должны разделить партицию по умолчанию, чтобы добавить партицию. |
Многораздельные таблицы используют следующее соглашение об именах. Имена партиционированных подтаблиц подчиняются требованиям уникальности и ограничениям длины.
<parentname>_<level>_prt_<partition_name>
Например:
sales_1_prt_jan16
Для автоматически сгенерированных партиций диапазона, где номер назначается, когда имя не указано:
sales_1_prt_1
Чтобы переименовать партиционированную дочернюю таблицу, переименуйте родительскую таблицу верхнего уровня. <parentname> изменяется в именах таблиц всех связанных партиций дочерней таблицы. Например:
ALTER TABLE sales RENAME TO globalsales;
Изменяет связанные имена таблиц:
globalsales_1_prt_1
Вы можете изменить имя партиции, чтобы ее было легче идентифицировать. Например:
ALTER TABLE sales RENAME PARTITION FOR ('2016-01-01') TO jan16;
Изменяет имя связанной таблицы следующим образом:
sales_1_prt_jan16
При изменении партиционированных таблиц с помощью команды ALTER TABLE всегда обращайтесь к таблицам по имени раздела (jan16), а не по полному имени таблицы (sales_1_prt_jan16).
Примечание. Имя таблицы не может быть именем партиции в операторе ALTER TABLE. Например, ALTER TABLE sales... является правильным, ALTER TABLE sales_1_part_jan16... не допускается. |
Вы можете добавить партицию по умолчанию в структуру партиций с помощью команды ALTER TABLE.
ALTER TABLE sales ADD DEFAULT PARTITION other;
Если структура вашей партиции многоуровневая, каждый уровень в иерархии должен иметь партицию по умолчанию. Например:
ALTER TABLE sales ALTER PARTITION FOR (RANK(1)) ADD DEFAULT
PARTITION other;
ALTER TABLE sales ALTER PARTITION FOR (RANK(2)) ADD DEFAULT
PARTITION other;
ALTER TABLE sales ALTER PARTITION FOR (RANK(3)) ADD DEFAULT
PARTITION other;
Если входящие данные не соответствуют ограничению CHECK партиции нет партиции по умолчанию, данные отклоняются. Партиции по умолчанию гарантируют, что входящие данные, которые не соответствуют партиции, вставляются в партицию по умолчанию.
Вы можете удалить партицию из схемы партиций с помощью команды ALTER TABLE. Когда вы удаляете партицию с подпартициями, подпартиции (и все данные в них) также автоматически удаляются. Для партиций диапазонов обычно удаляют более старые партиции из диапазона по мере удаления старых данных из хранилища данных. Например:
ALTER TABLE sales DROP PARTITION FOR (RANK(1));
Вы можете обрезать (truncate) партицию с помощью команды ALTER TABLE. Когда вы усекаете партицию, которая имеет подпартиции, подпартиции также автоматически обрезаются.
ALTER TABLE sales TRUNCATE PARTITION FOR (RANK(1));
Вы можете заменить партицию с помощью команды ALTER TABLE. При замене партиции одна таблица заменяется на существующую партицию. Вы можете обмениваться партициями только на самом низком уровне иерархии партиций (обмену подлежат только партиции, содержащие данные).
Вы не можете поменять партицию с реплицированной таблицей. Обмен партиции с многораздельной таблицей или дочерней партицией многораздельной таблицы не поддерживается.
Обмен партициями может быть полезен для загрузки данных. Например, загрузите промежуточную таблицу и поместите загруженную таблицу в структуру вашей партиции. Вы можете использовать обмен партициями, чтобы изменить тип хранения старых партиций на оптимизированные для добавления таблицы. Например:
CREATE TABLE jan12 (LIKE sales) WITH (appendoptimized=true);
INSERT INTO jan12 SELECT * FROM sales_1_prt_1 ;
ALTER TABLE sales EXCHANGE PARTITION FOR (DATE '2012-01-01')
WITH TABLE jan12;
Примечание. Этот пример относится к одноуровневому определению таблицы продаж до того, как партиции были добавлены и изменены в предыдущих примерах. |
Внимание. Если вы укажете предложение WITHOUT VALIDATION, вы должны убедиться, что данные в таблице, которыми вы обмениваетесь для существующей партиции, соответствуют ограничениям на партицию. В противном случае запросы к партиционированной таблице могут возвращать неверные результаты. |
Параметр конфигурации сервера базы данных RT.Warehouse gp_enable_exchange_default_partition управляет доступностью предложения EXCHANGE DEFAULT PARTITION. Значение по умолчанию для параметра отключено, предложение недоступно, и база данных RT.Warehouse возвращает ошибку, если предложение указано в команде ALTER TABLE.
Внимание. Перед заменой партиции по умолчанию необходимо убедиться, что данные в таблице, подлежащей обмену (новая партиция по умолчанию), действительны для партиции по умолчанию. Например, данные в новой партиции по умолчанию не должны содержать данных, которые были бы допустимы в других конечных дочерних партициях многораздельной таблицы. В противном случае запросы к партиционированной таблице с замененной партицией по умолчанию, выполняемые GPORCA, могут возвращать неверные результаты. |
Разделение партиции делит партицию на две партиции. Вы можете разделить партиции с помощью команды ALTER TABLE. Вы можете разделить партиции только на самом низком уровне иерархии партиций (партиции, содержащие данные). Для многоуровневой партиции можно разделить только партиции диапазона, а не партиции списка. Указанное вами значение разделения входит в последнюю партицию.
Например, чтобы разделить месячную партицию на две, первая из которых содержит даты с 1 по 15 января, а вторая партиция — с датами с 16 по 31 января:
ALTER TABLE sales SPLIT PARTITION FOR ('2017-01-01')
AT ('2017-01-16')
INTO (PARTITION jan171to15, PARTITION jan1716to31);
Если в вашей структуре партиций есть партиция по умолчанию, вы должны разделить партицию по умолчанию, чтобы добавить партицию.
При использовании предложения INTO укажите текущую партицию по умолчанию в качестве имени второго раздела. Например, чтобы разделить партицию диапазона по умолчанию, чтобы добавить новый ежемесячную партицию за январь 2017 года:
ALTER TABLE sales SPLIT DEFAULT PARTITION
START ('2017-01-01') INCLUSIVE
END ('2017-02-01') EXCLUSIVE
INTO (PARTITION jan17, default partition);
Используйте ALTER TABLE SET SUBPARTITION TEMPLATE, чтобы изменить шаблон подпартиции многораздельной таблицы. Партиции, добавленные после установки нового шаблона подпартиции, имеют новую структуру партиции. Существующие партиции не изменяются.
В следующем примере изменяется шаблон подпартиции этой партиционированной таблицы:
CREATE TABLE sales (trans_id int, date date, amount decimal(9,2), region text)
DISTRIBUTED BY (trans_id)
PARTITION BY RANGE (date)
SUBPARTITION BY LIST (region)
SUBPARTITION TEMPLATE
( SUBPARTITION usa VALUES ('usa'),
SUBPARTITION asia VALUES ('asia'),
SUBPARTITION europe VALUES ('europe'),
DEFAULT SUBPARTITION other_regions )
( START (date '2014-01-01') INCLUSIVE
END (date '2014-04-01') EXCLUSIVE
EVERY (INTERVAL '1 month') );
Эта команда ALTER TABLE изменяет шаблон подпартиции.
ALTER TABLE sales SET SUBPARTITION TEMPLATE
( SUBPARTITION usa VALUES ('usa'),
SUBPARTITION asia VALUES ('asia'),
SUBPARTITION europe VALUES ('europe'),
SUBPARTITION africa VALUES ('africa'),
DEFAULT SUBPARTITION regions );
Когда вы добавляете партицию таблицы sales с диапазоном дат, он включает в себя новую подпартицию регионального списка для Африки. Например, следующая команда создает подпартицию usa, asia, europe, africa и партицию по умолчанию с именем other:
ALTER TABLE sales ADD PARTITION "4"
START ('2014-04-01') INCLUSIVE
END ('2014-05-01') EXCLUSIVE ;
Чтобы просмотреть таблицы, созданные для партиционированной таблицы sales, вы можете использовать команду \dt sales* из командной строки psql.
Чтобы удалить шаблон подпартиции, используйте SET SUBPARTITION TEMPLATE с пустыми скобками. Например, чтобы очистить шаблон подпартиции таблицы продаж:
ALTER TABLE sales SET SUBPARTITION TEMPLATE ();
Вы можете заменить дочернюю leaf-партицию партиционированной таблицы на доступную для чтения внешнюю таблицу. Данные внешней таблицы могут находиться в файловой системе хоста, на монтировании NFS или в файловой системе Hadoop (HDFS).
Например, если у вас есть партиционированная таблица, созданная с ежемесячными партициями, и большинство запросов к таблице обращаются только к более новым данным, вы можете скопировать старые, менее используемые данные во внешние таблицы и заменить старые партиции внешними таблицами. Для запросов, которые обращаются только к более новым данным, вы можете создавать запросы, использующие исключение партиций, чтобы предотвратить сканирование старых, ненужных разделов.
Обмен дочерней leaf-партиции с внешней таблицей не поддерживается, если партиционированная таблица содержит столбец с проверочным ограничением или ограничением NOT NULL.
Для получения информации об обмене и изменении конечного дочернего раздела см. команду ALTER TABLE в Справочнике по командам базы данных RT.Warehouse.
Это пример, в котором конечная дочерняя leaf-партиция партиционированной таблицы заменяется внешней таблицей. Партиционированная таблица содержит данные за период с 2010 по 2013 год.
CREATE TABLE sales (id int, year int, qtr int, day int, region text)
DISTRIBUTED BY (id)
PARTITION BY RANGE (year)
( PARTITION yr START (2010) END (2014) EVERY (1) ) ;
Для партиционированной таблицы имеется четыре дочерних leaf-партиций. Каждая дочерняя leaf-партиция содержит данные за один год. Дочерняя leaf-партиционированная таблица партиций sales_1_prt_yr_1 содержит данные за 2010 год. Эти шаги заменяют таблицу sales_1_prt_yr_1 внешней таблицей, использующей протокол gpfdist:
1. Убедитесь, что протокол внешней таблицы включен для системы базы данных RT.Warehouse. В этом примере используется протокол gpfdist. Эта команда запускает протокол gpfdist.
$ gpfdist
2. Создайте доступную для записи внешнюю таблицу. Команда CREATE WRITABLE EXTERNAL TABLE создает доступную для записи внешнюю таблицу с теми же столбцами, что и партиционированная таблица.
CREATE WRITABLE EXTERNAL TABLE my_sales_ext ( LIKE sales_1_prt_yr_1 )
LOCATION ( 'gpfdist://gpdb_test/sales_2010' )
FORMAT 'csv'
DISTRIBUTED BY (id) ;
3. Создайте доступную для чтения внешнюю таблицу, которая считывает данные из места назначения доступной для записи внешней таблицы, созданной на предыдущем шаге. Команда CREATE EXTERNAL TABLE создает доступную для чтения внешнюю таблицу, которая использует те же внешние данные, что и доступные для записи внешние данные.
CREATE EXTERNAL TABLE sales_2010_ext ( LIKE sales_1_prt_yr_1)
LOCATION ( 'gpfdist://gpdb_test/sales_2010' )
FORMAT 'csv' ;
4. Скопируйте данные из дочерней leaf-партиции во внешнюю таблицу, доступную для записи. Эта команда INSERT копирует данные из дочерней leaf-партиционированной таблицы партиционированной таблицы во внешнюю таблицу.
INSERT INTO my_sales_ext SELECT * FROM sales_1_prt_yr_1 ;
5. Замените существующую дочернюю leaf-партицию внешней таблицей. Эта команда ALTER TABLE задает условие EXCHANGE PARTITION для переключения доступной для чтения внешней таблицы и конечной дочерней партиции.
ALTER TABLE sales ALTER PARTITION yr_1
EXCHANGE PARTITION yr_1
WITH TABLE sales_2010_ext WITHOUT VALIDATION;
Внешняя таблица становится дочерней leaf-партицией с именем таблицы sales_1_prt_yr_1, а старая дочерняя leaf-партиция становится таблицей sales_2010_ext.
Внимание. Чтобы гарантировать, что запросы к партиционированной таблице возвращают правильные результаты, данные внешней таблицы должны соответствовать ограничениям CHECK для дочерней leaf-партиции. В этом случае данные были взяты из дочерней leaf-партиции таблицы для которой были определены ограничения CHECK. |
6. Удалите таблицу, которая была развернута из партиционированной таблицы.
DROP TABLE sales_2010_ext ;
Вы можете переименовать дочернюю leaf-партицию, чтобы указать, что sales_1_prt_yr_1 является внешней таблицей.
В этом примере команда изменяет имя партиции на yr_1_ext, а имя дочерней leaf-партции таблицы — на sales_1_prt_yr_1_ext.
ALTER TABLE sales RENAME PARTITION yr_1 TO yr_1_ext ;
Объект последовательности базы данных RT.Warehouse представляет собой специальную таблицу с одной строкой, которая функционирует как генератор чисел. Вы можете использовать последовательность для создания уникальных целочисленных идентификаторов для строки, которую вы добавляете в таблицу. Объявление столбца типа SERIAL неявно создает счетчик последовательности для использования в этом столбце таблицы.
База данных RT.Warehouse предоставляет команды для создания, изменения и удаления последовательности. База данных RT.Warehouse также предоставляет встроенные функции для возврата следующего значения в последовательности (nextval()) или для установки в последовательности определенного начального значения (setval()).
Примечание. Функции последовательности PostgreSQL currval() и lastval() не поддерживаются в базе данных RT.Warehouse. |
Атрибуты объекта последовательности включают имя последовательности, значение ее приращения, а также последнее, минимальное и максимальное значения счетчика последовательности. Последовательности также имеют специальный логический атрибут с именем is_call, который управляет поведением автоинкремента операции nextval() на счетчике последовательности. Когда атрибут последовательности is_call имеет значение true, nextval() увеличивает счетчик последовательности, прежде чем вернуть значение. Когда значение атрибута is_call последовательности равно false, nextval() не увеличивает значение счетчика перед возвратом значения.
Команда CREATE SEQUENCE создает и инициализирует последовательность с заданным именем последовательности и необязательным начальным значением. Имя последовательности должно отличаться от имени любой другой последовательности, таблицы, индекса или представления в той же схеме. Например:
CREATE SEQUENCE myserial START 101;
Когда вы создаете новую последовательность, база данных RT.Warehouse устанавливает для атрибута последовательности is_call значение false. Вызов nextval() для только что созданной последовательности не увеличивает счетчик последовательности, а возвращает начальное значение последовательности и устанавливает для is_вызванного значение true.
После того как вы создадите последовательность с помощью команды CREATE SEQUENCE, вы сможете изучить последовательность и использовать встроенные функции последовательности.
Чтобы проверить текущие атрибуты последовательности, запросите последовательность напрямую. Например, чтобы проверить последовательность с именем myserial:
SELECT * FROM myserial;
Вы можете вызвать встроенную функцию nextval() для возврата и использования следующего значения в последовательности. Следующая команда вставляет следующее значение последовательности с именем myserial в первый столбец таблицы с именем vendors:
INSERT INTO vendors VALUES (nextval('myserial'), 'acme');
nextval() использует значение атрибута is_called последовательности, чтобы определить, следует ли увеличивать счетчик последовательности перед возвратом значения. nextval() увеличивает значение счетчика, когда is_CAlled имеет значение true. nextval() устанавливает для атрибута последовательности is_call значение true перед возвратом.
Операция nextval() никогда не откатывается (roll back). Выбранное значение считается использованным, даже если транзакция, выполнившая nextval(), не удалась. Это означает, что неудавшиеся транзакции могут оставить неиспользуемые пробелы в последовательности присвоенных значений.
Примечание. Вы не можете использовать функцию nextval() в операторах UPDATE или DELETE, если в базе данных RT.Warehouse включено зеркалирование. |
Вы можете использовать встроенную функцию setval() базы данных RT.Warehouse, чтобы установить значение счетчика для последовательности. Например, следующая команда устанавливает значение счетчика последовательности с именем myserial равным 201:
SELECT setval('myserial', 201);
setval() имеет две сигнатуры функций: setval(sequence, start_val) и setval(sequence, start_val, is_call). Поведение setval(sequence, start_val) по умолчанию устанавливает значение атрибута последовательности is_call равным true.
Если вы не хотите, чтобы счетчик последовательности увеличивался при следующем вызове nextval(), используйте сигнатуру функции setval(sequence, start_val, is_call), передав аргумент false:
SELECT setval('myserial', 201, false);
Операции setval() никогда не откатываются.
Команда ALTER SEQUENCE изменяет атрибуты существующей последовательности. Вы можете изменить начальные, минимальные, максимальные значения и значения приращения последовательности. Вы также можете перезапустить последовательность при начальном значении или при указанном значении.
Любые параметры, не заданные в команде ALTER SEQUENCE, сохраняют свои предыдущие настройки.
Команда ALTER SEQUENCE START WITH start_value устанавливает для атрибута start_value последовательности новое начальное значение. Он не влияет на атрибут last_value или значение, возвращаемое функцией nextval(sequence).
Команда ALTER SEQUENCE RESTART сбрасывает атрибут last_value последовательности в текущее значение атрибута start_value, а атрибут is_call — в false. Следующий вызов функции nextval(sequence) возвращает значение start_value.
ALTER SEQUENCE RESTART WITH restart_value устанавливает для атрибута last_value последовательности новое значение, а для атрибута is_call — значение false. Следующий вызов nextval(sequence) возвращает значение restart_value. Это эквивалент вызова setval(sequence, restart_value, false).
Следующая команда перезапускает последовательность с именем myserial со значением 105:
ALTER SEQUENCE myserial RESTART WITH 105;
Команда DROP SEQUENCE удаляет последовательность. Например, следующая команда удаляет последовательность с именем myserial:
DROP SEQUENCE myserial;
Вы можете ссылаться на последовательность непосредственно в команде CREATE TABLE в дополнение к использованию типов SERIAL или BIGSERIAL. Например:
CREATE TABLE tablename ( id INT4 DEFAULT nextval('myserial'), name text );
Вы также можете изменить столбец таблицы, чтобы установить его значение по умолчанию на счетчик последовательности:
ALTER TABLE tablename ALTER COLUMN id SET DEFAULT nextval('myserial');
По умолчанию последовательность не зацикливается. То есть, когда последовательность достигает максимального значения (+32767 для SMALLSERIAL, +2147483647 для SERIAL, +9223372036854775807 для BIGSERIAL), каждый последующий вызов nextval() приводит к ошибке. Вы можете изменить последовательность, чтобы она циклически повторялась и снова начиналась с 1:
ALTER SEQUENCE myserial CYCLE;
Вы также можете указать поведение циклического перехода при создании последовательности:
CREATE SEQUENCE myserial CYCLE;
В большинстве традиционных баз данных индексы могут значительно сократить время доступа к данным. Однако в распределенной базе данных, такой как RT.Warehouse, индексы следует использовать более экономно. База данных RT.Warehouse выполняет очень быстрое последовательное сканирование; индексы используют случайный шаблон поиска для поиска записей на диске. Данные RT.Warehouse распределяются по сегментам, поэтому каждый сегмент сканирует меньшую часть общих данных, чтобы получить результат. При партиционировании таблицы общий объем данных для сканирования может быть еще меньше. Поскольку рабочие нагрузки запросов бизнес-аналитики (BI) обычно возвращают очень большие наборы данных, использование индексов неэффективно.
Сначала попробуйте свою рабочую нагрузку запроса без добавления индексов. Индексы, скорее всего, повысят производительность рабочих нагрузок OLTP, когда запрос возвращает одну запись или небольшое подмножество данных. Индексы также могут повысить производительность сжатых таблиц, оптимизированных для добавления, для запросов, которые возвращают целевой набор строк, поскольку оптимизатор может использовать метод доступа к индексу, а не полное сканирование таблицы, когда это необходимо. Для сжатых данных метод доступа к индексу означает, что несжатыми являются только необходимые строки.
База данных RT.Warehouse автоматически создает ограничения PRIMARY KEY для таблиц с первичными ключами. Чтобы создать индекс для многораздельной таблицы, создайте индекс для созданной многораздельной таблицы. Индекс распространяется на все дочерние таблицы, созданные базой данных RT.Warehouse. Создание индекса для таблицы, созданной базой данных RT.Warehouse для использования многораздельной таблицей, не поддерживается.
Обратите внимание, что UNIQUE CONSTRAINT (такое как PRIMARY KEY CONSTRAINT) неявно создает UNIQUE INDEX, который должен включать все столбцы ключа распределения и любого ключа разделения. UNIQUE CONSTRAINT применяется ко всей таблице, включая все ее разделы (если они есть).
Индексы добавляют некоторую нагрузку на базу данных — они используют пространство для хранения и должны поддерживаться при обновлении таблицы. Убедитесь, что рабочая нагрузка запросов использует созданные вами индексы, и убедитесь, что добавленные вами индексы повышают производительность запросов (по сравнению с последовательным сканированием таблицы). Чтобы определить, используются ли индексы, проверьте планы EXPLAIN запроса.
При создании индексов учитывайте следующие моменты:
Использование команды CLUSTER для физического переупорядочивания таблицы на основе индекса может занять много времени для очень больших таблиц. Чтобы добиться тех же результатов намного быстрее, вы можете вручную изменить порядок данных на диске, создав промежуточную таблицу и загрузив данные в желаемом порядке. Например:
CREATE TABLE new_table (LIKE old_table)
AS SELECT * FROM old_table ORDER BY myixcolumn;
DROP old_table;
ALTER TABLE new_table RENAME TO old_table;
CREATE INDEX myixcolumn_ix ON old_table;
VACUUM ANALYZE old_table;
База данных RT.Warehouse поддерживает типы индексов Postgres B-tree, GiST, SP-GiST и GIN. Хэш-индексы не поддерживаются. Каждый тип индекса использует свой алгоритм, который лучше всего подходит для разных типов запросов. Индексы B-tree подходят для наиболее распространенных ситуаций и являются типом индекса по умолчанию.
База данных RT.Warehouse предоставляет тип индекса Битмап (Bitmap). Битмап индексы лучше всего подходят для приложений хранилищ данных и систем поддержки принятия решений с большими объемами данных, большим количеством специальных запросов и небольшим количеством транзакций модификации данных (DML).
Индекс предоставляет указатели на строки в таблице, содержащие заданное значение ключа. Обычный индекс хранит список идентификаторов кортежей для каждого ключа, соответствующего строкам с этим значением ключа. Битмап индексы хранят битовую карту для каждого значения ключа. Обычные индексы могут быть в несколько раз больше, чем данные в таблице, но битмап индексы обеспечивают ту же функциональность, что и обычные индексы, и используют часть размера индексированных данных.
Каждый бит в битмап индексе соответствует возможному идентификатору кортежа. Если бит установлен, строка с соответствующим идентификатором кортежа содержит значение ключа. Функция отображения преобразует позицию бита в идентификатор кортежа. Битмап сжимается для хранения. Если количество различных значений ключа невелико, битмап индексы намного меньше, лучше сжимаются и экономят много места по сравнению с обычным индексом. Размер битмапового индекса пропорционален количеству строк в таблице, умноженному на количество уникальных значений в индексированном столбце.
Битмаповые индексы наиболее эффективны для запросов, содержащих несколько условий в предложении WHERE. Строки, удовлетворяющие некоторым, но не всем условиям, отфильтровываются до обращения к таблице. Это улучшает время отклика, часто значительно.
Битмап индексы лучше всего подходят для приложений хранилища данных, где пользователи запрашивают данные, а не обновляют их. Битмап индексы лучше всего работают для столбцов, содержащих от 100 до 100 000 различных значений, и когда индексированный столбец часто запрашивается вместе с другими индексированными столбцами. Столбцы с менее чем 100 различными значениями, такие как столбец gender с двумя различными значениями (male и female), обычно не получают большой пользы от любого типа индекса. В столбце с более чем 100 000 различных значений производительность и эффективность использования пространства битмап индекса снижаются.
Битмап индексы могут повысить производительность запросов для нерегламентированных запросов. Условия И (AND) и ИЛИ (OR) в предложении WHERE запроса можно быстро разрешить, выполнив соответствующие логические операции непосредственно над битмап перед преобразованием результирующего битмапа в идентификаторы кортежей. Если результирующее количество строк невелико, на запрос можно быстро ответить, не прибегая к полному сканированию таблицы.
Не используйте битмап индексы для уникальных столбцов или столбцов с данными высокой кардинальности, такими как имена клиентов или номера телефонов. Прирост производительности и преимущества дискового пространства за счет битмап индексов начинают уменьшаться в столбцах со 100 000 или более уникальных значений, независимо от количества строк в таблице.
Битмап индексы не подходят для приложений OLTP с большим количеством одновременных транзакций, изменяющих данные.
Используйте битмап индексы экономно. Тестируйте и сравнивайте производительность запросов с индексом и без него. Добавляйте индекс только в том случае, если производительность запросов улучшается при использовании индексированных столбцов.
Команда CREATE INDEX определяет индекс таблицы. Индекс B-tree является типом индекса по умолчанию. Например, чтобы создать индекс B-tree для столбца «gender» в таблице «employee»:
CREATE INDEX gender_idx ON employee (gender);
Чтобы создать битмап индекс для столбца title в таблице films:
CREATE INDEX title_bmp_idx ON films USING bitmap (title);
Столбец индекса не обязательно должен быть просто столбцом базовой таблицы, он может быть функцией или скалярным выражением, вычисленным из одного или нескольких столбцов таблицы. Эта возможность полезна для получения быстрого доступа к таблицам по результатам вычислений.
Выражения индекса относительно дороги в обслуживании, потому что производные выражения должны вычисляться для каждой строки при вставке и всякий раз, когда она обновляется. Однако выражения индекса не пересчитываются во время поиска по индексу, поскольку они уже сохранены в индексе. В обоих следующих примерах система видит запрос просто как WHERE indexedcolumn = 'constant', поэтому скорость поиска эквивалентна любому другому простому индексному запросу. Таким образом, индексы выражений полезны, когда скорость извлечения важнее, чем скорость вставки и обновления.
Первый пример — это распространенный способ сравнения без учета регистра с нижней функцией:
SELECT * FROM test1 WHERE lower(col1) = 'value';
Этот запрос может использовать индекс, если он был определен для результата функции lower(col1):
CREATE INDEX test1_lower_col1_idx ON test1 (lower(col1));
В этом примере предполагается, что следующий тип запроса выполняется часто:
SELECT * FROM people WHERE (first_name || ' ' || last_name) = 'John Smith';
Запросу может помочь следующий индекс:
CREATE INDEX people_names ON people ((first_name || ' ' || last_name));
Синтаксис команды CREATE INDEX обычно требует заключения круглых скобок вокруг выражений индекса, как показано во втором примере. Скобки можно опустить, если выражение представляет собой просто вызов функции, как в первом примере.
Индексы базы данных RT.Warehouse не требуют обслуживания и настройки. Вы можете проверить, какие индексы используются в реальной рабочей нагрузке запросов. Используйте команду EXPLAIN, чтобы проверить использование индекса для запроса.
План запроса показывает шаги или узлы плана, которые база данных предпримет для ответа на запрос, и оценки времени для каждого узла плана. Чтобы изучить использование индексов, найдите следующие типы узлов плана запроса в выходных данных EXPLAIN:
Вы должны поэкспериментировать, чтобы определить индексы для создания. Обратите внимание на следующие моменты:
Используйте команду REINDEX для перестроения плохо работающего индекса. REINDEX перестраивает индекс, используя данные, хранящиеся в таблице индекса, заменяя старую копию индекса.
Для того, чтобы перестроить все индексы в таблице:
REINDEX my_table;
REINDEX my_index;
Команда DROP INDEX удаляет индекс. Например:
DROP INDEX title_idx;
При загрузке данных может быть быстрее удалить все индексы, загрузить их, а затем заново создать индексы.
Представления (View) позволяют сохранять часто используемые или сложные запросы, а затем обращаться к ним в операторе SELECT, как если бы они были таблицей. Представление физически не материализуется на диске: запрос выполняется как подзапрос при доступе к представлению.
При определении и использовании представления помните, что представление — это просто оператор SQL, который заменяется своим определением при выполнении запроса.
Вот некоторые распространенные варианты использования представлений:
Если подзапрос связан с одним запросом, рассмотрите возможность использования предложения WITH команды SELECT вместо создания редко используемого представления.
Как правило, эти виды использования не требуют вложенных представлений, то есть определения представлений на основе других представлений.
Далее два шаблона создания представлений, которые, как правило, проблематичны, поскольку во время выполнения запроса используется SQL представления:
Если в таблице есть зависимости представления, вы должны использовать ключевое слово CASCADE, чтобы удалить его. Кроме того, вы не можете изменить таблицу, если от нее есть зависимости представления. В этом примере показана зависимость представления от таблицы.
CREATE TABLE t (id integer PRIMARY KEY);
CREATE VIEW v AS SELECT * FROM t;
DROP TABLE t;
ERROR: cannot drop table t because other objects depend on it
DETAIL: view v depends on table t
HINT: Use DROP ... CASCADE to drop the dependent objects too.
ALTER TABLE t DROP id;
ERROR: cannot drop column id of table t because other objects depend on it
DETAIL: view v depends on column id of table t
HINT: Use DROP ... CASCADE to drop the dependent objects too.
Как показано в предыдущем примере, изменение таблицы может быть довольно сложной задачей, если существует глубокая иерархия представлений, поскольку вам необходимо создавать представления в правильном порядке. Вы не можете создать представление, если не присутствуют все требуемые объекты.
Вы можете использовать информацию о зависимостях представления, когда хотите изменить таблицу, на которую ссылается представление. Например, вы можете захотеть изменить тип данных столбца таблицы с целочисленного на bigint, потому что понимаете, что вам нужно хранить большие числа. Однако вы не можете этого сделать, если есть представления, использующие столбец. Сначала вам нужно удалить эти представления, затем изменить столбец, а затем выполнить все операторы CREATE VIEW, чтобы снова создать представления.
В следующем примере запрашивается информация представления списка о зависимостях от таблиц и столбцов.
Выходные данные примера основаны на данных примера в конце этого раздела (18.9.2.1.6).
Кроме того, вы можете использовать первый пример запроса «Поиск зависимостей Direct View в таблице», чтобы найти зависимости от пользовательских функций (или процедур). Запрос использует таблицу каталога pg_class, содержащую информацию о таблицах и представлениях. Для функций вы можете использовать таблицу каталога pg_proc для получения информации о функциях.
Чтобы узнать, какие представления напрямую зависят от таблицы t1, создайте запрос, выполняющий соединение между таблицами каталога, содержащими информацию о зависимостях, и настройте запрос так, чтобы он возвращал только зависимости представлений.
SELECT v.oid::regclass AS view,
d.refobjid::regclass AS ref_object -- name of table
-- d.refobjid::regproc AS ref_object -- name of function
FROM pg_depend AS d -- objects that depend on a table
JOIN pg_rewrite AS r -- rules depending on a table
ON r.oid = d.objid
JOIN pg_class AS v -- views for the rules
ON v.oid = r.ev_class
WHERE v.relkind = 'v' -- filter views only
-- dependency must be a rule depending on a relation
AND d.classid = 'pg_rewrite'::regclass
AND d.deptype = 'n' -- normal dependency
-- qualify object
AND d.refclassid = 'pg_class'::regclass -- dependent table
AND d.refobjid = 't1'::regclass
-- AND d.refclassid = 'pg_proc'::regclass -- dependent function
-- AND d.refobjid = 'f'::regproc
;
view | ref_object
------------+------------
v1 | t1
v2 | t1
v2 | t1
v3 | t1
mytest.vt1 | t1
mytest.v2a | t1
mytest.v2a | t1
(7 rows)
Запрос выполняет приведение к типу идентификатора объекта regclass.
В некоторых случаях представления перечислены несколько раз, поскольку представление ссылается на несколько столбцов таблицы. Вы можете удалить эти дубликаты, используя DISTINCT.
Вы можете изменить запрос, чтобы найти представления с прямыми зависимостями от функции f.
В примере запроса изменения закомментированы (с префиксом --). Вы можете закомментировать строки для таблицы и включить строки для функции.
Вы можете изменить предыдущий запрос, чтобы найти те представления, которые зависят от определенного столбца таблицы, что может быть полезно, если вы планируете удалить столбец (добавление столбца в базовую таблицу никогда не будет проблемой). Запрос использует информацию столбца таблицы в таблице каталога pg_attribute.
Этот запрос находит представления, которые зависят от идентификатора столбца таблицы t1:
SELECT v.oid::regclass AS view,
ns.nspname AS schema, -- view schema,
d.refobjid::regclass AS ref_object -- name of table
FROM pg_depend AS d -- objects that depend on a table
JOIN pg_rewrite AS r -- rules depending on a table
ON r.oid = d.objid
JOIN pg_class AS v -- views for the rules
ON v.oid = r.ev_class
JOIN pg_namespace AS ns -- schema information
ON ns.oid = v.relnamespace
WHERE v.relkind = 'v' -- filter views only
-- dependency must be a rule depending on a relation
AND d.classid = 'pg_rewrite'::regclass
AND d.refclassid = 'pg_class'::regclass -- referenced objects in pg_class -- tables and views
AND d.deptype = 'n' -- normal dependency
-- qualify object
AND ns.nspname NOT IN ('pg_catalog', 'information_schema', 'gp_toolkit') -- system schemas
AND NOT (v.oid = d.refobjid) -- not self-referencing dependency
;
view | schema | ref_object
------------+--------+------------
v1 | public | t1
v2 | public | t1
v2 | public | t1
v2 | public | v1
v3 | public | t1
vm1 | public | mytest.tm1
mytest.vm1 | mytest | t1
vm2 | public | mytest.tm1
mytest.v2a | mytest | t1
mytest.v2a | mytest | t1
mytest.v2a | mytest | v1
(11 rows)
Если вы создали представления в нескольких схемах, вы также можете перечислить представления, схему каждого представления и таблицу, на которую ссылается представление. Запрос извлекает схему из таблицы каталога pg_namespace и исключает системные схемы pg_catalog, information_schema и gp_toolkit. Кроме того, запрос не перечисляет представление, если представление ссылается на себя.
SELECT v.oid::regclass AS view,
ns.nspname AS schema, -- view schema,
d.refobjid::regclass AS ref_object -- name of table
FROM pg_depend AS d -- objects that depend on a table
JOIN pg_rewrite AS r -- rules depending on a table
ON r.oid = d.objid
JOIN pg_class AS v -- views for the rules
ON v.oid = r.ev_class
JOIN pg_namespace AS ns -- schema information
ON ns.oid = v.relnamespace
WHERE v.relkind = 'v' -- filter views only
-- dependency must be a rule depending on a relation
AND d.classid = 'pg_rewrite'::regclass
AND d.refclassid = 'pg_class'::regclass -- referenced objects in pg_class -- tables and views
AND d.deptype = 'n' -- normal dependency
-- qualify object
AND ns.nspname NOT IN ('pg_catalog', 'information_schema', 'gp_toolkit') -- system schemas
AND NOT (v.oid = d.refobjid) -- not self-referencing dependency
;
view | schema | ref_object
------------+--------+------------
v1 | public | t1
v2 | public | t1
v2 | public | t1
v2 | public | v1
v3 | public | t1
vm1 | public | mytest.tm1
mytest.vm1 | mytest | t1
vm2 | public | mytest.tm1
mytest.v2a | mytest | t1
mytest.v2a | mytest | t1
mytest.v2a | mytest | v1
(11 rows)
Этот запрос перечисляет представления, которые зависят от t1, столбца, на который ссылаются, и определения представления. Команда CREATE VIEW создается путем добавления соответствующего текста в определение вида.
SELECT v.relname AS view,
d.refobjid::regclass as ref_object,
d.refobjsubid as ref_col,
'CREATE VIEW ' || v.relname || ' AS ' || pg_get_viewdef(v.oid) AS view_def
FROM pg_depend AS d
JOIN pg_rewrite AS r
ON r.oid = d.objid
JOIN pg_class AS v
ON v.oid = r.ev_class
WHERE NOT (v.oid = d.refobjid)
AND d.refobjid = 't1'::regclass
ORDER BY d.refobjsubid
;
view | ref_object | ref_col | view_def
------+------------+---------+--------------------------------------------
v1 | t1 | 1 | CREATE VIEW v1 AS SELECT max(t1.id) AS id+
| | | FROM t1;
v2a | t1 | 1 | CREATE VIEW v2a AS SELECT t1.val +
| | | FROM (t1 +
| | | JOIN v1 USING (id));
vt1 | t1 | 1 | CREATE VIEW vt1 AS SELECT t1.id +
| | | FROM t1 +
| | | WHERE (t1.id < 3);
v2 | t1 | 1 | CREATE VIEW v2 AS SELECT t1.val +
| | | FROM (t1 +
| | | JOIN v1 USING (id));
v2a | t1 | 2 | CREATE VIEW v2a AS SELECT t1.val +
| | | FROM (t1 +
| | | JOIN v1 USING (id));
v3 | t1 | 2 | CREATE VIEW v3 AS SELECT (t1.val || f()) +
| | | FROM t1;
v2 | t1 | 2 | CREATE VIEW v2 AS SELECT t1.val +
| | | FROM (t1 +
| | | JOIN v1 USING (id));
(7 rows)
Этот запрос CTE содержит информацию о представлениях, которые ссылаются на другое представление.
Предложение WITH в этом запросе CTE выбирает все представления в пользовательских схемах. Основной оператор SELECT находит все представления, которые ссылаются на другое представление.
WITH views AS ( SELECT v.relname AS view,
d.refobjid AS ref_object,
v.oid AS view_oid,
ns.nspname AS namespace
FROM pg_depend AS d
JOIN pg_rewrite AS r
ON r.oid = d.objid
JOIN pg_class AS v
ON v.oid = r.ev_class
JOIN pg_namespace AS ns
ON ns.oid = v.relnamespace
WHERE v.relkind = 'v'
AND ns.nspname NOT IN ('pg_catalog', 'information_schema', 'gp_toolkit') -- exclude system schemas
AND d.deptype = 'n' -- normal dependency
AND NOT (v.oid = d.refobjid) -- not a self-referencing dependency
)
SELECT views.view, views.namespace AS schema,
views.ref_object::regclass AS ref_view,
ref_nspace.nspname AS ref_schema
FROM views
JOIN pg_depend as dep
ON dep.refobjid = views.view_oid
JOIN pg_class AS class
ON views.ref_object = class.oid
JOIN pg_namespace AS ref_nspace
ON class.relnamespace = ref_nspace.oid
WHERE class.relkind = 'v'
AND dep.deptype = 'n'
;
view | schema | ref_view | ref_schema
------+--------+----------+------------
v2 | public | v1 | public
v2a | mytest | v1 | public
Вывод примеров запросов основан на этих объектах и данных базы данных.
CREATE TABLE t1 (
id integer PRIMARY KEY,
val text NOT NULL
);
INSERT INTO t1 VALUES
(1, 'one'), (2, 'two'), (3, 'three');
CREATE FUNCTION f() RETURNS text
LANGUAGE sql AS 'SELECT ''suffix''::text';
CREATE VIEW v1 AS
SELECT max(id) AS id
FROM t1;
CREATE VIEW v2 AS
SELECT t1.val
FROM t1 JOIN v1 USING (id);
CREATE VIEW v3 AS
SELECT val || f()
FROM t1;
CREATE VIEW v5 AS
SELECT f() ;
CREATE SCHEMA mytest ;
CREATE TABLE mytest.tm1 (
id integer PRIMARY KEY,
val text NOT NULL
);
INSERT INTO mytest.tm1 VALUES
(1, 'one'), (2, 'two'), (3, 'three');
CREATE VIEW vm1 AS
SELECT id FROM mytest.tm1 WHERE id < 3 ;
CREATE VIEW mytest.vm1 AS
SELECT id FROM public.t1 WHERE id < 3 ;
CREATE VIEW vm2 AS
SELECT max(id) AS id
FROM mytest.tm1;
CREATE VIEW mytest.v2a AS
SELECT t1.val
FROM public.t1 JOIN public.v1 USING (id);
Представление (view) похоже на таблицу, оба являются отношениями - то есть "что-то со столбцами". Все такие объекты хранятся в таблице каталога pg_class. Вот общие отличия:
Правило перезаписи содержит определение представления и хранится в столбце ev_action таблицы каталога pg_rewrite.
Дополнительные технические сведения о представлениях см. в документации PostgreSQL о представлениях и системе правил.
Кроме того, определение представления хранится не в виде строки, а в виде дерева синтаксического анализа запроса. Представления анализируются при их создании, что имеет несколько последствий:
Обратите внимание, что способ, которым база данных RT.Warehouse обрабатывает представления, сильно отличается от способа, которым база данных RT.Warehouse обрабатывает функции: тела функций хранятся в виде строк и не анализируются при их создании. Следовательно, база данных RT.Warehouse не знает, от каких объектов зависит данная функция.
Эти таблицы системного каталога содержат информацию, используемую для определения таблиц, от которых зависит представление.
Важно отметить, что нет прямой зависимости представления от используемых им объектов: зависимый объект фактически является правилом перезаписи представления. Это добавляет еще один уровень косвенности для просмотра информации о зависимостях.
Команда CREATE VIEW определяет представление запроса. Например:
CREATE VIEW comedies AS SELECT * FROM films WHERE kind = 'comedy';
Представления игнорируют операции ORDER BY и SORT, хранящиеся в представлении.
Команда DROP VIEW удаляет представление. Например:
DROP VIEW topten;
Команда DROP VIEW ... CASCADE также удаляет все зависимые объекты. Например, если представление зависит от другого представления, которое должно быть удалено, то зависимое представление также будет удалено. Без параметра CASCADE команда DROP VIEW не будет выполнена.
Материализованные представления похожи на представления. Материализованное представление позволяет сохранить часто используемый или сложный запрос, а затем получить доступ к результатам запроса в операторе SELECT, как если бы они были таблицей. Материализованные представления сохраняют результаты запроса в виде таблицы. Хотя доступ к данным, хранящимся в материализованном представлении, может быть намного быстрее, чем доступ к базовым таблицам напрямую или через представление, данные не всегда актуальны.
Данные материализованного представления не могут быть обновлены напрямую. Чтобы обновить данные материализованного представления, используйте команду REFRESH MATERIALIZED VIEW. Запрос, используемый для создания материализованного представления, хранится точно так же, как хранится запрос представления. Например, вы можете создать материализованное представление, которое быстро отображает сводку исторических данных о продажах для ситуаций, когда допустимо наличие неполных данных на текущую дату.
CREATE MATERIALIZED VIEW sales_summary AS
SELECT seller_no, invoice_date, sum(invoice_amt)::numeric(13,2) as sales_amt
FROM invoice
WHERE invoice_date < CURRENT_DATE
GROUP BY seller_no, invoice_date
ORDER BY seller_no, invoice_date;
CREATE UNIQUE INDEX sales_summary_seller
ON sales_summary (seller_no, invoice_date);
Материализованное представление может быть полезно для отображения графика на информационной панели, созданной для продавцов. С помощью этой команды можно запланировать задание для обновления сводной информации каждую ночь.
REFRESH MATERIALIZED VIEW sales_summary;
Информация о материализованном представлении в системных каталогах базы данных RT.Warehouse точно такая же, как и о таблице или представлении. Материализованное представление — это отношение, такое же, как таблица или представление. Когда материализованное представление упоминается в запросе, данные возвращаются непосредственно из материализованного представления, как и из таблицы. Запрос в определении материализованного представления используется только для заполнения материализованного представления.
Если вы можете допустить периодическое обновление данных материализованного представления, выигрыш в производительности может быть существенным.
Одно из применений материализованного представления — обеспечить более быстрый доступ к данным, полученным из внешнего источника данных, такого как внешняя таблица или внешняя оболочка данных. Кроме того, вы можете определить индексы для материализованного представления, в то время как сторонние оболочки данных не поддерживают индексы; это преимущество может не применяться для других типов доступа к внешним данным.
Если подзапрос связан с одним запросом, рассмотрите возможность использования предложения WITH команды SELECT вместо создания редко используемого материализованного представления.
Команда CREATE MATERIALIZED VIEW определяет материализованное представление на основе запроса.
CREATE MATERIALIZED VIEW us_users AS SELECT u.id, u.name, a.zone FROM users u, address a WHERE a.country = 'USA';
Если запрос материализованного представления содержит предложение ORDER BY или SORT, предложение игнорируется при выполнении SELECT над материализованным запросом.
Команда REFRESH MATERIALIZED VIEW обновляет данные материализованного представления.
REFRESH MATERIALIZED VIEW us_users;
С предложением WITH NO DATA текущие данные удаляются, новые данные не генерируются, а материализованное представление остается в недоступном для сканирования состоянии. Ошибка возвращается, если запрос пытается получить доступ к несканируемому материализованному представлению.
REFRESH MATERIALIZED VIEW us_users WITH NO DATA;
Команда DROP MATERIALIZED VIEW удаляет определение материализованного представления и данные. Например:
DROP MATERIALIZED VIEW us_users;
Команда DROP MATERIALIZED VIEW ... CASCADE также удаляет все зависимые объекты. Например, если другое материализованное представление зависит от материализованного представления, которое должно быть удалено, другое материализованное представление также будет удалено. Без параметра CASCADE команда DROP MATERIALIZED VIEW не работает.
В этом разделе содержится информация об управлении данными и одновременном доступе к базе данных RT.Warehouse.
База данных RT.Warehouse и PostgreSQL не используют блокировки для управления параллелизмом. Они поддерживают согласованность данных с помощью многоверсионной модели Multiversion Concurrency Control (MVCC). MVCC обеспечивает изоляцию транзакций для каждого сеанса базы данных, и каждая транзакция запроса видит моментальный снимок данных. Это гарантирует, что транзакция видит согласованные данные, на которые не влияют другие параллельные транзакции.
Поскольку MVCC не использует явные блокировки для управления параллелизмом, конкуренция за блокировку сведена к минимуму, а база данных RT.Warehouse поддерживает приемлемую производительность в многопользовательских средах. Блокировки, полученные для запроса (чтения) данных, не конфликтуют с блокировками, полученными для записи данных.
База данных RT.Warehouse предоставляет несколько режимов блокировки для управления одновременным доступом к данным в таблицах. Большинство SQL-команд базы данных RT.Warehouse автоматически устанавливают соответствующие блокировки, чтобы гарантировать, что ссылочные таблицы не будут удалены или изменены несовместимым образом во время выполнения команды. Для приложений, которые не могут легко адаптироваться к поведению MVCC, вы можете использовать команду LOCK для получения явных блокировок. Однако правильное использование MVCC обычно обеспечивает лучшую производительность.
Таблица 115. Режимы блокировки в базе данных RT.Warehouse
Режим блокировки | Связанные команды SQL | Конфликты с |
---|---|---|
ACCESS SHARE | SELECT | ACCESS EXCLUSIVE |
ROW SHARE | SELECT FOR SHARE, SELECT...FOR UPDATE | EXCLUSIVE, ACCESS EXCLUSIVE |
ROW EXCLUSIVE | INSERT, COPY | SHARE, SHARE ROW EXCLUSIVE, EXCLUSIVE, ACCESS EXCLUSIVE |
SHARE UPDATE EXCLUSIVE | VACUUM (without FULL), ANALYZE | SHARE UPDATE EXCLUSIVE, SHARE, SHARE ROW EXCLUSIVE, EXCLUSIVE, ACCESS EXCLUSIVE |
SHARE | CREATE INDEX | ROW EXCLUSIVE, SHARE UPDATE EXCLUSIVE, SHARE ROW EXCLUSIVE, EXCLUSIVE, ACCESS EXCLUSIVE |
SHARE ROW EXCLUSIVE | ROW EXCLUSIVE, SHARE UPDATE EXCLUSIVE, SHARE, SHARE ROW EXCLUSIVE, EXCLUSIVE, ACCESS EXCLUSIVE | |
EXCLUSIVE | DELETE, UPDATE, SELECT...FOR UPDATE | ROW SHARE, ROW EXCLUSIVE, SHARE UPDATE EXCLUSIVE, SHARE, SHARE ROW EXCLUSIVE, EXCLUSIVE, ACCESS EXCLUSIVE |
ACCESS EXCLUSIVE | ALTER TABLE, DROP TABLE, TRUNCATE, REINDEX, CLUSTER, VACUUM FULL | ACCESS SHARE, ROW SHARE, ROW EXCLUSIVE, SHARE UPDATE EXCLUSIVE, SHARE, SHARE ROW EXCLUSIVE, EXCLUSIVE, ACCESS EXCLUSIVE |
Примечание. По умолчанию база данных RT.Warehouse получает более ограничительную блокировку EXCLUSIVE (вместо ROW EXCLUSIVE в PostgreSQL) для UPDATE, DELETE и SELECT...FOR UPDATE. Когда глобальный детектор взаимоблокировок включен, режим блокировки для операций DELETE, UPDATE и SELECT...FOR UPDATE для таблиц кучи — ROW EXCLUSIVE. |
Используйте команду INSERT для создания строк в таблице. Для этой команды требуется имя таблицы и значение для каждого столбца в таблице; вы можете дополнительно указать имена столбцов в любом порядке. Если вы не указываете имена столбцов, перечислите значения данных в порядке столбцов в таблице, разделив их запятыми.
Например, чтобы указать имена столбцов и значения для вставки:
INSERT INTO products (name, price, product_no) VALUES ('Cheese', 9.99, 1);
Чтобы указать только значения для вставки:
INSERT INTO products VALUES (1, 'Cheese', 9.99);
Обычно значения данных являются литералами (константами), но вы также можете использовать скалярные выражения. Например:
INSERT INTO films SELECT * FROM tmp_films WHERE date_prod <
'2016-05-07';
Вы можете вставить несколько строк в одну команду. Например:
INSERT INTO products (product_no, name, price) VALUES
(1, 'Cheese', 9.99),
(2, 'Bread', 1.99),
(3, 'Milk', 2.99);
Чтобы вставить данные в партиционированную таблицу, вы указываете корневую партиционированную таблицу, таблицу, созданную с помощью команды CREATE TABLE. Вы также можете указать дочернюю leaf-партиционированную таблицу партиционированной таблицы в команде INSERT. Ошибка возвращается, если данные недействительны для указанной дочерней leaf-таблицы. Указание дочерней таблицы, которая не является дочерней leaf- аблицей, в команде INSERT не поддерживается.
Для вставки больших объемов данных используйте внешние таблицы или команду COPY. Эти механизмы загрузки более эффективны, чем INSERT, для вставки большого количества строк. Дополнительные сведения о массовой загрузке данных см. в разделе Загрузка и выгрузка данных.
Модель хранения таблиц, оптимизированных для добавления, оптимизирована для массовой загрузки данных. RT.Warehouse не рекомендует однострочные операторы INSERT для таблиц, оптимизированных для добавления. Для таблиц, оптимизированных для добавления, база данных RT.Warehouse поддерживает максимум 127 одновременных транзакций INSERT в одной таблице, оптимизированной для добавления.
Команда UPDATE обновляет строки в таблице. Вы можете обновить все строки, подмножество всех строк или отдельные строки в таблице. Вы можете обновить каждый столбец отдельно, не затрагивая другие столбцы.
Для выполнения обновления необходимо:
Например, следующая команда обновляет все продукты с ценой 5 до цены 10:
UPDATE products SET price = 10 WHERE price = 5;
Использование UPDATE в базе данных RT.Warehouse имеет следующие ограничения:
Команда DELETE удаляет строки из таблицы. Укажите предложение WHERE, чтобы удалить строки, соответствующие определенным критериям. Если вы не укажете предложение WHERE, все строки в таблице будут удалены. В результате получается правильная, но пустая таблица. Например, чтобы удалить из таблицы товаров все строки с ценой 10:
DELETE FROM products WHERE price = 10;
Чтобы удалить все строки из таблицы:
DELETE FROM products;
Использование DELETE в базе данных RT.Warehouse имеет те же ограничения, что и использование UPDATE:
Используйте команду TRUNCATE, чтобы быстро удалить все строки в таблице. Например:
TRUNCATE mytable;
Эта команда очищает таблицу от всех строк за одну операцию. Обратите внимание, что TRUNCATE не сканирует таблицу, поэтому не обрабатывает унаследованные дочерние таблицы или правила перезаписи ON DELETE. Команда усекает только строки в именованной таблице.
Транзакции позволяют объединять несколько операторов SQL в одну операцию «все или ничего».
Ниже приведены команды транзакций SQL базы данных RT.Warehouse:
База данных RT.Warehouse принимает следующие стандартные уровни транзакций SQL:
Следующая информация описывает поведение уровней транзакций RT.Warehouse.
База данных RT.Warehouse не позволяет какой-либо команде увидеть незафиксированное обновление в другой параллельной транзакции, поэтому READ UNCOMMITTED ведет себя так же, как READ COMMITTED. READ COMMITTED обеспечивает быструю и простую частичную изоляцию транзакций. Команды SELECT, UPDATE и DELETE работают со снапшотом базы данных, сделанным при запуске запроса.
SELECT-запрос:
Последовательные запросы SELECT в одной и той же транзакции могут отображать разные данные, если другие параллельные транзакции фиксируют изменения между последовательными запросами. Команды UPDATE и DELETE находят только строки, зафиксированные до запуска команд.
Изоляция транзакций READ COMMITTED позволяет параллельным транзакциям изменять или блокировать строку до того, как UPDATE или DELETE найдет строку. Изоляция транзакций READ COMMITTED может быть недостаточной для приложений, выполняющих сложные запросы и обновления и требующих согласованного представления базы данных.
Изоляция транзакций SERIALIZABLE, как определено в стандарте SQL, гарантирует, что транзакции, которые выполняются одновременно, дают такие же результаты, как если бы они выполнялись одна за другой. Если вы укажете SERIALIZABLE, база данных RT.Warehouse вернется к REPEATABLE READ. Транзакции REPEATABLE READ предотвращают грязное чтение, неповторяемое чтение и фантомное чтение без дорогостоящей блокировки, но база данных RT.Warehouse не обнаруживает все взаимодействия сериализуемости, которые могут возникнуть во время параллельного выполнения транзакции. Параллельные транзакции должны быть проверены для выявления взаимодействий, которые не предотвращаются путем запрета одновременных обновлений одних и тех же данных. Вы можете предотвратить эти взаимодействия, используя явные блокировки таблиц или потребовав от конфликтующих транзакций обновить фиктивную строку, введенную для представления конфликта.
С транзакциями REPEATABLE READ запрос SELECT:
Уровень изоляции транзакций по умолчанию в базе данных RT.Warehouse — READ COMMITTED. Чтобы изменить уровень изоляции для транзакции, объявите уровень изоляции при начале (BEGIN) транзакции или используйте команду SET TRANSACTION после запуска транзакции.
Фоновый рабочий процесс Global Deadlock Detector базы данных RT.Warehouse собирает информацию о блокировках для всех сегментов и использует направленный алгоритм для обнаружения локальных и глобальных взаимоблокировок. Этот алгоритм позволяет базе данных RT.Warehouse ослабить одновременное обновление и удалить ограничения для таблиц кучи. (База данных RT.Warehouse по-прежнему использует блокировку на уровне таблиц для таблиц AO/CO, ограничивая одновременные операции UPDATE, DELETE и SELECT...FOR UPDATE.)
По умолчанию глобальный детектор взаимоблокировок отключен, и база данных RT.Warehouse последовательно выполняет параллельные операции обновления и удаления heap таблицы. Вы можете включить эти одновременные обновления и настроить глобальный детектор взаимоблокировок для определения наличия взаимоблокировки, установив параметр конфигурации сервера gp_enable_global_deadlock_detector.
Когда Глобальный детектор взаимоблокировок включен, фоновый рабочий процесс автоматически запускается на главном хосте при запуске базы данных RT.Warehouse. Вы настраиваете интервал, с которым Global Deadlock Detector собирает и анализирует данные об ожидании блокировки, с помощью параметра конфигурации сервера gp_global_deadlock_detector_period.
Если глобальный детектор взаимоблокировок определяет, что взаимоблокировка существует, он устраняет взаимоблокировку, отменяя один или несколько внутренних процессов, связанных с самой молодой задействованной транзакцией.
Когда Global Deadlock Detector определяет наличие взаимоблокировки для следующих типов транзакций, только одна из транзакций будет успешной. Другие транзакции завершатся сбоем с ошибкой, указывающей, что одновременные обновления одной и той же строки не разрешены.
Примечание. База данных RT.Warehouse использует интервал, указанный в параметре конфигурации сервера deadlock_timeout, для локального обнаружения взаимоблокировок. Поскольку локальные и глобальные алгоритмы обнаружения взаимоблокировок различаются, отмененные процессы могут различаться в зависимости от того, какой детектор (локальный или глобальный) базы данных RT.Warehouse срабатывает первым. |
Примечание. Если параметр конфигурации сервера lock_timeout включен и установлен на значение, меньшее, чем deadlock_timeout и gp_global_deadlock_detector_period, база данных RT.Warehouse прервет оператор до того, как она когда-либо инициирует проверку взаимоблокировки в этом сеансе. |
Чтобы просмотреть информацию об ожидании блокировки для всех сегментов, запустите определяемую пользователем функцию gp_dist_wait_status(). Вы можете использовать выходные данные этой функции, чтобы определить, какие транзакции ожидают блокировки, какие транзакции удерживают блокировки, типы и режим блокировки, идентификаторы сеанса ожидания и держателя и какие сегменты выполняют транзакции. Ниже приведен пример вывода функции gp_dist_wait_status():
SELECT * FROM pg_catalog.gp_dist_wait_status();
-[ RECORD 1 ]----+--------------
segid | 0
waiter_dxid | 11
holder_dxid | 12
holdTillEndXact | t
waiter_lpid | 31249
holder_lpid | 31458
waiter_lockmode | ShareLock
waiter_locktype | transactionid
waiter_sessionid | 8
holder_sessionid | 9
-[ RECORD 2 ]----+--------------
segid | 1
waiter_dxid | 12
holder_dxid | 11
holdTillEndXact | t
waiter_lpid | 31467
holder_lpid | 31250
waiter_lockmode | ShareLock
waiter_locktype | transactionid
waiter_sessionid | 9
holder_sessionid | 8
Когда он отменяет транзакцию для выхода из взаимоблокировки, глобальный детектор взаимоблокировок выдает следующее сообщение об ошибке:
ERROR: canceling statement due to user request: "cancelled by global deadlock detector"
Глобальный детектор взаимоблокировок может управлять параллельными обновлениями для этих типов команд UPDATE и DELETE в heap таблицах:
UPDATE t SET c2 = c2 + 1 WHERE c1 > 10;
DELETE FROM t WHERE c1 > 10;
UPDATE t SET c = c + 1; -- c is a distribution key
UPDATE t SET b = b + 1 WHERE c = 10; -- c is a distribution key
UPDATE t1 SET c = t1.c+1 FROM t2 WHERE t1.c = t2.c;
UPDATE t SET c = c + 1 WHERE c > ALL(SELECT * FROM t1);
DELETE FROM t USING t1 WHERE t.c > t1.c;
В следующей таблице показаны параллельные команды UPDATE или DELETE, которыми управляет глобальный детектор взаимоблокировок. Например, одновременные простые команды UPDATE в одной и той же строке таблицы управляются глобальным детектором взаимоблокировок. Для одновременного сложного UPDATE и простого UPDATE выполняется только одно UPDATE, а для другого UPDATE возвращается ошибка.
Таблица 116. Параллельные обновления и удаления, управляемые Global Deadlock Detector
Простое UPDATE | Простое DELETE | Раздельное UPDATE | Комплекс UPDATE | Комплекс DELETE | |
---|---|---|---|---|---|
Простое UPDATE | ДА | ДА | НЕТ | НЕТ | НЕТ |
Простое DELETE | ДА | ДА | НЕТ | ДА | ДА |
Раздельное UPDATE | НЕТ | НЕТ | НЕТ | НЕТ | НЕТ |
Комплекс UPDATE | НЕТ | ДА | НЕТ | НЕТ | НЕТ |
Комплекс DELETE | НЕТ | ДА | НЕТ | НЕТ | ДА |
Удаленные или обновленные строки данных занимают физическое пространство на диске, даже если новые транзакции их не видят. Периодический запуск команды VACUUM удаляет эти просроченные строки. Например:
VACUUM mytable;
Команда VACUUM собирает статистику на уровне таблицы, такую как количество строк и страниц. Очистите все таблицы после загрузки данных, включая таблицы, оптимизированные для добавления. Информацию о рекомендуемых рутинных вакуумных операциях см. в разделе 10.1 Регулярные Vacuum и Analyze
Примечание. Команды VACUUM, VACUUM FULL и VACUUM ANALYZE следует использовать для обслуживания данных в базе данных RT.Warehouse, особенно если в вашей базе данных часто выполняются обновления и удаления. |
В базе данных RT.Warehouse потенциально могут закончиться блокировки, когда операция базы данных обращается к нескольким таблицам в рамках одной транзакции. Резервное копирование и восстановление являются примерами таких операций.
Когда в базе данных RT.Warehouse заканчиваются блокировки, сообщение об ошибке, которое вы можете наблюдать, ссылается на ошибку общей памяти:
... "WARNING","53200","out of shared memory",,,,,,"LOCK TABLE ...
... "ERROR","53200","out of shared memory",,"You might need to increase max_locks_per_transaction.",,,,"LOCK TABLE ...
Примечание. «общая память» в этом контексте относится к общей памяти внутреннего объекта: слотов блокировки. «Недостаточно общей памяти» не относится к исчерпанию ресурсов памяти на уровне системы или RT.Warehouse. |
Как описано в подсказке, рассмотрите возможность увеличения параметра конфигурации сервера max_locks_per_transaction при возникновении этой ошибки.
База данных RT.Warehouse основана на равномерном распределении данных по сегментам.
В среде MPP без общего доступа общее время ответа на запрос измеряется временем завершения для всех сегментов. Скорость системы равна скорости самого медленного сегмента. Если данные искажены, сегменты с большим количеством данных будут выполняться дольше, поэтому каждый сегмент должен иметь примерно равное количество строк и выполнять примерно одинаковый объем обработки. Низкая производительность и нехватка памяти могут возникнуть, если в одном сегменте требуется обработать значительно больше данных, чем в других сегментах.
Оптимальное распределение имеет решающее значение при объединении больших таблиц. Для выполнения соединения совпадающие строки должны располагаться вместе в одном сегменте. Если данные не распределяются по одному и тому же столбцу соединения, строки, необходимые из одной из таблиц, динамически перераспределяются в другие сегменты. В некоторых случаях выполняется широковещательное движение, при котором каждый сегмент отправляет свои отдельные строки всем другим сегментам, а не движение перераспределения, когда каждый сегмент повторно хэширует данные и отправляет строки в соответствующие сегменты в соответствии с хэш-ключом.
Использование распределения хэшей, которое равномерно распределяет строки таблицы по всем сегментам и приводит к локальным соединениям, может обеспечить значительный прирост производительности. Когда соединенные строки находятся в одном сегменте, большая часть обработки может выполняться внутри экземпляра сегмента. Такие соединения называются локальными или совмещенными. Локальные соединения сводят к минимуму перемещение данных; каждый сегмент работает независимо от других сегментов, без сетевого трафика или связи между сегментами.
Чтобы получить локальные соединения для больших таблиц, обычно объединяемых вместе, распределите таблицы по одному и тому же столбцу. Локальные соединения требуют, чтобы обе стороны соединения были распределены по одним и тем же столбцам (и в одном и том же порядке) и чтобы при объединении таблиц использовались все столбцы в предложении распределения. Столбцы распределения также должны иметь один и тот же тип данных — хотя некоторые значения с разными типами данных могут иметь одинаковое представление, они хранятся по-разному и хэшируются для разных значений, поэтому они хранятся в разных сегментах.
Перекос данных может быть вызван неравномерным распределением данных из-за неправильного выбора ключей распределения или операций вставки, или копирования таблицы с одним кортежем. Перекос данных, присутствующий на уровне таблицы, часто является основной причиной низкой производительности запросов и нехватки памяти. Перекошенные данные влияют на производительность сканирования (чтения), но также влияют на все другие операции выполнения запросов, например на операции объединения и группировки.
Очень важно проверять распределения, чтобы гарантировать равномерное распределение данных после первоначальной загрузки. Не менее важно продолжать проверять дистрибутивы после добавочных загрузок.
Следующий запрос показывает количество строк в сегменте, а также отклонение от минимального и максимального количества строк:
SELECT 'Example Table' AS "Table Name",
max(c) AS "Max Seg Rows", min(c) AS "Min Seg Rows",
(max(c)-min(c))*100.0/max(c) AS "Percentage Difference Between Max & Min"
FROM (SELECT count(*) c, gp_segment_id FROM facts GROUP BY 2) AS a;
Схема gp_toolkit имеет два представления, которые можно использовать для проверки перекоса.
Когда вы создаете реплицированную таблицу (с предложением CREATE TABLE DISTRIBUTED REPLICATED), база данных RT.Warehouse распределяет каждую строку таблицы по каждому экземпляру сегмента. Реплицированные данные таблицы распределяются равномерно, так как каждый сегмент имеет одинаковые строки. Запрос, который использует системный столбец gp_segment_id в реплицированной таблице для проверки равномерности распределения данных, завершится ошибкой, поскольку база данных RT.Warehouse не позволяет запросам ссылаться на системные столбцы реплицированных таблиц.
Перекос обработки возникает, когда непропорциональное количество данных поступает и обрабатывается одним или несколькими сегментами. Это часто является причиной проблем с производительностью и стабильностью базы данных RT.Warehouse. Это может произойти с такими операциями, как объединение, сортировка, агрегация и различные операции OLAP. Перекос обработки происходит во время выполнения запроса, и его не так просто обнаружить, как перекос данных.
Если отдельные сегменты выходят из строя, то есть не все сегменты на хосте, это может быть проблемой перекоса обработки. Выявление перекоса обработки в настоящее время выполняется вручную. Сначала найдите spill-файлы. Если перекос есть, но не настолько, чтобы вызвать spill, это не станет проблемой производительности. Если вы определили, что перекос существует, найдите запрос, ответственный за перекос. Ниже приведены шаги и команды для использования (измените имена, такие как имя файла хоста, переданное в gpssh соответственно):
1. Найдите OID для базы данных, которая должна отслеживаться на предмет обработки перекосов:
SELECT oid, datname FROM pg_database;
Пример вывода:
oid | datname
-------+-----------
17088 | gpadmin
10899 | postgres
1 | template1
10898 | template0
38817 | pws
39682 | gpperfmon
(6 rows)
2. Запустите команду gpssh, чтобы проверить размеры файлов на всех узлах сегментов в системе. Замените <OID> на OID базы данных из предыдущей команды:
[gpadmin@mdw kend]$ gpssh -f ~/hosts -e \
"du -b /data[1-2]/primary/gpseg*/base/<OID>/pgsql_tmp/*" | \
grep -v "du -b" | sort | awk -F" " '{ arr[$1] = arr[$1] + $2 ; tot = tot + $2 }; END \
{ for ( i in arr ) print "Segment node" i, arr[i], "bytes (" arr[i]/(1024**3)" GB)"; \
print "Total", tot, "bytes (" tot/(1024**3)" GB)" }' -
Пример вывода:
Segment node[sdw1] 2443370457 bytes (2.27557 GB)
Segment node[sdw2] 1766575328 bytes (1.64525 GB)
Segment node[sdw3] 1761686551 bytes (1.6407 GB)
Segment node[sdw4] 1780301617 bytes (1.65804 GB)
Segment node[sdw5] 1742543599 bytes (1.62287 GB)
Segment node[sdw6] 1830073754 bytes (1.70439 GB)
Segment node[sdw7] 1767310099 bytes (1.64594 GB)
Segment node[sdw8] 1765105802 bytes (1.64388 GB)
Total 14856967207 bytes (13.8366 GB)
Если существует значительная и устойчивая разница в использовании диска, то выполняемые запросы должны быть исследованы на предмет возможного перекоса (приведенный выше пример не показывает значительного перекоса). В системах мониторинга всегда будет некоторый перекос, но часто он временный и непродолжительный.
3. Если появляется значительная и устойчивая асимметрия, то следующая задача — идентифицировать нарушающий запрос.
Команда на предыдущем шаге суммирует весь узел. На этот раз найдите фактический каталог сегмента. Вы можете сделать это из мастера или войдя в конкретный узел, указанный на предыдущем шаге. Ниже приведен пример запуска из мастера.
Этот пример специально предназначен для файлов сортировки. Не все файлы разлива или перекосы вызваны файлами сортировки, поэтому вам потребуется настроить команду:
$ gpssh -f ~/hosts -e
"ls -l /data[1-2]/primary/gpseg*/base/19979/pgsql_tmp/*"
| grep -i sort | awk '{sub(/base.*tmp\//, ".../", $10); print $1,$6,$10}' | sort -k2 -n
Вывод этой команды:
[sdw1] 288718848
/data1/primary/gpseg2/.../pgsql_tmp_slice0_sort_17758_0001.0[sdw1] 291176448
/data2/primary/gpseg5/.../pgsql_tmp_slice0_sort_17764_0001.0[sdw8] 924581888
/data2/primary/gpseg45/.../pgsql_tmp_slice10_sort_15673_0010.9[sdw4] 980582400
/data1/primary/gpseg18/.../pgsql_tmp_slice10_sort_29425_0001.0[sdw6] 986447872
/data2/primary/gpseg35/.../pgsql_tmp_slice10_sort_29602_0001.0...[sdw5] 999620608
/data1/primary/gpseg26/.../pgsql_tmp_slice10_sort_28637_0001.0[sdw2] 999751680
/data2/primary/gpseg9/.../pgsql_tmp_slice10_sort_3969_0001.0[sdw3] 1000112128
/data1/primary/gpseg13/.../pgsql_tmp_slice10_sort_24723_0001.0[sdw5] 1000898560
/data2/primary/gpseg28/.../pgsql_tmp_slice10_sort_28641_0001.0...[sdw8] 1008009216
/data1/primary/gpseg44/.../pgsql_tmp_slice10_sort_15671_0001.0[sdw5] 1008566272
/data1/primary/gpseg24/.../pgsql_tmp_slice10_sort_28633_0001.0[sdw4] 1009451008
/data1/primary/gpseg19/.../pgsql_tmp_slice10_sort_29427_0001.0[sdw7] 1011187712
/data1/primary/gpseg37/.../pgsql_tmp_slice10_sort_18526_0001.0[sdw8] 1573741824
/data2/primary/gpseg45/.../pgsql_tmp_slice10_sort_15673_0001.0[sdw8] 1573741824
/data2/primary/gpseg45/.../pgsql_tmp_slice10_sort_15673_0002.1[sdw8] 1573741824
/data2/primary/gpseg45/.../pgsql_tmp_slice10_sort_15673_0003.2[sdw8] 1573741824
/data2/primary/gpseg45/.../pgsql_tmp_slice10_sort_15673_0004.3[sdw8] 1573741824
/data2/primary/gpseg45/.../pgsql_tmp_slice10_sort_15673_0005.4[sdw8] 1573741824
/data2/primary/gpseg45/.../pgsql_tmp_slice10_sort_15673_0006.5[sdw8] 1573741824
/data2/primary/gpseg45/.../pgsql_tmp_slice10_sort_15673_0007.6[sdw8] 1573741824
/data2/primary/gpseg45/.../pgsql_tmp_slice10_sort_15673_0008.7[sdw8] 1573741824
/data2/primary/gpseg45/.../pgsql_tmp_slice10_sort_15673_0009.8
Сканирование этих выходных данных показывает, что виновником является сегмент gpseg45 на хосте sdw8, поскольку его файлы сортировки больше, чем другие в выходных данных.
4. Войдите на узел-нарушитель с помощью ssh и станьте пользователем root. Используйте команду lsof, чтобы найти PID процесса, которому принадлежит один из файлов сортировки:
[root@sdw8 ~]# lsof /data2/primary/gpseg45/base/19979/pgsql_tmp/pgsql_tmp_slice10_sort_15673_0002.1
COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME
postgres 15673 gpadmin 11u REG 8,48 1073741824 64424546751 /data2/primary/gpseg45/base/19979/pgsql_tmp/pgsql_tmp_slice10_sort_15673_0002.1
PID 15673 также является частью имени файла, но это не всегда так.
5. Используйте команду ps с PID, чтобы определить базу данных и информацию о соединении:
[root@sdw8 ~]# ps -eaf | grep 15673
gpadmin 15673 27471 28 12:05 ? 00:12:59 postgres: port 40003, sbaskin bdw
172.28.12.250(21813) con699238 seg45 cmd32 slice10 MPPEXEC SELECT
root 29622 29566 0 12:50 pts/16 00:00:00 grep 15673
6. На мастере проверьте файл журнала pg_log для пользователя в предыдущей команде (sbaskin), соединении (con699238) и команде (cmd32). Строка в файле журнала с этими тремя значениями должна быть строкой, содержащей запрос, но иногда номер команды может немного отличаться.Например, вывод ps может показывать cmd32, но в файле журнала это cmd34. Если запрос все еще выполняется, последний запрос для пользователя и подключения является некорректным запросом.
Средством устранения перекоса почти во всех случаях является переписывание запроса. Создание временных таблиц может устранить перекос. Временные таблицы могут быть распределены случайным образом для принудительного двухэтапного агрегирования.
В этом разделе содержится информация об использовании SQL в базах данных RT.Warehouse.
Вы вводите операторы SQL, называемые запросами, для просмотра, изменения и анализа данных в базе данных с помощью интерактивного клиента SQL psql и других клиентских инструментов.
В этом разделе представлен обзор того, как база данных RT.Warehouse обрабатывает запросы. Понимание этого процесса может быть полезно при написании и настройке запросов.
Пользователи отправляют запросы к базе данных RT.Warehouse, как и к любой системе управления базами данных. Они подключаются к экземпляру базы данных на главном хосте RT.Warehouse с помощью клиентского приложения, такого как psql, и отправляют операторы SQL.
Мастер получает, анализирует и оптимизирует запрос. Результирующий план запроса является либо параллельным, либо целевым. Мастер отправляет параллельные планы запросов во все сегменты, как показано на Рисунке 1. Мастер отправляет целевые планы запросов в один сегмент, как показано на Рисунке 2. Каждый сегмент отвечает за выполнение операций локальной базы данных со своим собственным набором данных.
Большинство операций базы данных, таких как сканирование таблиц, объединение, агрегирование и сортировка, выполняются во всех сегментах параллельно. Каждая операция выполняется в базе данных сегмента независимо от данных, хранящихся в других базах данных сегмента.
Рисунок 1. Отправка плана параллельного запроса
Некоторые запросы могут обращаться только к данным в одном сегменте, например однострочные операции INSERT, UPDATE, DELETE или SELECT или запросы, фильтрующие столбцы ключа распределения таблицы. В подобных запросах план запроса не рассылается всем сегментам, а нацелен на сегмент, который содержит затронутые или релевантные строки.
Рисунок 2. Отправка плана целевого запроса
План запроса — это набор операций, которые база данных RT.Warehouse будет выполнять для получения ответа на запрос. Каждый узел или шаг в плане представляет собой операцию с базой данных, такую как просмотр таблицы, объединение, агрегирование или сортировка. Планы читаются и выполняются снизу вверх.
В дополнение к обычным операциям с базой данных, таким как сканирование таблиц, объединение и т. д., в базе данных RT.Warehouse есть дополнительный тип операции, называемый перемещением (motion). Операция перемещения включает перемещение кортежей между сегментами во время обработки запроса. Обратите внимание, что не каждый запрос требует перемещения. Например, план целевого запроса не требует перемещения данных по интерконнекту.
Для достижения максимального параллелизма при выполнении запроса RT.Warehouse делит работу плана запроса на срезы. Срез — это часть плана, над которой сегменты могут работать независимо. План запроса нарезается везде, где в плане происходит операция перемещения, по одному срезу с каждой стороны движения.
Например, рассмотрим следующий простой запрос, включающий соединение между двумя таблицами:
SELECT customer, amount
FROM sales JOIN customer USING (cust_id)
WHERE dateCol = '04-30-2016';
План запроса для этого примера имеет перемещение перераспределения (redistribute motion), которое перемещает кортежи между сегментами для завершения соединения. Перемещение перераспределения необходимо, потому что таблица клиентов распределяется по сегментам по cust_id, а таблица продаж распределяется по сегментам по sale_id. Для выполнения объединения кортежи продаж должны быть перераспределены по cust_id. План нарезается по обе стороны от движения перераспределения, создавая срез 1 и срез 2.
Этот план запроса имеет другой тип операции перемещения, называемый перемещением сбора (gather motion). Перемещение сбора — это когда сегменты отправляют результаты обратно мастеру для представления клиенту. Поскольку план запроса всегда нарезается везде, где происходит перемещение, этот план также имеет неявный срез в самом верху плана (срез 3). Не все планы запросов включают перемещение сбора. Например, в инструкции CREATE TABLE x AS SELECT... не будет перемещения сбора, поскольку кортежи отправляются во вновь созданную таблицу, а не в главную.
На Рисунке 3 показан план запроса. Каждый сегмент получает копию плана запроса и работает над ним параллельно.
Рисунок 3. План среза запроса
RT.Warehouse создает ряд процессов базы данных для обработки запроса. На мастере рабочий процесс запросов называется диспетчером запросов (QD). QD отвечает за создание и отправку плана запроса. Он также накапливает и представляет окончательные результаты. В сегментах рабочий процесс запроса называется исполнителем запроса (QE). QE отвечает за выполнение своей части работы и передачу промежуточных результатов другим рабочим процессам.
Каждому фрагменту плана запроса назначен как минимум один рабочий процесс. Рабочий процесс работает над назначенной ему частью плана запроса независимо. Во время выполнения запроса в каждом сегменте будет несколько процессов, работающих над запросом параллельно.
Связанные процессы, работающие с одним и тем же фрагментом плана запроса, но с разными сегментами, называются группами. Когда часть работы завершена, кортежи переходят вверх по плану запроса от одной группы процессов к другой. Эта межпроцессная связь между сегментами называется компонентом интерконнектом базы данных RT.Warehouse.
На Рисунке 4 показаны рабочие процессы запроса на мастере и двух экземплярах сегмента для плана запроса, показанного на Рисунке 3.
Рисунок 4. Рабочие процессы запросов
В базе данных RT.Warehouse оптимизатор GPORCA по умолчанию сосуществует с планировщиком Postgres.
В следующих разделах описываются функциональные возможности и использование GPORCA.
GPORCA расширяет возможности планирования и оптимизации Postgres Planner. GPORCA является расширяемым и обеспечивает лучшую оптимизацию в средах с многоядерной архитектурой. База данных RT.Warehouse по умолчанию использует GPORCA для создания плана выполнения запроса, когда это возможно.
GPORCA также улучшает настройку производительности запросов базы данных RT.Warehouse в следующих областях:
В базе данных RT.Warehouse GPORCA сосуществует с планировщиком Postgres. По умолчанию база данных RT.Warehouse использует GPORCA. Если нельзя использовать GPORCA, используется планировщик Postgres.
На Рисунке 5 показано, как GPORCA вписывается в архитектуру планирования запросов.
Рисунок 5. Формирование плана запроса
Примечание. Все параметры конфигурации сервера Postgres Planner игнорируются GPORCA. Однако, если база данных RT.Warehouse вернется к планировщику Postgres, параметры конфигурации сервера планировщика повлияют на создание плана запроса. |
По умолчанию база данных RT.Warehouse использует GPORCA вместо планировщика Postgres. Параметры конфигурации сервера включают или отключают GPORCA.
Хотя GPORCA включен по умолчанию, вы можете настроить использование GPORCA на уровне системы, базы данных, сеанса или запроса, используя параметр оптимизатора. Обратитесь к одному из следующих разделов, если вы хотите изменить поведение по умолчанию:
Примечание. Вы можете отключить возможность включения или отключения GPORCA с помощью параметра конфигурации сервера optimizer_control. |
Установите оптимизатор параметров конфигурации сервера для системы базы данных RT.Warehouse.
1. Войдите на главный хост базы данных RT.Warehouse как gpadmin, администратор базы данных RT.Warehouse.
2. Установите значения параметров конфигурации сервера. Эти команды утилиты gpconfig базы данных RT.Warehouse устанавливают для параметров значение on:
$ gpconfig -c optimizer -v on --masteronly
3. Перезапустите базу данных RT.Warehouse. Эта служебная команда gpstop базы данных RT.Warehouse перезагружает файлы postgresql.conf мастера и сегментов, не закрывая базу данных RT.Warehouse.
gpstop -u
Установите оптимизатор параметров конфигурации сервера для отдельных баз данных RT.Warehouse с помощью команды ALTER DATABASE. Например, эта команда включает GPORCA для базы данных test_db.
> ALTER DATABASE test_db SET OPTIMIZER = ON ;
Вы можете использовать команду SET, чтобы установить параметр конфигурации сервера оптимизатора для сеанса. Например, после использования утилиты psql для подключения к базе данных RT.Warehouse эта команда SET включает GPORCA:
> set optimizer = on ;
Чтобы задать параметр для определенного запроса, включите команду SET перед выполнением запроса.
Для партиционированной таблицы GPORCA использует статистику корневой (root) партциии таблицы для создания планов запросов. Эти статистические данные используются для определения порядка соединения, для разделения и соединения агрегатных узлов, а также для расчета стоимости шагов запроса. Напротив, планировщик Postgres использует статистику каждой leaf-партиции.
Если вы выполняете запросы к партиционированным таблицам, вам следует собирать статистику по корневой партиции и периодически обновлять эту статистику, чтобы гарантировать, что GPORCA сможет генерировать оптимальные планы запросов. Если статистика корневой партиции не актуальна или не существует, GPORCA по-прежнему выполняет динамическое устранение партиции для запросов к таблице. Однако план запроса может быть не оптимальным.
По умолчанию при запуске команды ANALYZE в корневом разделе партиционированной таблицы выполняется выборка данных leaf-партиции в таблице и сохранение статистики для корневой партиции. ANALYZE собирает статистику по корневой и leaf партициям, включая статистику HyperLogLog (HLL) по leaf-партициям. ANALYZE ROOTPARTITION собирает статистику только по корневой партиции. Параметр конфигурации сервера optimizer_analyze_root_partition определяет, требуется ли ключевое слово ROOTPARTITION для сбора корневой статистики для корневой партциии партиционированной таблицы.
Имейте в виду, что ANALYZE всегда сканирует всю таблицу перед обновлением статистики корневой партиции. Если ваша таблица очень большая, эта операция может занять значительное время. ANALYZE ROOTPARTITION также использует блокировку ACCESS SHARE, которая предотвращает определенные операции, такие как операции TRUNCATE и VACUUM, во время выполнения. По этим причинам следует периодически планировать операции ANALYZE или при значительных изменениях в данных leaf-партиции.
Следуйте этим рекомендациям по запуску ANALYZE или ANALYZE ROOTPARTITION для партиционированных таблиц в вашей системе:
Хотя создание и поддержка статистики корневых партиций имеет решающее значение для производительности запросов GPORCA с партиционированными таблицами, поддержка статистики leaf-партиций также важна. Если GPORCA не может сгенерировать план для запроса к партиционированной таблице, тогда используется планировщик Postgres, и для создания оптимального плана для этого запроса необходима статистика leaf-партиций.
Сама GPORCA также использует статистику leaf-партиций для любых запросов, которые обращаются к leaf-партициям напрямую, вместо использования корневой партиции с предикатами для устранения партиции. Например, если вы знаете, какие партиции содержат необходимые кортежи для запроса, вы можете напрямую запросить саму leaf-партицию таблицы; в этом случае GPORCA использует статистику leaf-партиции.
Если вы не собираетесь выполнять запросы к партиционированным таблицам с помощью GPORCA (отключив параметр оптимизатора конфигурации сервера), то вы можете отключить автоматический сбор статистики по корневой партиции партиционированной таблицы. Параметр конфигурации сервера optimizer_analyze_root_partition определяет, требуется ли ключевое слово ROOTPARTITION для сбора корневой статистики для корневой партиции партиционированной таблицы. Значение по умолчанию для этого параметра включено, команда ANALYZE может собирать статистику корневой партиции без ключевого слова ROOTPARTITION. Вы можете отключить автоматический сбор статистики корневой партиции, установив для параметра значение off. Когда значение выключено, вы должны запустить ANALZYE ROOTPARTITION для сбора статистики корневой партиции.
1. Войдите на главный хост базы данных RT.Warehouse как gpadmin, администратор базы данных RT.Warehouse.
2. Установите значения параметров конфигурации сервера. Эти команды утилиты gpconfig базы данных RT.Warehouse отключают значение параметров:
$ gpconfig -c optimizer_analyze_root_partition -v off --masteronly
3. Перезапустите базу данных RT.Warehouse. Эта служебная команда gpstop базы данных RT.Warehouse перезагружает файлы postgresql.conf мастера и сегментов, не закрывая базу данных RT.Warehouse.
gpstop -u
Чтобы оптимально выполнять запросы с помощью GPORCA, необходимо учитывать критерии запроса.
Убедитесь, что соблюдены следующие критерии:
Примечание. Включение этого параметра снижает производительность кратковременных запросов к каталогу. Чтобы избежать этой проблемы, устанавливайте этот параметр только для сеанса или запроса. |
Если партиционированная таблица содержит более 20 000 партиций, рассмотрите возможность изменения схемы таблицы.
Эти параметры конфигурации сервера влияют на обработку запросов GPORCA:
Эти параметры конфигурации сервера управляют отображением и записью информации:
GPORCA создает минидампы для описания контекста оптимизации для данного запроса. Файлы минидампа используются службой поддержки для анализа проблем с базой данных RT.Warehouse. Информация в файле представлена в формате, который нельзя легко использовать для отладки или устранения неполадок. Файл минидампа находится в каталоге основных данных и использует следующий формат имени: Minidump_date_time.mdp
Когда команда EXPLAIN ANALYZE использует GPORCA, план EXPLAIN показывает только количество удаляемых разделов. Отсканированные разделы не отображаются. Чтобы отображать имена сканируемых разделов в журналах сегментов, включите параметр конфигурации сервера gp_log_dynamic_partition_pruning. В этом примере команда SET включает параметр:
SET gp_log_dynamic_partition_pruning = on;
GPORCA, оптимизатор запросов нового поколения RT.Warehouse, включает улучшения для определенных типов запросов и операций:
GPORCA также включает следующие улучшения оптимизации:
GPORCA включает следующие усовершенствования для запросов к партиционированным таблицам:
Partition Selector for Part_Table (dynamic scan id: 1)
Filter: a > 10
Partitions selected: 1 (out of 3)
В этом примере команда CREATE TABLE создает таблицу с партиционированием по диапазонам:
CREATE TABLE sales(order_id int, item_id int, amount numeric(15,2),
date date, yr_qtr int)
range partitioned by yr_qtr;
GPORCA улучшает следующие типы запросов к партиционированным таблицам:
SELECT * FROM sales;
SELECT * FROM sales WHERE yr_qtr = 201501;
SELECT * FROM sales WHERE yr_qtr BETWEEN 201601 AND 201704;
SELECT * FROM catalog_sales
WHERE date_id IN (SELECT id FROM date_dim WHERE month=12);
GPORCA более эффективно обрабатывает подзапросы.
Подзапрос - это запрос, вложенный во внешний блок запроса. В следующем запросе SELECT в пункте WHERE является подзапросом.
SELECT * FROM part
WHERE price > (SELECT avg(price) FROM part);
GPORCA также более эффективно обрабатывает запросы, содержащие коррелированный подзапрос (CSQ). Коррелированный подзапрос — это подзапрос, который использует значения из внешнего запроса. В следующем запросе столбец цены используется как во внешнем, так и в подзапросе.
SELECT * FROM part p1
WHERE price > (SELECT avg(price) FROM part p2
WHERE p2.brand = p1.brand);
GPORCA генерирует более эффективные планы для следующих типов подзапросов:
SELECT *,
(SELECT min(price) FROM part p2 WHERE p1.brand = p2.brand)
AS foo
FROM part p1;
SELECT FROM part p1 WHERE p_size > 40 OR
p_retailprice >
(SELECT avg(p_retailprice)
FROM part p2
WHERE p2.p_brand = p1.p_brand)
SELECT * FROM part p1 WHERE p1.p_partkey
IN (SELECT p_partkey FROM part p2 WHERE p2.p_retailprice =
(SELECT min(p_retailprice)
FROM part p3
WHERE p3.p_brand = p1.p_brand)
);
Примечание. Вложенные CSQ с корреляциями уровней пропуска не поддерживаются планировщиком Postgres. |
SELECT * FROM part p1 WHERE p1.p_retailprice =
(SELECT min(p_retailprice) FROM part p2 WHERE p2.p_brand <> p1.p_brand);
SELECT p_partkey,
(SELECT p_retailprice FROM part p2 WHERE p2.p_brand = p1.p_brand )
FROM part p1;
GPORCA обрабатывает запросы, содержащие предложение WITH. Предложение WITH, также известное как общее табличное выражение (CTE), создает временные таблицы, которые существуют только для запроса. Этот пример запроса содержит CTE.
WITH v AS (SELECT a, sum(b) as s FROM T where c < 10 GROUP BY a)
SELECT *FROM v AS v1 , v AS v2
WHERE v1.a <> v2.a AND v1.s < v2.s;
В рамках оптимизации запросов GPORCA может помещать предикаты в CTE. Например, запрос GPORCA отправляет предикаты равенства в CTE.
WITH v AS (SELECT a, sum(b) as s FROM T GROUP BY a)
SELECT *
FROM v as v1, v as v2, v as v3
WHERE v1.a < v2.a
AND v1.s < v3.s
AND v1.a = 10
AND v2.a = 20
AND v3.a = 30;
GPORCA может обрабатывать следующие типы CTE:
WITH cte1 AS (SELECT a, sum(b) as s FROM T
where c < 10 GROUP BY a),
cte2 AS (SELECT a, s FROM cte1 where s > 1000)
SELECT *
FROM cte1 as v1, cte2 as v2, cte2 as v3
WHERE v1.a < v2.a AND v1.s < v3.s;
WITH v AS (WITH w AS (SELECT a, b FROM foo
WHERE b < 5)
SELECT w1.a, w2.b
FROM w AS w1, w AS w2
WHERE w1.a = w2.a AND w1.a > 2)
SELECT v1.a, v2.a, v2.b
FROM v as v1, v as v2
WHERE v1.a < v2.a;
GPORCA содержит улучшения для операций DML, таких как INSERT, UPDATE и DELETE.
В этом примере плана показан оператор Split:
QUERY PLAN
--------------------------------------------------------------
Update (cost=0.00..5.46 rows=1 width=1)
-> Redistribute Motion 2:2 (slice1; segments: 2)
Hash Key: a
-> Result (cost=0.00..3.23 rows=1 width=48)
-> Split (cost=0.00..2.13 rows=1 width=40)
-> Result (cost=0.00..1.05 rows=1 width=40)
-> Seq Scan on dmltest
В этом примере плана показан оператор Assert:
QUERY PLAN
------------------------------------------------------------
Insert (cost=0.00..4.61 rows=3 width=8)
-> Assert (cost=0.00..3.37 rows=3 width=24)
Assert Cond: (dmlsource.a > 2) IS DISTINCT FROM
false
-> Assert (cost=0.00..2.25 rows=3 width=24)
Assert Cond: NOT dmlsource.b IS NULL
-> Result (cost=0.00..1.14 rows=3 width=24)
-> Seq Scan on dmlsource
Есть изменения в поведении базы данных RT.Warehouse при включенном оптимизаторе GPORCA (по умолчанию) по сравнению с планировщиком Postgres.
update r set b = r.b + 1 from s where r.a in (select a from s);
Существуют ограничения в базе данных RT.Warehouse при использовании оптимизатора GPORCA по умолчанию. GPORCA и Postgres Planner в настоящее время сосуществуют в базе данных RT.Warehouse, поскольку GPORCA не поддерживает все функции базы данных RT.Warehouse.
В этом разделе описаны ограничения:
Некоторые функции запросов не поддерживаются оптимизатором GPORCA по умолчанию. Когда выполняется неподдерживаемый запрос, RT.Warehouse регистрирует это уведомление вместе с текстом запроса:
Feature not supported by the Pivotal Query Optimizer: UTILITY command
Эти функции не поддерживаются, когда GPORCA включена (по умолчанию):
Следующие функции представляют собой известные регрессии производительности, возникающие при включении GPORCA:
Кроме того, расширенная функциональность функций из предыдущих версий может привести к необходимости дополнительного времени, когда GPORCA выполняет операторы SQL с функциями.
Когда GPORCA включен (по умолчанию), вы можете определить, использует ли база данных RT.Warehouse GPORCA или использует планировщик Postgres.
Вы можете изучить план запроса EXPLAIN для запроса, чтобы определить, какой оптимизатор запросов использовался базой данных RT.Warehouse для выполнения запроса:
Settings: optimizer=on
Optimizer status: Pivotal Optimizer (GPORCA) version 1.584
Settings: optimizer=on
Optimizer status: Postgres query optimizer
Settings: optimizer=off
Optimizer status: Postgres query optimizer
Лог файл содержит сообщения, указывающие, какой оптимизатор запросов использовался. Если база данных RT.Warehouse возвращается к Postgres Planner, в лог файл добавляется сообщение с информацией NOTICE, указывающее на неподдерживаемую функцию. Кроме того, метка Planner produced plan (Планировщик создал план): появляется перед запросом в сообщении лога выполнения запроса, когда база данных RT.Warehouse возвращается к оптимизатору Postgres.
Примечание. Вы можете настроить базу данных RT.Warehouse для отображения сообщений лога в командной строке psql, установив для параметра конфигурации сервера базы данных RT.Warehouse client_min_messages значение LOG. |
В этом примере показаны различия для запроса, который выполняется для партиционированных таблиц, когда включена функция GPORCA.
Этот оператор CREATE TABLE создает таблицу с одноуровневыми партициями:
CREATE TABLE sales (trans_id int, date date,
amount decimal(9,2), region text)
DISTRIBUTED BY (trans_id)
PARTITION BY RANGE (date)
(START (date '20160101')
INCLUSIVE END (date '20170101')
EXCLUSIVE EVERY (INTERVAL '1 month'),
DEFAULT PARTITION outlying_dates );
Этот запрос к таблице поддерживается GPORCA и не создает ошибок в лог файле:
select * from sales ;
Выходные данные плана EXPLAIN содержат только количество выбранных партиций.
-> Partition Selector for sales (dynamic scan id: 1) (cost=10.00..100.00 rows=50 width=4)
Partitions selected: 13 (out of 13)
Если запрос к партиционированной таблице не поддерживается GPORCA. База данных RT.Warehouse использует планировщик Postgres. В плане EXPLAIN, сгенерированном планировщиком Postgres, перечислены выбранные партиции. В этом примере показана часть плана объяснения, в которой перечислены некоторые выбранные партиции:
-> Append (cost=0.00..0.00 rows=26 width=53)
-> Seq Scan on sales2_1_prt_7_2_prt_usa sales2 (cost=0.00..0.00 rows=1 width=53)
-> Seq Scan on sales2_1_prt_7_2_prt_asia sales2 (cost=0.00..0.00 rows=1 width=53)
...
В этом примере показан вывод лога, когда база данных RT.Warehouse возвращается к планировщику Postgres из GPORCA.
При выполнении этого запроса база данных RT.Warehouse возвращается к планировщику Postgres:
explain select * from pg_class;
Сообщение добавляется в лог файл. Сообщение содержит эту информацию NOTICE, которая указывает причину, по которой GPORCA не выполнила запрос:
NOTICE,""Feature not supported: Queries on master-only tables"
GPORCA поддерживает запросы к таблице с многоуровневым партиционированием (MLP), если таблица MLP является таблицей с однородным партиционированием. Многоуровневая партиционированная таблица — это партиционированная таблица, созданная с помощью предложения SUBPARTITION. Однородная партиционированная таблица должна соответствовать этим требованиям:
Вы можете отображать информацию о партиционированных таблицах несколькими способами, включая отображение информации из следующих источников:
Эта команда CREATE TABLE создает унифицированную партиционированную таблицу.
CREATE TABLE mlp (id int, year int, month int, day int,
region text)
DISTRIBUTED BY (id)
PARTITION BY RANGE ( year)
SUBPARTITION BY LIST (region)
SUBPARTITION TEMPLATE (
SUBPARTITION usa VALUES ( 'usa'),
SUBPARTITION europe VALUES ( 'europe'),
SUBPARTITION asia VALUES ( 'asia'))
( START ( 2006) END ( 2016) EVERY ( 5));
Это дочерние таблицы и иерархия партиций, созданные для таблицы mlp. Эта иерархия состоит из одного уровня подпартиций, который содержит две ветви.
mlp_1_prt_11
mlp_1_prt_11_2_prt_usa
mlp_1_prt_11_2_prt_europe
mlp_1_prt_11_2_prt_asia
mlp_1_prt_21
mlp_1_prt_21_2_prt_usa
mlp_1_prt_21_2_prt_europe
mlp_1_prt_21_2_prt_asia
Иерархия таблицы единая, каждая партиция содержит набор из трех дочерних таблиц (подпартиций). Ограничения для подпартиций региона унифицированы, набор ограничений для дочерних таблиц для таблицы ответвлений mlp_1_prt_11 такой же, как ограничения для дочерних таблиц для таблицы ответвлений mlp_1_prt_21.
В качестве быстрой проверки этот запрос отображает ограничения для партиций.
WITH tbl AS (SELECT oid, partitionlevel AS level,
partitiontablename AS part
FROM pg_partitions, pg_class
WHERE tablename = 'mlp' AND partitiontablename=relname
AND partitionlevel=1 )
SELECT tbl.part, consrc
FROM tbl, pg_constraint
WHERE tbl.oid = conrelid ORDER BY consrc;
Примечание. Вам нужно будет изменить запрос для более сложных партиционированных таблиц. Например, запрос не учитывает имена таблиц в разных схемах. |
Столбец consrc отображает ограничения для подпартиций. Набор региональных ограничений для подпартиций в mlp_1_prt_1 соответствует ограничениям для подпартиций в mlp_1_prt_2. Ограничения для года наследуются от ветвей родительских таблиц.
part | consrc
--------------------------+------------------------------------
mlp_1_prt_2_2_prt_asia | (region = 'asia'::text)
mlp_1_prt_1_2_prt_asia | (region = 'asia'::text)
mlp_1_prt_2_2_prt_europe | (region = 'europe'::text)
mlp_1_prt_1_2_prt_europe | (region = 'europe'::text)
mlp_1_prt_1_2_prt_usa | (region = 'usa'::text)
mlp_1_prt_2_2_prt_usa | (region = 'usa'::text)
mlp_1_prt_1_2_prt_asia | ((year >= 2006) AND (year < 2011))
mlp_1_prt_1_2_prt_usa | ((year >= 2006) AND (year < 2011))
mlp_1_prt_1_2_prt_europe | ((year >= 2006) AND (year < 2011))
mlp_1_prt_2_2_prt_usa | ((year >= 2011) AND (year < 2016))
mlp_1_prt_2_2_prt_asia | ((year >= 2011) AND (year < 2016))
mlp_1_prt_2_2_prt_europe | ((year >= 2011) AND (year < 2016))
(12 rows)
Если вы добавите партицию по умолчанию в пример партиционированной таблицы с помощью этой команды:
ALTER TABLE mlp ADD DEFAULT PARTITION def
Партиционированная таблица остается однородной партиционированной таблицей. Ветвь, созданная для партиции по умолчанию, содержит три дочерние таблицы, и набор ограничений для дочерних таблиц соответствует существующим наборам ограничений для дочерних таблиц.
В приведенном выше примере, если вы удалите подпартицию mlp_1_prt_21_2_prt_asia и добавите еще одну подпартицию для региона Канада, ограничения перестанут быть одинаковыми.
ALTER TABLE mlp ALTER PARTITION FOR (RANK(2))
DROP PARTITION asia ;
ALTER TABLE mlp ALTER PARTITION FOR (RANK(2))
ADD PARTITION canada VALUES ('canada');
Кроме того, если вы добавите партицию Canada под mlp_1_prt_21, иерархия партиций будет неоднородной.
Однако если вы добавите подпартицию canada и к mlp_1_prt_21, и к mlp_1_prt_11 исходной партиционированной таблицы, она останется единой партиционированной таблицей.
Примечание. Только ограничения на наборы партиций на уровне партиции должны быть одинаковыми. Имена партиций могут быть разными. |
База данных RT.Warehouse основана на реализации стандарта SQL в PostgreSQL.
В этом разделе описывается, как создавать запросы SQL в базе данных RT.Warehouse.
SQL — это стандартный язык для доступа к базам данных. Язык состоит из элементов, которые позволяют хранить данные, извлекать их, анализировать, просматривать, манипулировать ими и так далее. Вы используете команды SQL для создания запросов и команд, понятных механизму базы данных RT.Warehouse. SQL - запросы состоят из последовательности команд. Команды состоят из последовательности допустимых токенов в правильном синтаксическом порядке, заканчивающихся точкой с запятой (;).
База данных RT.Warehouse использует структуру и синтаксис PostgreSQL, за некоторыми исключениями.
Выражения значений SQL состоят из одного или нескольких значений, символов, операторов, функций SQL и данных. Выражения сравнивают данные или выполняют вычисления и возвращают значение в качестве результата. Вычисления включают логические, арифметические операции и операции над множествами.
Ниже приведены выражения значений:
Конструкции SQL, такие как функции и операторы, являются выражениями, но не подчиняются каким-либо общим правилам синтаксиса. Дополнительные сведения об этих конструкциях см. в разделе Использование функций и операторов (21.5).
Ссылка на столбец имеет вид:
correlation.columnname
Здесь корреляция — это имя таблицы (возможно, дополненное именем схемы) или псевдоним для таблицы, определенной с помощью предложения FROM или одного из ключевых слов NEW или OLD. NEW и OLD могут появляться только в правилах перезаписи, но вы можете использовать другие корреляционные имена в любом операторе SQL. Если имя столбца уникально для всех таблиц в запросе, можно опустить часть «корреляции» в ссылке на столбец.
Позиционные параметры — это аргументы операторов или функций SQL, на которые вы ссылаетесь по их позициям в ряду аргументов. Например, $1 относится к первому аргументу, $2 — ко второму аргументу и так далее. Значения позиционных параметров устанавливаются из аргументов, внешних по отношению к оператору SQL, или предоставляются при вызове функций SQL. Некоторые клиентские библиотеки поддерживают указание значений данных отдельно от команды SQL, и в этом случае параметры относятся к внешним значениям данных. Ссылка на параметр имеет вид:
$number
Например:
CREATE FUNCTION dept(text) RETURNS dept
AS $$ SELECT * FROM dept WHERE name = $1 $$
LANGUAGE SQL;
Здесь $1 ссылается на значение первого аргумента функции всякий раз, когда функция вызывается.
Если выражение возвращает значение типа массива, вы можете извлечь определенный элемент значения массива следующим образом:
expression[subscript]
Вы можете извлечь несколько смежных элементов, называемых фрагментом массива, следующим образом (включая скобки):
expression[lower_subscript:upper_subscript]
Каждый сабскрипт представляет собой выражение и дает целочисленное значение.
Выражения массива обычно должны быть заключены в круглые скобки, но вы можете опустить круглые скобки, если индексируемое выражение является ссылкой на столбец или позиционным параметром. Вы можете объединить несколько сабскриптов, если исходный массив многомерный. Например (включая скобки):
mytable.arraycolumn[4]
mytable.two_d_column[17][34]
$1[10:42]
(arrayfunction(a,b))[42]
Если выражение возвращает значение составного типа (типа строки), вы можете извлечь конкретное поле строки следующим образом:
expression.fieldname
Выражение строки обычно должно быть в круглых скобках, но вы можете опустить эти круглые скобки, если выражение, из которого нужно выбрать, является ссылкой на таблицу или позиционным параметром. Например:
mytable.mycolumn
$1.somecolumn
(rowfunction(a,b)).col3
Полная ссылка на столбец является частным случаем синтаксиса выбора поля.
Вызовы операторов имеют следующий возможный синтаксис:
expression operator expression(binary infix operator)
operator expression(unary prefix operator)
expression operator(unary postfix operator)
Где оператор является токеном оператора, одним из ключевых слов И (AND), ИЛИ (OR) или НЕ (NOT), или квалифицированным именем оператора в форме:
OPERATOR(schema.operatorname)
Доступные операторы и то, являются ли они унарными или бинарными, зависят от операторов, которые определяет система или пользователь. Дополнительные сведения о встроенных операторах см. в разделе Встроенные функции и операторы (21.5).
Синтаксис вызова функции — это имя функции (возможно, дополненное именем схемы), за которым следует список ее аргументов, заключенный в круглые скобки:
function ([expression [, expression ... ]])
Например, следующий вызов функции вычисляет квадратный корень из 2:
sqrt(2)
Агрегатное выражение применяет агрегатную функцию к строкам, выбранным запросом. Агрегатная функция выполняет вычисление набора значений и возвращает одно значение, например сумму или среднее значение набора значений. Синтаксис агрегатного выражения может быть одним из следующих:
Где aggregate_name — это ранее определенное агрегатное выражение (возможно, уточненное схемой), а выражение — любое выражение значения, не содержащее агрегатное выражение.
Например, count(*) возвращает общее количество входных строк, count(f1) возвращает количество входных строк, в которых f1 не равно NULL, а count(distinct f1) дает количество различных ненулевых значений f1.
Если FILTER указан, то только те входные строки, для которых filter_clause оценивается как true, передаются в агрегатную функцию; другие строки отбрасываются. Например:
SELECT
count(*) AS unfiltered,
count(*) FILTER (WHERE i < 5) AS filtered
FROM generate_series(1,10) AS s(i);
unfiltered | filtered
------------+----------
10 | 4
(1 row)
Предопределенные агрегатные функции см. в разделе Встроенные функции и операторы (далее, по тексту, раздел Использование функций и операторов (21.5.3)).
База данных RT.Warehouse предоставляет агрегатную функцию MEDIAN, которая возвращает пятидесятый процентиль результата PERCENTILE_CONT и специальные агрегатные выражения для обратных функций распределения следующим образом:
PERCENTILE_CONT(_percentage_) WITHIN GROUP (ORDER BY _expression_)
PERCENTILE_DISC(_percentage_) WITHIN GROUP (ORDER BY _expression_)
В настоящее время вы можете использовать только эти два выражения с ключевым словом WITHIN GROUP.
Ниже приведены текущие ограничения агрегатных выражений:
Оконные выражения позволяют разработчикам приложений более легко составлять сложные запросы интерактивной аналитической обработки (OLAP) с использованием стандартных команд SQL. Например, с помощью оконных выражений пользователи могут вычислять скользящие средние значения или суммы за различные интервалы, сбрасывать агрегации и ранги по мере изменения значений в выбранных столбцах и выражать сложные соотношения простыми словами.
Оконное выражение представляет собой применение оконной функции к оконному фрейму, который определяется предложением OVER(). Это сравнимо с типом вычислений, которые можно выполнить с помощью агрегатной функции и предложения GROUP BY. В отличие от агрегатных функций, которые возвращают одно значение результата для каждой группы строк, оконные функции возвращают значение результата для каждой строки, но это значение вычисляется относительно набора строк в фрейм окна, к которой принадлежит строка. Предложение OVER() позволяет разделить строки на разделы, а затем дополнительно ограничить фрейм окна, указав, какие строки, предшествующие или следующие за текущей строкой в ее разделе, должны быть включены в расчет.
База данных RT.Warehouse не поддерживает указание оконной функции в качестве аргумента для другой оконной функции.
Синтаксис оконного выражения:
window_function ( [expression [, ...]] ) [ FILTER ( WHERE filter_clause ) ] OVER ( window_specification )
Где window_function — это одна из функций — оконная функция, определяемая пользователем, выражение — любое выражение значения, не содержащее выражения окна, а window_specification — это:
[window_name]
[PARTITION BY expression [, ...]]
[[ORDER BY expression [ASC | DESC | USING operator] [NULLS {FIRST | LAST}] [, ...]
[{RANGE | ROWS}
{ UNBOUNDED PRECEDING
| expression PRECEDING
| CURRENT ROW
| BETWEEN window_frame_bound AND window_frame_bound }]]
и где window_frame_bound может быть одним из:
UNBOUNDED PRECEDING
expression PRECEDING
CURRENT ROW
expression FOLLOWING
UNBOUNDED FOLLOWING
Оконное выражение может появиться только в списке выбора команды SELECT. Например:
SELECT count(*) OVER(PARTITION BY customer_id), * FROM sales;
Если указан FILTER, то оконной функции передаются только те входные строки, для которых filter_clause оценивается как true, другие строки отбрасываются. В оконном выражении предложение FILTER может использоваться только с функцией window_function, которая является агрегатной функцией.
В оконном выражении, выражение должно содержать предложение OVER. Предложение OVER определяет фрейм окна — строки, которые будут обрабатываться оконной функцией. Это синтаксически отличает функцию от обычной или агрегатной функции.
В агрегатной функции окна, которая используется в оконном выражении, база данных RT.Warehouse не поддерживает предложение DISTINCT с несколькими входными выражениями.
Спецификация окна имеет следующие характеристики:
Примечание. Столбцы типов данных без согласованного порядка, таких как время, не являются хорошими кандидатами для использования в предложении ORDER BY спецификации окна. Время, с указанным часовым поясом или без него, не имеет последовательного порядка, потому что сложение и вычитание не имеют ожидаемых эффектов. Например, следующее утверждение обычно неверно: x::time < x::time + '2 hour'::interval |
Предложение ROWS или RANGE определяет фрейм окна для агрегированных (не ранжирующих) оконных функций. Фрейм окна определяет набор строк внутри раздела окна. Когда фрейм окна определен, оконная функция вычисляет содержимое этого движущегося фрейма, а не фиксированное содержимое всего раздела окна. Оконные рамы основаны на строках (ROWS) или на основе значений (RANGE).
Следующие примеры демонстрируют использование оконных функций с партициями и оконными фреймами.
Список PARTITION BY в предложении OVER делит строки на группы или партиции, которые имеют те же значения, что и указанные выражения.
В этом примере сравниваются зарплаты сотрудников со средней зарплатой по их отделам:
SELECT depname, empno, salary, avg(salary) OVER(PARTITION BY depname)
FROM empsalary;
depname | empno | salary | avg
-----------+-------+--------+-----------------------
develop | 9 | 4500 | 5020.0000000000000000
develop | 10 | 5200 | 5020.0000000000000000
develop | 11 | 5200 | 5020.0000000000000000
develop | 7 | 4200 | 5020.0000000000000000
develop | 8 | 6000 | 5020.0000000000000000
personnel | 5 | 3500 | 3700.0000000000000000
personnel | 2 | 3900 | 3700.0000000000000000
sales | 1 | 5000 | 4866.6666666666666667
sales | 3 | 4800 | 4866.6666666666666667
sales | 4 | 4800 | 4866.6666666666666667
(10 rows)
Первые три выходных столбца берутся из таблицы empsalary, и для каждой строки в таблице есть одна выходная строка. Четвертый столбец представляет собой среднее значение, рассчитанное для всех строк, имеющих то же значение depname, что и текущая строка. Строки с одинаковым значением depname составляют партицию и в этом примере их три. Функция avg аналогична обычной агрегатной функции avg, но предложение OVER заставляет ее применяться как оконную функцию.
Вы также можете поместить спецификацию окна в предложение WINDOW и сослаться на него в списке выбора. Этот пример эквивалентен предыдущему запросу:
SELECT depname, empno, salary, avg(salary) OVER(mywindow)
FROM empsalary
WINDOW mywindow AS (PARTITION BY depname);
Определение именованного окна полезно, когда в списке выбора есть несколько оконных функций, использующих одну и ту же спецификацию окна.
Предложение ORDER BY в предложении OVER управляет порядком, в котором строки обрабатываются оконными функциями. Список ORDER BY для оконной функции не обязательно должен совпадать с порядком вывода запроса. В этом примере оконная функция rank() используется для ранжирования зарплат сотрудников в их отделах:
SELECT depname, empno, salary,
rank() OVER (PARTITION BY depname ORDER BY salary DESC)
FROM empsalary;
depname | empno | salary | rank
-----------+-------+--------+------
develop | 8 | 6000 | 1
develop | 11 | 5200 | 2
develop | 10 | 5200 | 2
develop | 9 | 4500 | 4
develop | 7 | 4200 | 5
personnel | 2 | 3900 | 1
personnel | 5 | 3500 | 2
sales | 1 | 5000 | 1
sales | 4 | 4800 | 2
sales | 3 | 4800 | 2
(10 rows)
Предложение RANGE или ROWS определяет оконный фрейм — набор строк в разделе, которую оконная функция включает в расчет. ROWS указывает физический набор строк для обработки, например, все строки от начала раздела до текущей строки.
В этом примере вычисляется промежуточная сумма окладов сотрудников по отделам с использованием функции sum() для суммирования строк от начала раздела до текущей строки:
SELECT depname, empno, salary,
sum(salary) OVER (PARTITION BY depname ORDER BY salary
ROWS between UNBOUNDED PRECEDING AND CURRENT ROW)
FROM empsalary ORDER BY depname, sum;
depname | empno | salary | sum
-----------+-------+--------+-------
develop | 7 | 4200 | 4200
develop | 9 | 4500 | 8700
develop | 11 | 5200 | 13900
develop | 10 | 5200 | 19100
develop | 8 | 6000 | 25100
personnel | 5 | 3500 | 3500
personnel | 2 | 3900 | 7400
sales | 4 | 4800 | 4800
sales | 3 | 4800 | 9600
sales | 1 | 5000 | 14600
(10 rows)
RANGE задает логические значения на основе значений выражения ORDER BY в предложении OVER. Этот пример демонстрирует разницу между ROWS и RANGE. Фрейм содержит все строки со значениями зарплаты, меньшими или равными текущей строке. В отличие от предыдущего примера, для сотрудников с одинаковой зарплатой сумма одинакова и включает зарплаты всех этих сотрудников.
SELECT depname, empno, salary,
sum(salary) OVER (PARTITION BY depname ORDER BY salary
RANGE between UNBOUNDED PRECEDING AND CURRENT ROW)
FROM empsalary ORDER BY depname, sum;
depname | empno | salary | sum
-----------+-------+--------+-------
develop | 7 | 4200 | 4200
develop | 9 | 4500 | 8700
develop | 11 | 5200 | 19100
develop | 10 | 5200 | 19100
develop | 8 | 6000 | 25100
personnel | 5 | 3500 | 3500
personnel | 2 | 3900 | 7400
sales | 4 | 4800 | 9600
sales | 3 | 4800 | 9600
sales | 1 | 5000 | 14600
(10 rows)
Приведение (cast) типа определяет преобразование одного типа данных в другой. Приведение, применяемое к выражению значения известного типа, является преобразованием типа во время выполнения. Приведение выполняется только в том случае, если определено подходящее преобразование типа. Это отличается от использования приведения с константами. Приведение, примененное к строковому литералу, представляет собой начальное присвоение типа буквальному значению константы, поэтому оно успешно для любого типа, если содержимое строкового литерала является приемлемым входным синтаксисом для данного типа данных.
База данных RT.Warehouse поддерживает три типа приведения, применяемых к выражению значения:
CAST ( expression AS type )
expression::type
Синтаксис CAST соответствует SQL; синтаксис с использованием :: является историческим использованием PostgreSQL.
INSERT INTO tbl1 (f1) VALUES (42);
SELECT * FROM tbl1 WHERE tbl1.c2 = (4.3 + tbl1.c1) ;
Обычно вы можете опустить явное приведение типа, если нет двусмысленности в отношении типа, который должно создавать выражение значения (например, когда оно присваивается столбцу таблицы); система автоматически применяет преобразование типа. База данных RT.Warehouse неявно применяет приведения только к приведениям, определенным в контексте приведения присваивания или явным образом в системных каталогах. Другие приведения должны вызываться с явным синтаксисом приведения, чтобы предотвратить применение неожиданных преобразований без ведома пользователя.
Вы можете отобразить информацию о приведении с помощью метакоманды psql \dC. Информация о приведении хранится в таблице каталога pg_cast, а информация о типе хранится в таблице каталога pg_type.
Скалярный подзапрос — это запрос SELECT в круглых скобках, который возвращает ровно одну строку с одним столбцом. Не используйте запрос SELECT, который возвращает несколько строк или столбцов в качестве скалярного подзапроса. Запрос выполняется и использует возвращенное значение в выражении окружающего значения. Коррелированный скалярный подзапрос содержит ссылки на внешний блок запроса.
Коррелированный подзапрос (CSQ) — это запрос SELECT с предложением WHERE или целевым списком, который содержит ссылки на родительское внешнее предложение. CSQ эффективно выражают результаты в терминах результатов другого запроса. База данных RT.Warehouse поддерживает коррелированные подзапросы, обеспечивающие совместимость со многими существующими приложениями. CSQ — это скалярный или табличный подзапрос, в зависимости от того, возвращает ли он одну или несколько строк. База данных RT.Warehouse не поддерживает коррелированные подзапросы с корреляциями на уровне пропуска.
Пример 1. Скалярный коррелированный подзапрос
SELECT * FROM t1 WHERE t1.x
> (SELECT MAX(t2.x) FROM t2 WHERE t2.y = t1.y);
Пример 2. Коррелированный подзапрос EXISTS
SELECT * FROM t1 WHERE
EXISTS (SELECT 1 FROM t2 WHERE t2.x = t1.x);
База данных RT.Warehouse использует один из следующих методов для запуска CSQ:
В следующих примерах показано, как переписать некоторые из этих типов запросов для повышения производительности.
Пример 3. CSQ в списке выбора
Исходный запрос
SELECT T1.a,
(SELECT COUNT(DISTINCT T2.z) FROM t2 WHERE t1.x = t2.y) dt2
FROM t1;
Перепишите этот запрос, чтобы сначала выполнить inner join с t1, а затем снова выполнить left join с t1. Перезапись применяется только для эквивалентного соединения в коррелированном условии.
Переписанный запрос
SELECT t1.a, dt2 FROM t1
LEFT JOIN
(SELECT t2.y AS csq_y, COUNT(DISTINCT t2.z) AS dt2
FROM t1, t2 WHERE t1.x = t2.y
GROUP BY t1.x)
ON (t1.x = csq_y);
Пример 4. CSQ, соединенные операторами OR
Исходный запрос
SELECT * FROM t1
WHERE
x > (SELECT COUNT(*) FROM t2 WHERE t1.x = t2.x)
OR x < (SELECT COUNT(*) FROM t3 WHERE t1.y = t3.y)
Перепишите этот запрос, чтобы разделить его на две части с помощью union на условиях OR.
Переписанный запрос
SELECT * FROM t1
WHERE x > (SELECT count(*) FROM t2 WHERE t1.x = t2.x)
UNION
SELECT * FROM t1
WHERE x < (SELECT count(*) FROM t3 WHERE t1.y = t3.y)
Чтобы просмотреть план запроса, используйте EXPLAIN SELECT или EXPLAIN ANALYZE SELECT. Узлы подплана в плане запроса указывают, что запрос будет выполняться для каждой строки внешнего запроса и этот запрос является кандидатом на переписывание. Дополнительные сведения об этих операторах см. в разделе Профилирование Запросов (21.12).
Конструктор массива — это выражение, которое строит значение массива из значений его элементов-членов. Простой конструктор массива состоит из ключевого слова ARRAY, левой квадратной скобки [, одного или нескольких выражений, разделенных запятыми для значений элементов массива, и правой квадратной скобки]. Например:
SELECT ARRAY[1,2,3+4];
array
---------
{1,2,7}
Тип элемента массива — это общий тип его выражений-членов, определяемый с использованием тех же правил, что и для конструкций UNION или CASE.
Вы можете создавать значения многомерного массива путем вложения конструкторов массива. Во внутренних конструкторах можно опустить ключевое слово ARRAY. Например, следующие два оператора SELECT дают одинаковый результат:
SELECT ARRAY[ARRAY[1,2], ARRAY[3,4]];
SELECT ARRAY[[1,2],[3,4]];
array
---------------
{{1,2},{3,4}}
Поскольку многомерные массивы должны быть прямоугольными, внутренние конструкторы одного уровня должны создавать подмассивы одинаковых размеров.
Элементы конструктора многомерного массива не ограничены конструкцией sub-ARRAY; это все, что создает массив надлежащего вида. Например:
CREATE TABLE arr(f1 int[], f2 int[]);
INSERT INTO arr VALUES (ARRAY[[1,2],[3,4]],
ARRAY[[5,6],[7,8]]);
SELECT ARRAY[f1, f2, '{{9,10},{11,12}}'::int[]] FROM arr;
array
------------------------------------------------
{{{1,2},{3,4}},{{5,6},{7,8}},{{9,10},{11,12}}}
Вы можете построить массив из результатов подзапроса. Напишите конструктор массива с ключевым словом ARRAY, за которым следует подзапрос в скобках. Например:
SELECT ARRAY(SELECT oid FROM pg_proc WHERE proname LIKE 'bytea%');
?column?
-----------------------------------------------------------
{2011,1954,1948,1952,1951,1244,1950,2005,1949,1953,2006,31}
Подзапрос должен возвращать один столбец. Результирующий одномерный массив содержит элемент для каждой строки результата подзапроса с типом элемента, совпадающим с типом выходного столбца подзапроса. Нижние индексы значения массива, построенного с помощью ARRAY, всегда начинаются с 1.
Конструктор строк — это выражение, которое строит значение строки (также называемое составным значением) из значений его полей-членов. Например:
SELECT ROW(1,2.5,'this is a test');
Конструкторы строк имеют синтаксис rowvalue.*, который расширяется до списка элементов значения строки, как при использовании синтаксиса .* на верхнем уровне списка SELECT. Например, если в таблице t есть столбцы f1 и f2, следующие запросы будут одинаковыми:
SELECT ROW(t.*, 42) FROM t;
SELECT ROW(t.f1, t.f2, 42) FROM t;
По умолчанию значение, созданное выражением ROW, имеет тип анонимной записи. При необходимости его можно привести к именованному составному типу — либо к типу строк таблицы, либо к составному типу, созданному с помощью CREATE TYPE AS. Чтобы избежать двусмысленности, при необходимости вы можете явно привести значение. Например:
CREATE TABLE mytable(f1 int, f2 float, f3 text);
CREATE FUNCTION getf1(mytable) RETURNS int AS 'SELECT $1.f1'
LANGUAGE SQL;
В следующем запросе вам не нужно приводить значение, потому что есть только одна функция getf1() и, следовательно, нет двусмысленности:
SELECT getf1(ROW(1,2.5,'this is a test'));
getf1
-------
1
CREATE TYPE myrowtype AS (f1 int, f2 text, f3 numeric);
CREATE FUNCTION getf1(myrowtype) RETURNS int AS 'SELECT
$1.f1' LANGUAGE SQL;
Теперь нам нужно приведение, чтобы указать, какую функцию вызывать:
SELECT getf1(ROW(1,2.5,'this is a test'));
ERROR: function getf1(record) is not unique
SELECT getf1(ROW(1,2.5,'this is a test')::mytable);
getf1
-------
1
SELECT getf1(CAST(ROW(11,'this is a test',2.5) AS
myrowtype));
getf1
-------
11
Вы можете использовать конструкторы строк для создания составных значений, которые будут храниться в столбце таблицы составного типа или передаваться в функцию, которая принимает составной параметр.
Порядок вычисления подвыражений не определен. Входные данные оператора или функции не обязательно оцениваются слева направо или в любом другом фиксированном порядке.
Если вы можете определить результат выражения, оценивая только некоторые части выражения, то другие подвыражения могут вообще не оцениваться. Например, в следующем выражении:
SELECT true OR somefunc();
somefunc(), вероятно, вообще не будет вызываться. То же верно и в следующем выражении:
SELECT somefunc() OR true;
Это не то же самое, что порядок вычисления слева направо, который применяют логические операторы в некоторых языках программирования.
Не используйте функции с побочными эффектами в составе сложных выражений, особенно в предложениях WHERE и HAVING, так как эти предложения сильно перерабатываются при разработке плана выполнения. Булевы выражения (комбинации AND/OR/NOT) в этих предложениях могут быть реорганизованы любым способом, допускаемым законами булевой алгебры.
Используйте конструкцию CASE для принудительного порядка оценки. В следующем примере показан ненадежный способ избежать деления на ноль в предложении WHERE:
SELECT ... WHERE x <> 0 AND y/x > 1.5;
В следующем примере показан надежный порядок оценки:
SELECT ... WHERE CASE WHEN x <> 0 THEN y/x > 1.5 ELSE false
END;
Использование конструкции CASE препятствует попыткам оптимизации; используйте его только при необходимости.
Предложение WITH позволяет использовать подзапросы или выполнять операцию изменения данных в более крупном запросе SELECT. Вы также можете использовать предложение WITH в командах INSERT, UPDATE или DELETE.
См. SELECT в предложении WITH для получения информации об использовании SELECT в предложении WITH.
См. Операторы изменения данных в предложении WITH для получения информации об использовании INSERT, UPDATE или DELETE в предложении WITH.
Примечание. Это ограничения для использования предложения WITH:
|
По умолчанию ключевое слово RECURSIVE для предложения WITH включено. RECURSIVE можно отключить, установив для параметра конфигурации сервера gp_recursive_cte значение false.
Подзапросы, которые часто называют общими табличными выражениями (Common Table Expressions) или CTE, можно рассматривать как определяющие временные таблицы, которые существуют только для запроса. В этих примерах показано использование предложения WITH с командой SELECT. Примеры предложений WITH можно использовать таким же образом с INSERT, UPDATE или DELETE. В каждом случае предложение WITH эффективно предоставляет временные таблицы, на которые можно ссылаться в основной команде.
Команда SELECT в предложении WITH оценивается только один раз при выполнении родительского запроса, даже если родительский запрос или одноуровневые предложения WITH ссылаются на нее более одного раза. Таким образом, дорогостоящие вычисления, которые необходимы в нескольких местах, могут быть помещены в предложение WITH, чтобы избежать избыточной работы. Другое возможное применение — предотвращение нежелательных множественных вычислений функций с побочными эффектами. Однако другая сторона этой медали заключается в том, что оптимизатор в меньшей степени способен перенести ограничения из родительского запроса в запрос WITH, чем обычный подзапрос. Запрос WITH обычно оценивается как написанный, без подавления строк, которые впоследствии могут быть отброшены родительским запросом. Однако оценка может быть остановлена раньше, если ссылки на запрос требуют только ограниченное количество строк.
Одним из применений этой функции является разбиение сложных запросов на более простые части. В этом примере запроса отображаются итоговые данные о продажах по каждому продукту только в самых популярных регионах продаж:
WITH regional_sales AS (
SELECT region, SUM(amount) AS total_sales
FROM orders
GROUP BY region
), top_regions AS (
SELECT region
FROM regional_sales
WHERE total_sales > (SELECT SUM(total_sales)/10 FROM regional_sales)
)
SELECT region,
product,
SUM(quantity) AS product_units,
SUM(amount) AS product_sales
FROM orders
WHERE region IN (SELECT region FROM top_regions)
GROUP BY region, product;
Запрос можно было написать без предложения WITH, но для этого потребовались бы два уровня вложенных SELECT. Легче следовать с предложением WITH.
Когда необязательное ключевое слово RECURSIVE включено, предложение WITH может выполнять действия, которые в противном случае невозможны в стандартном SQL. Используя RECURSIVE, запрос в предложении WITH может ссылаться на собственный вывод. Это простой пример, который вычисляет сумму целых чисел от 1 до 100:
WITH RECURSIVE t(n) AS (
VALUES (1)
UNION ALL
SELECT n+1 FROM t WHERE n < 100
)
SELECT sum(n) FROM t;
Общая форма рекурсивного предложения WITH (предложение WITH, в котором используется ключевое слово RECURSIVE) — это нерекурсивный терм, за которым следует UNION (или UNION ALL), а затем рекурсивный терм, где только рекурсивный терм может содержать ссылка на результат запроса.
non_recursive_term UNION [ ALL ] recursive_term
Рекурсивный запрос WITH, содержащий UNION [ALL], выполняется следующим образом:
Примечание. Строго говоря, процесс представляет собой итерацию, а не рекурсию, но терминология RECURSIVE выбрана комитетом по стандартам SQL. |
Рекурсивные запросы WITH обычно используются для работы с иерархическими или древовидными данными. Примером может служить этот запрос, чтобы найти все прямые и косвенные части продукта, учитывая только таблицу, которая показывает непосредственные включения:
WITH RECURSIVE included_parts(sub_part, part, quantity) AS (
SELECT sub_part, part, quantity FROM parts WHERE part = 'our_product'
UNION ALL
SELECT p.sub_part, p.part, p.quantity
FROM included_parts pr, parts p
WHERE p.part = pr.sub_part
)
SELECT sub_part, SUM(quantity) as total_quantity
FROM included_parts
GROUP BY sub_part ;
При работе с рекурсивными запросами WITH вы должны убедиться, что рекурсивная часть запроса в конечном итоге не возвращает кортежей, иначе запрос зациклится на неопределенный срок. В примере, в котором вычисляется сумма целых чисел, рабочая таблица содержит одну строку на каждом шаге и принимает значения от 1 до 100 на последовательных шагах. На 100-м шаге нет вывода из-за предложения WHERE, и запрос завершается.
Для некоторых запросов использование UNION вместо UNION ALL может гарантировать, что рекурсивная часть запроса в конечном итоге не вернет кортежи, отбрасывая строки, которые дублируют предыдущие выходные строки. Однако часто цикл не включает в себя выходные строки, которые являются полными дубликатами: может быть достаточно проверить только одно или несколько полей, чтобы увидеть, была ли достигнута та же самая точка ранее. Стандартным методом обработки таких ситуаций является вычисление массива посещенных значений. Например, рассмотрим следующий запрос, выполняющий поиск в табличном графике с использованием поля ссылки:
WITH RECURSIVE search_graph(id, link, data, depth) AS (
SELECT g.id, g.link, g.data, 1
FROM graph g
UNION ALL
SELECT g.id, g.link, g.data, sg.depth + 1
FROM graph g, search_graph sg
WHERE g.id = sg.link
)
SELECT * FROM search_graph;
Этот запрос зацикливается, если отношения ссылок содержат циклы. Поскольку запрос требует вывода глубины, изменение UNION ALL на UNION не устраняет зацикливание. Вместо этого запрос должен определить, достиг ли он снова той же строки, следуя определенному пути ссылок. Этот модифицированный запрос добавляет два столбца, путь и цикл, к запросу, подверженному зацикливанию:
WITH RECURSIVE search_graph(id, link, data, depth, path, cycle) AS (
SELECT g.id, g.link, g.data, 1,
ARRAY[g.id],
false
FROM graph g
UNION ALL
SELECT g.id, g.link, g.data, sg.depth + 1,
path || g.id,
g.id = ANY(path)
FROM graph g, search_graph sg
WHERE g.id = sg.link AND NOT cycle
)
SELECT * FROM search_graph;
Помимо обнаружения циклов, значение пути в массиве полезно само по себе, поскольку оно представляет собой путь, по которому можно добраться до любой конкретной строки.
В общем случае, когда для распознавания цикла необходимо проверить более одного поля, можно использовать массив строк. Например, если нам нужно сравнить поля f1 и f2:
WITH RECURSIVE search_graph(id, link, data, depth, path, cycle) AS (
SELECT g.id, g.link, g.data, 1,
ARRAY[ROW(g.f1, g.f2)],
false
FROM graph g
UNION ALL
SELECT g.id, g.link, g.data, sg.depth + 1,
path || ROW(g.f1, g.f2),
ROW(g.f1, g.f2) = ANY(path)
FROM graph g, search_graph sg
WHERE g.id = sg.link AND NOT cycle
)
SELECT * FROM search_graph;
Совет. Опустите синтаксис ROW() в случае, когда для распознавания цикла необходимо проверить только одно поле. При этом используется простой массив, а не массив составного типа, что повышает эффективность. |
Совет. Алгоритм рекурсивной оценки запроса выдает результат в порядке поиска в ширину. Вы можете отобразить результаты в порядке поиска в глубину, сделав внешний запрос ORDER BY столбцом пути, построенным таким образом. |
Полезный метод тестирования запроса, когда вы не уверены, что он может бесконечно зацикливаться — это поместить LIMIT в родительский запрос. Например, этот запрос будет бесконечно повторяться без предложения LIMIT:
WITH RECURSIVE t(n) AS (
SELECT 1
UNION ALL
SELECT n+1 FROM t
)
SELECT n FROM t LIMIT 100;
Этот метод работает, потому что рекурсивная реализация WITH оценивает только столько строк запроса WITH, сколько фактически выбрано родительским запросом. Использование этого метода в производственной среде не рекомендуется, поскольку другие системы могут работать по-другому. Кроме того, этот метод может не работать, если внешний запрос сортирует рекурсивные результаты WITH или объединяет результаты с другой таблицей.
Для команды SELECT вы можете использовать команды изменения данных INSERT, UPDATE или DELETE в предложении WITH. Это позволяет выполнять несколько разных операций в одном запросе.
Оператор изменения данных в предложении WITH выполняется ровно один раз и всегда до конца, независимо от того, считывает ли первичный запрос все (или вообще какие-либо) выходные данные. Это отличается от правила, когда при использовании SELECT в предложении WITH выполнение SELECT продолжается только до тех пор, пока первичный запрос требует его вывода.
Этот простой CTE-запрос удаляет строки из продуктов. DELETE в предложении WITH удаляет указанные строки из продуктов, возвращая их содержимое с помощью предложения RETURNING.
WITH deleted_rows AS (
DELETE FROM products
WHERE
"date" >= '2010-10-01' AND
"date" < '2010-11-01'
RETURNING *
)
SELECT * FROM deleted_rows;
Операторы изменения данных в предложении WITH должны иметь предложения RETURNING, как показано в предыдущем примере. Именно выходные данные предложения RETURNING, а не целевая таблица оператора, изменяющего данные, формируют временную таблицу, на которую может ссылаться остальная часть запроса. Если в операторе изменения данных в WITH отсутствует предложение RETURNING, возвращается ошибка.
Если необязательное ключевое слово RECURSIVE включено, рекурсивные ссылки на самих себя в операторах, изменяющих данные, не допускаются. В некоторых случаях это ограничение можно обойти, обратившись к выходным данным рекурсивного оператора WITH. Например, этот запрос удалит все прямые и косвенные части продукта.
WITH RECURSIVE included_parts(sub_part, part) AS (
SELECT sub_part, part FROM parts WHERE part = 'our_product'
UNION ALL
SELECT p.sub_part, p.part
FROM included_parts pr, parts p
WHERE p.part = pr.sub_part
)
DELETE FROM parts
WHERE part IN (SELECT part FROM included_parts);
Подоператоры в предложении WITH выполняются одновременно друг с другом и с основным запросом. Следовательно, при использовании оператора изменения данных в WITH оператор выполняется в снапшоте. Эффекты оператора не видны в целевых таблицах. Возвращаемые (RETURNING) данные — это единственный способ сообщить об изменениях между различными подоператорами WITH и основным запросом. В этом примере внешний SELECT возвращает исходные цены до действия UPDATE в предложении WITH:
WITH t AS (
UPDATE products SET price = price * 1.05
RETURNING *
)
SELECT * FROM products;
В этом примере внешний SELECT возвращает обновленные данные:
WITH t AS (
UPDATE products SET price = price * 1.05
RETURNING *
)
SELECT * FROM t;
Обновление одной и той же строки дважды в одном операторе не поддерживается. Последствия такого обновления не будут предсказуемы. Происходит только одна из модификаций, но предсказать, какая модификация произойдет, непросто (а иногда и невозможно).
Любая таблица, используемая в качестве цели оператора изменения данных в предложении WITH, не должна иметь условного правила, правила ALSO или правила INSTEAD, которое расширяется до нескольких операторов.
Описание пользовательских и встроенных функций и операторов в базе данных RT.Warehouse.
Когда вы вызываете функцию в базе данных RT.Warehouse, атрибуты функции управляют выполнением функции. Атрибуты волатильности (IMMUTABLE, STABLE, VOLATILE) и атрибуты EXECUTE ON управляют двумя разными аспектами выполнения функции. В общем, волатильность указывает, когда функция выполняется, а EXECUTE ON указывает, где она выполняется. Атрибуты волатильности — это атрибуты на основе PostgreSQL, атрибуты EXECUTE ON — это атрибуты базы данных RT.Warehouse.
Например, функция, определенная с атрибутом IMMUTABLE, может выполняться во время планирования запроса, а функция с атрибутом VOLATILE должна выполняться для каждой строки в запросе. Функция с атрибутом EXECUTE ON MASTER выполняется только на основном экземпляре, а функция с атрибутом EXECUTE ON ALL SEGMENTS выполняется на всех экземплярах основного сегмента (не на главном).
Далее, в таблицах обобщаются предположения базы данных RT.Warehouse о выполнении функций на основе атрибута.
Таблица 117. Атрибуты волатильности функций в базе данных RT.Warehouse
Атрибут функции | Поддержка RT.Warehouse | Описание | Комментарии |
---|---|---|---|
IMMUTABLE | Да | Полагается только на информацию непосредственно в списке аргументов. При одинаковых значениях аргументов всегда возвращает один и тот же результат. | |
STABLE | Да, в большинстве случаев | При сканировании одной таблицы возвращает один и тот же результат для одних и тех же значений аргументов, но результаты меняются для разных операторов SQL. | Результаты зависят от поиска в базе данных или значений параметров. Семейство функций current_timestamp STABLE (СТАБИЛЬНО); значения не меняются в процессе выполнения. |
VOLATILE | Ограниченная | Значения функций могут изменяться в течение одного сканирования таблицы. Например: random(), timeofday(). Это атрибут по умолчанию. | Любая функция с побочными эффектами волатильна, даже если ее результат предсказуем. Например: setval(). |
Таблица 118. Функция EXECUTE ON атрибутов в базе данных RT.Warehouse
Атрибут функции | Описание | Комментарии |
---|---|---|
EXECUTE ON ANY | Указывает, что функция может выполняться на мастере или любом экземпляре сегмента и она возвращает один и тот же результат независимо от того, где она выполняется. Это атрибут по умолчанию. | База данных RT.Warehouse определяет, где выполняется функция. |
EXECUTE ON MASTER | Указывает, что функция должна выполняться на мастере. | Укажите этот атрибут, если определяемая пользователем функция выполняет запросы для доступа к таблицам. |
EXECUTE ON ALL SEGMENTS | Указывает, что для каждого вызова функция должна выполняться на всех экземплярах основного сегмента, но не на главном. |
Вы можете отобразить волатильность функции и информацию об атрибуте EXECUTE ON с помощью команды psql \df+ function.
Для получения дополнительной информации о классификации волатильности функций базы данных RT.Warehouse можно обратиться к документации по категориям волатильности функций PostgreSQL.
В базе данных RT.Warehouse данные разделены по сегментам — каждый сегмент представляет собой отдельную базу данных PostgreSQL. Чтобы предотвратить противоречивые или неожиданные результаты, не выполняйте функции, классифицированные как VOLATILE, на уровне сегмента, если они содержат команды SQL или каким-либо образом изменяют базу данных. Например, такие функции, как setval(), не могут выполняться для распределенных данных в базе данных RT.Warehouse, поскольку они могут привести к несогласованности данных между экземплярами сегментов.
Функция может выполнять запросы только для чтения к реплицированным таблицам (DISTRIBUTED REPLICATED) в сегментах, но любая команда SQL, которая изменяет данные, должна выполняться на мастере.
Примечание. На скрытые системные столбцы (ctid, cmin, cmax, xmin, xmax и gp_segment_id) нельзя ссылаться в пользовательских запросах к реплицированным таблицам, поскольку они не имеют единого однозначного значения. База данных RT.Warehouse возвращает для запроса ошибку столбца не существует. |
Чтобы обеспечить согласованность данных, вы можете безопасно использовать функции VOLATILE и STABLE в операторах, которые оцениваются и запускаются из мастера. Например, следующие операторы выполняются на мастере (операторы без предложения FROM):
SELECT setval('myseq', 201);
SELECT foo();
Если в операторе есть предложение FROM, содержащее распределенную таблицу, а функция в предложении FROM возвращает набор строк, оператор может выполняться на сегментах:
SELECT * from foo();
База данных RT.Warehouse не поддерживает функции, возвращающие ссылку на таблицу (rangeFuncs), или функции, использующие тип данных refCursor.
Существует относительно небольшая разница между категориями волатильности функций STABLE и IMMUTABLE для простых интерактивных запросов, которые планируются и немедленно выполняются. Не имеет большого значения, выполняется ли функция один раз при планировании или один раз при запуске выполнения запроса. Но есть большая разница, когда вы сохраняете план и используете его позже. Если вы неправильно пометите функцию IMMUTABLE, база данных RT.Warehouse может преждевременно преобразовать ее в константу во время планирования, возможно, повторно используя устаревшее значение при последующем выполнении плана. Вы можете столкнуться с этой опасностью при использовании операторов PREPARE или при использовании таких языков, как PL/pgSQL, которые кэшируют планы.
База данных RT.Warehouse поддерживает пользовательские функции.
Используйте оператор CREATE FUNCTION для регистрации определяемых пользователем функций, которые используются, как описано в разделе Использование функций в базе данных RT.Warehouse (21.5.1). По умолчанию пользовательские функции объявлены как VOLATILE, поэтому, если ваша пользовательская функция является IMMUTABLE или STABLE, вы должны указать правильный уровень волатильности при регистрации функции.
По умолчанию пользовательские функции объявлены как EXECUTE ON ANY. Функция, выполняющая запросы на доступ к таблицам, поддерживается только в том случае, если эта функция выполняется на мастере, за исключением того, что функция может выполнять команды SELECT, которые обращаются только к реплицированным таблицам в экземплярах сегмента. Функция, которая обращается к хеш-распределенным или произвольно распределенным таблицам, должна быть определена с атрибутом EXECUTE ON MASTER. В противном случае функция может возвращать неправильные результаты при использовании функции в сложном запросе. Без этого атрибута оптимизация планировщика может определить, что было бы полезно передать вызов функции экземплярам сегмента.
При создании пользовательских функций избегайте фатальных ошибок или деструктивных вызовов. База данных RT.Warehouse может реагировать на такие ошибки внезапным завершением работы или перезапуском.
В базе данных RT.Warehouse файлы общих библиотек для пользовательских функций должны находиться в одном и том же пути к библиотеке на каждом хосте в массиве базы данных RT.Warehouse (мастер, сегменты и зеркала).
Вы также можете создавать и выполнять анонимные блоки кода, написанные на процедурном языке базы данных RT.Warehouse, таком как PL/pgSQL. Анонимные блоки работают как временные анонимные функции.
В следующей таблице перечислены категории встроенных функций и операторов, поддерживаемых PostgreSQL. Все функции и операторы поддерживаются в базе данных RT.Warehouse так же, как и в PostgreSQL, за исключением функций STABLE и VOLATILE, на которые распространяются ограничения, указанные в разделе Использование функций в базе данных RT.Warehouse (21.5.1).
База данных RT.Warehouse включает функции обработки JSON, которые манипулируют значениями типа данных json. Дополнительные сведения о данных JSON см. в разделе Работа с данными JSON (21.6).
Таблица 119. Встроенные функции и операторы
Категория оператора/функции | VOLATILE функции | STABLE функции | Ограничения |
---|---|---|---|
Logical Operators | |||
Comparison Operators | |||
Mathematical Functions and Operators |
random setseed |
||
String Functions and Operators | All built-in conversion functions |
convert pg_client_encoding |
|
Binary String Functions and Operators | |||
Bit String Functions and Operators | |||
Pattern Matching | |||
Data Type Formatting Functions |
to_char to_timestamp |
||
Date/Time Functions and Operators | timeofday |
age current_date current_time current_timestamp localtime localtimestamp now |
|
Enum Support Functions | |||
Geometric Functions and Operators | |||
Network Address Functions and Operators | |||
Sequence Manipulation Functions |
nextval() setval() |
||
Conditional Expressions | |||
Array Functions and Operators | All array functions | ||
Aggregate Functions | |||
Subquery Expressions | |||
Row and Array Comparisons | |||
Set Returning Functions | generate_series | ||
System Information Functions |
All session information functions All access privilege inquiry functions All schema visibility inquiry functions All system catalog information functions All comment information functions All transaction ids and snapshots |
||
System Administration Functions |
set_config pg_cancel_backend pg_terminate_backend pg_reload_conf pg_rotate_logfile pg_start_backup pg_stop_backup pg_size_pretty pg_ls_dir pg_read_file pg_stat_file |
current_setting All database object size functions |
Примечание. Функция pg_column_size отображает байты, необходимые для хранения значения, возможно, со сжатием TOAST. |
XML Functions and function-like expressions |
cursor_to_xml(cursor refcursor, count int, nulls boolean, tableforest boolean, targetns text) cursor_to_xmlschema(cursor refcursor, nulls boolean, tableforest boolean, targetns text) database_to_xml(nulls boolean, tableforest boolean, targetns text) database_to_xmlschema(nulls boolean, tableforest boolean, targetns text) database_to_xml_and_xmlschema( nulls boolean, tableforest boolean, targetns text) query_to_xml(query text, nulls boolean, tableforest boolean, targetns text) query_to_xmlschema(query text, nulls boolean, tableforest boolean, targetns text) query_to_xml_and_xmlschema( query text, nulls boolean, tableforest boolean, targetns text) schema_to_xml(schema name, nulls boolean, tableforest boolean, targetns text) schema_to_xmlschema( schema name, nulls boolean, tableforest boolean, targetns text) schema_to_xml_and_xmlschema( schema name, nulls boolean, tableforest boolean, targetns text) table_to_xml(tbl regclass, nulls boolean, tableforest boolean, targetns text) table_to_xmlschema( tbl regclass, nulls boolean, tableforest boolean, targetns text) table_to_xml_and_xmlschema( tbl regclass, nulls boolean, tableforest boolean, targetns text) xmlagg(xml) xmlconcat(xml[, ...]) xmlelement(name name [, xmlattributes(value [AS attname] [, ... ])] [, content, ...]) xmlexists(text, xml) xmlforest(content [AS name] [, ...]) xml_is_well_formed(text) xml_is_well_formed_document(text) xml_is_well_formed_content(text) xmlparse ( { DOCUMENT | CONTENT } value) xpath(text, xml) xpath(text, xml, text[]) xpath_exists(text, xml) xpath_exists(text, xml, text[]) xmlpi(name target [, content]) xmlroot(xml, version text | no value [, standalone yes|no|no value]) xmlserialize ( { DOCUMENT | CONTENT } value AS type ) xml(text) text(xml) xmlcomment(xml) xmlconcat2(xml, xml) |
Следующие встроенные оконные функции являются расширениями RT.Warehouse для базы данных PostgreSQL. Все оконные функции неизменяемы. Дополнительные сведения об оконных функциях см. в разделе Оконные выражения (21.3.2.8).
Таблица 120. Оконные функции
Функция | Тип | Синтаксис | Описание |
---|---|---|---|
cume_dist() | double precision | CUME_DIST() OVER ( [PARTITION BY expr ] ORDER BY expr ) | Вычисляет кумулятивное распределение значения в группе значений. Строки с одинаковыми значениями всегда оцениваются как одно и то же кумулятивное значение распределения. |
dense_rank() | bigint | DENSE_RANK () OVER ( [PARTITION BY expr ] ORDER BY expr ) | Вычисляет ранг строки в упорядоченной группе строк без пропуска значений ранга. Строкам с одинаковыми значениями присваивается одинаковое значение ранга. |
first_value(expr) | same as input expr type | FIRST_VALUE( expr ) OVER ( [PARTITION BY expr ] ORDER BY expr [ROWS|RANGE frame_expr ] ) | Возвращает первое значение в упорядоченном наборе значений. |
lag(expr [,offset] [,default]) | same as input expr type | LAG( expr [, offset ] [, default ]) OVER ( [PARTITION BY expr ] ORDER BY expr ) | Предоставляет доступ к более чем одной строке одной и той же таблицы без самостоятельного соединения. Учитывая серию строк, возвращенных из запроса, и позицию курсора, LAG обеспечивает доступ к строке с заданным физическим смещением до этой позиции. Смещение по умолчанию равно 1. По умолчанию задается значение, которое возвращается, если смещение выходит за рамки окна. Если значение по умолчанию не указано, значение по умолчанию равно null. |
last_value(expr) | same as input expr type | LAST_VALUE(expr) OVER ( [PARTITION BY expr] ORDER BY expr [ROWS|RANGE frame_expr] ) | Возвращает последнее значение в упорядоченном наборе значений. |
lead(expr [,offset] [,default]) | same as input expr type | LEAD(expr [,offset] [,exprdefault]) OVER ( [PARTITION BY expr] ORDER BY expr ) | Предоставляет доступ к более чем одной строке одной и той же таблицы без самостоятельного соединения. Учитывая серию строк, возвращенных из запроса, и позицию курсора, лидерство обеспечивает доступ к строке с заданным физическим смещением после этой позиции. Если смещение не указано, смещение по умолчанию равно 1. По умолчанию задается значение, которое возвращается, если смещение выходит за рамки окна. Если значение по умолчанию не указано, значение по умолчанию равно null. |
ntile(expr) | bigint | NTILE(expr) OVER ( [PARTITION BY expr] ORDER BY expr ) | Делит упорядоченный набор данных на несколько сегментов (как определено expr) и присваивает номер сегмента каждой строке. |
percent_rank() | double precision | PERCENT_RANK () OVER ( [PARTITION BY expr] ORDER BY expr ) | Вычисляет ранг гипотетической строки R минус 1, разделенный на 1 меньше числа оцениваемых строк (в пределах раздела окна). |
rank() | bigint | RANK () OVER ( [PARTITION BY expr] ORDER BY expr ) | Вычисляет ранг строки в упорядоченной группе значений. Строки с одинаковыми значениями критериев ранжирования получают одинаковый ранг. Количество связанных строк добавляется к номеру ранга для расчета следующего значения ранга. В этом случае ранги не могут быть последовательными числами. |
row_number() | bigint | ROW_NUMBER () OVER ( [PARTITION BY expr] ORDER BY expr ) | Присваивает уникальный номер каждой строке, к которой он применяется (либо каждой строке в разделе окна, либо каждой строке запроса). |
Следующие встроенные расширенные агрегатные функции являются расширениями базы данных PostgreSQL от RT.Warehouse. Эти функции неизменны.
Примечание. Расширение RT.Warehouse MADlib для аналитики предоставляет дополнительные расширенные функции для выполнения статистического анализа и машинного обучения с данными базы данных RT.Warehouse. |
Таблица 121. Расширенные агрегатные функции
Функция | Тип | Синтаксис | Описание |
---|---|---|---|
MEDIAN (expr) | timestamp, timestamptz, interval, float |
MEDIAN (expression) Пример:
|
Может принимать двумерный массив в качестве входных данных. Рассматривает такие массивы как матрицы. |
sum(array[]) | smallint[], int[], bigint[], float[] |
sum(array[[1,2],[3,4]]) Пример:
|
Выполняет суммирование матриц. Может принимать в качестве входных данных двумерный массив, который обрабатывается как матрица. |
pivot_sum (label[], label, expr) | int[], bigint[], float[] | pivot_sum( array['A1','A2'], attr, value) | Сводная агрегация с использованием суммы для разрешения повторяющихся записей. |
unnest (array[]) | set of any element | unnest( array['one', 'row', 'per', 'item']) | Преобразует одномерный массив в строки. Возвращает набор любых элементов, полиморфный псевдотип в PostgreSQL. |
База данных RT.Warehouse поддерживает типы данных json и jsonb, в которых хранятся данные JSON (нотация объектов JavaScript).
База данных RT.Warehouse поддерживает JSON, как указано в документе RFC 7159, и обеспечивает достоверность данных в соответствии с правилами JSON. Существуют также специфичные для JSON функции и операторы, доступные для типов данных json и jsonb. См. Функции и операторы JSON (21.6.6).
База данных RT.Warehouse поддерживает два типа данных JSON: json и jsonb. Они принимают на вход почти одинаковые наборы значений. Основное отличие заключается в эффективности.
Тип данных jsonb поддерживает индексацию.
Как правило, данные JSON следует хранить как тип данных jsonb, если только нет особых потребностей, таких как устаревшие предположения о порядке ключей объектов.
Документ RFC 7159 позволяет строкам JSON содержать escape-последовательности Unicode, обозначаемые \uXXXX. Однако база данных RT.Warehouse допускает только одну кодировку набора символов для каждой базы данных. Тип данных json не может строго соответствовать спецификации JSON, если только кодировка базы данных не является UTF8. Попытки включить символы, которые не могут быть представлены в кодировке базы данных, потерпят неудачу. Допускаются символы, которые могут быть представлены в кодировке базы данных, но не в UTF8.
Примечание. Многие функции обработки JSON, описанные в разделе Функции и операторы JSON, преобразуют escape-последовательности Unicode в обычные символы. Функции выдают ошибку для символов, которые не могут быть представлены в кодировке базы данных. По возможности следует избегать смешивания escape-последовательности Unicode в JSON с кодировкой базы данных, отличной от UTF8. |
При преобразовании ввода текста JSON в данные jsonb примитивные типы данных, описанные в RFC 7159, эффективно сопоставляются с собственными типами данных базы данных RT.Warehouse, как показано в следующей таблице.
Таблица 122. Примитивные типы JSON и соответствующие типы данных базы данных RT.Warehouse
Примитивный тип данных JSON | Тип данных RT.Warehouse | Примечания |
---|---|---|
string | text | \u0000 не допускается. Escape-последовательности Unicode, отличные от ASCII, разрешены только в том случае, если кодировка базы данных — UTF8. |
number | numeric | Значения NaN и infinity запрещены |
boolean | boolean | Допускаются только true и false варианты (в нижнем регистре) |
null | (none) | Тип примитива JSON null отличается от SQL NULL. |
Существуют некоторые незначительные ограничения на то, что представляет собой допустимые данные jsonb, которые не применяются к типу данных json или к JSON в абстрактном виде, что соответствует ограничениям на то, что может быть представлено базовым типом данных. Примечательно, что при преобразовании данных в тип данных jsonb числа, выходящие за пределы диапазона числового типа данных базы данных RT.Warehouse, отклоняются, в то время как тип данных json не отклоняет такие числа.
Такие ограничения, определяемые реализацией, разрешены RFC 7159. Однако на практике такие проблемы могут возникать в других реализациях, поскольку обычно числовой примитивный тип JSON представляется как число с плавающей запятой двойной точности IEEE 754 (что RFC 7159 явно предполагает и допускает).
При использовании JSON в качестве формата обмена с другими системами помните о возможности потери числовой точности по сравнению с данными, изначально хранящимися в базе данных RT.Warehouse.
Кроме того, как отмечено в предыдущей таблице, существуют некоторые незначительные ограничения на входной формат примитивных типов JSON, которые не применяются к соответствующим типам данных базы данных RT.Warehouse.
Синтаксис ввода и вывода для типа данных json указан в RFC 7159.
Ниже приведены все допустимые выражения json:
-- Simple scalar/primitive value
-- Primitive values can be numbers, quoted strings, true, false, or null
SELECT '5'::json;
-- Array of zero or more elements (elements need not be of same type)
SELECT '[1, 2, "foo", null]'::json;
-- Object containing pairs of keys and values
-- Note that object keys must always be quoted strings
SELECT '{"bar": "baz", "balance": 7.77, "active": false}'::json;
-- Arrays and objects can be nested arbitrarily
SELECT '{"foo": [true, "bar"], "tags": {"a": 1, "b": null}}'::json;
Как указывалось ранее, когда значение JSON вводится, а затем печатается без какой-либо дополнительной обработки, тип данных json выводит тот же текст, который был введен, в то время как тип данных jsonb не сохраняет семантически несущественные детали, такие как пробелы. Например, обратите внимание на различия здесь:
SELECT '{"bar": "baz", "balance": 7.77, "active":false}'::json;
json
-------------------------------------------------
{"bar": "baz", "balance": 7.77, "active":false}
(1 row)
SELECT '{"bar": "baz", "balance": 7.77, "active":false}'::jsonb;
jsonb
--------------------------------------------------
{"bar": "baz", "active": false, "balance": 7.77}
(1 row)
Стоит отметить одну семантически незначительную деталь: с типом данных jsonb числа будут печататься в соответствии с поведением базового числового типа. На практике это означает, что числа, введенные с нотацией Е, будут печататься без нее, например:
SELECT '{"reading": 1.230e-5}'::json, '{"reading": 1.230e-5}'::jsonb;
json | jsonb
-----------------------+-------------------------
{"reading": 1.230e-5} | {"reading": 0.00001230}
(1 row)
Однако тип данных jsonb сохраняет нули в конце дробной части, как показано в предыдущем примере, даже если они семантически несущественны для таких целей, как проверка на равенство.
Представление данных в виде JSON может быть значительно более гибким, чем традиционная реляционная модель данных, которая актуальна в средах с изменчивыми требованиями. Вполне возможно, что оба подхода сосуществуют и дополняют друг друга в рамках одного приложения. Однако даже для приложений, где требуется максимальная гибкость, рекомендуется, чтобы документы JSON имели несколько фиксированную структуру. Структура обычно не применяется (хотя декларативное применение некоторых бизнес-правил возможно), но наличие предсказуемой структуры упрощает написание запросов, которые с пользой обобщают набор документов JSON (датумы) в таблице.
Данные JSON подлежат тем же соображениям контроля параллелизма, что и данные любого другого типа при хранении в таблице. Хотя хранение больших документов возможно, имейте в виду, что любое обновление получает блокировку на уровне строки для всей строки. Рассмотрите возможность ограничения документов JSON управляемым размером, чтобы уменьшить конфликт блокировок между обновляемыми транзакциями. В идеале, каждый документ JSON должен представлять атомарные данные, которые согласно бизнес-правилам не могут быть разумно разделены на более мелкие данные, которые можно изменять независимо друг от друга.
Проверка содержания — важная возможность jsonb. аналогичного набора для типа json не существует. Содержание проверяет, содержит ли один документ jsonb другой. Эти примеры возвращают true, за исключением отмеченного:
-- Simple scalar/primitive values contain only the identical value:
SELECT '"foo"'::jsonb @> '"foo"'::jsonb;
-- The array on the right side is contained within the one on the left:
SELECT '[1, 2, 3]'::jsonb @> '[1, 3]'::jsonb;
-- Order of array elements is not significant, so this is also true:
SELECT '[1, 2, 3]'::jsonb @> '[3, 1]'::jsonb;
-- Duplicate array elements don't matter either:
SELECT '[1, 2, 3]'::jsonb @> '[1, 2, 2]'::jsonb;
-- The object with a single pair on the right side is contained
-- within the object on the left side:
SELECT '{"product": "Greenplum", "version": "6.0.0", "jsonb":true}'::jsonb @> '{"version":"6.0.0"}'::jsonb;
-- The array on the right side is not considered contained within the
-- array on the left, even though a similar array is nested within it:
SELECT '[1, 2, [1, 3]]'::jsonb @> '[1, 3]'::jsonb; -- yields false
-- But with a layer of nesting, it is contained:
SELECT '[1, 2, [1, 3]]'::jsonb @> '[[1, 3]]'::jsonb;
-- Similarly, containment is not reported here:
SELECT '{"foo": {"bar": "baz", "zig": "zag"}}'::jsonb @> '{"bar": "baz"}'::jsonb; -- yields false
-- But with a layer of nesting, it is contained:
SELECT '{"foo": {"bar": "baz", "zig": "zag"}}'::jsonb @> '{"foo": {"bar": "baz"}}'::jsonb;
Общий принцип заключается в том, что содержащийся объект должен соответствовать содержащемуся объекту по структуре и содержанию данных, возможно, после отбрасывания некоторых несовпадающих элементов массива или пар ключ/значение объекта из содержащего объекта. Для содержания порядок элементов массива не имеет значения при выполнении сопоставления содержания, и повторяющиеся элементы массива фактически учитываются только один раз.
В качестве исключения из общего принципа, согласно которому структуры должны совпадать, массив может содержать примитивное значение:
-- This array contains the primitive string value:
SELECT '["foo", "bar"]'::jsonb @> '"bar"'::jsonb;
-- This exception is not reciprocal -- non-containment is reported here:
SELECT '"bar"'::jsonb @> '["bar"]'::jsonb; -- yields false
jsonb также имеет оператор существования, который представляет собой вариацию на тему вмещения: он проверяет, появляется ли строка (заданная в виде текстового значения) как ключ объекта или элемент массива на верхнем уровне значения jsonb. Эти примеры возвращают true, за исключением отмеченного:
-- String exists as array element:
SELECT '["foo", "bar", "baz"]'::jsonb ? 'bar';
-- String exists as object key:
SELECT '{"foo": "bar"}'::jsonb ? 'foo';
-- Object values are not considered:
SELECT '{"foo": "bar"}'::jsonb ? 'bar'; -- yields false
-- As with containment, existence must match at the top level:
SELECT '{"foo": {"bar": "baz"}}'::jsonb ? 'bar'; -- yields false
-- A string is considered to exist if it matches a primitive JSON string:
SELECT '"foo"'::jsonb ? 'foo';
Объекты JSON лучше, чем массивы, подходят для проверки содержания или существования, когда задействовано много ключей или элементов, потому что, в отличие от массивов, они внутренне оптимизированы для поиска и не нуждаются в линейном поиске.
Различные операторы включения и существования, а также все другие операторы и функции JSON описаны в разделе Функции и операторы JSON (21.6.6).
Поскольку включение JSON является вложенным, соответствующий запрос может пропустить явный выбор подобъектов. В качестве примера предположим, что у нас есть столбец документа, содержащий объекты на верхнем уровне, при этом большинство объектов содержат поля тегов, которые содержат массивы подобъектов. Этот запрос находит записи, в которых появляются подобъекты, содержащие как «термин»: «париж», так и «термин»: «еда», игнорируя любые такие ключи вне массива тегов:
SELECT doc->'site_name' FROM websites
WHERE doc @> '{"tags":[{"term":"paris"}, {"term":"food"}]}';
Запрос с этим предикатом может выполнить то же самое.
SELECT doc->'site_name' FROM websites
WHERE doc->'tags' @> '[{"term":"paris"}, {"term":"food"}]';
Однако второй подход менее гибкий и зачастую менее эффективный.
С другой стороны, оператор существования JSON не является вложенным: он будет искать указанный ключ или элемент массива только на верхнем уровне значения JSON.
Тип данных jsonb базы данных RT.Warehouse поддерживает GIN, btree и хэш-индексы.
Индексы GIN можно использовать для эффективного поиска ключей или пар ключ/значение, встречающихся в большом количестве документов jsonb (датумов). Предусмотрены два класса операторов GIN, предлагающие различные компромиссы между производительностью и гибкостью.
Класс оператора GIN по умолчанию для jsonb поддерживает запросы с @>, ?, ?& и ?| операторами. Пример создания индекса с помощью этого класса операторов:
CREATE INDEX idxgin ON api USING gin (jdoc);
Класс операторов GIN не по умолчанию jsonb_path_ops поддерживает индексирование только оператора @>. Пример создания индекса с помощью этого класса операторов:
CREATE INDEX idxginp ON api USING gin (jdoc jsonb_path_ops);
Рассмотрим пример таблицы, в которой хранятся документы JSON, полученные из стороннего веб-сервиса, с задокументированным определением схемы. Это типовой документ:
{
"guid": "9c36adc1-7fb5-4d5b-83b4-90356a46061a",
"name": "Angela Barton",
"is_active": true,
"company": "Magnafone",
"address": "178 Howard Place, Gulf, Washington, 702",
"registered": "2009-11-07T08:53:22 +08:00",
"latitude": 19.793713,
"longitude": 86.513373,
"tags": [
"enim",
"aliquip",
"qui"
]
}
Документы JSON хранятся в таблице с именем api в столбце jsonb с именем jdoc. Если для этого столбца создан индекс GIN, такие запросы могут использовать индекс:
-- Find documents in which the key "company" has value "Magnafone"
SELECT jdoc->'guid', jdoc->'name' FROM api WHERE jdoc @> '{"company": "Magnafone"}';
Однако индекс нельзя было использовать для запросов, подобных приведенным ниже. Оператор ? индексируется, однако сравнение не применяется непосредственно к индексированному столбцу jdoc:
-- Find documents in which the key "tags" contains key or array element "qui"
SELECT jdoc->'guid', jdoc->'name' FROM api WHERE jdoc -> 'tags' ? 'qui';
При соответствующем использовании индексов выражений приведенный выше запрос может использовать индекс. Если запрос определенных элементов в ключе tags является обычным явлением, то может быть целесообразным определение такого индекса:
CREATE INDEX idxgintags ON api USING gin ((jdoc -> 'tags'));
Теперь предложение WHERE jdoc -> 'tags' ? 'qui' распознается как применение индексируемого оператора ? к индексированному выражению jdoc -> 'tags'. Сведения об индексах выражений см. в разделе Индексы выражений (18.8.3.1).
Другой подход к запросам документов JSON заключается в использовании содержания, например:
-- Find documents in which the key "tags" contains array element "qui"
SELECT jdoc->'guid', jdoc->'name' FROM api WHERE jdoc @> '{"tags": ["qui"]}';
Простой индекс GIN в столбце jdoc может поддерживать этот запрос. Однако индекс будет хранить копии каждого ключа и значения в столбце jdoc, тогда как индекс выражения в предыдущем примере хранит только данные, найденные в ключе tags. Хотя подход с простым индексом является гораздо более гибким (поскольку он поддерживает запросы по любому ключу), индексы с целевым выражением, вероятно, будут меньше и быстрее для поиска, чем простой индекс.
Хотя класс операторов jsonb_path_ops поддерживает только запросы с оператором @>, он имеет преимущества в производительности по сравнению с классом операторов по умолчанию jsonb_ops. Индекс jsonb_path_ops обычно намного меньше индекса jsonb_ops для тех же данных, а специфичность поиска лучше, особенно когда запросы содержат ключи, которые часто встречаются в данных. Поэтому операции поиска обычно выполняются лучше, чем с классом операторов по умолчанию.
Техническая разница между GIN-индексом jsonb_ops и jsonb_path_ops заключается в том, что первый создает независимые элементы индекса для каждого ключа и значения в данных, а второй создает элементы индекса только для каждого значения в данных.
Примечание. В данном обсуждении термин «значение» (value) включает элементы массива, хотя в терминологии JSON иногда элементы массива рассматриваются отдельно от значений внутри объектов. |
По сути, каждый элемент индекса jsonb_path_ops представляет собой хэш значения и ключей, ведущих к нему. Например, для индексации {"foo": {"bar": "baz"}} будет создан один элемент индекса, включающий все три из foo, bar и baz в хэш-значение. Таким образом, запрос на включение, ищущий эту структуру, приведет к чрезвычайно специфичному поиску по индексу, но нет никакого способа узнать, появляется ли foo как ключ. С другой стороны, индекс jsonb_ops создаст три элемента индекса, представляющих foo, bar и baz по отдельности, затем, чтобы выполнить запрос на включение, он будет искать строки, содержащие все три из этих элементов. Хотя индексы GIN могут выполнять такой поиск по И (AND) достаточно эффективно, он все равно будет менее точным и медленным, чем эквивалентный поиск jsonb_path_ops, особенно, если имеется очень большое количество строк, содержащих любой один из трех элементов индекса.
Недостатком подхода jsonb_path_ops является то, что он не создает записей индекса для структур JSON, не содержащих никаких значений, таких как {"a": {}}. Если запрашивается поиск документов, содержащих такую структуру, то потребуется сканирование полного индекса, что довольно медленно. jsonb_path_ops плохо подходит для приложений, которые часто выполняют такой поиск.
jsonb также поддерживает индексы btree и hash. Обычно они полезны только тогда, когда важно проверить равенство полных документов JSON.
Для полноты картины порядок btree для данных jsonb следующий:
Object > Array > Boolean > Number > String > Null
Object with n pairs > object with n - 1 pairs
Array with n elements > array with n - 1 elements
Объекты с одинаковым количеством пар сравниваются в порядке:
key-1, value-1, key-2 ...
Ключи объектов сравниваются в порядке их хранения. В частности, поскольку более короткие ключи хранятся перед более длинными ключами, это может привести к порядку, который может быть не интуитивно понятным, например:
{ "aa": 1, "c": 1} > {"b": 1, "d": 1}
Аналогично сравниваются массивы с одинаковым количеством элементов в порядке:
element-1, element-2 ...
Примитивные значения JSON сравниваются с использованием тех же правил сравнения, что и для базового типа данных базы данных RT.Warehouse. Строки сравниваются с использованием сортировки базы данных по умолчанию.
База данных RT.Warehouse включает встроенные функции и операторы, которые создают данные JSON и управляют ими.
Примечание. Для значений типа данных json все пары ключ/значение сохраняются, даже если объект JSON содержит повторяющиеся ключи. Для повторяющихся ключей функции обработки JSON считают последнее значение рабочим. Для типа данных jsonb дубликаты ключей объектов не сохраняются. Если ввод содержит повторяющиеся ключи, сохраняется только последнее значение. |
В Таблице 123 описаны операторы, доступные для использования с типами данных json и jsonb.
Таблица 123. json и jsonb операторы
Оператор | Правильный тип операнда | Описание | Пример | Пример результата |
---|---|---|---|---|
-> | int | Получите элемент массива JSON (индексированный с нуля). |
|
|
-> | text | Получить поле объекта JSON по ключу. |
|
|
->> | int | Получите элемент массива JSON в виде текста. |
|
3 |
->> | text | Получите поле объекта JSON в виде текста. |
|
2 |
#> | text[] | Получить объект JSON по указанному пути. |
|
|
#>> | text[] | Получить объект JSON по указанному пути в виде текста. |
|
3 |
Примечание. Существуют параллельные варианты этих операторов для типов данных json и jsonb. Операторы извлечения поля, элемента и пути возвращают тот же тип данных, что и их ввод слева (либо json, либо jsonb), за исключением тех, которые указаны как возвращающие текст, которые приводят значение к тексту. Операторы извлечения полей, элементов и путей возвращают NULL, а не сбой, если входные данные JSON не имеют правильной структуры для соответствия запросу. Например, если такого элемента не существует. |
Операторы, которым требуется тип данных jsonb в качестве левого операнда, описаны в следующей таблице. Многие из этих операторов могут быть проиндексированы классами операторов jsonb.
Таблица 124. Операторы jsonb
Оператор | Правильный тип операнда | Описание | Пример |
---|---|---|---|
@> | jsonb | Содержит ли левое значение JSON правильное значение? |
|
<@ | jsonb | Содержится ли левое значение JSON в правом значении? |
|
? | text | Существует ли строка ключа/элемента в значении JSON? |
|
?| | text[] | Существуют ли какие-либо из этих строк ключей/элементов? |
|
?& | text[] | Существуют ли все эти строки ключей/элементов? |
|
Стандартные операторы сравнения в следующей таблице доступны только для типа данных jsonb (но не для типа данных json). Они следуют правилам упорядочения операций B-tree, описанным в индексировании jsonb.
Таблица 125. Операторы сравнения jsonb
Оператор | Описание |
---|---|
< | меньше чем |
> | больше чем |
<= | меньше или равно |
>= | больше или равно |
= | равно |
<> or != | не равно |
Примечание. Оператор != преобразуется в <> на этапе парсера. Невозможно реализовать операторы != и <>, которые делают разные вещи. |
В Таблице 126 описаны функции, которые создают значения типа данных json. (В настоящее время для jsonb нет эквивалентных функций, но вы можете преобразовать результат одной из этих функций в jsonb.)
Таблица 126. Функции создания JSON
Функция | Описание | Пример | Пример результата |
---|---|---|---|
to_json(anyelement) | Возвращает значение в виде объекта JSON. Массивы и композиты обрабатываются рекурсивно и преобразуются в массивы и объекты. Если входные данные содержат приведение типа к json, для выполнения преобразования используется функция приведения; в противном случае создается скалярное значение JSON. Для любого скалярного типа, отличного от числа, логического или нулевого значения, будет использоваться текстовое представление, правильно заключенное в кавычки и экранированное, чтобы оно было допустимой строкой JSON. |
|
|
array_to_json(anyarray [, pretty_bool]) |
Возвращает массив в виде массива JSON. Многомерный массив становится массивом массивов JSON. Перевод строки будет добавлен между элементами измерения-1 (dimension-1), если pretty_bool имеет значение true. |
|
|
row_to_json(record [, pretty_bool]) |
Возвращает строку как объект JSON. Перевод строки будет добавлен между элементами уровня 1 (level-1), если pretty_bool имеет значение true. |
|
|
json_build_array(VARIADIC "any") | Создает массив JSON с возможным гетерогенным типом из списка аргументов VARIADIC. |
|
|
json_build_object(VARIADIC "any") | Создает объект JSON из списка аргументов VARIADIC. Список аргументов берется по порядку и преобразуется в набор пар ключ/значение. |
|
|
json_object(text[]) |
Создает объект JSON из текстового массива. Массив должен быть либо одномерным, либо двумерным. Одномерный массив должен иметь четное количество элементов. Элементы берутся как пары ключ/значение. Для двумерного массива каждый внутренний массив должен иметь ровно два элемента, которые берутся как пара ключ/значение. |
|
|
json_object(keys text[], values text[]) | Создает объект JSON из текстового массива. Эта форма json_object берет ключи и значения попарно из двух отдельных массивов. Во всем остальном она идентична одноаргументной форме. |
|
|
Примечание. array_to_json и row_to_json имеют то же поведение, что и to_json, за исключением того, что они предлагают возможность красивой печати. Поведение, описанное для to_json, также применимо к каждому отдельному значению, преобразованному другими функциями создания JSON. |
В Таблице 127 показаны функции, объединяющие записи в массив объектов JSON и пары значений в объект JSON.
Таблица 127. Агрегированные функции JSON
Функция | Типы аргументов | Возвращаемый тип | Описание |
---|---|---|---|
json_agg(record) | record | json | Объединяет записи в виде массива объектов JSON. |
json_object_agg(name, value) | ("any", "any") | json | Объединяет пары имя/значение в виде объекта JSON. |
В Таблице 128 показаны функции, доступные для обработки значений json и jsonb.
Многие из этих функций и операторов обработки преобразуют escape-последовательности Unicode в строках JSON в соответствующий одиночный символ. Это не проблема, если тип входных данных — jsonb, потому что преобразование уже было выполнено. Однако для ввода данных типа json это может привести к возникновению ошибки.
Таблица 128. Функции обработки JSON
Функция | Возвращаемый тип | Описание | Пример | Пример результата |
---|---|---|---|---|
json_array_length(json) jsonb_array_length(jsonb) |
int | Возвращает количество элементов в крайнем массиве JSON. |
|
5 |
json_each(json) jsonb_each(jsonb) |
setof key text, value json setof key text, value jsonb |
Расширяет внешний объект JSON в набор пар ключ/значение. |
|
|
json_each_text(json) jsonb_each_text(jsonb) |
setof key text, value text | Расширяет внешний объект JSON в набор пар ключ/значение. Возвращаемые значения будут иметь тип text. |
|
|
json_extract_path(from_json json, VARIADIC path_elems text[]) jsonb_extract_path(from_json jsonb, VARIADIC path_elems text[]) |
json jsonb |
Возвращает значение JSON, на которое указывает path_elems (эквивалентно оператору #>). |
|
|
json_extract_path_text(from_json json, VARIADIC path_elems text[]) jsonb_extract_path_text(from_json jsonb, VARIADIC path_elems text[]) |
text | Возвращает значение JSON, на которое указывает path_elems, в виде текста. Эквивалентно оператору #>>. |
|
foo |
json_object_keys(json) jsonb_object_keys(jsonb) |
setof text | Возвращает набор ключей в самом внешнем объекте JSON. |
|
|
json_populate_record(base anyelement, from_json json) jsonb_populate_record(base anyelement, from_json jsonb) |
anyelement | Расширяет объект в from_json до строки, столбцы которой соответствуют типу записи, определенному базой. См. Примечание 1 (ниже). |
|
|
json_populate_recordset(base anyelement, from_json json) jsonb_populate_recordset(base anyelement, from_json jsonb) |
setof anyelement | Расширяет самый внешний массив объектов в from_json до набора строк, столбцы которых соответствуют типу записи, определенному базой. См. Примечание 1 (ниже). |
|
|
json_array_elements(json) jsonb_array_elements(jsonb) |
setof json setof jsonb |
Расширяет массив JSON до набора значений JSON. |
|
|
json_array_elements_text(json) jsonb_array_elements_text(jsonb) |
setof text | Расширяет массив JSON до набора текстовых значений. |
|
|
json_typeof(json) jsonb_typeof(jsonb) |
text | Возвращает тип самого внешнего значения JSON в виде текстовой строки. Возможные типы: объект, массив, строка, число, логическое значение и null. См. Примечание 2 (ниже). |
|
number |
json_to_record(json) jsonb_to_record(jsonb) |
record |
Создает произвольную запись из объекта JSON. См. Примечание 1 (ниже). Как и во всех функциях, возвращающих запись, вызывающая сторона должна явно определить структуру записи с предложением AS. |
|
|
json_to_recordset(json) jsonb_to_recordset(jsonb) |
setof record |
Создает произвольный набор записей из массива объектов JSON. См. Примечание 1 (ниже). Как и во всех функциях, возвращающих запись, вызывающая сторона должна явно определить структуру записи с предложением AS. |
|
|
Примечание. 1. В примерах для функций json_populate_record(), json_populate_recordset(), json_to_record() и json_to_recordset() используются константы. Однако типичным использованием будет ссылка на таблицу в предложении FROM и использование одного из ее столбцов json или jsonb в качестве аргумента функции. Затем на извлеченные ключевые значения можно ссылаться в других частях запроса. Например, на значение можно ссылаться в предложениях WHERE и целевых списках. Извлечение нескольких значений таким образом может повысить производительность по сравнению с извлечением их по отдельности с помощью операторов для каждого ключа. Ключи JSON сопоставляются с идентичными именами столбцов в целевом типе строки. Приведение типа JSON для этих функций может не привести к желаемым значениям для некоторых типов. Поля JSON, которые не отображаются в целевом типе строки, будут исключены из выходных данных, а целевые столбцы, не соответствующие ни одному полю JSON, будут иметь значение NULL. 2. Возвращаемое значение null функции json_typeof не следует путать с SQL NULL. В то время как вызов json_typeof('null'::json) вернет null, вызов json_typeof(NULL::json) вернет SQL NULL. |
База данных RT.Warehouse поддерживает тип данных xml, в котором хранятся данные XML.
Тип данных xml проверяет входные значения на корректность, предоставляя преимущество по сравнению с простым хранением данных XML в текстовом поле. Кроме того, вспомогательные функции позволяют вам выполнять над этими данными операции с типобезопасностью.
Тип xml может хранить правильно сформированные «документы», как определено стандартом XML, а также фрагменты «содержимого», которые определяются ссылкой на более разрешительный узел документа модели XQuery и XPath. Грубо говоря, это означает, что фрагменты контента могут иметь более одного элемента верхнего уровня или символьного узла. Выражение xmlvalue IS DOCUMENT можно использовать для оценки того, является ли конкретное значение xml полным документом или только фрагментом содержимого.
Чтобы создать значение типа xml из символьных данных, используйте функцию xmlparse:
xmlparse ( { DOCUMENT | CONTENT } value)
Например:
XMLPARSE (DOCUMENT '<?xml version="1.0"?><book><title>Manual</title><chapter>...</chapter></book>')
XMLPARSE (CONTENT 'abc<foo>bar</foo><bar>foo</bar>')
Приведенный выше метод преобразует строки символов в значения XML в соответствии со стандартом SQL, но вы также можете использовать синтаксис базы данных RT.Warehouse, например следующий:
xml '<foo>bar</foo>'
'<foo>bar</foo>'::xml
Тип xml не проверяет входные значения на соответствие объявлению типа документа (DTD), даже если входное значение указывает DTD. Кроме того, в настоящее время нет встроенной поддержки проверки по другим языкам схемы XML, таким как схема XML.
Обратная операция, создающая значение строки символов из xml, использует функцию xmlserialize:
xmlserialize ( { DOCUMENT | CONTENT } value AS type )
type может быть символьным, изменяющимся символом или текстом (или псевдонимом для одного из них). Опять же, согласно стандарту SQL, это единственный способ преобразования между типом xml и символьными типами, но база данных RT.Warehouse также позволяет вам просто преобразовать значение.
Когда значение символьной строки приводится к типу xml или из него без использования XMLPARSE или XMLSERIALIZE, соответственно, выбор между DOCUMENT и CONTENT определяется параметром конфигурации сеанса XML OPTION, который можно установить с помощью стандартной команды:
SET XML OPTION { DOCUMENT | CONTENT };
или просто как база данных RT.Warehouse:
SET XML OPTION TO { DOCUMENT | CONTENT };
Значение по умолчанию — CONTENT, поэтому разрешены все формы XML-данных.
Будьте осторожны при работе с несколькими кодировками символов на клиенте, сервере и в передаваемых через них XML-данных. При использовании текстового режима для передачи запросов на сервер и результатов запроса клиенту (что является обычным режимом) база данных RT.Warehouse преобразует все символьные данные, передаваемые между клиентом и сервером, и наоборот, в кодировку символов соответствующей конечной точку. Сюда входят строковые представления значений XML, например, в приведенных выше примерах. Обычно это означает, что объявления кодировки, содержащиеся в XML-данных, могут стать недействительными, поскольку символьные данные преобразуются в другие кодировки при перемещении между клиентом и сервером, поскольку встроенное объявление кодировки не изменяется. Чтобы справиться с этим поведением, объявления кодировки, содержащиеся в символьных строках, представленных для ввода в тип xml, игнорируются, и предполагается, что содержимое находится в текущей кодировке сервера. Следовательно, для корректной обработки строки символов XML-данных должны быть отправлены клиентом в текущей клиентской кодировке. Клиент несет ответственность либо за преобразование документов в текущую клиентскую кодировку перед их отправкой на сервер, либо за соответствующую настройку клиентской кодировки. На выходе значения типа xml не будут иметь объявления кодировки, и клиенты должны предполагать, что все данные находятся в текущей клиентской кодировке.
При использовании двоичного режима для передачи параметров запроса на сервер и возврата результатов запроса клиенту преобразование набора символов не выполняется, поэтому ситуация отличается. В этом случае будет соблюдаться объявление кодировки в XML-данных и если оно отсутствует, данные будут считаться в UTF-8 (как того требует стандарт XML; обратите внимание, что база данных RT.Warehouse не поддерживает UTF-16). ). На выходе данные будут иметь декларацию кодировки, определяющую кодировку клиента, если кодировка клиента не является UTF-8, то в этом случае она будет опущена.
Примечание. Обработка данных XML с помощью базы данных RT.Warehouse будет менее подверженной ошибкам и более эффективной, если кодировка данных XML, кодировка клиента и кодировка сервера будут одинаковыми. Поскольку XML-данные внутри обрабатываются в UTF-8, вычисления будут наиболее эффективными, если кодировка сервера также является UTF-8. |
Тип данных xml необычен тем, что не предоставляет никаких операторов сравнения. Это связано с тем, что не существует четко определенного и универсально полезного алгоритма сравнения XML-данных. Одним из следствий этого является невозможность извлечения строк путем сравнения столбца xml со значением поиска. Поэтому XML значения обычно должны сопровождаться отдельным ключевым полем, например идентификатором. Альтернативное решение для сравнения значений XML состоит в том, чтобы сначала преобразовать их в строки символов, но обратите внимание, что сравнение строк символов имеет мало общего с полезным методом сравнения XML.
Поскольку для типа данных xml нет операторов сравнения, невозможно создать индекс непосредственно для столбца этого типа. Если требуется быстрый поиск в данных XML, возможные обходные пути включают приведение выражения к типу символьной строки и его индексирование или индексирование выражения XPath. Фактический запрос должен быть скорректирован для поиска по индексированному выражению.
Для обработки значений типа данных xml база данных RT.Warehouse предлагает функции xpath и xpath_exists, которые оценивают выражения XPath 1.0.
xpath(xpath, xml [, nsarray])
Функция xpath сравнивает выражение XPath xpath (текстовое значение) со значением XML (xml значение). Он возвращает массив значений XML, соответствующих набору узлов, созданному выражением XPath.
Второй аргумент должен быть правильно сформированным XML документом. В частности, он должен иметь один элемент корневого узла.
Необязательный третий аргумент функции — это массив сопоставлений пространств имен. Этот массив должен быть двумерным текстовым массивом с длиной второй оси, равной 2 (т. е. это должен быть массив массивов, каждый из которых состоит ровно из 2 элементов). Первый элемент каждой записи массива — это имя пространства имен (псевдоним), второй — URI пространства имен. Не требуется, чтобы псевдонимы, представленные в этом массиве, были такими же, как те, которые используются в самом XML-документе (другими словами, как в XML-документе, так и в контексте функции xpath псевдонимы являются локальными).
Например:
SELECT xpath('/my:a/text()', '<my:a xmlns:my="http://example.com">test</my:a>',
ARRAY[ARRAY['my', 'http://example.com']]);
xpath
--------
{test}
(1 row)
Чтобы взаимодействовать с пространствами имен (анонимными) по умолчанию проделайте:
SELECT xpath('//mydefns:b/text()', '<a xmlns="http://example.com"><b>test</b></a>',
ARRAY[ARRAY['mydefns', 'http://example.com']]);
xpath
--------
{test}
(1 row)
xpath_exists(xpath, xml [, nsarray])
Функция xpath_exists является специализированной формой функции xpath. Вместо того, чтобы возвращать отдельные значения XML, удовлетворяющие XPath, эта функция возвращает логическое значение, указывающее, был ли удовлетворен запрос. Эта функция эквивалентна стандартному предикату XMLEXISTS, за исключением того, что она также предлагает поддержку аргумента сопоставления пространства имен.
Например:
SELECT xpath_exists('/my:a/text()', '<my:a xmlns:my="http://example.com">test</my:a>',
ARRAY[ARRAY['my', 'http://example.com']]);
xpath_exists
--------------
t
(1 row)
Следующие функции отображают содержимое реляционных таблиц в значения XML. Их можно рассматривать как функциональные возможности экспорта XML:
table_to_xml(tbl regclass, nulls boolean, tableforest boolean, targetns text)
query_to_xml(query text, nulls boolean, tableforest boolean, targetns text)
cursor_to_xml(cursor refcursor, count int, nulls boolean,
tableforest boolean, targetns text)
Тип возвращаемого значения каждой функции — xml.
table_to_xml отображает содержимое именованной таблицы, переданной в качестве параметра tbl. Тип regclass принимает строки, идентифицирующие таблицы, используя обычную нотацию, включая необязательные уточнения схемы и двойные кавычки. query_to_xml выполняет запрос, текст которого передается в качестве параметра запроса, и отображает набор результатов. cursor_to_xml извлекает указанное количество строк из курсора, указанного параметром cursor. Этот вариант рекомендуется, если необходимо отобразить большие таблицы, поскольку значение результата создается в памяти каждой функцией.
Если tableforest имеет значение false, результирующий XML документ выглядит следующим образом:
<tablename>
<row>
<columnname1>data</columnname1>
<columnname2>data</columnname2>
</row>
<row>
...
</row>
...
</tablename>
Если tableforest имеет значение true, результатом является фрагмент содержимого XML, который выглядит следующим образом:
<tablename>
<columnname1>data</columnname1>
<columnname2>data</columnname2>
</tablename>
<tablename>
...
</tablename>
...
Если имя таблицы недоступно, то есть при отображении запроса или курсора, таблица строк используется в первом формате, строка во втором формате.
Выбор между этими форматами остается за пользователем. Первый формат — это правильный XML-документ, который будет важен во многих приложениях. Второй формат, как правило, более полезен в функции cursor_to_xml, если результирующие значения должны быть позже повторно собраны в один документ. Рассмотренные выше функции для создания содержимого XML, в частности xmlelement, можно использовать для изменения результатов по желанию.
Значения данных отображаются так же, как описано выше для функции xmlelement.
Параметр nulls определяет, следует ли включать в вывод нулевые значения. Если true, нулевые значения в столбцах представлены как:
<columnname xsi:nil="true"/>
где xsi — это префикс пространства имен XML для экземпляра схемы XML. К результирующему значению будет добавлено соответствующее объявление пространства имен. Если false, столбцы, содержащие нулевые значения, просто исключаются из вывода.
Параметр targetns указывает желаемое пространство имен XML для результата. Если конкретное пространство имен не требуется, следует передать пустую строку.
Следующие функции возвращают документы схемы XML, описывающие сопоставления, выполненные соответствующими функциями выше:
able_to_xmlschema(tbl regclass, nulls boolean, tableforest boolean, targetns text)
query_to_xmlschema(query text, nulls boolean, tableforest boolean, targetns text)
cursor_to_xmlschema(cursor refcursor, nulls boolean, tableforest boolean, targetns text)
Важно, чтобы одни и те же параметры передавались для получения соответствующих отображений данных XML и документов схемы XML.
Следующие функции создают сопоставления данных XML и соответствующую схему XML в одном документе. Они могут быть полезны, когда желательны автономные и самоописывающие результаты:
table_to_xml_and_xmlschema(tbl regclass, nulls boolean, tableforest boolean, targetns text)
query_to_xml_and_xmlschema(query text, nulls boolean, tableforest boolean, targetns text)
Кроме того, доступны следующие функции для создания аналогичных отображений целых схем или всей текущей базы данных:
schema_to_xml(schema name, nulls boolean, tableforest boolean, targetns text)
schema_to_xmlschema(schema name, nulls boolean, tableforest boolean, targetns text)
schema_to_xml_and_xmlschema(schema name, nulls boolean, tableforest boolean, targetns text)
database_to_xml(nulls boolean, tableforest boolean, targetns text)
database_to_xmlschema(nulls boolean, tableforest boolean, targetns text)
database_to_xml_and_xmlschema(nulls boolean, tableforest boolean, targetns text)
Обратите внимание, что они потенциально создают большие объемы данных, которые необходимо накапливать в памяти. При запросе сопоставления содержимого больших схем или баз данных рассмотрите возможность сопоставления таблиц отдельно, возможно, даже с помощью курсора.
Результат сопоставления содержимого схемы выглядит следующим образом:
<schemaname>
table1-mapping
table2-mapping
...
</schemaname>
где формат отображения таблицы зависит от параметра tableforest, как описано выше.
Результат сопоставления содержимого базы данных выглядит следующим образом:
<dbname>
<schema1name>
...
</schema1name>
<schema2name>
...
</schema2name>
...
</dbname>
где отображение схемы такое же, как указано выше.
В приведенном ниже примере демонстрируется использование выходных данных, созданных этими функциями. В примере показана таблица стилей XSLT, которая преобразует выходные данные схемы table_to_xml_and_xmlschema в документ HTML, содержащий табличное представление данных таблицы. Аналогичным образом результаты этих функций могут быть преобразованы в другие форматы на основе XML.
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://www.w3.org/1999/xhtml"
>
<xsl:output method="xml"
doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
doctype-public="-//W3C/DTD XHTML 1.0 Strict//EN"
indent="yes"/>
<xsl:template match="/*">
<xsl:variable name="schema" select="//xsd:schema"/>
<xsl:variable name="tabletypename"
select="$schema/xsd:element[@name=name(current())]/@type"/>
<xsl:variable name="rowtypename"
select="$schema/xsd:complexType[@name=$tabletypename]/xsd:sequence/xsd:element[@name='row']/@type"/>
<html>
<head>
<title><xsl:value-of select="name(current())"/></title>
</head>
<body>
<table>
<tr>
<xsl:for-each select="$schema/xsd:complexType[@name=$rowtypename]/xsd:sequence/xsd:element/@name">
<th><xsl:value-of select="."/></th>
</xsl:for-each>
</tr>
<xsl:for-each select="row">
<tr>
<xsl:for-each select="*">
<td><xsl:value-of select="."/></td>
</xsl:for-each>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Функции, описанные в этом разделе, работают со значениями типа xml. Раздел Предикаты XML также содержит информацию о функциях xml и выражениях, подобных функциям.
Функция: xmlcomment
Синопсис:
xmlcomment(text)
Функция xmlcomment создает значение XML, содержащее комментарий XML с указанным текстом в качестве содержимого. Текст не может содержать "--" или заканчиваться знаком "-", чтобы результирующая конструкция была допустимым комментарием XML. Если аргумент равен нулю, результат равен нулю.
Пример:
SELECT xmlcomment('hello');
xmlcomment
--------------
<!--hello-->
Функция: xmlconcat
Синопсис:
xmlconcat(xml[, …])
Функция xmlconcat объединяет список отдельных значений XML, чтобы создать одно значение, содержащее фрагмент содержимого XML. Нулевые значения опущены; результат будет нулевым только в том случае, если нет ненулевых аргументов.
Пример:
SELECT xmlconcat('<abc/>', '<bar>foo</bar>');
xmlconcat
----------------------
<abc/><bar>foo</bar>
XML-декларации, если они есть, объединяются следующим образом:
Объявления кодировки игнорируются и удаляются во всех случаях.
Пример:
SELECT xmlconcat('<?xml version="1.1"?><foo/>', '<?xml version="1.1" standalone="no"?><bar/>');
xmlconcat
-----------------------------------
<?xml version="1.1"?><foo/><bar/>
Функция: xmlelement
Синопсис:
xmlelement(name name [, xmlattributes(value [AS attname] [, ... ])] [, content, ...])
Выражение xmlelement создает элемент XML с заданным именем, атрибутами и содержимым.
Пример:
SELECT xmlelement(name foo);
xmlelement
------------
<foo/>
SELECT xmlelement(name foo, xmlattributes('xyz' as bar));
xmlelement
------------------
<foo bar="xyz"/>
SELECT xmlelement(name foo, xmlattributes(current_date as bar), 'cont', 'ent');
xmlelement
-------------------------------------
<foo bar="2017-01-26">content</foo>
Имена элементов и атрибутов, которые не являются допустимыми именами XML, экранируются путем замены ошибочных символов последовательностью _xHHHH_, где HHHH — это кодовая точка Unicode символа в шестнадцатеричном представлении. Например:
SELECT xmlelement(name "foo$bar", xmlattributes('xyz' as "a&b"));
xmlelement
----------------------------------
<foo_x0024_bar a_x0026_b="xyz"/>
Нет необходимости указывать явное имя атрибута, если значением атрибута является ссылка на столбец, и в этом случае имя столбца будет использоваться в качестве имени атрибута по умолчанию. В других случаях атрибуту должно быть присвоено явное имя. Действительный пример:
CREATE TABLE test (a xml, b xml);
SELECT xmlelement(name test, xmlattributes(a, b)) FROM test;
А вот этот нет:
SELECT xmlelement(name test, xmlattributes('constant'), a, b) FROM test;
SELECT xmlelement(name test, xmlattributes(func(a, b))) FROM test;
Содержимое элемента, если оно указано, будет отформатировано в соответствии с его типом данных. Если само содержимое имеет тип xml, можно создавать сложные XML документы. Например:
SELECT xmlelement(name foo, xmlattributes('xyz' as bar),
xmlelement(name abc),
xmlcomment('test'),
xmlelement(name xyz));
xmlelement
----------------------------------------------
<foo bar="xyz"><abc/><!--test--><xyz/></foo>
Содержимое других типов будет отформатировано в допустимые символьные данные XML. Это означает, в частности, что символы <, > и & будут преобразованы в объекты. Двоичные данные (тип данных bytea) будут представлены в кодировке base64 или hex, в зависимости от настройки параметра конфигурации xmlbinary. Ожидается, что конкретное поведение для отдельных типов данных будет развиваться, чтобы согласовать типы данных SQL и базы данных RT.Warehouse со спецификацией схемы XML, после чего появится более точное описание.
Функция: xmlforest
Синопсис:
xmlforest(content [AS name] [, ...])
Выражение xmlforest создает XML-forest (последовательность) элементов, используя заданные имена и содержимое.
Пример:
SELECT xmlforest('abc' AS foo, 123 AS bar);
xmlforest
------------------------------
<foo>abc</foo><bar>123</bar>
SELECT xmlforest(table_name, column_name)
FROM information_schema.columns
WHERE table_schema = 'pg_catalog';
xmlforest
-------------------------------------------------------------------------------------------
<table_name>pg_authid</table_name><column_name>rolname</column_name>
<table_name>pg_authid</table_name><column_name>rolsuper</column_name>
Как видно во втором примере, имя элемента можно опустить, если значение содержимого является ссылкой на столбец, в этом случае имя столбца используется по умолчанию. В противном случае необходимо указать имя.
Имена элементов, которые не являются допустимыми именами XML, экранируются, как показано выше для xmlelement. Точно так же данные содержимого экранируются для создания действительного XML-содержимого, если только оно уже не имеет тип xml.
Обратите внимание, что XML-forest не являются допустимыми XML-документами, если они состоят из более чем одного элемента, поэтому может быть полезно заключать выражения xmlforest в xmlelement.
Функция: xmlpi
Синопсис:
xmlpi(name target [, content])
Выражение xmlpi создает инструкцию обработки XML. Содержимое, если оно присутствует, не должно содержать последовательность символов ?>.
Пример:
SELECT xmlpi(name php, 'echo "hello world";');
xmlpi
-----------------------------
<?php echo "hello world";?>
Функция: xmlroot
Синопсис:
xmlroot(xml, version text | no value [, standalone yes|no|no value])
Выражение xmlroot изменяет свойства корневого узла значения XML. Если указана версия, она заменяет значение в объявлении версии корневого узла; если указан автономный параметр, он заменяет значение в автономном объявлении корневого узла.
SELECT xmlroot(xmlparse(document '<?xml version="1.1"?><content>abc</content>'),
version '1.0', standalone yes);
xmlroot
----------------------------------------
<?xml version="1.0" standalone="yes"?>
<content>abc</content>
Функция: xmlagg
Синопсис:
xmlagg (xml)
Функция xmlagg, в отличие от других функций, описанных здесь, является агрегатной функцией. Он объединяет входные значения с вызовом агрегатной функции, как это делает xmlconcat, за исключением того, что объединение происходит между строками, а не между выражениями в одной строке.
Пример:
CREATE TABLE test (y int, x xml);
INSERT INTO test VALUES (1, '<foo>abc</foo>');
INSERT INTO test VALUES (2, '<bar/>');
SELECT xmlagg(x) FROM test;
xmlagg
----------------------
<foo>abc</foo><bar/>
Чтобы определить порядок конкатенации к агрегированному вызову может быть добавлено предложение ORDER BY. Например:
SELECT xmlagg(x ORDER BY y DESC) FROM test;
xmlagg
----------------------
<bar/><foo>abc</foo>
Следующий нестандартный может быть полезен в определенных случаях:
SELECT xmlagg(x) FROM (SELECT * FROM test ORDER BY y DESC) AS tab;
xmlagg
----------------------
<bar/><foo>abc</foo>
Выражения, описанные в этом разделе, проверяют свойства значений xml.
Выражение: IS DOCUMENT
Синопсис:
xml IS DOCUMENT
Выражение IS DOCUMENT возвращает значение true, если значение XML аргумента является правильным XML-документом, false, если это не так (т. е. это фрагмент содержимого), или null, если аргумент имеет значение null.
Выражение: XMLEXISTS
Синопсис:
XMLEXISTS(text PASSING [BY REF] xml [BY REF])
Функция xmlexists возвращает значение true, если выражение XPath в первом аргументе возвращает какие-либо узлы, и значение false в противном случае. (Если любой из аргументов имеет значение NULL, результат равен NULL.)
Пример:
SELECT xmlexists('//town[text() = ''Toronto'']' PASSING BY REF '<towns><town>Toronto</town><town>Ottawa</town></towns>');
xmlexists
------------
t
(1 row)
Предложения BY REF не действуют в базе данных RT.Warehouse, но разрешены для соответствия SQL и совместимости с другими реализациями. Согласно стандарту SQL, первый BY REF является обязательным, второй — необязательным. Также обратите внимание, что стандарт SQL определяет конструкцию xmlexists для приема выражения XQuery в качестве первого аргумента, но база данных RT.Warehouse в настоящее время поддерживает только XPath, который является подмножеством XQuery.
Выражение: xml_is_well_formed
Синопсис:
xml_is_well_formed(text)
xml_is_well_formed_document(text)
xml_is_well_formed_content(text)
Эти функции проверяют, является ли текстовая строка правильным форматом XML, возвращая логический результат. xml_is_well_formed_document проверяет правильность формата документа, а xml_is_well_formed_content проверяет правильность содержимого. xml_is_well_formed выполняет первое, если для параметра конфигурации xmloption установлено значение DOCUMENT, или второе, если для него установлено значение CONTENT. Это означает, что xml_is_well_formed полезна для проверки того, будет ли успешным простое приведение к типу xml, тогда как две другие функции полезны для проверки того, будут ли успешными соответствующие варианты XMLPARSE.
Пример:
SET xmloption TO DOCUMENT;
SELECT xml_is_well_formed('<>');
xml_is_well_formed
--------------------
f
(1 row)
SELECT xml_is_well_formed('<abc/>');
xml_is_well_formed
--------------------
t
(1 row)
SET xmloption TO CONTENT;
SELECT xml_is_well_formed('abc');
xml_is_well_formed
--------------------
t
(1 row)
SELECT xml_is_well_formed_document('<pg:foo xmlns:pg="http://postgresql.org/stuff">bar</pg:foo>');
xml_is_well_formed_document
-----------------------------
t
(1 row)
SELECT xml_is_well_formed_document('<pg:foo xmlns:pg="http://postgresql.org/stuff">bar</my:foo>');
xml_is_well_formed_document
-----------------------------
f
(1 row)
Последний пример показывает, что проверки включают правильность сопоставления пространств имен.
База данных RT.Warehouse предоставляет типы данных, функции, операторы, типы индексов и конфигурации для запросов к документам на естественном языке.
В этом разделе представлен обзор полнотекстового поиска в базе данных RT.Warehouse, основных выражений текстового поиска, а также настройки и настройки текстового поиска.
Полнотекстовый поиск (или просто «текстовый поиск») позволяет идентифицировать документы на естественном языке, удовлетворяющие запросу, и, при необходимости, ранжировать их по релевантности запросу. Наиболее распространенным типом поиска является поиск всех документов, содержащих заданные термины запроса, и возврат их в порядке их сходства с запросом.
База данных RT.Warehouse предоставляет тип данных tsvector для хранения предварительно обработанных документов и тип данных tsquery для хранения обработанных запросов. Для этих типов данных доступно множество функций и операторов, наиболее важным из которых является оператор сопоставления @@, который мы вводим в разделе «Основы сопоставления текста». Полнотекстовый поиск можно ускорить с помощью индексов (индексы GiST и GIN для текстового поиска).
Понятия запроса и подобия очень гибкие и зависят от конкретного приложения. В простейшем поиске запрос рассматривается как набор слов, а сходство — как частота встречаемости слов запроса в документе.
База данных RT.Warehouse поддерживает стандартные операторы сопоставления текста ~, ~*, LIKE и ILIKE для текстовых типов данных, но у этих операторов отсутствуют многие важные свойства, необходимые для поиска документов:
Полнотекстовое индексирование позволяет предварительно обрабатывать документы и сохранять индекс для последующего быстрого поиска. Предварительная обработка включает в себя:
Словари позволяют точно контролировать, как нормализуются токены. Используя соответствующие словари, вы можете:
Документ — это единица поиска в системе полнотекстового поиска; например, статья в журнале или сообщение электронной почты. Система текстового поиска должна иметь возможность анализировать документы и сохранять ассоциации лексем (ключевых слов) с их родительским документом. Позже эти ассоциации используются для поиска документов, содержащих слова-запросы.
Для поиска в базе данных RT.Warehouse документ обычно представляет собой текстовое поле в строке таблицы базы данных или, возможно, комбинацию (конкатенацию) таких полей, которые могут храниться в нескольких таблицах или получаться динамически. Другими словами, документ может быть составлен из разных частей для индексации и может нигде не храниться как единое целое. Например:
SELECT title || ' ' || author || ' ' || abstract || ' ' || body AS document
FROM messages
WHERE mid = 12;
SELECT m.title || ' ' || m.author || ' ' || m.abstract || ' ' || d.body AS document
FROM messages m, docs d
WHERE mid = did AND mid = 12;
Примечание. На самом деле, в этих примерах запросов следует использовать объединение, чтобы один атрибут NULL не приводил к результату NULL для всего документа. |
Другая возможность состоит в том, чтобы хранить документы как простые текстовые файлы в файловой системе. В этом случае базу данных можно использовать для хранения полнотекстового индекса и выполнения поиска, а для извлечения документа из файловой системы можно использовать некоторый уникальный идентификатор. Однако для извлечения файлов извне базы данных требуются права суперпользователя или поддержка специальных функций, поэтому это обычно менее удобно, чем хранение всех данных внутри базы данных RT.Warehouse. Кроме того, хранение всего в базе данных позволяет легко получить доступ к метаданным документа, чтобы помочь в индексировании и отображении.
Для целей текстового поиска каждый документ должен быть приведен к предварительно обработанному формату tsvector. Поиск и ранжирование полностью выполняются на tsvector представлении документа — исходный текст нужно извлекать только тогда, когда документ был выбран для отображения пользователю. Поэтому мы часто говорим о tsvector как о документе, но, конечно, это всего лишь компактное представление всего документа.
Полнотекстовый поиск в базе данных RT.Warehouse основан на операторе соответствия @@, который возвращает true, если tsvector (документ) соответствует tsquery (запросу). Неважно, какой тип данных записывается первым:
SELECT 'a fat cat sat on a mat and ate a fat rat'::tsvector @@ 'cat & rat'::tsquery;
?column?
----------
t
SELECT 'fat & cow'::tsquery @@ 'a fat cat sat on a mat and ate a fat rat'::tsvector;
?column?
----------
f
Как следует из приведенного выше примера, tsquery — это не просто необработанный текст, как и tsvector. Запрос tsquery содержит поисковые термины, которые должны быть уже нормализованными лексемами и могут объединять несколько терминов с использованием операторов AND, OR, NOT. Существуют функции to_tsquery и plainto_tsquery, которые помогают преобразовывать текст, написанный пользователем, в правильный tsquery, например, нормализуя слова, появляющиеся в тексте. Точно так же to_tsvector используется для разбора и нормализации строки документа. Таким образом, на практике совпадение текстового поиска будет выглядеть примерно так:
SELECT to_tsvector('fat cats ate fat rats') @@ to_tsquery('fat & rat');
?column?
----------
t
Обратите внимание, что это совпадение не будет успешным, если оно будет записано как:
SELECT 'fat cats ate fat rats'::tsvector @@ to_tsquery('fat & rat');
?column?
----------
f
так как здесь не произойдет никакой нормализации слова rats. Элементами tsvector являются лексемы, которые считаются уже нормализованными, поэтому rats не соответствует rat.
Оператор @@ также поддерживает ввод текста, позволяя в простых случаях пропустить явное преобразование текстовой строки в tsvector или tsquery. Доступны следующие варианты:
tsvector @@ tsquery
tsquery @@ tsvector
text @@ tsquery
text @@ text
Текст формы @@ tsquery эквивалентен to_tsvector(x) @@ y. Текст формы @@текст эквивалентен to_tsvector(x) @@plainto_tsquery(y).
Выше приведены все простые примеры текстового поиска. Как упоминалось ранее, функциональность полнотекстового поиска включает в себя возможность делать гораздо больше: пропускать индексирование определенных слов (стоп-слов), обрабатывать синонимы и использовать сложный синтаксический анализ, например, синтаксический анализ, основанный не только на пробелах. Эта функция управляется конфигурациями текстового поиска. База данных RT.Warehouse поставляется с предопределенными конфигурациями для многих языков, и вы можете легко создавать свои собственные конфигурации. (Команда psql \dF показывает все доступные конфигурации.)
Во время установки выбирается соответствующая конфигурация и устанавливается значение default_text_search_config соответствующим образом в postgresql.conf. Если вы используете одинаковую конфигурацию текстового поиска для всего кластера, вы можете использовать значение в postgresql.conf. Чтобы использовать разные конфигурации в кластере, но одну и ту же конфигурацию в любой базе данных, используйте ALTER DATABASE ... SET. В противном случае вы можете установить default_text_search_config в каждом сеансе.
Каждая функция текстового поиска, зависящая от конфигурации, имеет необязательный аргумент regconfig, так что используемая конфигурация может быть указана явно. default_text_search_config используется только тогда, когда этот аргумент опущен.
Чтобы упростить создание настраиваемых конфигураций текстового поиска, конфигурация создается из более простых объектов базы данных. Средство текстового поиска базы данных RT.Warehouse предоставляет четыре типа объектов базы данных, связанных с конфигурацией:
Парсеры и шаблоны текстового поиска построены из низкоуровневых функций C; поэтому для разработки новых требуются навыки программирования на C и права суперпользователя для их установки в базу данных. Примеры дополнительных парсеров и шаблонов есть в области contrib/ дистрибутива базы данных RT.Warehouse. Поскольку словари и конфигурации просто параметризуют и соединяют вместе некоторые базовые парсеры и шаблоны, для создания нового словаря или конфигурации не требуется специальных прав.
В этом разделе показано, как использовать операторы текстового поиска для поиска в таблицах базы данных и как создавать индексы для ускорения текстового поиска.
Примеры в предыдущем разделе иллюстрировали полнотекстовое сопоставление с использованием простых строк-констант. В этом разделе показано, как искать данные таблицы, при необходимости используя индексы.
Полнотекстовый поиск возможен без индекса. Простой запрос для вывода заголовка каждой строки, содержащей слово friend в поле body, выглядит следующим образом:
SELECT title
FROM pgweb
WHERE to_tsvector('english', body) @@ to_tsquery('english', 'friend');
Это также позволит найти родственные слова, такие как friends и friendly, поскольку все они сводятся к одной нормализованной лексеме.
В приведенном выше запросе указано, что для разбора и нормализации строк должна использоваться английскую конфигурацию. В качестве альтернативы мы можем опустить параметры конфигурации:
SELECT title
FROM pgweb
WHERE to_tsvector(body) @@ to_tsquery('friend');
Этот запрос будет использовать конфигурацию, заданную параметром default_text_search_config.
Более сложный пример - выбрать десять самых последних документов, содержащих create и table в заголовке или теле:
SELECT title
FROM pgweb
WHERE to_tsvector(title || ' ' || body) @@ to_tsquery('create & table')
ORDER BY last_mod_date DESC
LIMIT 10;
Для ясности были опущены вызовы функции coalesce, которые потребовались бы для поиска строк, содержащих NULL в одном из двух полей.
Хотя эти запросы будут работать и без индекса, большинство приложений сочтут такой подход слишком медленным, за исключением, возможно, случайных специальных поисков. Практическое использование текстового поиска обычно требует создания индекса.
Мы можем создать индекс GIN (индексы GiST и GIN для текстового поиска), чтобы ускорить текстовый поиск:
Обратите внимание, что используется версия to_tsvector с двумя аргументами. В индексах выражений можно использовать только функции текстового поиска, которые задают имя конфигурации. Это связано с тем, что содержимое индекса не должно зависеть от default_text_search_config. Если бы они были затронуты, содержимое индекса могло бы оказаться несогласованным, потому что разные записи могли бы содержать tsvector, созданные с разными конфигурациями текстового поиска, и было бы невозможно угадать, какой из них какой. Правильно сбросить и восстановить такой индекс было бы невозможно.
Поскольку в приведенном выше индексе использовалась версия to_tsvector с двумя аргументами, этот индекс будет использоваться только в ссылке запроса, которая использует версию to_tsvector с двумя аргументами с тем же именем конфигурации. То есть, WHERE to_tsvector('english', body) @@ 'a & b' может использовать индекс, но WHERE to_tsvector(body) @@ 'a & b' не может. Это гарантирует, что индекс будет использоваться только с той же конфигурацией, которая использовалась для создания записей индекса.
Можно настроить более сложные индексы выражений, в которых имя конфигурации указывается другим столбцом, например:
CREATE INDEX pgweb_idx ON pgweb USING gin(to_tsvector(config_name, body));
где config_name — столбец в таблице pgweb. Это позволяет использовать смешанные конфигурации в одном и том же индексе, записывая, какая конфигурация использовалась для каждой записи индекса. Это было бы полезно, например, если коллекция документов содержала документы на разных языках. Опять же, запросы, предназначенные для использования индекса, должны быть сформулированы так, чтобы соответствовать, например, WHERE to_tsvector(config_name, body) @@ 'a & b'.
Индексы могут даже объединять столбцы:
CREATE INDEX pgweb_idx ON pgweb USING gin(to_tsvector('english', title || ' ' || body));
Другой подход заключается в создании отдельного столбца tsvector для хранения выходных данных to_tsvector. Этот пример представляет собой конкатенацию заголовка и тела с использованием объединения, чтобы гарантировать, что одно поле все еще будет проиндексировано, когда другое равно NULL:
ALTER TABLE pgweb ADD COLUMN textsearchable_index_col tsvector;
UPDATE pgweb SET textsearchable_index_col =
to_tsvector('english', coalesce(title,'') || ' ' || coalesce(body,''));
Затем мы создаем индекс GIN для ускорения поиска:
CREATE INDEX textsearch_idx ON pgweb USING gin(textsearchable_index_col);
Теперь мы готовы выполнить быстрый полнотекстовый поиск:
SELECT title FROM pgweb WHERE textsearchable_index_col @@ to_tsquery('create & table')
ORDER BY last_mod_date DESC LIMIT 10;
Одно из преимуществ подхода с отдельными столбцами перед индексом выражений заключается в том, что для использования индекса не нужно явно указывать конфигурацию текстового поиска в запросах. Как показано в примере выше, запрос может зависеть от default_text_search_config. Еще одним преимуществом является то, что поиск станет быстрее, поскольку не нужно будет повторять вызовы to_tsvector для проверки совпадений с индексом (это более важно при использовании индекса GiST, чем индекса GIN). Однако подход с использованием индекса выражений проще в настройке и требует меньше места на диске, поскольку представление tsvector не хранится в явном виде.
В этом разделе показано, как создавать векторы поиска и запроса, как ранжировать результаты поиска и как выделять условия поиска в результатах запросов текстового поиска.
Для реализации полнотекстового поиска должна быть функция создания tsvector из документа и tsquery из пользовательского запроса. Кроме того, нам нужно возвращать результаты в удобном порядке, поэтому нам нужна функция, которая сравнивает документы с точки зрения их релевантности запросу. Также важно уметь красиво отображать результаты. База данных RT.Warehouse обеспечивает поддержку всех этих функций.
База данных RT.Warehouse предоставляет функцию to_tsvector для преобразования документа в тип данных tsvector.
to_tsvector([config regconfig, ] document text) returns tsvector
to_tsvector разбирает текстовый документ на лексемы, сводит лексемы к лексемам и возвращает tsvector, в котором перечислены лексемы вместе с их позициями в документе. Документ обрабатывается в соответствии с заданной конфигурацией или конфигурацией текстового поиска по умолчанию. Вот простой пример:
SELECT to_tsvector('english', 'a fat cat sat on a mat - it ate a fat rats');
to_tsvector
-----------------------------------------------------
'ate':9 'cat':3 'fat':2,11 'mat':7 'rat':12 'sat':4
В примере выше видно, что полученный tsvector не содержит слов a, on или it, слово rats стало rat, а знак препинания — проигнорирован.
Функция to_tsvector внутренне вызывает парсер, который разбивает текст документа на токены и назначает тип каждому токену. Для каждого токена сверяется со списком словарей (словарей текстового поиска), где список может варьироваться в зависимости от типа токена. Первый словарь, который распознает маркер, выдает одну или несколько нормализованных лексем для представления маркера. Например, rats стали rats, потому что один из словарей признал, что слово rats является множественной формой слова rat. Некоторые слова распознаются как стоп-слова, что приводит к их игнорированию, поскольку они встречаются слишком часто, чтобы их можно было использовать при поиске. В нашем примере это a, on и it. Если ни один словарь в списке не распознает маркер, он также игнорируется. В этом примере это произошло со знаком препинания, потому что на самом деле нет словарей, назначенных для его типа токена (символы пробела), а это означает, что токены пробела никогда не будут индексироваться. Выбор парсера, словарей и типов токенов для индексации определяется выбранной конфигурацией текстового поиска (пример конфигурации текстового поиска). В одной и той же базе данных может быть много разных конфигураций, а предопределенные конфигурации доступны для разных языков. В нашем примере мы использовали конфигурацию по умолчанию для английского языка.
Функцию setweight можно использовать для маркировки записей tsvector с заданным весом, где вес — это одна из букв A, B, C или D. Обычно это используется для маркировки записей, поступающих из разных частей документа, например, заголовок против тела. Позже эту информацию можно использовать для ранжирования результатов поиска.
Поскольку to_tsvector(NULL) вернет NULL, рекомендуется использовать объединение всякий раз, когда поле может быть нулевым. Вот рекомендуемый метод создания tsvector из структурированного документа:
UPDATE tt SET ti = setweight(to_tsvector(coalesce(title,'')), 'A')
|| setweight(to_tsvector(coalesce(keyword,'')), 'B')
|| setweight(to_tsvector(coalesce(abstract,'')), 'C')
|| setweight(to_tsvector(coalesce(body,'')), 'D');
Здесь использовали setweight для обозначения источника каждой лексемы в готовом tsvector, а затем объединили помеченные значения tsvector с помощью оператора конкатенации tsvector ||.
База данных RT.Warehouse предоставляет функции to_tsquery и plainto_tsquery для преобразования запроса в тип данных tsquery. to_tsquery предлагает доступ к большему количеству функций, чем plainto_tsquery, но менее снисходителен к вводу данных.
to_tsquery([config regconfig, ] querytext text) returns tsquery
to_tsquery создает значение tsquery из querytext, которое должно состоять из отдельных токенов, разделенных логическими операторами & (AND, И), | (OR, ИЛИ) и !(NOT, НЕ). Эти операторы могут быть сгруппированы с помощью круглых скобок. Другими словами, ввод to_tsquery уже должен соответствовать общим правилам ввода tsquery. Разница в том, что в то время как базовый ввод tsquery принимает токены по номинальной стоимости, to_tsquery нормализует каждый токен в лексему, используя указанную конфигурацию или конфигурацию по умолчанию и отбрасывает любые токены, которые являются стоп-словами в соответствии с конфигурацией. Например:
SELECT to_tsquery('english', 'The & Fat & Rats');
to_tsquery
---------------
'fat' & 'rat'
Как и в базовом вводе tsquery, к каждой лексеме можно прикрепить вес(а), чтобы ограничить ее соответствие только лексемам tsvector с этим весом(ами). Например:
SELECT to_tsquery('english', 'Fat | Rats:AB');
to_tsquery
------------------
'fat' | 'rat':AB
Кроме того, * может быть присоединен к лексеме, чтобы указать соответствие префикса:
SELECT to_tsquery('supern:*A & star:A*B');
to_tsquery
--------------------------
'supern':*A & 'star':*AB
Такая лексема будет соответствовать любому слову в tsvector, которое начинается с данной строки.
to_tsquery также может принимать фразы в одинарных кавычках. Это в первую очередь полезно, когда конфигурация включает словарь тезауруса, который может срабатывать по таким фразам. В приведенном ниже примере тезаурус содержит правило supernovae stars: sn:
SELECT to_tsquery('''supernovae stars'' & !crab');
to_tsquery
---------------
'sn' & !'crab'
Без кавычек to_tsquery будет генерировать синтаксическую ошибку для токенов, которые не разделены оператором И или ИЛИ.
plainto_tsquery([ config regconfig, ] querytext ext) returns tsquery
plainto_tsquery преобразует неформатированный текст запроса в tsquery. Текст анализируется и нормализуется так же, как и для to_tsvector, затем между оставшимися словами вставляется логический оператор & (И).
Пример:
SELECT plainto_tsquery('english', 'The Fat Rats');
plainto_tsquery
-----------------
'fat' & 'rat'
Обратите внимание, что plainto_tsquery не может распознавать логические операторы, метки весов или метки совпадения префиксов во входных данных:
SELECT plainto_tsquery('english', 'The Fat & Rats:C');
plainto_tsquery
---------------------
'fat' & 'rat' & 'c'
Здесь все входные знаки препинания были отброшены как символы пробела.
Ранжирование пытается измерить, насколько документы релевантны конкретному запросу, поэтому при наличии большого количества совпадений наиболее релевантные из них могут быть показаны первыми. База данных базы данных предоставляет две предопределенные функции ранжирования, которые учитывают лексическую информацию, близость и структурную информацию; то есть они учитывают, как часто термины запроса появляются в документе, насколько близко друг к другу расположены термины в документе и насколько важна часть документа, в которой они встречаются. Однако понятие релевантности расплывчато и очень специфично для приложения. Для разных приложений может потребоваться дополнительная информация для ранжирования, например, время модификации документа. Встроенные функции ранжирования являются только примерами. Вы можете написать свои собственные функции ранжирования и/или комбинировать их результаты с дополнительными факторами в соответствии с вашими конкретными потребностями.
В настоящее время доступны две функции ранжирования:
ts_rank([ weights float4[], ] vector tsvector, query tsquery [, normalization integer ]) returns float4
1. Ранжирует векторы на основе частоты их совпадающих лексем.
ts_rank_cd([ weights float4[], ] vector tsvector, query tsquery [, normalization integer ]) returns float4
2.1 Эта функция вычисляет рейтинг плотности покрытия для заданного вектора документов и запроса. Плотность покрытия аналогична к ранжированию ts_rank, за исключением того, что принимается во внимание близость совпадающих лексем друг к другу.
2.2 Эта функция требует информацию о позиции лексемы для выполнения ее вычисления. Таким образом, он игнорирует любые «вырезанные» лексемы в tsvector. Если во входных данных нет неразрезанных лексем, результат будет нулевым.
Для обеих этих функций необязательный аргумент веса предлагает возможность более или менее взвешивать экземпляры слов в зависимости от того, как они помечены. Весовые массивы указывают, насколько сильно взвешивать каждую категорию слов, в следующем порядке:
{D-weight, C-weight, B-weight, A-weight}
Если веса не указаны, используются следующие значения по умолчанию:
{0.1, 0.2, 0.4, 1.0}
Обычно веса используются для обозначения слов из специальных областей документа, таких как заголовок или начальная аннотация, поэтому к ним можно относиться с большей или меньшей важностью, чем к словам в теле документа.
Поскольку более длинный документ имеет больше шансов содержать термин запроса, разумно принять во внимание размер документа, например, документ из ста слов с пятью экземплярами искомого слова, вероятно, более релевантен, чем документ из тысячи слов с пятью экземплярами. Обе функции ранжирования используют опцию целочисленной нормализации, которая указывает, должна ли и как длина документа влиять на его ранг. Опция integer управляет несколькими вариантами поведения, поэтому она является битовой маской: вы можете указать один или несколько вариантов поведения с помощью | (например, 2|4).
Если указано более одного флагового бита, преобразования применяются в указанном порядке.
Важно отметить, что функции ранжирования не используют какую-либо глобальную информацию, поэтому невозможно произвести справедливую нормализацию до 1% или 100%, как это иногда требуется. Параметр нормализации 32 (ранг/(ранг+1)) может быть применен для масштабирования всех рангов в диапазоне от нуля до единицы, но, конечно, это просто косметическое изменение, это не повлияет на порядок результатов поиска.
Вот пример, в котором выбираются только десять совпадений с наивысшим рейтингом:
SELECT title, ts_rank_cd(textsearch, query) AS rank
FROM apod, to_tsquery('neutrino|(dark & matter)') query
WHERE query @@ textsearch
ORDER BY rank DESC
LIMIT 10;
title | rank
-----------------------------------------------+----------
Neutrinos in the Sun | 3.1
The Sudbury Neutrino Detector | 2.4
A MACHO View of Galactic Dark Matter | 2.01317
Hot Gas and Dark Matter | 1.91171
The Virgo Cluster: Hot Plasma and Dark Matter | 1.90953
Rafting for Solar Neutrinos | 1.9
NGC 4650A: Strange Galaxy and Dark Matter | 1.85774
Hot Gas and Dark Matter | 1.6123
Ice Fishing for Cosmic Neutrinos | 1.6
Weak Lensing Distorts the Universe | 0.818218
Это тот же пример с использованием нормализованного ранжирования:
SELECT title, ts_rank_cd(textsearch, query, 32 /* rank/(rank+1) */ ) AS rank
FROM apod, to_tsquery('neutrino|(dark & matter)') query
WHERE query @@ textsearch
ORDER BY rank DESC
LIMIT 10;
title | rank
-----------------------------------------------+-------------------
Neutrinos in the Sun | 0.756097569485493
The Sudbury Neutrino Detector | 0.705882361190954
A MACHO View of Galactic Dark Matter | 0.668123210574724
Hot Gas and Dark Matter | 0.65655958650282
The Virgo Cluster: Hot Plasma and Dark Matter | 0.656301290640973
Rafting for Solar Neutrinos | 0.655172410958162
NGC 4650A: Strange Galaxy and Dark Matter | 0.650072921219637
Hot Gas and Dark Matter | 0.617195790024749
Ice Fishing for Cosmic Neutrinos | 0.615384618911517
Weak Lensing Distorts the Universe | 0.450010798361481
Ранжирование может быть дорогостоящим, поскольку оно требует обращения к tsvector каждого соответствующего документа, который может быть привязан к вводу-выводу и, следовательно, замедляться. К сожалению, этого почти невозможно избежать, так как практические запросы часто приводят к большому количеству совпадений.
Для представления результатов поиска идеально показывать часть каждого документа и то, как он связан с запросом. Обычно поисковые системы показывают фрагменты документа с отмеченными условиями поиска. База данных RT.Warehouse предоставляет функцию ts_headline, реализующую эту функциональность.
ts_headline([config regconfig, ] document text, query tsquery [, options text ]) returns text
ts_headline принимает документ вместе с запросом и возвращает выдержку из документа, в которой выделены термины из запроса. Конфигурация, которая будет использоваться для разбора документа, может быть указана в config, если config опущен, используется конфигурация default_text_search_config.
Если указана строка параметров, она должна состоять из списка, разделенного запятыми, состоящего из одной или нескольких пар параметр=значение (option=value). Доступные варианты:
Любые неуказанные параметры получают следующие значения по умолчанию:
StartSel=<b>, StopSel=</b>,
MaxWords=35, MinWords=15, ShortWord=3, HighlightAll=FALSE,
MaxFragments=0, FragmentDelimiter=" ... "
Например:
SELECT ts_headline('english',
'The most common type of search
is to find all documents containing given query terms
and return them in order of their similarity to the
query.',
to_tsquery('query & similarity'));
ts_headline
------------------------------------------------------------
containing given <b>query</b> terms
and return them in order of their <b>similarity</b> to the
<b>query</b>.
SELECT ts_headline('english',
'The most common type of search
is to find all documents containing given query terms
and return them in order of their similarity to the
query.',
to_tsquery('query & similarity'),
'StartSel = <, StopSel = >');
ts_headline
-------------------------------------------------------
containing given <query> terms
and return them in order of their <similarity> to the
<query>.
ts_headline использует исходный документ, а не сводку tsvector, поэтому он может работать медленно и его следует использовать с осторожностью. Типичной ошибкой является вызов ts_headline для каждого подходящего документа, когда нужно отобразить только десять документов. Подзапросы SQL могут помочь, например:
SELECT id, ts_headline(body, q), rank
FROM (SELECT id, body, q, ts_rank_cd(ti, q) AS rank
FROM apod, to_tsquery('stars') q
WHERE ti @@ q
ORDER BY rank DESC
LIMIT 10) AS foo;
База данных RT.Warehouse имеет дополнительные функции и операторы, которые можно использовать для управления векторами поиска и запросов, а также для перезаписи поисковых запросов.
Разбор документов показал, как необработанные текстовые документы можно преобразовать в значения tsvector. База данных RT.Warehouse также предоставляет функции и операторы, которые можно использовать для управления документами, которые уже находятся в форме tsvector.
tsvector || tsvector
Оператор конкатенации tsvector возвращает вектор, который объединяет лексемы и позиционную информацию двух векторов, заданных в качестве аргументов. Позиции и метки веса сохраняются во время конкатенации. Позиции, появляющиеся в правом векторе, смещаются на наибольшую позицию, указанную в левом векторе, так что результат почти эквивалентен результату выполнения to_tsvector при объединении двух исходных строк документа (эквивалентность не является точной, потому что любые стоп-слова, удаленные из конца левого аргумента, не повлияют на результат, тогда как они повлияли бы на позиции лексем в правом аргументе, если бы использовалась текстовая конкатенация).
Одним из преимуществ использования конкатенации в векторной форме по сравнению с конкатенацией текста перед применением to_tsvector является то, что вы можете использовать разные конфигурации для анализа разных разделов документа. Кроме того, поскольку функция setweight помечает все лексемы данного вектора одинаково, необходимо проанализировать текст и выполнить setweight перед конкатенацией, если вы хотите пометить разные части документа с разным весом.
setweight(vector tsvector, weight "char") returns tsvector
setweight возвращает копию входного вектора, в котором каждая позиция была помечена заданным весом, A, B, C или D (D является значением по умолчанию для новых векторов и поэтому не отображается на выходе). Эти метки сохраняются при объединении векторов, что позволяет по-разному взвешивать слова из разных частей документа с помощью функций ранжирования.
Обратите внимание, что весовые метки применяются к позициям, а не к лексемам. Если входной вектор лишен позиций, то setweight ничего не делает.
length(vector tsvector) returns integer
Возвращает количество лексем, хранящихся в векторе.
strip(vector tsvector) returns tsvector
Возвращает вектор, в котором перечислены те же лексемы, что и в заданном векторе, но отсутствует какая-либо информация о позиции или весе. Хотя возвращенный вектор гораздо менее полезен для ранжирования релевантности, чем вектор без разделения, он обычно будет намного меньше.
Анализ запросов показал, как необработанные текстовые запросы могут быть преобразованы в значения tsquery. База данных RT.Warehouse также предоставляет функции и операторы, которые можно использовать для управления запросами, которые уже находятся в форме tsquery.
tsquery && tsquery
Возвращает И(AND)-комбинацию двух заданных запросов.
tsquery || tsquery
Возвращает ИЛИ(OR)-комбинацию двух заданных запросов.
!! tsquery
Возвращает отрицание (NOT) данного запроса.
numnode(query tsquery) returns integer
Возвращает количество узлов (лексем плюс операторы) в tsquery. Эта функция полезна для определения того, является ли запрос осмысленным (возвращает > 0) или содержит только стоп-слова (возвращает 0). Примеры:
SELECT numnode(plainto_tsquery('the any'));
NOTICE: query contains only stopword(s) or doesn't contain lexeme(s), ignored
numnode
---------
0
SELECT numnode('foo & bar'::tsquery);
numnode
---------
3
querytree(query tsquery) returns text
Возвращает часть tsquery, которую можно использовать для поиска по индексу. Эта функция полезна для обнаружения неиндексируемых запросов, например, тех, которые содержат только стоп-слова или только отрицательные термины. Например:
SELECT querytree(to_tsquery('!defined'));
querytree
-----------
Семейство функций ts_rewrite выполняет поиск в заданном tsquery вхождений целевого подзапроса и заменяет каждое вхождение замещающим подзапросом. По сути, эта операция представляет собой специфичную для tsquery версию замены подстроки. Комбинацию цели и замены можно рассматривать как правило перезаписи запроса. Набор таких правил перезаписи может быть мощным средством поиска. Например, вы можете расширить поиск, используя синонимы (например, new york, big apple, nyc, gotham) или сузить поиск, чтобы направить пользователя на какую-то актуальную тему. Функциональность этой функции и словарей-тезаурусов (Thesaurus Dictionary) частично совпадает. Однако вы можете изменять набор правил перезаписи на лету без переиндексации, в то время как для эффективного обновления тезауруса требуется переиндексация.
ts_rewrite(query tsquery, target tsquery, substitute tsquery) returns tsquery
Эта форма ts_rewrite просто применяет единственное правило перезаписи: цель заменяется заменой везде, где она встречается в запросе. Например:
SELECT ts_rewrite('a & b'::tsquery, 'a'::tsquery, 'c'::tsquery);
ts_rewrite
------------
'b' & 'c'
ts_rewrite(query tsquery, select text) returns tsquery
Эта форма ts_rewrite принимает начальный запрос и команду выбора SQL, которая задается в виде текстовой строки. Выбор должен давать два столбца типа tsquery. Для каждой строки результата выбора вхождения значения первого столбца (целевого) заменяются значением второго столбца (заместителем) в текущем значении запроса. Например:
CREATE TABLE aliases (id int, t tsquery, s tsquery);
INSERT INTO aliases VALUES('a', 'c');
SELECT ts_rewrite('a & b'::tsquery, 'SELECT t,s FROM aliases');
ts_rewrite
------------
'b' & 'c'
Обратите внимание, что когда таким образом применяются несколько правил перезаписи, порядок применения может быть важен; поэтому на практике вы захотите, чтобы исходный запрос ORDER BY имел некоторый ключ упорядочения.
Рассмотрим пример. Мы расширим запрос supernovae с помощью правил переписывания, управляемых таблицами:
CREATE TABLE aliases (id int, t tsquery primary key, s tsquery);
INSERT INTO aliases VALUES(1, to_tsquery('supernovae'), to_tsquery('supernovae|sn'));
SELECT ts_rewrite(to_tsquery('supernovae & crab'), 'SELECT t, s FROM aliases');
ts_rewrite
---------------------------------
'crab' & ( 'supernova' | 'sn' )
Мы можем изменить правила перезаписи, просто обновив таблицу:
UPDATE aliases
SET s = to_tsquery('supernovae|sn & !nebulae')
WHERE t = to_tsquery('supernovae');
SELECT ts_rewrite(to_tsquery('supernovae & crab'), 'SELECT t, s FROM aliases');
ts_rewrite
---------------------------------------------
'crab' & ( 'supernova' | 'sn' & !'nebula' )
Перезапись может быть медленной, когда имеется много правил перезаписи, поскольку она проверяет каждое правило на предмет возможного совпадения. Чтобы отфильтровать очевидные правила, не являющиеся кандидатами, мы можем использовать операторы включения для типа tsquery. В приведенном ниже примере выбираем только те правила, которые могут соответствовать исходному запросу:
SELECT ts_rewrite('a & b'::tsquery,
'SELECT t,s FROM aliases WHERE ''a & b''::tsquery @> t');
ts_rewrite
------------
'b' & 'c'
Функция ts_stat полезна для проверки конфигурации и поиска кандидатов на стоп-слова.
ts_stat(sqlquery text, [ weights text, ]
OUT word text, OUT ndoc integer,
OUT nentry integer) returns setof record
sqlquery — это текстовое значение, содержащее SQL-запрос, который должен возвращать один столбец tsvector. ts_stat выполняет запрос и возвращает статистику о каждой отдельной лексеме (слове), содержащейся в данных tsvector. Возвращаемые столбцы:
Если указаны веса, учитываются только вхождения, имеющие один из этих весов.
Например, чтобы найти десять наиболее часто встречающихся слов в коллекции документов:
SELECT * FROM ts_stat('SELECT vector FROM apod')
ORDER BY nentry DESC, ndoc DESC, word
LIMIT 10;
То же самое, но с учетом только вхождений слов с весом A или B:
SELECT * FROM ts_stat('SELECT vector FROM apod', 'ab')
ORDER BY nentry DESC, ndoc DESC, word
LIMIT 10;
В этом разделе описываются типы токенов, которые парсер текстового поиска базы данных RT.Warehouse создает из необработанного текста.
Парсеры для поиска текста отвечают за разбиение необработанного текста документа на лексемы и определение типа каждой лексемы, причем набор возможных типов определяется самим парсером. Обратите внимание, что парсер вообще не изменяет текст - он просто определяет правдоподобные границы слов. Из-за этой ограниченной области применения, потребность в пользовательских парсерах, специфичных для конкретного приложения, меньше, чем в пользовательских словарях. В настоящее время RT.Warehouse предоставляет только один встроенный парсер, который оказался полезным для широкого круга приложений.
Встроенный анализатор (парсер) называется pg_catalog.default. Он распознает 23 типа токенов, показанных в Таблице 129.
Таблица 129. Функции обработки JSON
Псевдоним | Описание | Пример |
---|---|---|
asciiword | Слово, все буквы ASCII | elephant |
word | Слово, все буквы | mañana |
numword | Слово, буквы и цифры | beta1 |
asciihword | Слово через дефис, все ASCII | up-to-date |
hword | Слово через дефис, все буквы | lógico-matemática |
numhword | Слово, написанное через дефис, буквы и цифры | postgresql-beta1 |
hword_asciipart | Часть слова через дефис, все ASCII | postgresql in the context postgresql-beta1 |
hword_part | Часть слова через дефис, все буквы | lógico or matemática in the context lógico-matemática |
hword_numpart | Часть слова через дефис, буквы и цифры | beta1 in the context postgresql-beta1 |
Адрес электронной почты | foo@example.com | |
protocol | Глава протокола | http:// |
url | URL | example.com/stuff/index.html |
host | Хост | example.com |
url_path | URL-адрес | /stuff/index.html, in the context of a URL |
file | Имя файла или путь | /usr/local/foo.txt, if not within a URL |
sfloat | Экспоненциальная запись | -1.234e56 |
float | Десятичная запись | -1.234 |
int | Целое число | -1234 |
uint | Беззнаковое целое | 1234 |
version | Номер версии | 8.3.0 |
tag | XML-тег | <a href="dictionaries.html"> |
entity | XML-сущность | & |
blank | Пробелы | любые пробелы или знаки препинания, не распознанные другим способом |
Примечание. Понятие парсера "буквы" определяется настройкой локали базы данных, в частности, lc_ctype. Слова, содержащие только основные буквы ASCII, указываются как отдельный тип лексемы, поскольку иногда полезно различать их. В большинстве европейских языков типы токенов word и asciiword должны рассматриваться одинаково. электронная почта не поддерживает все допустимые символы электронной почты, как определено в RFC 5322. В частности, единственными небуквенно-цифровыми символами, поддерживаемыми для имен пользователей электронной почты являются точка, тире и подчеркивание. |
Парсер может создавать перекрывающиеся токены из одного и того же фрагмента текста. Например, слово, написанное через дефис, будет отображаться как слово целиком, так и каждый компонент:
SELECT alias, description, token FROM ts_debug('foo-bar-beta1');
alias | description | token
-----------------+------------------------------------------+---------------
numhword | Hyphenated word, letters and digits | foo-bar-beta1
hword_asciipart | Hyphenated word part, all ASCII | foo
blank | Space symbols | -
hword_asciipart | Hyphenated word part, all ASCII | bar
blank | Space symbols | -
hword_numpart | Hyphenated word part, letters and digits | beta1
Такое поведение является желательным, поскольку оно позволяет выполнять поиск как для всего составного слова, так и для компонентов. Вот еще один поучительный пример:
SELECT alias, description, token FROM ts_debug('http://example.com/stuff/index.html');
alias | description | token
----------+---------------+------------------------------
protocol | Protocol head | http://
url | URL | example.com/stuff/index.html
host | Host | example.com
url_path | URL path | /stuff/index.html
Токены, созданные парсером полнотекстового поиска базы данных RT.Warehouse, проходят через цепочку словарей для создания нормализованного термина или «лексемы». Доступны различные виды словарей для фильтрации и преобразования токенов различными способами и для разных языков.
Словари используются для исключения слов, которые не следует учитывать при поиске (стоп-слова), и для нормализации слов, чтобы совпадали разные производные формы одного и того же слова. Удачно нормализованное слово называется лексемой. Помимо улучшения качества поиска, нормализация и удаление стоп-слов уменьшает размер векторного представления документа, тем самым повышая производительность. Нормализация не всегда имеет лингвистическое значение и обычно зависит от семантики приложения.
Некоторые примеры нормализации:
Словарь — это программа, которая принимает на вход токен и возвращает:
База данных RT.Warehouse предоставляет предопределенные словари для многих языков. Есть также несколько предопределенных шаблонов, которые можно использовать для создания новых словарей с пользовательскими параметрами. Каждый предопределенный шаблон словаря описан ниже. Если ни один из существующих шаблонов не подходит, можно создать новые.
Конфигурация текстового поиска связывает парсер вместе с набором словарей для обработки выходных токенов синтаксического анализатора. Для каждого типа токена, который может возвращать синтаксический анализатор, в конфигурации задается отдельный список словарей. Когда синтаксический анализатор находит токен этого типа, по очереди обращается к каждому словарю в списке, пока какой-нибудь словарь не распознает его как известное слово. Если оно идентифицировано как стоп-слово или если ни один словарь не распознает токен, он будет отброшен и не будет индексироваться или искаться. Обычно первый словарь, возвращающий ненулевой вывод, определяет результат, а все остальные словари не консультируются; но фильтрующий словарь может заменить данное слово модифицированным словом, которое затем передается в последующие словари.
Общее правило для настройки списка словарей состоит в том, чтобы сначала разместить самый узкий, самый конкретный словарь, затем более общие словари, заканчивая очень общим словарем, таким как стеммер Snowball или простой, который распознает все. Например, для специального астрономического поиска (конфигурация astro_en) можно привязать тип токена asciiword (слово ASCII) к словарю синонимов астрономических терминов, общему английскому словарю и стеммеру Snowball English:
ALTER TEXT SEARCH CONFIGURATION astro_en
ADD MAPPING FOR asciiword WITH astrosyn, english_ispell, english_stem;
Словарь-фильтр можно разместить в любом месте списка, кроме конца, где он бесполезен. Фильтрующие словари полезны для частичной нормализации слов, чтобы упростить задачу последующих словарей. Например, фильтрующий словарь можно использовать для удаления акцентов из акцентированных букв, как это делает модуль unaccent.
Стоп-слова — это слова, которые очень распространены, встречаются почти в каждом документе и не имеют дискриминационного значения. Поэтому их можно игнорировать в контексте полнотекстового поиска. Например, каждый английский текст содержит такие слова, как a и the, поэтому хранить их в указателе бесполезно. Однако стоп-слова влияют на позиции в tsvector, что, в свою очередь, влияет на ранжирование:
SELECT to_tsvector('english','in the list of stop words');
to_tsvector
----------------------------
'list':3 'stop':5 'word':6
Пропущенные позиции 1, 2, 4 вызваны стоп-словами. Ранги, рассчитанные для документов со стоп-словами и без них, сильно различаются:
SELECT ts_rank_cd (to_tsvector('english','in the list of stop words'), to_tsquery('list & stop'));
ts_rank_cd
------------
0.05
SELECT ts_rank_cd (to_tsvector('english','list stop words'), to_tsquery('list & stop'));
ts_rank_cd
------------
0.1
То, как он обрабатывает стоп-слова, зависит от конкретного словаря. Например, словари ispell сначала нормализуют слова, а затем просматривают список стоп-слов, а стеммеры Snowball сначала проверяют список стоп-слов. Причиной различного поведения является попытка уменьшить шум.
Простой шаблон словаря работает путем преобразования входного токена в нижний регистр и проверки его по файлу стоп-слов. Если он найден в файле, возвращается пустой массив, в результате чего токен отбрасывается. Если нет, то в качестве нормализованной лексемы возвращается строчная форма слова. В качестве альтернативы словарь можно настроить так, чтобы он сообщал о непрерывных словах как о нераспознанных, что позволяет передавать их в следующий словарь в списке.
Вот пример определения словаря с использованием простого шаблона:
CREATE TEXT SEARCH DICTIONARY public.simple_dict (
TEMPLATE = pg_catalog.simple,
STOPWORDS = english
);
Здесь english — это базовое имя файла стоп-слов. Полное имя файла будет $SHAREDIR/tsearch_data/english.stop, где $SHAREDIR означает каталог общих данных установки базы данных RT.Warehouse, часто /usr/local/greenplum-db-<версия>/share/postgresql (используйте pg_config -- sharedir, чтобы определить это, если вы не уверены). Формат файла — это просто список слов, по одному в строке. Пустые строки и конечные пробелы игнорируются, а верхний регистр преобразуется в нижний, но никакая другая обработка содержимого файла не выполняется.
Теперь можно протестировать словарь:
SELECT ts_lexize('public.simple_dict','YeS');
ts_lexize
-----------
{yes}
SELECT ts_lexize('public.simple_dict','The');
ts_lexize
-----------
{}
Также можно выбрать возврат NULL вместо слова в нижнем регистре, если оно не найдено в файле стоп-слов. Это поведение выбирается путем установки для параметра Accept словаря значения false. Продолжая пример:
ALTER TEXT SEARCH DICTIONARY public.simple_dict ( Accept = false );
SELECT ts_lexize('public.simple_dict','YeS');
ts_lexize
-----------
{yes}
SELECT ts_lexize('public.simple_dict','The');
ts_lexize
-----------
{}
При настройке по умолчанию Accept = true полезно размещать простой словарь только в конце списка словарей, поскольку он никогда не передаст какой-либо токен в следующий словарь. И наоборот, Accept = false полезен только тогда, когда есть хотя бы один следующий словарь.
Внимание. Большинство типов словарей полагаются на файлы конфигурации, такие как файлы стоп-слов. Эти файлы должны храниться в кодировке UTF-8. Они будут переведены в фактическую кодировку базы данных, если она отличается, когда они считываются на сервер. |
Внимание. Обычно сеанс базы данных считывает файл конфигурации словаря только один раз, когда он впервые используется в рамках сеанса. Если вы изменяете файл конфигурации и хотите заставить существующие сеансы подбирать новое содержимое, выполните команду ALTER TEXT SEARCH DICTIONARY для словаря. Это может быть «фиктивное» обновление, которое на самом деле не изменяет никаких значений параметров. |
Этот шаблон словаря используется для создания словарей, которые заменяют слово синонимом. Фразы не поддерживаются — используйте для этого шаблон тезауруса (Thesaurus Dictionary). Словарь синонимов можно использовать для преодоления лингвистических проблем, например, для предотвращения сокращения слова «Paris» до «pari» с помощью стеммерного словаря английского языка. Достаточно иметь строку Paris paris в словаре синонимов и поставить ее перед словарем english_stem. Например:
SELECT * FROM ts_debug('english', 'Paris');
alias | description | token | dictionaries | dictionary | lexemes
-----------+-----------------+-------+----------------+--------------+---------
asciiword | Word, all ASCII | Paris | {english_stem} | english_stem | {pari}
CREATE TEXT SEARCH DICTIONARY my_synonym (
TEMPLATE = synonym,
SYNONYMS = my_synonyms
);
ALTER TEXT SEARCH CONFIGURATION english
ALTER MAPPING FOR asciiword
WITH my_synonym, english_stem;
SELECT * FROM ts_debug('english', 'Paris');
alias | description | token | dictionaries | dictionary | lexemes
-----------+-----------------+-------+---------------------------+------------+---------
asciiword | Word, all ASCII | Paris | {my_synonym,english_stem} | my_synonym | {paris}
Единственный параметр, требуемый шаблоном синонимов — это SYNONYMS, которое является базовым именем его файла конфигурации — my_synonyms в приведенном выше примере. Полное имя файла будет $SHAREDIR/tsearch_data/my_synonyms.syn (где $SHAREDIR означает каталог общих данных установки базы данных RT.Warehouse). Формат файла — всего одна строка на слово, которое нужно заменить, при этом за словом следует его синоним, разделенный пробелом. Пустые строки и пробелы в конце игнорируются.
Шаблон синонима также имеет необязательный параметр CaseSensitive, значение которого по умолчанию равно false. Когда CaseSensitive имеет значение false, слова в файле синонимов преобразуются в нижний регистр, как и вводимые токены. Когда это правда, слова и токены не преобразуются в нижний регистр, а сравниваются как есть.
В конце синонима в конфигурационном файле можно поставить звездочку (*). Это указывает на то, что синоним является префиксом. Звездочка игнорируется, когда запись используется в to_tsvector(), но когда она используется в to_tsquery(), результатом будет элемент запроса с маркером совпадения префикса . Например, предположим, что есть эти записи в $SHAREDIR/tsearch_data/synonym_sample.syn:
postgres pgsql postgresql pgsql postgre pgsql
gogle googl
indices index*
Тогда получим такие результаты:
mydb=# CREATE TEXT SEARCH DICTIONARY syn (template=synonym, synonyms='synonym_sample');
mydb=# SELECT ts_lexize('syn','indices');
ts_lexize
-----------
{index}
(1 row)
mydb=# CREATE TEXT SEARCH CONFIGURATION tst (copy=simple);
mydb=# ALTER TEXT SEARCH CONFIGURATION tst ALTER MAPPING FOR asciiword WITH syn;
mydb=# SELECT to_tsvector('tst','indices');
to_tsvector
-------------
'index':1
(1 row)
mydb=# SELECT to_tsquery('tst','indices');
to_tsquery
------------
'index':*
(1 row)
mydb=# SELECT 'indexes are very useful'::tsvector;
tsvector
---------------------------------
'are' 'indexes' 'useful' 'very'
(1 row)
mydb=# SELECT 'indexes are very useful'::tsvector @@ to_tsquery('tst','indices');
?column?
----------
t
(1 row)
Тезаурусный словарь (Thesaurus Dictionary, иногда сокращенно TZ) представляет собой набор слов, который включает информацию о взаимоотношениях слов и фраз, т. е. более широкие термины (broader terms, BT), более узкие термины (narrower terms, NT), предпочтительные термины, непредпочтительные термины, родственные термины, и т. д.
В основном словарь-тезаурус заменяет все непредпочтительные термины одним предпочтительным термином и, при необходимости, также сохраняет исходные термины для индексации. Текущая реализация словаря тезауруса в базе данных RT.Warehouse является расширением словаря синонимов с добавленной поддержкой фраз. Для словаря тезауруса требуется файл конфигурации следующего формата:
# this is a comment
sample word(s) : indexed word(s)
more sample word(s) : more indexed word(s)
...
где символ двоеточия (:) действует как разделитель между фразой и ее заменой.
Словарь-тезаурус использует подсловарь (который указывается в конфигурации словаря) для нормализации входного текста перед проверкой совпадений фраз. Можно выбрать только один подсловарь. Сообщается об ошибке, если подсловарь не может распознать слово. В этом случае вы должны исключить использование слова или научить подсловарь этому. Вы можете поместить звездочку (*) в начале проиндексированного слова, чтобы пропустить применение к нему подсловаря, но все примеры слов должны быть известны подсловарю.
Словарь тезауруса выбирает самое длинное совпадение, если есть несколько фраз, соответствующих входным данным, и ничья нарушается при использовании последнего определения.
Конкретные стоп-слова, распознаваемые подсловарем, не могут быть указаны; вместо этого используйте ? чтобы отметить место, где может появиться любое стоп-слово. Например, если предположить, что a и the являются стоп-словами согласно подсловарю:
? one ? two : swsw
соответствует a one the two и the one a two; оба будут заменены на swsw.
Поскольку тезаурусный словарь может распознавать фразы, он должен запоминать свое состояние и взаимодействовать с парсером. Тезаурусный словарь использует эти присваивания, чтобы проверить, должен ли он обрабатывать следующее слово или остановить накопление. Словарь тезауруса должен быть тщательно настроен. Например, если словарь тезауруса предназначен для обработки только токена asciiword, то определение словаря тезауруса like one 7, не будет работать, поскольку тип токена uint не назначен словарю тезауруса.
Внимание. Тезаурусы используются во время индексации, поэтому любое изменение параметров словаря тезауруса требует повторной индексации. Для большинства других типов словарей небольшие изменения, такие как добавление или удаление стоп-слов, не приводят к переиндексации. |
CREATE TEXT SEARCH DICTIONARY thesaurus_simple (
TEMPLATE = thesaurus,
DictFile = mythesaurus,
Dictionary = pg_catalog.english_stem
);
Здесь:
Теперь можно привязать словарь тезауруса thesaurus_simple к нужным типам токенов в конфигурации, например:
ALTER TEXT SEARCH CONFIGURATION russian
ALTER MAPPING FOR asciiword, asciihword, hword_asciipart
WITH thesaurus_simple;
Рассмотрим простой астрономический тезаурус thesaurus_astro, содержащий несколько астрономических словосочетаний:
supernovae stars : sn
crab nebulae : crab
Ниже мы создаем словарь и связываем некоторые типы токенов с астрономическим тезаурусом и стеммером английского языка:
CREATE TEXT SEARCH DICTIONARY thesaurus_astro (
TEMPLATE = thesaurus,
DictFile = thesaurus_astro,
Dictionary = english_stem
);
ALTER TEXT SEARCH CONFIGURATION russian
ALTER MAPPING FOR asciiword, asciihword, hword_asciipart
WITH thesaurus_astro, english_stem;
Теперь можно увидеть, как это работает. ts_lexize не очень полезен для тестирования тезауруса, потому что он обрабатывает вводимые данные как одиночный токен. Вместо этого мы можем использовать plainto_tsquery и to_tsvector, которые разобьют их входные строки на несколько токенов:
SELECT plainto_tsquery('supernova star');
plainto_tsquery
-----------------
'sn'
SELECT to_tsvector('supernova star');
to_tsvector
-------------
'sn':1
В принципе, можно использовать to_tsquery, если указать аргумент:
SELECT to_tsquery('''supernova star''');
to_tsquery
------------
'sn'
Обратите внимание, что сверхновая звезда соответствует сверхновым звездам в thesaurus_astro, потому что мы указали стеммер english_stem в определении тезауруса. Стеммер удалил e и s.
Чтобы проиндексировать исходную фразу, а также замену, просто включите ее в правую часть определения:
supernovae stars : sn supernovae stars
SELECT plainto_tsquery('supernova star');
plainto_tsquery
-----------------------------
'sn' & 'supernova' & 'star'
Шаблон словаря Ispell поддерживает морфологические словари, которые могут нормализовать множество различных лингвистических форм слова в одну и ту же лексему. Например, словарь английского языка Ispell может соответствовать всем склонениям и спряжениям поискового термина bank, например: banking, banked, banks, banks' и bank's.
Стандартный дистрибутив базы данных RT.Warehouse не включает файлы конфигурации Ispell. Словари для большого количества языков доступны в Ispell. Также поддерживаются некоторые более современные форматы файлов словарей — MySpell (OO < 2.0.1) и Hunspell (OO >= 2.0.2).
Чтобы создать словарь Ispell, используйте встроенный шаблон ispell и укажите несколько параметров:
CREATE TEXT SEARCH DICTIONARY english_ispell (
TEMPLATE = ispell,
DictFile = english,
AffFile = english,
StopWords = english
);
Здесь DictFile, AffFile и StopWords определяют базовые имена файлов словарей, аффиксов и стоп-слов. Файл стоп-слов имеет тот же формат, что описан выше для простого типа словаря. Формат других файлов здесь не указан, но доступен на вышеупомянутых веб-сайтах.
Словари Ispell обычно распознают ограниченный набор слов, поэтому за ними должен следовать другой, более широкий словарь; например, словарь Snowball, который распознает все.
Словари Ispell поддерживают разделение сложных слов (полезная функция). Обратите внимание, что в файле аффикса должен быть указан специальный флаг, использующий управляемый оператор составных слов, который помечает словарные слова, которые могут участвовать в формировании составных слов:
compoundwords controlled z
Вот несколько примеров для норвежского языка:
SELECT ts_lexize('norwegian_ispell', 'overbuljongterningpakkmesterassistent');
{over,buljong,terning,pakk,mester,assistent}
SELECT ts_lexize('norwegian_ispell', 'sjokoladefabrikk');
{sjokoladefabrikk,sjokolade,fabrikk}
Примечание. MySpell не поддерживает составные слова. Hunspell имеет сложную поддержку составных слов. В настоящее время база данных RT.Warehouse реализует только основные операции со сложными словами Hunspell. |
Snowball предоставляет алгоритмы поиска корней для многих языков. Каждый алгоритм понимает, как свести распространенные вариантные формы слов к основе или основе написания в своем языке. В словаре Snowball требуется языковой параметр, чтобы определить, какой параграф использовать, и, при желании, можно указать имя файла стоп-слова, которое дает список слов, которые нужно исключить (стандартные списки стоп-слов также предоставляются проектом Snowball). Например, имеется встроенное определение, эквивалентное
CREATE TEXT SEARCH DICTIONARY english_stem (
TEMPLATE = snowball,
Language = english,
StopWords = english
);
Словарь Snowball распознает все, независимо от того, может ли он упростить слова, поэтому его следует поместить в конец списка словарей. Бесполезно иметь его перед любым другим словарем, потому что токен никогда не пройдет через него в следующий словарь.
В этом разделе показано, как создать пользовательскую конфигурацию текстового поиска для обработки текста документа и запроса.
Конфигурация текстового поиска определяет все параметры, необходимые для преобразования документа в tsvector: парсер, используемый для разбиения текста на токены и словари, используемые для преобразования каждого токена в лексему. Каждый вызов to_tsvector или to_tsquery нуждается в конфигурации текстового поиска для выполнения его обработки. Параметр конфигурации default_text_search_config задает имя конфигурации по умолчанию, которая используется функциями текстового поиска, если не задан явный параметр конфигурации. Его можно установить в postgresql.conf с помощью утилиты командной строки gpconfig или установить для отдельного сеанса с помощью команды SET.
Доступно несколько предопределенных конфигураций текстового поиска, и вы можете легко создавать собственные конфигурации. Для облегчения управления объектами текстового поиска доступен набор команд SQL, а также несколько команд psql, отображающих информацию об объектах текстового поиска (поддержка psql).
В качестве примера создадим конфигурацию pg, начав с дублирования встроенной английской конфигурации:
CREATE TEXT SEARCH CONFIGURATION public.pg ( COPY = pg_catalog.english );
Мы будем использовать список синонимов для PostgreSQL и хранить его в $SHAREDIR/tsearch_data/pg_dict.syn. Содержимое файла выглядит так:
postgres pg
pgsql pg
postgresql pg
Мы определяем словарь синонимов следующим образом:
CREATE TEXT SEARCH DICTIONARY pg_dict (
TEMPLATE = synonym,
SYNONYMS = pg_dict
);
Далее регистрируем словарь Ispell english_ispell, у которого есть свои конфигурационные файлы:
CREATE TEXT SEARCH DICTIONARY english_ispell (
TEMPLATE = ispell,
DictFile = english,
AffFile = english,
StopWords = english
);
Теперь можно настроить сопоставления слов в конфигурации pg:
ALTER TEXT SEARCH CONFIGURATION pg
ALTER MAPPING FOR asciiword, asciihword, hword_asciipart,
word, hword, hword_part
WITH pg_dict, english_ispell, english_stem;
Решили не индексировать и не искать некоторые типы токенов, которые поддерживает встроенная конфигурация:
ALTER TEXT SEARCH CONFIGURATION pg
DROP MAPPING FOR email, url, url_path, sfloat, float;
Теперь можно протестировать конфигурацию:
SELECT * FROM ts_debug('public.pg', '
PostgreSQL, the highly scalable, SQL compliant, open source object-relational
database management system, is now undergoing beta testing of the next
version of our software.
');
Следующий шаг — настроить сеанс на использование новой конфигурации, созданной в общедоступной схеме:
=> \dF
List of text search configurations
Schema | Name | Description
---------+------+-------------
public | pg |
SET default_text_search_config = 'public.pg';
SET
SHOW default_text_search_config;
default_text_search_config
----------------------------
public.pg
В этом разделе представлены функции базы данных RT.Warehouse, которые можно использовать для тестирования и отладки конфигурации поиска или отдельных парсеров и словарей, указанных в конфигурации.
В пользовательской конфигурации текстового поиска может легко запутаться. Функции, описанные в этом разделе, полезны для тестирования объектов текстового поиска. Вы можете протестировать полную конфигурацию или тестировать парсеры и словари по отдельности.
Функция ts_debug позволяет легко тестировать конфигурацию текстового поиска.
ts_debug([config regconfig, ] document text,
OUT alias text,
OUT description text,
OUT token text,
OUT dictionaries regdictionary[],
OUT dictionary regdictionary,
OUT lexemes text[])
returns setof record
ts_debug отображает информацию о каждом токене документа, созданном парсером и обработанном настроенными словарями. Он использует конфигурацию, указанную в config, или default_text_search_config, если этот аргумент опущен.
ts_debug возвращает одну строку для каждой лексемы, идентифицированной парсером в тексте. Возвращаемые столбцы
Пример:
SELECT * FROM ts_debug('english','a fat cat sat on a mat - it ate a fat rats');
alias | description | token | dictionaries | dictionary | lexemes
-----------+-----------------+-------+----------------+--------------+---------
asciiword | Word, all ASCII | a | {english_stem} | english_stem | {}
blank | Space symbols | | {} | |
asciiword | Word, all ASCII | fat | {english_stem} | english_stem | {fat}
blank | Space symbols | | {} | |
asciiword | Word, all ASCII | cat | {english_stem} | english_stem | {cat}
blank | Space symbols | | {} | |
asciiword | Word, all ASCII | sat | {english_stem} | english_stem | {sat}
blank | Space symbols | | {} | |
asciiword | Word, all ASCII | on | {english_stem} | english_stem | {}
blank | Space symbols | | {} | |
asciiword | Word, all ASCII | a | {english_stem} | english_stem | {}
blank | Space symbols | | {} | |
asciiword | Word, all ASCII | mat | {english_stem} | english_stem | {mat}
blank | Space symbols | | {} | |
blank | Space symbols | - | {} | |
asciiword | Word, all ASCII | it | {english_stem} | english_stem | {}
blank | Space symbols | | {} | |
asciiword | Word, all ASCII | ate | {english_stem} | english_stem | {ate}
blank | Space symbols | | {} | |
asciiword | Word, all ASCII | a | {english_stem} | english_stem | {}
blank | Space symbols | | {} | |
asciiword | Word, all ASCII | fat | {english_stem} | english_stem | {fat}
blank | Space symbols | | {} | |
asciiword | Word, all ASCII | rats | {english_stem} | english_stem | {rat}
Для более подробной демонстрации сначала создадим конфигурацию public.english и словарь Ispell для английского языка:
CREATE TEXT SEARCH CONFIGURATION public.english ( COPY = pg_catalog.english );
CREATE TEXT SEARCH DICTIONARY english_ispell (
TEMPLATE = ispell,
DictFile = english,
AffFile = english,
StopWords = english
);
ALTER TEXT SEARCH CONFIGURATION public.english
ALTER MAPPING FOR asciiword WITH english_ispell, english_stem;
SELECT * FROM ts_debug('public.english','The Brightest supernovaes');
alias | description | token | dictionaries | dictionary | lexemes
-----------+-----------------+-------------+-------------------------------+----------------+-------------
asciiword | Word, all ASCII | The | {english_ispell,english_stem} | english_ispell | {}
blank | Space symbols | | {} | |
asciiword | Word, all ASCII | Brightest | {english_ispell,english_stem} | english_ispell | {bright}
blank | Space symbols | | {} | |
asciiword | Word, all ASCII | supernovaes | {english_ispell,english_stem} | english_stem | {supernova}
В этом примере слово Brightest было распознано парсером как слово ASCII (псевдоним asciiword). Для этого типа токена список словарей english_ispell и english_stem. Слово было распознано english_ispell, что сократило его до существительного «bright». Слово supernovaes неизвестно словарю english_ispell, поэтому оно было передано в следующий словарь и распознано (на самом деле, english_stem — это словарь Snowball, который распознает все, поэтому он был помещен в конец списка словарей).
Слово The было распознано словарем english_ispell как стоп-слово (Stop Words) и не будет индексироваться. Пробелы тоже отбрасываются, так как конфигурация вообще не предусматривает для них словарей.
Вы можете уменьшить ширину вывода, явно указав, какие столбцы вы хотите видеть:
SELECT alias, token, dictionary, lexemes FROM ts_debug('public.english','The Brightest supernovaes');
alias | token | dictionary | lexemes
-----------+-------------+----------------+-------------
asciiword | The | english_ispell | {}
blank | | |
asciiword | Brightest | english_ispell | {bright}
blank | | |
asciiword | supernovaes | english_stem | {supernova}
Следующие функции позволяют напрямую тестировать парсер текстового поиска.
ts_parse(parser_name text, document text,
OUT tokid integer, OUT token text) returns setof record
ts_parse(parser_oid oid, document text,
OUT tokid integer, OUT token text) returns setof record
ts_parse анализирует данный документ и возвращает серию записей, по одной для каждого токена, полученного в результате анализа. Каждая запись включает токен, показывающий назначенный тип токена, и токен, который представляет собой текст токена. Например:
SELECT * FROM ts_parse('default', '123 - a number');
tokid | token
-------+--------
22 | 123
12 |
12 | -
1 | a
12 |
1 | number
ts_token_type(parser_name text, OUT tokid integer,
OUT alias text, OUT description text) returns setof record
ts_token_type(parser_oid oid, OUT tokid integer,
OUT alias text, OUT description text) returns setof record
ts_token_type возвращает таблицу, описывающую каждый тип токена, который может распознать указанный парсер. Для каждого типа токена в таблице указан целочисленный токен, который парсер использует для маркировки токена этого типа, псевдоним, который называет тип токена в командах настройки, и краткое описание. Например:
SELECT * FROM ts_token_type('default');
tokid | alias | description
-------+-----------------+------------------------------------------
1 | asciiword | Word, all ASCII
2 | word | Word, all letters
3 | numword | Word, letters and digits
4 | email | Email address
5 | url | URL
6 | host | Host
7 | sfloat | Scientific notation
8 | version | Version number
9 | hword_numpart | Hyphenated word part, letters and digits
10 | hword_part | Hyphenated word part, all letters
11 | hword_asciipart | Hyphenated word part, all ASCII
12 | blank | Space symbols
13 | tag | XML tag
14 | protocol | Protocol head
15 | numhword | Hyphenated word, letters and digits
16 | asciihword | Hyphenated word, all ASCII
17 | hword | Hyphenated word, all letters
18 | url_path | URL path
19 | file | File or path name
20 | float | Decimal notation
21 | int | Signed integer
22 | uint | Unsigned integer
23 | entity | XML entity
Функция ts_lexize облегчает тестирование словаря.
ts_lexize(dictreg dictionary, token text) returns text[]
ts_lexize возвращает массив лексем, если входной токен известен словарю, или пустой массив, если токен известен словарю, но является стоп-словом, или NULL, если это неизвестное слово.
Примеры:
SELECT ts_lexize('english_stem', 'stars');
ts_lexize
-----------
{star}
SELECT ts_lexize('english_stem', 'a');
ts_lexize
-----------
{}
Примечание. Функция ts_lexize ожидает единственный токен, а не текст. Вот случай, когда это может сбить с толку:
Словарь-тезаурус thesaurus_astro знает словосочетание supernovae stars, но ts_lexize терпит неудачу, поскольку не анализирует входной текст, а обрабатывает его как отдельный токен. Используйте plainto_tsquery или to_tsvector для проверки словарей-тезаурусов, например:
|
В этом разделе описываются и сравниваются типы индексов базы данных RT.Warehouse, которые используются для полнотекстового поиска.
Существует два типа индексов, которые можно использовать для ускорения полнотекстового поиска. Индексы не являются обязательными для полнотекстового поиска, но в случаях, когда поиск в столбце выполняется регулярно, индекс обычно желателен.
CREATE INDEX name ON table USING gist(column);
Создает индекс на основе GiST (Generalized Search Tree). Столбец может быть типа tsvector или tsquery.
CREATE INDEX name ON table USING gin(column);
Создает индекс на основе GIN (Generalized Inverted Index). Столбец должен быть типа tsvector.
Между двумя типами индексов существуют значительные различия в производительности, поэтому важно понимать их характеристики.
Индекс GiST является индексом с потерями, что означает, что индекс может давать ложные совпадения, и для устранения таких ложных совпадений необходимо проверить фактическую строку таблицы (база данных RT.Warehouse делает это автоматически, когда это необходимо). GiST-индексы являются индексами с потерями, в том числе, потому что каждый документ представлен в индексе подписью фиксированной длины. Подпись генерируется путем хэширования каждого слова в один бит в n-битной строке, а затем все эти биты объединяются в n-битную подпись документа. Когда два слова хэшируются в одну и ту же позицию бита, возникает ложное совпадение. Если все слова в запросе имеют совпадения (реальные или ложные), то необходимо получить строку таблицы, чтобы проверить правильность совпадения.
Потери приводят к снижению производительности из-за ненужных выборок записей таблицы, которые оказываются ложными совпадениями. Поскольку произвольный доступ к записям таблицы медленный, это ограничивает полезность индексов GiST. Вероятность ложных совпадений зависит от нескольких факторов, в частности от количества уникальных слов, поэтому рекомендуется использовать словари для уменьшения этого числа.
Индексы GIN не имеют потерь для стандартных запросов, но их производительность логарифмически зависит от количества уникальных слов. Однако индексы GIN хранят только слова (лексемы) значений tsvector, а не их весовые метки. Таким образом, при использовании запроса, включающего веса, требуется перепроверка строки таблицы.
При выборе типа индекса для использования, GiST или GIN, учитывайте следующие различия в производительности:
Как правило, индексы GIN лучше всего подходят для статических данных, поскольку поиск выполняется быстрее. Для динамических данных индексы GiST обновляются быстрее. В частности, индексы GiST очень хороши для динамических данных и быстры, если количество уникальных слов (лексем) меньше 100 000. Индексы GIN же лучше обрабатывают более 100 000 лексем, но медленнее обновляются.
Обратите внимание, что время построения индекса GIN часто можно улучшить, увеличив Maintenance_work_mem, в то время как время построения индекса GiST не зависит от этого параметра.
Разделение больших коллекций и правильное использование индексов GiST и GIN позволяет реализовать очень быстрый поиск с онлайн-обновлением. Разделение может быть выполнено на уровне базы данных с использованием наследования таблиц или путем распределения документов по серверам и сбора результатов поиска с помощью dblink. Последнее возможно, поскольку функции ранжирования используют только локальную информацию.
Утилита командной строки psql предоставляет метакоманду для отображения информации о конфигурациях полнотекстового поиска в базе данных RT.Warehouse.
Информацию об объектах конфигурации текстового поиска можно получить в psql с помощью набора команд:
\dF{d,p,t}[+] [PATTERN]
Дополнительный знак + позволяет получить более подробную информацию.
Необязательный параметр PATTERN может быть именем объекта текстового поиска, необязательно дополненным схемой. Если PATTERN опущен, то будет отображаться информация обо всех видимых объектах. PATTERN может быть регулярным выражением и может предоставлять отдельные шаблоны для имен схем и объектов. Следующие примеры иллюстрируют это:
=> \dF *fulltext*
List of text search configurations
Schema | Name | Description
--------+--------------+-------------
public | fulltext_cfg |
=> \dF *.fulltext*
List of text search configurations
Schema | Name | Description
----------+----------------------------
fulltext | fulltext_cfg |
public | fulltext_cfg |
Доступными командами являются:
\dF[+] [PATTERN].
Список конфигураций поиска текста (добавьте + для более подробного описания).
=> \dF russian
List of text search configurations
Schema | Name | Description
------------+---------+------------------------------------
pg_catalog | russian | configuration for russian language
=> \dF+ russian
Text search configuration "pg_catalog.russian"
Parser: "pg_catalog.default"
Token | Dictionaries
-----------------+--------------
asciihword | english_stem
asciiword | english_stem
email | simple
file | simple
float | simple
host | simple
hword | russian_stem
hword_asciipart | english_stem
hword_numpart | simple
hword_part | russian_stem
int | simple
numhword | simple
numword | simple
sfloat | simple
uint | simple
url | simple
url_path | simple
version | simple
word | russian_stem
\dFd[+] [PATTERN]
Список словарей для поиска текста (добавьте + для более подробного описания).
=> \dFd
List of text search dictionaries
Schema | Name | Description
------------+-----------------+-----------------------------------------------------------
pg_catalog | danish_stem | snowball stemmer for danish language
pg_catalog | dutch_stem | snowball stemmer for dutch language
pg_catalog | english_stem | snowball stemmer for english language
pg_catalog | finnish_stem | snowball stemmer for finnish language
pg_catalog | french_stem | snowball stemmer for french language
pg_catalog | german_stem | snowball stemmer for german language
pg_catalog | hungarian_stem | snowball stemmer for hungarian language
pg_catalog | italian_stem | snowball stemmer for italian language
pg_catalog | norwegian_stem | snowball stemmer for norwegian language
pg_catalog | portuguese_stem | snowball stemmer for portuguese language
pg_catalog | romanian_stem | snowball stemmer for romanian language
pg_catalog | russian_stem | snowball stemmer for russian language
pg_catalog | simple | simple dictionary: just lower case and check for stopword
pg_catalog | spanish_stem | snowball stemmer for spanish language
pg_catalog | swedish_stem | snowball stemmer for swedish language
pg_catalog | turkish_stem | snowball stemmer for turkish language
\dFp[+] [PATTERN]
Список парсеров текстового поиска (добавьте + для более подробной информации).
=> \dFp
List of text search parsers
Schema | Name | Description
------------+---------+---------------------
pg_catalog | default | default word parser
=> \dFp+
Text search parser "pg_catalog.default"
Method | Function | Description
-----------------+----------------+-------------
Start parse | prsd_start |
Get next token | prsd_nexttoken |
End parse | prsd_end |
Get headline | prsd_headline |
Get token types | prsd_lextype |
Token types for parser "pg_catalog.default"
Token name | Description
-----------------+------------------------------------------
asciihword | Hyphenated word, all ASCII
asciiword | Word, all ASCII
blank | Space symbols
email | Email address
entity | XML entity
file | File or path name
float | Decimal notation
host | Host
hword | Hyphenated word, all letters
hword_asciipart | Hyphenated word part, all ASCII
hword_numpart | Hyphenated word part, letters and digits
hword_part | Hyphenated word part, all letters
int | Signed integer
numhword | Hyphenated word, letters and digits
numword | Word, letters and digits
protocol | Protocol head
sfloat | Scientific notation
tag | XML tag
uint | Unsigned integer
url | URL
url_path | URL path
version | Version number
word | Word, all letters
(23 rows)
\dFt[+] [PATTERN]
Список шаблонов поиска текста (добавьте + для более подробного описания).
=> \dFt
List of text search templates
Schema | Name | Description
------------+-----------+-----------------------------------------------------------
pg_catalog | ispell | ispell dictionary
pg_catalog | simple | simple dictionary: just lower case and check for stopword
pg_catalog | snowball | snowball stemmer
pg_catalog | synonym | synonym dictionary: replace word by its synonym
pg_catalog | thesaurus | thesaurus dictionary: phrase by phrase substitution
В этом разделе перечислены ограничения и максимумы для объектов полнотекстового поиска базы данных RT.Warehouse.
Текущие ограничения функций текстового поиска в базе данных RT.Warehouse являются:
MapReduce — это модель программирования, разработанная для обработки и создания больших наборов данных на множестве обычных серверов. RT.Warehouse MapReduce позволяет программистам, знакомым с моделью MapReduce, писать функции отображения и редукции и отправлять их в параллельный механизм базы данных RT.Warehouse для обработки.
Вы настраиваете задание RT.Warehouse MapReduce с помощью файла конфигурации в формате YAML, затем передаете файл программе RT.Warehouse MapReduce, gpmapreduce, для выполнения параллельным механизмом базы данных RT.Warehouse. Система базы данных RT.Warehouse распределяет входные данные, выполняет программу на нескольких машинах, обрабатывает сбои машин и управляет необходимой связью между машинами.
Обратитесь к gpmapreduce для получения подробной информации о запуске программы RT.Warehouse MapReduce.
В этом разделе объясняются некоторые основы формата файла конфигурации RT.Warehouse MapReduce, чтобы помочь вам приступить к созданию собственных файлов конфигурации RT.Warehouse MapReduce. RT.Warehouse использует формат документа YAML 1.1, а затем реализует собственную схему для определения различных шагов задания MapReduce.
Все файлы конфигурации RT.Warehouse MapReduce должны сначала объявлять версию спецификации YAML, которую они используют. После этого три тире (---) обозначают начало документа, а три точки (...) обозначают конец документа без начала нового (документ в этом контексте эквивалентен заданию MapReduce). Строки комментариев начинаются с символа решетки (#). Вы можете объявить несколько документов/работ RT.Warehouse MapReduce в одном файле:
%YAML 1.1
---
# Begin Document 1
# ...
---
# Begin Document 2
# ...
В документе RT.Warehouse MapReduce существует три основных типа структур данных или узлов: скаляры, последовательности и сопоставления.
Скаляр — это базовая строка текста, разделенная пробелом. Если у вас есть скалярный ввод, который охватывает несколько строк, предшествующая вертикальная черта ( | ) обозначает литеральный стиль, где все разрывы строк имеют значение. В качестве альтернативы предшествующая угловая скобка ( > ) заменяет одиночный разрыв строки пробелом для последующих строк с таким же уровнем отступа. Если строка содержит символы, имеющие зарезервированное значение, строка должна быть заключена в кавычки или специальный символ должен быть экранирован обратной косой чертой ( \ ).
# Read each new line literally
somekey: | this value contains two lines
and each line is read literally
# Treat each new line as a space
anotherkey: >
this value contains two lines
but is treated as one continuous line
# This quoted string contains a special character
ThirdKey: "This is a string: not a mapping"
Последовательность — это список, в котором каждая запись в списке находится на отдельной строке, обозначенной дефисом и пробелом (-). Кроме того, вы можете указать встроенную последовательность в виде списка, разделенного запятыми, в квадратных скобках. Последовательность предоставляет набор данных и упорядочивает их. Когда вы загружаете список в программу RT.Warehouse MapReduce, порядок сохраняется.
# list sequence
- this
- is
- a list
- with
- five scalar values
# inline sequence
[this, is, a list, with, five scalar values]
Сопоставление используется для сопоставления значений данных с идентификаторами, называемыми ключами. Сопоставления используют двоеточие и пробел (:) для каждой пары ключ: значение или также могут быть указаны в виде списка, разделенного запятыми, в фигурных скобках. Ключ используется в качестве индекса для извлечения данных из сопоставления.
# a mapping of items
title: War and Peace
author: Leo Tolstoy
date: 1865
# same mapping written inline
{title: War and Peace, author: Leo Tolstoy, date: 1865}
Ключи используются для связывания метаинформации с каждым узлом и указания ожидаемого типа узла (скалярный, последовательность или сопоставление).
Программа RT.Warehouse MapReduce обрабатывает узлы документа по порядку и использует отступы (пробелы) для определения иерархии документа и взаимоотношений узлов друг с другом. Использование белого пространства имеет большое значение. Пробелы не должны использоваться просто для целей форматирования, а вкладки вообще не должны использоваться.
Обратитесь к gpmapreduce.yaml для получения подробной информации о формате файла конфигурации RT.Warehouse MapReduce и поддерживаемых ключах и значениях.
В этом примере вы создаете задание MapReduce, которое обрабатывает текстовые документы и сообщает о количестве вхождений определенных ключевых слов в каждом документе. Документы и ключевые слова хранятся в отдельных таблицах базы данных RT.Warehouse, которые вы создаете как часть упражнения.
В этом примере задания MapReduce используется недоверенный язык plpythonu, поэтому вы должны запускать задание от имени пользователя с административными привилегиями базы данных RT.Warehouse.
1. Войдите на мастер хост базы данных RT.Warehouse как пользователь-администратор gpadmin и настройте свою среду. Пример:
$ ssh gpadmin@<gpmaster>
gpadmin@gpmaster$ . /usr/local/greenplum-db/greenplum_path.sh
2. Создайте новую базу данных для примера MapReduce. Пример:
gpadmin@gpmaster$ createdb mapredex_db
3. Запустите подсистему psql, подключившись к новой базе данных:
gpadmin@gpmaster$ psql -d mapredex_db
4. Зарегистрируйте язык PL/Python в базе данных. Пример:
mapredex_db=> CREATE EXTENSION plpythonu;
5. Создайте таблицу документов и добавьте в нее некоторые данные. Пример:
CREATE TABLE documents (doc_id int, url text, data text);
INSERT INTO documents VALUES (1, 'http:/url/1', 'this is one document in the corpus');
INSERT INTO documents VALUES (2, 'http:/url/2', 'i am the second document in the corpus');
INSERT INTO documents VALUES (3, 'http:/url/3', 'being third never really bothered me until now');
INSERT INTO documents VALUES (4, 'http:/url/4', 'the document before me is the third document');
6. Создайте таблицу ключевых слов и добавьте в нее некоторые данные. Например:
CREATE TABLE keywords (keyword_id int, keyword text);
INSERT INTO keywords VALUES (1, 'the');
INSERT INTO keywords VALUES (2, 'document');
INSERT INTO keywords VALUES (3, 'me');
INSERT INTO keywords VALUES (4, 'being');
INSERT INTO keywords VALUES (5, 'now');
INSERT INTO keywords VALUES (6, 'corpus');
INSERT INTO keywords VALUES (7, 'is');
INSERT INTO keywords VALUES (8, 'third');
7. Создайте файл конфигурации MapReduce YAML. Например, откройте файл с именем mymrjob.yaml в выбранном вами редакторе и скопируйте/вставьте следующий большой текстовый блок:
# This example MapReduce job processes documents and looks for keywords in them.
# It takes two database tables as input:
# - documents (doc_id integer, url text, data text)
# - keywords (keyword_id integer, keyword text)#
# The documents data is searched for occurrences of keywords and returns results of
# url, data and keyword (a keyword can be multiple words, such as "high performance # computing")
%YAML 1.1
---
VERSION: 1.0.0.2
# Connect to Greenplum Database using this database and role
DATABASE: mapredex_db
USER: gpadmin
# Begin definition section
DEFINE:
# Declare the input, which selects all columns and rows from the
# 'documents' and 'keywords' tables.
- INPUT:
NAME: doc
TABLE: documents
- INPUT:
NAME: kw
TABLE: keywords
# Define the map functions to extract terms from documents and keyword
# This example simply splits on white space, but it would be possible
# to make use of a python library like nltk (the natural language toolkit)
# to perform more complex tokenization and word stemming.
- MAP:
NAME: doc_map
LANGUAGE: python
FUNCTION: |
i = 0 # the index of a word within the document
terms = {}# a hash of terms and their indexes within the document
# Lower-case and split the text string on space
for term in data.lower().split():
i = i + 1# increment i (the index)
# Check for the term in the terms list:
# if stem word already exists, append the i value to the array entry
# corresponding to the term. This counts multiple occurrences of the word.
# If stem word does not exist, add it to the dictionary with position i.
# For example:
# data: "a computer is a machine that manipulates data"
# "a" [1, 4]
# "computer" [2]
# "machine" [3]
# …
if term in terms:
terms[term] += ','+str(i)
else:
terms[term] = str(i)
# Return multiple lines for each document. Each line consists of
# the doc_id, a term and the positions in the data where the term appeared.
# For example:
# (doc_id => 100, term => "a", [1,4]
# (doc_id => 100, term => "computer", [2]
# …
for term in terms:
yield([doc_id, term, terms[term]])
OPTIMIZE: STRICT IMMUTABLE
PARAMETERS:
- doc_id integer
- data text
RETURNS:
- doc_id integer
- term text
- positions text
# The map function for keywords is almost identical to the one for documents
# but it also counts of the number of terms in the keyword.
- MAP:
NAME: kw_map
LANGUAGE: python
FUNCTION: |
i = 0
terms = {}
for term in keyword.lower().split():
i = i + 1
if term in terms:
terms[term] += ','+str(i)
else:
terms[term] = str(i)
# output 4 values including i (the total count for term in terms):
yield([keyword_id, i, term, terms[term]])
OPTIMIZE: STRICT IMMUTABLE
PARAMETERS:
- keyword_id integer
- keyword text
RETURNS:
- keyword_id integer
- nterms integer
- term text
- positions text
# A TASK is an object that defines an entire INPUT/MAP/REDUCE stage
# within a Greenplum MapReduce pipeline. It is like EXECUTION, but it is
# executed only when called as input to other processing stages.
# Identify a task called 'doc_prep' which takes in the 'doc' INPUT defined earlier
# and runs the 'doc_map' MAP function which returns doc_id, term, [term_position]
- TASK:
NAME: doc_prep
SOURCE: doc
MAP: doc_map
# Identify a task called 'kw_prep' which takes in the 'kw' INPUT defined earlier
# and runs the kw_map MAP function which returns kw_id, term, [term_position]
- TASK:
NAME: kw_prep
SOURCE: kw
MAP: kw_map
# One advantage of Greenplum MapReduce is that MapReduce tasks can be
# used as input to SQL operations and SQL can be used to process a MapReduce task.
# This INPUT defines a SQL query that joins the output of the 'doc_prep'
# TASK to that of the 'kw_prep' TASK. Matching terms are output to the 'candidate'
# list (any keyword that shares at least one term with the document).
- INPUT:
NAME: term_join
QUERY: |
SELECT doc.doc_id, kw.keyword_id, kw.term, kw.nterms,
doc.positions as doc_positions,
kw.positions as kw_positions
FROM doc_prep doc INNER JOIN kw_prep kw ON (doc.term = kw.term)
# In Greenplum MapReduce, a REDUCE function is comprised of one or more functions.
# A REDUCE has an initial 'state' variable defined for each grouping key. that is
# A TRANSITION function adjusts the state for every value in a key grouping.
# If present, an optional CONSOLIDATE function combines multiple
# 'state' variables. This allows the TRANSITION function to be executed locally at
# the segment-level and only redistribute the accumulated 'state' over
# the network. If present, an optional FINALIZE function can be used to perform
# final computation on a state and emit one or more rows of output from the state.
#
# This REDUCE function is called 'term_reducer' with a TRANSITION function
# called 'term_transition' and a FINALIZE function called 'term_finalizer'
- REDUCE:
NAME: term_reducer
TRANSITION: term_transition
FINALIZE: term_finalizer
- TRANSITION:
NAME: term_transition
LANGUAGE: python
PARAMETERS:
- state text
- term text
- nterms integer
- doc_positions text
- kw_positions text
FUNCTION: |
# 'state' has an initial value of '' and is a colon delimited set
# of keyword positions. keyword positions are comma delimited sets of
# integers. For example, '1,3,2:4:'
# If there is an existing state, split it into the set of keyword positions
# otherwise construct a set of 'nterms' keyword positions - all empty
if state:
kw_split = state.split(':')
else:
kw_split = []
for i in range(0,nterms):
kw_split.append('')
# 'kw_positions' is a comma delimited field of integers indicating what
# position a single term occurs within a given keyword.
# Splitting based on ',' converts the string into a python list.
# add doc_positions for the current term
for kw_p in kw_positions.split(','):
kw_split[int(kw_p)-1] = doc_positions
# This section takes each element in the 'kw_split' array and strings
# them together placing a ':' in between each element from the array.
# For example: for the keyword "computer software computer hardware",
# the 'kw_split' array matched up to the document data of
# "in the business of computer software software engineers"
# would look like: ['5', '6,7', '5', '']
# and the outstate would look like: 5:6,7:5:
outstate = kw_split[0]
for s in kw_split[1:]:
outstate = outstate + ':' + s
return outstate
- FINALIZE:
NAME: term_finalizer
LANGUAGE: python
RETURNS:
- count integer
MODE: MULTI
FUNCTION: |
if not state:
yield 0
kw_split = state.split(':')
# This function does the following:
# 1) Splits 'kw_split' on ':'
# for example, 1,5,7:2,8 creates '1,5,7' and '2,8'
# 2) For each group of positions in 'kw_split', splits the set on ','
# to create ['1','5','7'] from Set 0: 1,5,7 and
# eventually ['2', '8'] from Set 1: 2,8
# 3) Checks for empty strings
# 4) Adjusts the split sets by subtracting the position of the set
# in the 'kw_split' array
# ['1','5','7'] - 0 from each element = ['1','5','7']
# ['2', '8'] - 1 from each element = ['1', '7']
# 5) Resulting arrays after subtracting the offset in step 4 are
# intersected and their overlapping values kept:
# ['1','5','7'].intersect['1', '7'] = [1,7]
# 6) Determines the length of the intersection, which is the number of
# times that an entire keyword (with all its pieces) matches in the
# document data.
previous = None
for i in range(0,len(kw_split)):
isplit = kw_split[i].split(',')
if any(map(lambda(x): x == '', isplit)):
yield 0
adjusted = set(map(lambda(x): int(x)-i, isplit))
if (previous):
previous = adjusted.intersection(previous)
else:
previous = adjusted
# return the final count
if previous:
yield len(previous)
# Define the 'term_match' task which is then executed as part
# of the 'final_output' query. It takes the INPUT 'term_join' defined
# earlier and uses the REDUCE function 'term_reducer' defined earlier
- TASK:
NAME: term_match
SOURCE: term_join
REDUCE: term_reducer
- INPUT:
NAME: final_output
QUERY: |
SELECT doc.*, kw.*, tm.count
FROM documents doc, keywords kw, term_match tm
WHERE doc.doc_id = tm.doc_id
AND kw.keyword_id = tm.keyword_id
AND tm.count > 0
# Execute this MapReduce job and send output to STDOUT
EXECUTE:
- RUN:
SOURCE: final_output
TARGET: STDOUT
8. Сохраните файл и выйдите из редактора.
9. Запустите задание MapReduce. Пример:
gpadmin@gpmaster$ gpmapreduce -f mymrjob.yaml
Задание отображает количество вхождений каждого ключевого слова в каждом документе в стандартный вывод.
На Рисунке 6 показана блок-схема потока выполнения задания MapReduce, определенного в примере:
Рисунок 6. Блок-схема для примера MapReduce
База данных RT.Warehouse динамически устраняет ненужные разделы в таблице и оптимально распределяет память для различных операторов в запросе. Эти усовершенствования сканируют меньше данных для запроса, ускоряют обработку запросов и поддерживают больше параллелизма.
В базе данных RT.Warehouse значения, доступные только при выполнении запроса, используются для динамического сокращения партиций, что повышает скорость обработки запроса. Включите или отключите динамическое удаление партиций, установив для параметра конфигурации сервера gp_dynamic_partition_pruning значение ON или OFF (он включен по умолчанию).
База данных RT.Warehouse оптимально распределяет память для различных операторов в запросе, а также освобождает и перераспределяет память на этапах обработки запроса.
Примечание. База данных RT.Warehouse по умолчанию использует GPORCA, оптимизатор запросов нового поколения RT.Warehouse. GPORCA расширяет возможности планирования и оптимизации оптимизатора Postgres. Сведения о функциях и ограничениях GPORCA см. в разделе Обзор GPORCA (21.2.1). |
База данных RT.Warehouse создает spill-файлы, также известные как рабочие файлы, на диске, если в памяти недостаточно памяти для выполнения SQL-запроса.
Максимальное количество spill-файлов для данного запроса определяется настройкой параметра конфигурации сервера gp_workfile_limit_files_per_query. Значение по умолчанию, равное 100 000 spill-файлов, достаточно для большинства запросов.
Если запрос создает больше, чем настроенное количество spill-файлов, база данных RT.Warehouse возвращает эту ошибку:
ERROR: number of workfiles per query limit exceeded
База данных RT.Warehouse может генерировать большое количество spill-файлов, когда:
Вы можете успешно выполнить запрос, изменив распределение данных или изменив конфигурацию системной памяти. Представления gp_toolkit gp_workfile_* отображают информацию об использовании spill-файла. Вы можете использовать эту информацию для устранения неполадок и настройки запросов.
Изучите планы запросов с низкой производительностью, чтобы определить допустимые возможности настройки производительности.
База данных RT.Warehouse разрабатывает план запроса для каждого запроса. Выбор правильного плана запроса, соответствующего запросу и структуре данных, необходим для хорошей производительности. План запроса определяет, как база данных RT.Warehouse будет выполнять запрос в среде параллельного выполнения.
Оптимизатор запросов использует статистику данных, поддерживаемую базой данных, чтобы выбрать план запроса с наименьшей возможной стоимостью. Стоимость измеряется в дисковых операциях ввода-вывода, отображаемых в единицах выборки страниц на диске. Цель состоит в том, чтобы минимизировать общую стоимость выполнения плана.
Просмотрите план для данного запроса с помощью команды EXPLAIN. EXPLAIN показывает расчетную стоимость плана запроса оптимизатора запросов. Например:
EXPLAIN SELECT * FROM names WHERE id=22;
EXPLAIN ANALYZE запускает оператор в дополнение к отображению его плана. Это полезно для определения того, насколько оценки оптимизатора близки к реальности. Например:
EXPLAIN ANALYZE SELECT * FROM names WHERE id=22;
Примечание. В базе данных RT.Warehouse оптимизатор GPORCA по умолчанию сосуществует с планировщиком Postgres. Выходные данные EXPLAIN, созданные GPORCA, отличаются от выходных данных, созданных планировщиком Postgres. По умолчанию база данных RT.Warehouse использует GPORCA для создания плана выполнения запроса, когда это возможно. Когда команда EXPLAIN ANALYZE использует GPORCA, план EXPLAIN показывает только количество удаляемых разделов. Отсканированные разделы не отображаются. Чтобы отображать имена сканируемых разделов в журналах сегментов, включите параметр конфигурации сервера gp_log_dynamic_partition_pruning. В этом примере команда SET включает параметр.
|
План запроса представляет собой дерево узлов. Каждый узел в плане представляет одну операцию, такую как просмотр таблицы, объединение, агрегирование или сортировка.
Читайте планы снизу вверх: каждый узел передает строки узлу непосредственно над ним. Нижние узлы плана обычно представляют собой операции сканирования таблицы: последовательное, индексное или растровое сканирование индекса. Если запрос требует объединения, агрегирования, сортировки или других операций над строками, то над узлами сканирования есть дополнительные узлы для выполнения этих операций. Верхними узлами плана обычно являются узлы движения (motion nodes) базы данных RT.Warehouse: перераспределение (redistribute), явное перераспределение (explicit redistribute), трансляция (broadcast) или сбор движений (gather motions). Эти операции перемещают строки между экземплярами сегментов во время обработки запроса.
Вывод EXPLAIN имеет одну строку для каждого узла в дереве плана и показывает базовый тип узла и следующие оценки стоимости выполнения для этого узла плана:
Обратите внимание на следующее:
Как внешние, так и сторонние таблицы обеспечивают доступ к данным, хранящимся в источниках данных за пределами базы данных RT.Warehouse, как если бы данные хранились в обычных таблицах базы данных. Вы можете читать данные и записывать данные во внешние и сторонние таблицы.
Внешняя таблица — это таблица базы данных RT.Warehouse, в основе которой лежат данные, находящиеся вне базы данных. Вы создаете доступную для чтения внешнюю таблицу для чтения данных из внешнего источника данных и создаете доступную для записи внешнюю таблицу для записи данных во внешний источник. Вы можете использовать внешние таблицы в командах SQL так же, как и обычную таблицу базы данных. Например, вы можете SELECT (доступная для чтения внешняя таблица), INSERT (доступная для записи внешняя таблица) и объединять внешние таблицы с другими таблицами RT.Warehouse. Внешние таблицы чаще всего используются для загрузки и выгрузки данных базы данных. Дополнительные сведения об использовании внешних таблиц для доступа к внешним данным см. в разделе Определение внешних таблиц (22.2).
Доступ к внешним данным с помощью PXF (22.1) описывает использование PXF и внешних таблиц для доступа к внешним источникам данных.
Внешняя таблица — это другой тип таблицы базы данных RT.Warehouse, поддерживаемой данными, которые находятся за пределами базы данных. Вы можете читать и записывать в одну и ту же внешнюю таблицу. Аналогичным образом вы можете использовать сторонние таблицы в командах SQL, как описано выше для внешних таблиц. Дополнительные сведения о доступе к внешним данным с помощью внешних таблиц см. в разделе Доступ к внешним данным с помощью внешних таблиц (22.3).
Внешние веб-таблицы обеспечивают доступ к данным, обслуживаемым HTTP-сервером или процессом операционной системы.
Данные, которыми управляет ваша организация, могут уже находиться во внешних источниках, таких как Hadoop, хранилища объектов и другие базы данных SQL. RT.Warehouse Platform Extension Framework (PXF) обеспечивает доступ к этим внешним данным через встроенные соединители, которые сопоставляют внешний источник данных с определением таблицы базы данных RT.Warehouse.
PXF устанавливается с коннекторами Hadoop и Object Storage. Эти соединители позволяют считывать внешние данные, хранящиеся в текстовых форматах, Avro, JSON, RCFile, Parquet, SequenceFile и ORC. Вы можете использовать коннектор JDBC для доступа к внешней базе данных SQL.
RT.Warehouse Platform Extension Framework включает библиотеку протокола C и службу Java. После настройки и инициализации PXF вы запускаете один процесс PXF JVM на каждом узле сегмента базы данных RT.Warehouse. Этот длительно выполняющийся процесс одновременно обслуживает несколько запросов.
Подробную информацию об архитектуре и использовании PXF см. в отдельном документе RT.Warehouse Platform Extension Framework (PXF).
Внешние таблицы позволяют обращаться к внешним данным, как если бы это была обычная таблица базы данных. Они часто используются для перемещения данных в базу данных RT.Warehouse и из нее.
Чтобы создать определение внешней таблицы, вы указываете формат ваших входных файлов и расположение ваших внешних источников данных.
Используйте один из следующих протоколов для доступа к внешним источникам данных таблицы. Вы не можете смешивать протоколы в операторах CREATE EXTERNAL TABLE:
Протоколы pxf:// и s3:// — это пользовательские протоколы доступа к данным, где протоколы file://, gpfdist:// и gpfdists:// реализованы внутри базы данных RT.Warehouse. Пользовательские и внутренние протоколы различаются следующим образом:
Внешние таблицы получают доступ к внешним файлам из базы данных, как если бы они были обычными таблицами базы данных. Внешние таблицы, определенные с помощью протоколов gpfdist/gpfdists, pxf и s3, используют параллелизм RT.Warehouse , используя ресурсы всех сегментов базы данных RT.Warehouse для загрузки или выгрузки данных. Протокол pxf использует параллельную архитектуру распределенной файловой системы Hadoop для доступа к файлам в этой системе. Протокол s3 использует возможности Amazon Web Services (AWS).
Вы можете запрашивать данные внешних таблиц напрямую и параллельно, используя такие команды SQL, как SELECT, JOIN или SORT EXTERNAL TABLE DATA, а также создавать представления для внешних таблиц.
Шаги для использования внешних таблиц:
1. Определите внешнюю таблицу. Чтобы использовать протокол pxf или s3, вы также должны настроить базу данных RT.Warehouse и включить протокол.
2. Выполните одно из следующих действий:
3. Поместите файлы данных в правильные места.
4. Запросите внешнюю таблицу с помощью команд SQL.
Базы данных RT.Warehouse предоставляет читаемые и записываемые внешние таблицы:
Внешние таблицы могут быть файловыми или сетевыми. Внешние таблицы, использующие протокол file://, доступны только для чтения.
Операции резервного копирования и восстановления базы данных RT.Warehouse выполняют резервное копирование и восстановление только определений внешних и внешних веб-таблиц, а не данных источника данных.
Протокол file:// используется в URI, указывающем расположение файла операционной системы.
URI включает имя хоста, порт и путь к файлу. Каждый файл должен находиться на хосте сегмента в месте, доступном суперпользователю базы данных RT.Warehouse (gpadmin). Имя хоста, используемое в URI, должно совпадать с именем хоста сегмента, зарегистрированным в таблице системного каталога gp_segment_configuration.
Предложение LOCATION может иметь несколько URI, как показано в этом примере:
CREATE EXTERNAL TABLE ext_expenses (
name text, date date, amount float4, category text, desc1 text )
LOCATION ('file://host1:5432/data/expense/*.csv',
'file://host2:5432/data/expense/*.csv',
'file://host3:5432/data/expense/*.csv')
FORMAT 'CSV' (HEADER);
Количество URI, которое вы указываете в предложении LOCATION - это количество экземпляров сегментов, которые будут работать параллельно для доступа к внешней таблице. Для каждого URI RT.Warehouse назначает файлу первичный сегмент на указанном хосте. Для достижения максимального параллелизма при загрузке данных разделите данные на столько файлов одинакового размера, сколько у вас первичных сегментов. Это гарантирует, что все сегменты будут участвовать в загрузке. Количество внешних файлов на хост сегмента не может превышать количество экземпляров первичного сегмента на этом хосте. Например, если ваш массив имеет четыре экземпляра первичного сегмента на каждый сегментный узел, вы можете разместить четыре внешних файла на каждом сегментном узле. Таблицы, основанные на протоколе file://, могут быть только таблицами, доступными для чтения.
Системное представление pg_max_external_files показывает, сколько внешних файлов таблиц разрешено для каждой внешней таблицы. Это представление перечисляет доступные слоты для файлов на хост сегмента при использовании протокола file://. Представление применимо только для протокола file://. Например:
SELECT * FROM pg_max_external_files;
Протокол gpfdist:// используется в URI для ссылки на работающий экземпляр gpfdist.
Утилита gpfdist обслуживает внешние файлы данных из каталога на файловом хосте для всех сегментов базы данных RT.Warehouse параллельно.
gpfdist находится в каталоге $GPHOME/bin на мастер хосте базы данных RT.Warehouse и на каждом хосте сегмента.
Запустите gpfdist на хосте, где находятся файлы внешних данных. Для читаемых внешних таблиц gpfdist автоматически распаковывает файлы gzip (.gz) и bzip2 (.bz2). Для записываемых внешних таблиц данные сжимаются с помощью gzip, если целевой файл имеет расширение .gz. Вы можете использовать подстановочный знак (*) или другой шаблон соответствия в стиле C, чтобы обозначить несколько файлов для чтения. Предполагается, что указанные файлы относятся к каталогу, который вы указали при запуске экземпляра gpfdist.
Примечание. Сжатие не поддерживается для внешних таблиц, доступных для чтения и записи, когда утилита gpfdist работает на платформах Windows. |
Все первичные сегменты обращаются к внешнему файлу (файлам) параллельно, в зависимости от количества сегментов, заданного в параметре конфигурации сервера gp_external_max_segments. Используйте несколько источников данных gpfdist в операторе CREATE EXTERNAL TABLE для масштабирования производительности сканирования внешней таблицы.
gpfdist поддерживает преобразования данных. Вы можете написать процесс трансформации для преобразования внешних данных из или в формат, который напрямую не поддерживается внешними таблицами базы данных RT.Warehouse.
Для получения дополнительной информации о настройке gpfdist смотрите раздел Использование параллельного файлового сервера RT.Warehouse(gpfdist) (22.4).
Дополнительную информацию об использовании gpfdist с внешними таблицами смотрите в справочной документации gpfdist.
Протокол gpfdists:// — это безопасная версия протокола gpfdist://.
Чтобы использовать его, вы запускаете утилиту gpfdist с параметром --ssl. При указании в URI протокол gpfdists:// обеспечивает зашифрованную связь и безопасную идентификацию файлового сервера и базы данных RT.Warehouse для защиты от таких атак таких, как прослушивание и атаки «человек посередине».
gpfdists реализует SSL-безопасность по схеме клиент/сервер со следующими атрибутами и ограничениями:
Примечание. Сервер, запущенный с параметром gpfdist --ssl, может взаимодействовать только с протоколом gpfdists. Сервер, запущенный с помощью gpfdist без параметра --ssl, может взаимодействовать только с протоколом gpfdist. |
Используйте один из следующих методов для вызова протокола gpfdists:
Для использования gpfdists необходимо, чтобы следующие клиентские сертификаты находились в каталоге $PGDATA/gpfdists в каждом сегменте:
Параметр конфигурации сервера verify_gpfdists_cert определяет, включена ли аутентификация сертификата SSL, когда база данных RT.Warehouse связывается с утилитой gpfdist для чтения данных или записи данных во внешний источник данных. Вы можете установить для параметра значение false, чтобы отключить аутентификацию при тестировании связи между внешней таблицей базы данных RT.Warehouse и утилитой gpfdist, обслуживающей внешние данные. Если значение равно false, эти исключения SSL игнорируются:
Внимание. Отключение проверки подлинности SSL-сертификата подвергает риску безопасность, так как не проверяется SSL-сертификат gpfdists. |
Вы можете использовать протокол RT.Warehouse Platform Extension Framework (PXF) pxf:// для доступа к данным, находящимся в системах хранения объектов (Azure, Google Cloud Storage, Minio, S3), внешних системах Hadoop (HDFS, Hive, HBase) и базах данных SQL.
Протокол PXF pxf упакован как расширение базы данных RT.Warehouse. Протокол pxf поддерживает чтение из внешних хранилищ данных. Вы также можете записывать данные в текстовом, двоичном и паркетном формате с помощью протокола pxf.
При использовании протокола pxf для запроса к внешнему хранилищу данных вы указываете каталог, файл или таблицу, к которым хотите получить доступ. PXF запрашивает данные из хранилища данных и доставляет соответствующие части параллельно каждому экземпляру сегмента базы данных RT.Warehouse, обслуживающему запрос.
Вы должны явно инициализировать и запустить PXF, прежде чем сможете использовать протокол pxf для чтения или записи внешних данных. Вы также должны включить PXF в каждой базе данных, в которой вы хотите разрешить пользователям создавать внешние таблицы для доступа к внешним данным, и предоставить разрешения на протокол pxf этим пользователям базы данных RT.Warehouse.
Подробную информацию о настройке и использовании PXF и протокола pxf см. в разделе Доступ к внешним данным с помощью PXF (22.1).
Протокол s3 используется в URL-адресе, который указывает расположение корзины Amazon S3 и префикс, используемый для чтения или записи файлов в корзине.
Вы можете определить внешние таблицы только для чтения, которые используют существующие файлы данных в корзине S3 для табличных данных, или внешние таблицы с возможностью записи, которые сохраняют данные из операций INSERT в файлы в корзине S3. База данных RT.Warehouse использует URL-адрес S3 и префикс, указанные в URL-адресе протокола, либо для выбора одного или нескольких файлов для таблицы, доступной только для чтения, либо для определения местоположения и формата имени файла, которые будут использоваться при загрузке файлов S3 для операций INSERT в доступные для записи таблицы.
Примечание. Протокол pxf может получать доступ к данным в S3 и других системах хранения объектов, таких как Azure, Google Cloud Storage и Minio. Протокол pxf также может получать доступ к данным во внешних системах Hadoop (HDFS, Hive, HBase) и базах данных SQL. См. протокол pxf://. |
Выполните следующие шаги, чтобы настроить протокол S3 и использовать внешние таблицы S3., См. также Ограничения протокола s3, чтобы лучше понять возможности и ограничения внешних таблиц S3.
1. Настройте каждую базу данных для поддержки протокола s3:
a. В каждой базе данных, которая будет обращаться к корзине S3 по протоколу s3, создайте функции чтения и записи для библиотеки протокола s3:
CREATE OR REPLACE FUNCTION write_to_s3() RETURNS integer AS
'$libdir/gps3ext.so', 's3_export' LANGUAGE C STABLE;
CREATE OR REPLACE FUNCTION read_from_s3() RETURNS integer AS
'$libdir/gps3ext.so', 's3_import' LANGUAGE C STABLE;
b. В каждой базе данных, которая будет обращаться к корзине S3, объявите протокол s3 и укажите функции чтения и записи, которые вы создали на предыдущем шаге:
CREATE PROTOCOL s3 (writefunc = write_to_s3, readfunc = read_from_s3);
Примечание. Имя протокола s3 должно совпадать с протоколом URL-адреса, указанного для внешней таблицы, которую вы создаете для доступа к ресурсу S3. Соответствующая функция вызывается каждым экземпляром сегмента базы данных RT.Warehouse. Все узлы сегмента должны иметь доступ к корзине S3. |
2. В каждом сегменте базы данных RT.Warehouse создайте и установите файл конфигурации протокола s3:
a. Создайте файл конфигурации протокола s3 шаблона с помощью утилиты gpcheckcloud:
gpcheckcloud -t > ./mytest_s3.config
b. Отредактируйте файл шаблона, чтобы указать accessid и secret, необходимые для подключения к местоположению S3. См. Файл конфигурации протокола s3 для получения информации о других параметрах конфигурации протокола s3.
c. Скопируйте файл в то же место и имя файла для всех сегментов базы данных RT.Warehouse на всех хостах. Расположение файла по умолчанию: gpseg_data_dir/gpseg_prefixN/s3/s3.conf. gpseg_data_dir — это путь к каталогу данных сегмента базы данных RT.Warehouse, gpseg_prefix — префикс сегмента, а N — идентификатор сегмента. Каталог данных сегмента, префикс и идентификатор задаются при инициализации системы базы данных RT.Warehouse.
Если вы копируете файл в другое место или имя файла, вы должны указать это место с помощью параметра конфигурации в URL-адресе протокола s3. См. раздел О параметре конфигурации протокола s3.
d. Используйте утилиту gpcheckcloud для проверки подключения к корзине S3:
gpcheckcloud -c "s3://<s3-endpoint>/<s3-bucket> config=./mytest_s3.config"
Укажите правильный путь к файлу конфигурации для вашей системы, а также имя конечной точки S3 и корзину, которую вы хотите проверить. gpcheckcloud пытается подключиться к конечной точке S3 и перечисляет все файлы в корзине S3, если они доступны. Успешное подключение заканчивается сообщением:
Your configuration works well.
При желании вы можете использовать gpcheckcloud для проверки загрузки и скачивания из корзины S3, как описано в разделе Использование утилиты gpcheckcloud.
3. После выполнения предыдущих шагов по созданию и настройке протокола s3 можно указать URL-адрес протокола s3 в команде CREATE EXTERNAL TABLE для определения внешних таблиц S3. Для таблиц S3, доступных только для чтения, URL-адрес определяет расположение и префикс, используемый для выбора существующих файлов данных, составляющих таблицу S3. Например:
CREATE READABLE EXTERNAL TABLE S3TBL (date text, time text, amt int)
LOCATION('s3://s3-us-west-2.amazonaws.com/s3test.example.com/dataset1/normal/ config=/home/gpadmin/aws_s3/s3.conf')
FORMAT 'csv';
Для таблиц S3, доступных для записи, URL-адрес протокола определяет расположение S3, в котором база данных RT.Warehouse хранит файлы данных для таблицы, а также префикс, используемый при создании файлов для операций INSERT таблицы. Например:
CREATE WRITABLE EXTERNAL TABLE S3WRIT (LIKE S3TBL)
LOCATION('s3://s3-us-west-2.amazonaws.com/s3test.example.com/dataset1/normal/ config=/home/gpadmin/aws_s3/s3.conf')
FORMAT 'csv';
Для протокола s3 вы указываете расположение файлов и необязательное расположение файла конфигурации в предложении LOCATION команды CREATE EXTERNAL TABLE. Это синтаксис:
's3://S3_endpoint[:port]/bucket_name/[S3_prefix] [region=S3_region] [config=config_file_location]'
Протокол s3 требует, чтобы вы указали конечную точку S3 и имя корзины S3. Каждый экземпляр сегмента базы данных RT.Warehouse должен иметь доступ к местоположению S3. Необязательное значение S3_prefix используется для выбора файлов для таблиц S3, доступных только для чтения, или в качестве префикса имени файла для использования при загрузке файлов для таблиц S3 с возможностью записи.
Примечание. URL-адрес протокола s3 базы данных RT.Warehouse должен включать имя хоста конечной точки S3. |
Чтобы указать конечную точку ECS в предложении LOCATION, необходимо установить для версии файла конфигурации s3 значение 2. Параметр версии определяет, используется ли параметр региона в предложении LOCATION.
Примечание. Хотя префикс S3 является необязательным элементом синтаксиса, вы всегда должны включать префикс S3 как для таблиц S3, доступных для записи, так и для таблиц S3 только для чтения, чтобы разделить наборы данных в рамках синтаксиса CREATE EXTERNAL TABLE. |
Для таблиц S3 с возможностью записи URL-адрес протокола s3 указывает конечную точку и имя корзины, в которую база данных RT.Warehouse загружает файлы данных для таблицы. Разрешения корзины S3 должны быть «Загрузка/удаление» для идентификатора пользователя S3, который загружает файлы. Префикс файла S3 используется для каждого нового файла, загружаемого в расположение S3 в результате вставки данных в таблицу.
Для таблиц S3, доступных только для чтения, префикс файла S3 необязателен. Если указать префикс S3, протокол s3 выбирает все файлы, начинающиеся с указанного префикса, в качестве файлов данных для внешней таблицы. Протокол s3 не использует символ косой черты (/) в качестве разделителя, поэтому символ косой черты, следующий за префиксом, рассматривается как часть самого префикса.
Например, рассмотрим следующие 5 файлов, каждый из которых имеет S3_endpoint с именем s3-us-west-2.amazonaws.com и Bucket_name test1:
s3://s3-us-west-2.amazonaws.com/test1/abc
s3://s3-us-west-2.amazonaws.com/test1/abc/
s3://s3-us-west-2.amazonaws.com/test1/abc/xx
s3://s3-us-west-2.amazonaws.com/test1/abcdef
s3://s3-us-west-2.amazonaws.com/test1/abcdefff
Подстановочные знаки не поддерживаются в S3_prefix, однако префикс S3 действует так, как если бы за самим префиксом сразу же следовал подстановочный знак.
Все файлы, выбранные по URL-адресу S3 (S3_endpoint/bucket_name/S3_prefix), используются в качестве источника для внешней таблицы, поэтому они должны иметь одинаковый формат. Каждый файл также должен содержать полные строки данных. Строка данных не может быть разделена между файлами. Разрешения для файлов S3 должны быть «Открыть/Загрузить» и «Просмотр» для идентификатора пользователя S3, который обращается к файлам.
Для каждой операции INSERT в доступной для записи таблице S3 каждый сегмент базы данных RT.Warehouse загружает один файл в настроенную корзину S3, используя формат имени файла <prefix><segment_id><random>.<extension>[.gz], где:
Для доступных для записи таблиц S3 вы можете настроить размер буфера и количество потоков, которые сегменты используют для загрузки файлов.
Для таблиц S3, доступных только для чтения, все файлы, указанные в расположении файла S3 (конечная точка S3/имя_блока/префикс_S3), используются в качестве источника для внешней таблицы и должны иметь одинаковый формат. Каждый файл также должен содержать полные строки данных. Если файлы содержат необязательную строку заголовка, имена столбцов в строке заголовка не могут содержать символ новой строки (\n) или возврат каретки (\r). Кроме того, разделителем столбцов не может быть символ новой строки (\n) или символ возврата каретки (\r).
Протокол s3 распознает формат gzip и распаковывает файлы. Поддерживается только формат сжатия gzip.
Разрешения для файлов S3 должны быть «Открыть/Загрузить» и «Просмотр» для идентификатора пользователя S3, который обращается к файлам. Доступные для записи таблицы S3 требуют, чтобы идентификатор пользователя S3 имел разрешения на загрузку/удаление.
Для таблиц S3, доступных только для чтения, каждый сегмент может загружать по одному файлу за раз из местоположения S3, используя несколько потоков. Чтобы воспользоваться преимуществами параллельной обработки, выполняемой сегментами базы данных RT.Warehouse, файлы в расположении S3 должны быть одинакового размера, а количество файлов должно позволять нескольким сегментам загружать данные из расположения S3. Например, если система базы данных RT.Warehouse состоит из 16 сегментов и имеется достаточная пропускная способность сети, создание 16 файлов в расположении S3 позволяет каждому сегменту загрузить файл из расположения S3. Напротив, если местоположение содержало только 1 или 2 файла, только 1 или 2 сегмента загружали данные.
База данных RT.Warehouse поддерживает шифрование на стороне сервера с использованием ключей, управляемых Amazon S3 (SSE-S3), для файлов AWS S3, доступ к которым осуществляется с помощью внешних таблиц с возможностью чтения и записи, созданных с использованием протокола s3. SSE-S3 шифрует данные вашего объекта при записи на диск и прозрачно расшифровывает данные для вас, когда вы к ним обращаетесь.
Примечание. Протокол s3 поддерживает SSE-S3 только для файлов Amazon Web Services S3. SS3-SE не поддерживается при доступе к файлам в службах, совместимых с S3. |
Ваши разрешения accessid и secret S3 управляют вашим доступом ко всем объектам корзины S3, независимо от того, зашифрованы данные или нет. Однако вы должны настроить свой клиент для использования ключей, управляемых S3, для доступа к зашифрованным данным.
Шифрование на стороне сервера протокола s3 по умолчанию отключено. Чтобы воспользоваться преимуществами шифрования на стороне сервера для объектов AWS S3, которые вы пишете с использованием протокола s3 базы данных RT.Warehouse, необходимо установить для параметра конфигурации server_side_encryption в файле конфигурации s3 значение sse-s3:
server_side_encryption = sse-s3
Когда файл конфигурации, который вы предоставляете для вызова CREATE WRITABLE EXTERNAL TABLE с использованием протокола s3, включает параметр server_side_encryption = sse-s3, база данных RT.Warehouse применяет для вас заголовки шифрования ко всем операциям INSERT в этой внешней таблице. Затем S3 шифрует при записи объект(ы), идентифицированный URI, который вы указали в предложении LOCATION.
S3 прозрачно расшифровывает данные во время операций чтения зашифрованных файлов, доступ к которым осуществляется через доступные для чтения внешние таблицы, которые вы создаете с помощью протокола s3. Никакой дополнительной настройки не требуется.
Для дополнительной детализации конфигурации шифрования вы можете рассмотреть возможность создания политик корзин Amazon Web Services S3, определения объектов, которые вы хотите зашифровать, и действий записи для этих объектов, как описано в разделе Защита данных с помощью шифрования на стороне сервера с помощью Amazon S3-Managed. Ключи шифрования (SSE-S3) Документация AWS.
Вы можете указать URL-адрес, который является прокси-сервером, который S3 использует для подключения к источнику данных. S3 поддерживает следующие протоколы: HTTP и HTTPS. Вы можете указать прокси с помощью параметра конфигурации протокола s3 proxy или переменной среды. Если параметр конфигурации установлен, переменные среды игнорируются.
Чтобы указать прокси с переменной среды, вы устанавливаете переменную среды на основе протокола: http_proxy или https_proxy. Вы можете указать разные URL-адреса для каждого протокола, установив соответствующую переменную среды. S3 поддерживает эти переменные среды:
Переменные среды должны быть установлены и должны быть доступны для базы данных RT.Warehouse на всех хостах базы данных RT.Warehouse.
Необязательный параметр конфигурации указывает расположение требуемого файла конфигурации протокола s3. Файл содержит учетные данные для подключения к Amazon Web Services (AWS) и параметры связи.
Файл конфигурации требуется на всех хостах сегментов базы данных RT.Warehouse. Это расположение по умолчанию — это расположение в каталоге данных каждого экземпляра сегмента базы данных RT.Warehouse.
gpseg_data_dir/gpseg_prefixN/s3/s3.conf
gpseg_data_dir — это путь к каталогу данных сегмента базы данных RT.Warehouse, gpseg_prefix — префикс сегмента, а N — идентификатор сегмента. Каталог данных сегмента, префикс и идентификатор задаются при инициализации системы базы данных RT.Warehouse.
Если у вас есть несколько экземпляров сегмента на узлах сегмента, вы можете упростить настройку, создав одно местоположение на каждом узле сегмента. Затем вы указываете абсолютный путь к местоположению с параметром конфигурации в предложении LOCATION протокола s3. В этом примере указывается местоположение в домашнем каталоге gpadmin.
LOCATION ('s3://s3-us-west-2.amazonaws.com/test/my_data config=/home/gpadmin/s3.conf')
Все экземпляры сегментов на хостах используют файл /home/gpadmin/s3.conf.
При использовании протокола s3 файл конфигурации протокола s3 требуется для всех сегментов базы данных RT.Warehouse. Расположение по умолчанию:
gpseg_data_dir/gpseg-prefixN/s3/s3.conf
gpseg_data_dir — это путь к каталогу данных сегмента базы данных RT.Warehouse, gpseg-prefix — это префикс сегмента, а N — идентификатор сегмента. Каталог данных сегмента, префикс и идентификатор задаются при инициализации системы базы данных RT.Warehouse.
Если у вас есть несколько экземпляров сегмента на узлах сегмента, вы можете упростить настройку, создав одно местоположение на каждом узле сегмента. Затем вы можете указать абсолютный путь к местоположению с параметром конфигурации в предложении LOCATION протокола s3. Однако обратите внимание, что как внешние таблицы S3, доступные только для чтения, так и доступные для записи, используют одни и те же значения параметров для своих подключений. Если вы хотите по-разному настроить параметры протокола для таблиц S3, доступных только для чтения и для записи, вы должны использовать два разных файла конфигурации протокола s3 и указать правильный файл в операторе CREATE EXTERNAL TABLE при создании каждой таблицы.
В этом примере указывается расположение одного файла в каталоге s3 домашнего каталога gpadmin:
config=/home/gpadmin/s3/s3.conf
Все экземпляры сегментов на хостах используют файл /home/gpadmin/s3/s3.conf.
Файл конфигурации протокола s3 представляет собой текстовый файл, состоящий из раздела [default] и параметров. Это пример файла конфигурации:
[default]
secret = "secret"
accessid = "user access id"
threadnum = 3
chunksize = 67108864
Вы можете использовать утилиту gpcheckcloud базы данных RT.Warehouse для проверки файла конфигурации S3.
accessid — Необходимый. Идентификатор AWS S3 для доступа к корзине S3.
secret — Необходимый. Пароль AWS S3 для идентификатора S3 для доступа к корзине S3.
autocompress — Для доступных для записи внешних таблиц S3 этот параметр указывает, следует ли сжимать файлы (с помощью gzip) перед загрузкой в S3. Файлы сжимаются по умолчанию, если вы не укажете этот параметр.
chunksize — Размер буфера, который каждый поток сегмента использует для чтения или записи на сервер S3. По умолчанию 64 МБ. Минимальный размер составляет 8 МБ, а максимальный — 128 МБ.
При вставке данных в доступную для записи таблицу S3 каждый сегмент базы данных RT.Warehouse записывает данные в свой буфер (используя несколько потоков до значения threadnum) до тех пор, пока он не заполнится, после чего он записывает буфер в файл в корзину S3. Затем этот процесс повторяется по мере необходимости для каждого сегмента, пока операция вставки не завершится.
Поскольку Amazon S3 допускает не более 10 000 частей для многокомпонентной загрузки, минимальное значение размера фрагмента 8 МБ поддерживает максимальный размер вставки 80 ГБ на сегмент базы данных RT.Warehouse. Максимальный размер фрагмента 128 МБ поддерживает максимальный размер вставки 1,28 ТБ на сегмент. Для таблиц S3, доступных для записи, вы должны убедиться, что настройка размера фрагмента может поддерживать ожидаемый размер вашей таблицы.
encryption — Используйте соединения, защищенные с помощью Secure Sockets Layer (SSL). Значение по умолчанию — true. Значения true, t, on, yes и y (без учета регистра) считаются истинными. Любое другое значение считается ложным.
Если порт не указан в URL-адресе в предложении LOCATION команды CREATE EXTERNAL TABLE, параметр шифрования файла конфигурации влияет на порт, используемый протоколом s3 (порт 80 для HTTP или порт 443 для HTTPS). Если порт указан, этот порт используется независимо от настройки шифрования.
gpcheckcloud_newline — При загрузке файлов из местоположения S3 утилита gpcheckcloud добавляет символ новой строки к последней строке файла, если в последней строке файла нет символа EOL (конец строки). Символ по умолчанию — \n (новая строка). Значение может быть \n, \r (возврат каретки) или \n\r (перевод строки/возврат каретки).
Добавление символа EOL предотвращает объединение последней строки одного файла с первой строкой следующего файла.
low_speed_limit — Нижний предел скорости загрузки/выгрузки в байтах в секунду. Скорость по умолчанию 10240 (10K). Если скорость загрузки или выгрузки ниже предела дольше, чем время, указанное в параметре low_speed_time, то соединение прерывается и выполняется повторная попытка. После 3 попыток протокол s3 возвращает ошибку. Значение 0 указывает отсутствие нижнего предела.
low_speed_time — Когда скорость соединения меньше, чем low_speed_limit, этот параметр указывает время ожидания в секундах, прежде чем прервать загрузку в корзину S3 или загрузку из нее. По умолчанию 60 секунд. Значение 0 указывает отсутствие ограничения по времени.
proxy — Укажите URL-адрес, который является прокси-сервером, который S3 использует для подключения к источнику данных. S3 поддерживает следующие протоколы: HTTP и HTTPS. Формат параметра:
proxy = protocol://[user:password@]proxyhost[:port]
Если этот параметр не задан или представляет собой пустую строку (proxy = ""), S3 использует прокси, указанный в переменной среды http_proxy или https_proxy (и переменных среды all_proxy и no_proxy). Переменная среды, которую использует S3, зависит от протокола.
В файле конфигурации может быть не более одного параметра прокси. URL-адрес, указанный параметром, является прокси для всех поддерживаемых протоколов.
server_side_encryption — Метод шифрования на стороне сервера S3, настроенный для корзины. База данных RT.Warehouse поддерживает только шифрование на стороне сервера с помощью управляемых Amazon S3 ключей, определяемых значением параметра конфигурации sse-s3. Шифрование на стороне сервера отключено (нет) по умолчанию.
threadnum — Максимальное количество одновременных потоков, которые сегмент может создать при загрузке данных в корзину S3 или загрузке данных из нее. Значение по умолчанию — 4. Минимум — 1, максимум — 8.
verifycert — Управляет тем, как протокол s3 обрабатывает аутентификацию при установлении зашифрованной связи между клиентом и источником данных S3 через HTTPS. Значение либо true, либо false. Значение по умолчанию true.
Установка значения false может быть полезна в средах тестирования и разработки, чтобы разрешить обмен данными без изменения сертификатов.
Внимание. Установка значения false подвергает риску безопасность, поскольку недействительные учетные данные игнорируются при установлении связи между клиентом и хранилищем данных S3. |
version — Задает версию информации, указанной в предложении LOCATION команды CREATE EXTERNAL TABLE. Значение равно 1 или 2. Значение по умолчанию равно 1.
Если значение равно 1, предложение LOCATION поддерживает URL-адрес Amazon S3 и не содержит параметр региона. Если значение равно 2, предложение LOCATION поддерживает службы, совместимые с S3, и должно включать параметр региона. Параметр region указывает регион источника данных S3. Для этого URL-адреса S3 s3://s3-us-west-2.amazonaws.com/s3test.example.com/dataset1/normal/ регион AWS S3 — us-west-2.
Если версия равна 1 или не указана, это пример предложения LOCATION команды CREATE EXTERNAL TABLE, указывающей конечную точку Amazon S3.
LOCATION ('s3://s3-us-west-2.amazonaws.com/s3test.example.com/dataset1/normal/ config=/home/gpadmin/aws_s3/s3.conf')
Если версия равна 2, это пример предложения LOCATION с параметром региона для сервиса, совместимого с AWS S3.
LOCATION ('s3://test.company.com/s3test.company/test1/normal/ region=local-test config=/home/gpadmin/aws_s3/s3.conf')
Если версия равна 2, предложение LOCATION также может указывать конечную точку Amazon S3. В этом примере указывается конечная точка Amazon S3, использующая параметр региона.
LOCATION ('s3://s3-us-west-2.amazonaws.com/s3test.example.com/dataset1/normal/ region=us-west-2 config=/home/gpadmin/aws_s3/s3.conf')
Примечание. Базе данных RT.Warehouse может потребоваться до threadnum * размер фрагмента памяти на каждом хосте сегмента при загрузке или скачивании файлов S3. Учитывайте это требование к памяти протокола s3 при настройке общей памяти базы данных RT.Warehouse. |
Ограничения протокола s3:
s3://S3_endpoint/bucketname/[S3_prefix]
Утилита базы данных RT.Warehouse gpcheckcloud помогает пользователям создавать и тестировать файл конфигурации протокола s3. Вы можете указать параметры, чтобы проверить возможность доступа к корзине S3 с файлом конфигурации, а также, при необходимости, загрузить данные в файлы в корзине или загрузить данные из них.
Если вы запустите утилиту без каких-либо опций, она отправит файл конфигурации шаблона в STDOUT. Вы можете записать выходные данные и создать файл конфигурации s3 для подключения к Amazon S3.
Утилита устанавливается в директорию $GPHOME/bin базы данных RT.Warehouse.
Синтаксис:
gpcheckcloud {-c | -d} "s3://S3_endpoint/bucketname/[S3_prefix] [config=path_to_config_file]"
gpcheckcloud -u <file_to_upload> "s3://S3_endpoint/bucketname/[S3_prefix] [config=path_to_config_file]"
gpcheckcloud -t
gpcheckcloud -h
Параметры:
-c
Подключитесь к указанному расположению S3 с конфигурацией, указанной в URL-адресе протокола s3, и верните информацию о файлах в расположении S3.
В случае сбоя подключения утилита отображает информацию об ошибках, такую как неверные учетные данные, префикс или адрес сервера (ошибка DNS), или сервер недоступен.
-d
Загрузите данные из указанного местоположения S3 с конфигурацией, указанной в URL-адресе протокола s3, и отправьте выходные данные в STDOUT.
Если файлы сжаты gzip, несжатые данные отправляются в STDOUT.
-u
Загрузите файл в корзину S3, указанную в URL-адресе протокола s3, используя указанный файл конфигурации, если он доступен. Используйте эту опцию, чтобы протестировать параметры сжатия, размера фрагмента и автосжатия для вашей конфигурации.
-t
Отправляет файл конфигурации шаблона в STDOUT. Вы можете записать выходные данные и создать файл конфигурации s3 для подключения к Amazon S3.
-h
Показать справку gpcheckcloud.
Примеры:
В примере ниже утилита запускается без параметров для создания файла конфигурации шаблона s3 mytest_s3.config в текущем каталоге:
gpcheckcloud -t > ./mytest_s3.config
В примере ниже предпринимается попытка загрузить локальный файл test-data.csv в корзину S3 с помощью файла конфигурации s3 s3.mytestconf:
gpcheckcloud -u ./test-data.csv "s3://s3-us-west-2.amazonaws.com/test1/abc config=s3.mytestconf"
В результате успешной загрузки один или несколько файлов помещаются в корзину S3 с использованием формата имени файла abc<segment_id><random>.data[.gz].
В примере ниже выполняется попытка подключения к корзине S3 с помощью файла конфигурации s3 s3.mytestconf.
gpcheckcloud -c "s3://s3-us-west-2.amazonaws.com/test1/abc config=s3.mytestconf"
Загрузите все файлы из корзины S3 и отправьте вывод в STDOUT.
gpcheckcloud -d "s3://s3-us-west-2.amazonaws.com/test1/abc config=s3.mytestconf"
Пользовательский протокол позволяет подключить базу данных RT.Warehouse к источнику данных, к которому нельзя получить доступ с помощью протоколов file://, gpfdist:// или pxf://.
Создание пользовательского протокола требует, чтобы вы реализовали набор функций C с указанными интерфейсами, объявили функции в базе данных RT.Warehouse, а затем использовали команду CREATE TRUSTED PROTOCOL, чтобы активировать протокол в базе данных.
Пример см. в разделе Пример пользовательского протокола доступа к данным (23.11).
По умолчанию, если данные внешней таблицы содержат ошибку, команда завершается ошибкой, и данные не загружаются в целевую таблицу базы данных.
Определите внешнюю таблицу с обработкой ошибок одной строки, чтобы разрешить загрузку правильно отформатированных строк и изолировать ошибки данных в данных внешней таблицы. См. в разделе Обработка ошибок при загрузке (23.3).
Файловый сервер gpfdist использует протокол HTTP. Запросы к внешним таблицам, использующие LIMIT, завершают соединение после извлечения строк, вызывая ошибку сокета HTTP. Если вы используете LIMIT в запросах внешних таблиц, использующих протоколы gpfdist:// или http://, игнорируйте эти ошибки — данные возвращаются в базу данных, как и ожидалось.
Внешние веб-таблицы позволяют базе данных RT.Warehouse обрабатывать динамические источники данных как обычные таблицы базы данных. Поскольку данные веб-таблицы могут изменяться по мере выполнения запроса, повторное сканирование данных невозможно.
CREATE EXTERNAL WEB TABLE создает определение веб-таблицы. Вы можете определить внешние веб-таблицы на основе команд или URL-адресов. Формы определения различны: вы не можете смешивать определения на основе команд и URL-адресов.
Выходные данные команды оболочки или сценария определяют данные веб-таблицы на основе команд. Укажите команду в предложении EXECUTE команды CREATE EXTERNAL WEB TABLE. Данные актуальны на момент выполнения команды. Предложение EXECUTE запускает команду оболочки или сценарий на указанном мастер хосте и/или хосте или хостах сегмента. Команда или сценарий должны находиться на хостах, соответствующих хостам, определенным в предложении EXECUTE.
По умолчанию команда запускается на хостах сегментов, когда в активных сегментах есть выходные строки для обработки. Например, если на каждом хосте сегмента запущено четыре основных экземпляра сегмента, у которых есть выходные строки для обработки, команда выполняется четыре раза для каждого хоста сегмента. При желании вы можете ограничить количество экземпляров сегмента, которые выполняют команду веб-таблицы. Все сегменты, включенные в определение веб-таблицы в предложении ON, выполняют команду параллельно.
Команда, которую вы указываете в определении внешней таблицы, выполняется из базы данных и не может получить доступ к переменным среды из .bashrc или .profile. Задайте переменные среды в предложении EXECUTE. Например:
=# CREATE EXTERNAL WEB TABLE output (output text)
EXECUTE 'PATH=/home/gpadmin/programs; export PATH; myprogram.sh'
FORMAT 'TEXT';
Скрипты должны выполняться пользователем gpadmin и находиться в одном и том же месте на мастер хосте или хосте сегмента.
Следующая команда определяет веб-таблицу, которая запускает скрипт. Сценарий запускается на каждом хосте сегмента, где у сегмента есть выходные строки для обработки.
=# CREATE EXTERNAL WEB TABLE log_output
(linenum int, message text)
EXECUTE '/var/load_scripts/get_log_data.sh' ON HOST
FORMAT 'TEXT' (DELIMITER '|');
Веб-таблица на основе URL получает доступ к данным с веб-сервера по протоколу HTTP. Данные веб-таблицы являются динамическими, данные не подлежат повторному сканированию.
Укажите LOCATION файлов на веб-сервере, используя http://. Файлы веб-данных должны находиться на веб-сервере, к которому могут получить доступ узлы сегмента RT.Warehouse. Количество указанных URL-адресов соответствует количеству экземпляров сегмента, которые работают параллельно для доступа к веб-таблице. Например, если вы укажете два внешних файла для системы базы данных RT.Warehouse с восемью первичными сегментами, два из восьми сегментов будут обращаться к веб-таблице параллельно во время выполнения запроса.
В следующем примере команды определяется веб-таблица, которая получает данные с нескольких URL-адресов:
=# CREATE EXTERNAL WEB TABLE ext_expenses (name text,
date date, amount float4, category text, description text)
LOCATION (
'http://intranet.company.com/expenses/sales/file.csv',
'http://intranet.company.com/expenses/exec/file.csv',
'http://intranet.company.com/expenses/finance/file.csv',
'http://intranet.company.com/expenses/ops/file.csv',
'http://intranet.company.com/expenses/marketing/file.csv',
'http://intranet.company.com/expenses/eng/file.csv'
)
FORMAT 'CSV' ( HEADER );
В этих примерах показано, как определять внешние данные с помощью различных протоколов. Каждая команда CREATE EXTERNAL TABLE может содержать только один протокол.
Примечание. При использовании IPv6 всегда заключайте числовые IP-адреса в квадратные скобки. |
Запустите gpfdist перед созданием внешних таблиц с помощью протокола gpfdist. Следующий код запускает программу файлового сервера gpfdist в фоновом режиме на порту 8081, обслуживающую файлы из каталога /var/data/staging. Логи сохраняются в /home/gpadmin/log.
gpfdist -p 8081 -d /var/data/staging -l /home/gpadmin/log &
Создает читаемую внешнюю таблицу ext_expenses, используя протокол gpfdist. Файлы отформатированы с вертикальной чертой (|) в качестве разделителя столбцов.
=# CREATE EXTERNAL TABLE ext_expenses ( name text,
date date, amount float4, category text, desc1 text )
LOCATION ('gpfdist://etlhost-1:8081/*')
FORMAT 'TEXT' (DELIMITER '|');
Создает читаемую внешнюю таблицу ext_expenses, используя протокол gpfdist из всех файлов с расширением txt. Разделителем столбцов является вертикальная черта ( | ), а NULL (' ') — пробел.
=# CREATE EXTERNAL TABLE ext_expenses ( name text,
date date, amount float4, category text, desc1 text )
LOCATION ('gpfdist://etlhost-1:8081/*.txt',
'gpfdist://etlhost-2:8081/*.txt')
FORMAT 'TEXT' ( DELIMITER '|' NULL ' ') ;
Создает читаемую внешнюю таблицу ext_expenses из всех файлов с расширением txt, используя протокол gpfdists. Разделителем столбцов является вертикальная черта ( | ), а NULL (' ') — пробел. Сведения о расположении сертификатов безопасности см. в разделе Протокол gpfdists:// (22.2.3).
=# CREATE EXTERNAL TABLE ext_expenses ( name text,
date date, amount float4, category text, desc1 text )
LOCATION ('gpfdists://etlhost-1:8081/*.txt',
'gpfdists://etlhost-2:8082/*.txt')
FORMAT 'TEXT' ( DELIMITER '|' NULL ' ') ;
Использует протокол gpfdist для создания читаемой внешней таблицы ext_expenses из всех файлов с расширением txt. Разделителем столбцов является вертикальная черта ( | ), а NULL (' ') — пробел.
Доступ к внешней таблице осуществляется в режиме изоляции однострочных ошибок. Ошибки форматирования входных данных фиксируются внутри базы данных RT.Warehouse с описанием ошибки. Вы можете просмотреть ошибки, исправить проблемы, а затем повторно загрузить отклоненные данные. Если количество ошибок в сегменте больше пяти (значение SEGMENT REJECT LIMIT), вся операция с внешней таблицей завершается с ошибкой и никакие строки не обрабатываются.
=# CREATE EXTERNAL TABLE ext_expenses ( name text,
date date, amount float4, category text, desc1 text )
LOCATION ('gpfdist://etlhost-1:8081/*.txt',
'gpfdist://etlhost-2:8082/*.txt')
FORMAT 'TEXT' ( DELIMITER '|' NULL ' ')
LOG ERRORS SEGMENT REJECT LIMIT 5;
Для создания удобочитаемой таблицы ext_expenses из текстовых файлов в формате CSV:
=# CREATE EXTERNAL TABLE ext_expenses ( name text,
date date, amount float4, category text, desc1 text )
LOCATION ('gpfdist://etlhost-1:8081/*.txt',
'gpfdist://etlhost-2:8082/*.txt')
FORMAT 'CSV' ( DELIMITER ',' )
LOG ERRORS SEGMENT REJECT LIMIT 5;
Создает удобочитаемую внешнюю таблицу ext_expenses, используя протокол pxf. Разделителем столбцов является вертикальная черта ( | ).
=# CREATE EXTERNAL TABLE ext_expenses ( name text,
date date, amount float4, category text, desc1 text )
LOCATION ('pxf://dir/data/filename.txt?PROFILE=hdfs:text')
FORMAT 'TEXT' (DELIMITER '|');
См. Доступ к внешним данным с помощью PXF(22.1) для получения информации об использовании RT.Warehouse Platform Extension Framework (PXF) для доступа к данным в распределенной файловой системе Hadoop.
Создает читаемую внешнюю таблицу ext_expenses, используя файловый протокол. Файлы имеют формат CSV и имеют строку заголовка.
=# CREATE EXTERNAL TABLE ext_expenses ( name text,
date date, amount float4, category text, desc1 text )
LOCATION ('file://filehost/data/international/*',
'file://filehost/data/regional/*',
'file://filehost/data/supplement/*.csv')
FORMAT 'CSV' (HEADER);
Создает читаемую внешнюю веб-таблицу, которая выполняет скрипт один раз для каждого узла сегмента:
=# CREATE EXTERNAL WEB TABLE log_output (linenum int,
message text)
EXECUTE '/var/load_scripts/get_log_data.sh' ON HOST
FORMAT 'TEXT' (DELIMITER '|');
Создает доступную для записи внешнюю таблицу sales_out, которая использует gpfdist для записи выходных данных в файл sales.out. Разделителем столбцов является вертикальная черта ( | ), а NULL (' ') — пробел. Файл будет создан в каталоге, указанном при запуске файлового сервера gpfdist.
=# CREATE WRITABLE EXTERNAL TABLE sales_out (LIKE sales)
LOCATION ('gpfdist://etl1:8081/sales.out')
FORMAT 'TEXT' ( DELIMITER '|' NULL ' ')
DISTRIBUTED BY (txn_id);
Создает доступную для записи внешнюю веб-таблицу, camp_out, которая направляет выходные данные, полученные сегментами, в исполняемый скрипт to_adreport_etl.sh:
=# CREATE WRITABLE EXTERNAL WEB TABLE campaign_out
(LIKE campaign)
EXECUTE '/var/unload_scripts/to_adreport_etl.sh'
FORMAT 'TEXT' (DELIMITER '|');
База данных RT.Warehouse может читать и записывать XML-данные во внешние таблицы и из них с помощью gpfdist.
База данных RT.Warehouse реализует части спецификации SQL/MED, позволяя вам получать доступ к данным, которые находятся за пределами RT.Warehouse, с помощью обычных запросов SQL. Такие данные называются внешними(foreign) или внешними данными.
Вы можете получить доступ к внешним данным с помощью обертки сторонних данных (foreign-data wrapper). Обертка сторонних данных — это библиотека, которая взаимодействует с удаленным источником данных. Эта библиотека скрывает сведения о подключении к источнику и доступе к данным.
Примечание. Большинство оберток сторонних данных PostgreSQL должны работать с базой данных RT.Warehouse. Однако обертка сторонних данных PostgreSQL подключаются только через мастер базы данных RT.Warehouse и не имеют прямого доступа к экземплярам сегментов базы данных RT.Warehouse. |
Если ни одна из существующих оберток сторонних данных не подходит вам, вы можете написать свою собственную, как описано в разделе Создание обертки сторонних данных (22.3.1).
Чтобы получить доступ к внешним данным, создайте объект внешнего сервера, который определяет, как подключиться к конкретному удаленному источнику данных в соответствии с набором параметров, используемых поддерживающей его оберткой сторонних данных. Затем вы создаете одну или несколько внешних таблиц, которые определяют структуру удаленных данных. Внешняя таблица может использоваться в запросах так же, как и обычная таблица, но внешняя таблица не хранится на сервере базы данных RT.Warehouse. Всякий раз, когда осуществляется доступ к сторонней таблице, база данных RT.Warehouse запрашивает у обертки сторонних данных получение данных или обновление данных (если поддерживается оберткой) из удаленного источника.
Примечание. GPORCA не поддерживает сторонние таблицы. Запрос к сторонней таблице всегда возвращается к Планировщику Postgres. |
Для доступа к удаленным данным может потребоваться аутентификация на удаленном источнике данных. Эта информация может быть предоставлена сопоставлением пользователей, которое может предоставить дополнительные данные, такие как имя пользователя и пароль, в зависимости от текущей роли базы данных RT.Warehouse.
Все операции с внешней таблицей обрабатываются через ее обертку сторонних данных (Foreign Data Wrapper, FDW) — библиотеку, состоящую из набора функций, которые вызывает основной сервер базы данных RT.Warehouse. Обертка сторонних данных отвечает за выборку данных из удаленного хранилища данных и возврат их исполнителю базы данных RT.Warehouse. Если поддерживается обновление внешних данных, обертка также должна обрабатывать это.
Примечание. Стандарт SQL определяет интерфейс для написания обертка сторонних данных. Однако база данных RT.Warehouse не реализует этот API, поскольку его внедрение в RT.Warehouse потребует значительных усилий, а стандартный API еще не получил широкого распространения. |
При разработке с помощью API-интерфейса обертки сторонних данных базы данных RT.Warehouse:
Реализация обертки сторонних данных базы данных RT.Warehouse имеет следующие известные проблемы и ограничения:
Файлы заголовков базы данных RT.Warehouse, которые вы можете использовать при разработке обертки для сторонних данных, установлены в $GPHOME/ include/postgresql/server/ directory:
Ваш код FDW также может зависеть от файлов заголовков и библиотек, необходимых для доступа к удаленному хранилищу данных.
Разработчик обертки сторонних данных должен реализовать функцию обработчика, и, необязательно, функцию проверки, вызываемые SQL. Обе функции должны быть написаны на скомпилированном языке, таком как C, с использованием интерфейса версии 1.
Функция-обработчик просто возвращает структуру указателей на функции обратного вызова, которые будут вызываться планировщиком, исполнителем базы данных RT.Warehouse и различными командами обслуживания. Функция-обработчик должна быть зарегистрирована в базе данных RT.Warehouse как не принимающая аргументов и возвращающая специальный псевдотип fdw_handler. Например:
CREATE FUNCTION NEW_fdw_handler()
RETURNS fdw_handler
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;
Большая часть усилий при написании обертки сторонних данных приходится на реализацию функций обратного вызова. Функции обратного вызова FDW API, простые функции C, которые не видны и не могут быть вызваны на уровне SQL, описаны в разделе Функции обратного вызова обертки сторонних данных (22.3.1.5).
Функция проверки отвечает за проверку параметров, предоставленных в командах CREATE и ALTER для своей обертки сторонних данных, а также внешних серверов, сопоставлений пользователей и внешних таблиц, использующих эту обертку. Функция валидатора должна быть зарегистрирована как принимающая два аргумента: текстовый массив, содержащий проверяемые параметры, и OID, представляющий тип объекта, с которым связаны параметры. Например:
CREATE FUNCTION NEW_fdw_validator( text[], oid )
RETURNS void
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;
Аргумент OID отражает тип системного каталога, в котором будет храниться объект: ForeignDataWrapperRelationId, ForeignServerRelationId, UserMappingRelationId или ForeignTableRelationId. Если обертка сторонних данных не предоставляет функцию проверки подлинности, база данных RT.Warehouse не проверяет действительность опции во время создания или изменения объекта.
API-интерфейс обертки сторонних данных определяет функции обратного вызова (Callback), которые вызывает база данных RT.Warehouse при сканировании и обновлении внешних таблиц. API также включает обратные вызовы для выполнения операций объяснения и анализа сторонней таблицы.
Функция-обработчик обертки сторонних данных возвращает структуру FdwRoutine, созданную palloc, содержащую указатели на функции обратного вызова, описанные ниже. Структура FdwRoutine находится в заголовочном файле foreign/fdwapi.h и определяется следующим образом:
/*
* FdwRoutine is the struct returned by a foreign-data wrapper's handler
* function. It provides pointers to the callback functions needed by the
* planner and executor.
*
* More function pointers are likely to be added in the future. Therefore
* it's recommended that the handler initialize the struct with
* makeNode(FdwRoutine) so that all fields are set to NULL. This will
* ensure that no fields are accidentally left undefined.
*/
typedef struct FdwRoutine
{
NodeTag type;
/* Functions for scanning foreign tables */
GetForeignRelSize_function GetForeignRelSize;
GetForeignPaths_function GetForeignPaths;
GetForeignPlan_function GetForeignPlan;
BeginForeignScan_function BeginForeignScan;
IterateForeignScan_function IterateForeignScan;
ReScanForeignScan_function ReScanForeignScan;
EndForeignScan_function EndForeignScan;
/*
* Remaining functions are optional. Set the pointer to NULL for any that
* are not provided.
*/
/* Functions for updating foreign tables */
AddForeignUpdateTargets_function AddForeignUpdateTargets;
PlanForeignModify_function PlanForeignModify;
BeginForeignModify_function BeginForeignModify;
ExecForeignInsert_function ExecForeignInsert;
ExecForeignUpdate_function ExecForeignUpdate;
ExecForeignDelete_function ExecForeignDelete;
EndForeignModify_function EndForeignModify;
IsForeignRelUpdatable_function IsForeignRelUpdatable;
/* Support functions for EXPLAIN */
ExplainForeignScan_function ExplainForeignScan;
ExplainForeignModify_function ExplainForeignModify;
/* Support functions for ANALYZE */
AnalyzeForeignTable_function AnalyzeForeignTable;
} FdwRoutine;
Вы должны реализовать функции, связанные со сканированием, в обертке сторонних данных; реализация других функций обратного вызова не является обязательной. В Таблице 130 представлены функции обратного вызова.
Таблица 130. Функции обратного вызова (Callback Signature), связанные со сканированием
Callback Signature | Описание |
---|---|
|
Получите оценки размера отношения для внешней таблицы. Вызывается в начале планирования запроса к сторонней таблице. |
|
Создайте возможные пути доступа для сканирования сторонней таблицы. Вызывается во время планирования запроса. Примечание: FDW, совместимый с базой данных RT.Warehouse, должен вызывать create_foreignscan_path() в своей функции обратного вызова GetForeignPaths(). |
|
Создайте узел плана ForeignScan из выбранного внешнего пути доступа. Вызывается в конце планирования запроса. |
|
Начните выполнение внешнего сканирования. Вызывается во время запуска исполнителя. |
|
Получите одну строку из внешнего источника, возвращая ее в слот таблицы кортежей. Вернуть NULL, если больше нет доступных строк. |
|
Перезапустите сканирование с самого начала. |
|
Завершите сканирование и освободите ресурсы. |
FDW API экспортирует несколько вспомогательных функций с главного сервера базы данных RT.Warehouse, чтобы авторы оберток сторонних данных имели легкий доступ к атрибутам объектов, связанных с FDW, например к параметрам, предоставляемым, когда пользователь создает или изменяет обертку сторонних данных, сервер или внешнюю таблицу. Чтобы использовать эти вспомогательные функции, вы должны включить заголовочный файл foreign.h в свой исходный файл:
#include "foreign/foreign.h"
FDW API включает вспомогательные функции, перечисленные в Таблице 131 ниже. Дополнительные сведения об этих функциях см. в разделе Вспомогательные функции обертки сторонних данных в документации PostgreSQL.
Таблица 131. Вспомогательные функции обертки сторонних данных
Helper Signature | Описание |
---|---|
|
Возвращает объект ForeignDataWrapper для обертки сторонних данных с заданным OID. |
|
Возвращает объект ForeignDataWrapper для обертки сторонних данных с заданным именем. |
|
Возвращает объект ForeignServer для внешнего сервера с заданным OID. |
|
Возвращает объект ForeignServer для внешнего сервера с заданным именем. |
|
Возвращает объект UserMapping для отображения пользователя данной роли на данном сервере. |
|
Возвращает объект ForeignTable для внешней таблицы с заданным OID. |
|
Возвращает параметры FDW для каждого столбца для столбца с заданным OID внешней таблицы и номером атрибута. |
Пользователь базы данных RT.Warehouse может указать параметр mpp_execute при создании или изменении внешней таблицы, внешнего сервера или обертки сторонних данных. Обертка сторонних данных, совместимая с базой данных RT.Warehouse, проверяет значение параметра mpp_execute при сканировании и использует его, чтобы определить, откуда запрашивать данные — от мастера (по умолчанию), любого (мастера или любого сегмента) или всех сегментов.
Примечание. Операции записи/обновления с использованием обертки сторонних данных всегда выполняются в мастере базы данных RT.Warehouse, независимо от настройки mpp_execute. |
Следующий фрагмент кода сканирования проверяет значение mpp_execute, связанное с внешней таблицей:
ForeignTable *table = GetForeignTable(foreigntableid);
if (table->exec_location == FTEXECLOCATION_ALL_SEGMENTS)
{
...
}
else if (table->exec_location == FTEXECLOCATION_ANY)
{
...
}
else if (table->exec_location == FTEXECLOCATION_MASTER)
{
...
}
Если внешняя таблица не была создана с настройкой параметра mpp_execute, проверяется и используется параметр mpp_execute внешнего сервера, а затем обертка сторонних данных. Если ни один из объектов, связанных с внешними данными, не имеет параметра mpp_execute, параметр по умолчанию — master.
Если обертка сторонних данных поддерживает mpp_execute 'all', она реализует политику, которая сопоставляет сегменты RT.Warehouse с данными. Чтобы не дублировать данные, полученные с удаленного устройства, FDW в каждом сегменте должен иметь возможность установить, за какую часть данных они отвечают. FDW может использовать идентификатор сегмента и количество сегментов, чтобы облегчить это определение. Следующий фрагмент кода демонстрирует, как обертка сторонних данных может получить номер сегмента и общее количество сегментов:
int segmentNumber = GpIdentity.segindex;
int totalNumberOfSegments = getgpsegmentCount();
Вы компилируете функции обертки сторонних данных, которые вы написали с помощью FDW API, в одну или несколько общих библиотек, которые сервер базы данных RT.Warehouse загружает по запросу.
Вы можете использовать инфраструктуру расширения сборки PostgreSQL (PGXS) для сборки исходного кода вашей обертки сторонних данных для установки базы данных RT.Warehouse. Этот фреймворк автоматизирует общие правила сборки для простых модулей. Если у вас есть более сложный вариант использования, вам нужно будет написать собственную систему сборки.
Чтобы использовать инфраструктуру PGXS для создания общей библиотеки для FDW, создайте простой файл Makefile, в котором задаются переменные, специфичные для PGXS.
Примечание. Обратитесь к разделу Инфраструктура построения расширений в документации PostgreSQL для получения информации о переменных Makefile, поддерживаемых PGXS. |
Например, следующий Makefile создает разделяемую библиотеку в текущем рабочем каталоге с именем base_fdw.so из двух исходных файлов C, base_fdw_1.c и base_fdw_2.c:
MODULE_big = base_fdw
OBJS = base_fdw_1.o base_fdw_2.o
PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
PG_CPPFLAGS = -I$(shell $(PG_CONFIG) --includedir)
SHLIB_LINK = -L$(shell $(PG_CONFIG) --libdir)
include $(PGXS)
Далее следует описание директив, используемых в этом Makefile:
Чтобы упаковать обертку сторонних данных как расширение базы данных RT.Warehouse, вы создаете файлы сценария (newfdw--version.sql) и управляющие файлы (newfdw.control), которые регистрируют функции обработчика и проверки FDW, создаете обертку сторонних данных и идентифицируете характеристики файла общей библиотеки FDW.
Примечание. Раздел Упаковка связанных объектов в расширение в документации PostgreSQL описывает, как упаковать расширение. |
Пример файла сценария расширения обертки сторонних данных с именем base_fdw--1.0.sql:
CREATE FUNCTION base_fdw_handler()
RETURNS fdw_handler
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;
CREATE FUNCTION base_fdw_validator(text[], oid)
RETURNS void
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;
CREATE FOREIGN DATA WRAPPER base_fdw
HANDLER base_fdw_handler
VALIDATOR base_fdw_validator;
Пример управляющего файла FDW с именем base_fdw.control:
# base_fdw FDW extension
comment = 'base foreign-data wrapper implementation; does not do much'
default_version = '1.0'
module_pathname = '$libdir/base_fdw'
relocatable = true
Когда вы добавляете следующие директивы в Makefile, вы определяете базовое имя управляющего файла расширения FDW (EXTENSION) и сценарий SQL (DATA):
EXTENSION = base_fdw
DATA = base_fdw--1.0.sql
Запуск make install с этими директивами в Makefile копирует общую библиотеку и FDW SQL и управляющие файлы в указанные или заданные по умолчанию местоположения в вашей установке базы данных RT.Warehouse ($GPHOME).
Вы должны упаковать общую библиотеку FDW и файлы расширения в форме, подходящей для развертывания в кластере базы данных RT.Warehouse. При создании и развертывании пакета примите во внимание следующее:
Протокол gpfdist используется в SQL-команде CREATE EXTERNAL TABLE для доступа к внешним данным, обслуживаемым утилитой файлового сервера gpfdist базы данных RT.Warehouse. Когда внешние данные обслуживаются gpfdist, все сегменты в системе базы данных RT.Warehouse могут читать или записывать данные внешней таблицы параллельно.
В этом разделе описываются задачи настройки и управления для использования gpfdist с внешними таблицами.
Утилита файлового сервера gpfdist находится в каталоге $GPHOME/bin на мастер хосте базы данных RT.Warehouse и на каждом хосте сегмента. Когда вы запускаете экземпляр gpfdist, вы указываете порт для прослушивания и путь к каталогу, содержащему файлы для чтения или куда файлы должны быть записаны. Например, эта команда запускает gpfdist в фоновом режиме, прослушивая порт 8801 и обслуживая файлы в каталоге /home/gpadmin/external_files:
$ gpfdist -p 8801 -d /home/gpadmin/external_files &
Предложение LOCATION команды CREATE EXTERNAL TABLE соединяет определение внешней таблицы с одним или несколькими экземплярами gpfdist. Если внешняя таблица доступна для чтения, сервер gpfdist считывает записи данных из файлов из указанного каталога, упаковывает их в блок и отправляет блок в ответ на запрос сегмента базы данных RT.Warehouse. Сегменты распаковывают полученные строки и распределяют их в соответствии с политикой распределения внешней таблицы. Если внешняя таблица доступна для записи, сегменты отправляют блоки строк в запросе к gpfdist, а gpfdist записывает их во внешний файл.
Внешние файлы данных могут содержать строки в формате CSV или любом текстовом формате с разделителями, поддерживаемом предложением FORMAT команды CREATE EXTERNAL TABLE. Кроме того, gpfdist можно настроить с файлом в формате YAML для преобразования внешних файлов данных между поддерживаемым текстовым форматом и другим форматом, например XML или JSON.
Для читаемых внешних таблиц gpfdist автоматически распаковывает файлы gzip (.gz) и bzip2 (.bz2). Вы можете использовать подстановочный знак (*) или другой шаблон соответствия в стиле C, чтобы обозначить несколько файлов для чтения. Предполагается, что внешние файлы относятся к каталогу, указанному при запуске экземпляра gpfdist.
Вы можете запускать экземпляры gpfdist на нескольких хостах, и вы можете запускать несколько экземпляров gpfdist на каждом хосте. Это позволяет вам развертывать серверы gpfdist стратегически, чтобы вы могли достичь высокой скорости загрузки и выгрузки данных, используя всю доступную полосу пропускания сети и параллелизм базы данных RT.Warehouse.
На Рисунке 7 показана внешняя таблица с использованием одного экземпляра gpfdist с несколькими сетевыми адаптерами.
Рисунок 7. Внешняя таблица с использованием одного экземпляра gpfdist с несколькими сетевыми адаптерами
На Рисунке 8 показана схема Внешних таблиц, использующих несколько экземпляров gpfdist с несколькими сетевыми адаптерами.
Рисунок 8 Внешние таблицы, использующие несколько экземпляров gpfdist с несколькими сетевыми адаптерами
Примечание. Используйте вертикальные черты (|) для разделения форматированного текста при отправке файлов в gpfdist. База данных RT.Warehouse заключает текстовые строки, разделенные запятыми, в одинарные или двойные кавычки. gpfdist должен удалить кавычки для разбора строк. Использование разделителей для разделения форматированного текста позволяет избежать лишних действий и повысить производительность. |
Параметр конфигурации сервера gp_external_max_segs управляет количеством экземпляров сегмента, которые могут одновременно обращаться к одному экземпляру gpfdist. 64 по умолчанию. Вы можете установить количество сегментов таким образом, чтобы некоторые сегменты обрабатывали внешние файлы данных, а некоторые выполняли другую обработку базы данных. Установите этот параметр в файле postgresql.conf вашего главного экземпляра.
gpfdist устанавливается в $GPHOME/bin вашей установки мастер хоста базы данных RT.Warehouse. Запустите gpfdist на компьютере, отличном от главного или резервного мастера базы данных RT.Warehouse, например, на компьютере, предназначенном для обработки ETL. Запуск gpfdist на мастере или резервном мастер сервере может повлиять на производительность выполнения запросов. Чтобы установить gpfdist на свой сервер ETL следуйте инструкциям по установке.
Вы можете запустить gpfdist в вашем текущем местоположении каталога или в любом каталоге, который вы укажете. Порт по умолчанию — 8080.
В текущем каталоге введите:
gpfdist &
Из другого каталога укажите каталог, из которого следует обслуживать файлы, и, при необходимости, HTTP-порт для запуска.
Чтобы запустить gpfdist в фоновом режиме и записать выходные сообщения и ошибки в файл лога:
$ gpfdist -d /var/load_files -p 8081 -l /home/gpadmin/log &
Для нескольких экземпляров gpfdist на одном хосте ETL (см. Рисунок 7) используйте разные базовые каталоги и порты для каждого экземпляра. Например:
$ gpfdist -d /var/load_files1 -p 8081 -l /home/gpadmin/log1 &
$ gpfdist -d /var/load_files2 -p 8082 -l /home/gpadmin/log2 &
Чтобы остановить gpfdist, когда он работает в фоновом режиме:
Сначала найдите его идентификатор процесса.
$ ps -ef | grep gpfdist
Затем убейте процесс, например (где 3456 - идентификатор процесса в данном примере):
$ kill 3456
Сегменты обращаются к gpfdist во время выполнения. Убедитесь, что хосты сегмента RT.Warehouse имеют сетевой доступ к gpfdist. gpfdist — это веб-сервер: проверьте подключение, выполнив следующую команду на каждом хосте в массиве RT.Warehouse (сегменты и мастер):
$ wget http://gpfdist_hostname:port/filename
Определение CREATE EXTERNAL TABLE должно иметь правильное имя хоста, порт и имена файлов для gpfdist. Укажите имена файлов и пути относительно каталога, из которого gpfdist обслуживает файлы (путь к каталогу, указанный при запуске gpfdist).
Если вы запускаете gpfdist в своей системе и сеть IPv6 отключена, gpfdist отображает это предупреждающее сообщение при проверке порта IPv6.
[WRN gpfdist.c:2050] Creating the socket failed
Если соответствующий порт IPv4 доступен, gpfdist использует этот порт, а предупреждение для порта IPv6 можно игнорировать. Чтобы просмотреть информацию о портах, которые тестирует gpfdist, используйте опцию -V.
При чтении или записи данных с помощью протокола gpfdist или gfdists утилита gpfdist отклоняет HTTP-запросы, которые не содержат X-GP-PROTO в заголовке запроса. Если X-GP-PROTO не обнаружен в заголовке запроса, gpfist возвращает ошибку 400 в строке состояния заголовка ответа HTTP: 400 invalid request (no gp-proto).
База данных RT.Warehouse включает X-GP-PROTO в заголовок HTTP-запроса, чтобы указать, что запрос исходит от базы данных RT.Warehouse.
Если утилита gpfdist зависает без активности чтения или записи, вы можете сгенерировать дамп ядра при следующем зависании, чтобы помочь отладить проблему. Установите переменную окружения GPFDIST_WATCHDOG_TIMER на количество секунд отсутствия активности, которое необходимо выждать, прежде чем gpfdist будет принудительно завершена. Если переменная окружения установлена и gpfdist зависает, утилита прерывается через указанное количество секунд, создает дамп ядра и отправляет информацию о прерывании в файл лога.
Следующий пример устанавливает переменную окружения в системе Linux таким образом, что gpfdist завершает работу после 300 секунд (5 минут) отсутствия активности.
export GPFDIST_WATCHDOG_TIMER=300
В этом разделе описаны методы загрузки и записи данных в базу данных RT.Warehouse, а также форматирование файлов данных.
RT.Warehouse поддерживает высокопроизводительную параллельную загрузку и выгрузку данных, а для небольших объемов данных - однофайловый непараллельный импорт и экспорт данных.
База данных RT.Warehouse может читать и записывать в несколько типов внешних источников данных, включая текстовые файлы, файловые системы Hadoop, Amazon S3 и веб-серверы.
Более конкретную информацию об интеграции RT.Warehouse и коннекторов см. в отдельном документе Краткое описание интеграции RT.Warehouse и коннекторов.
Выбранный вами метод загрузки данных зависит от характеристик исходных данных — их местоположения, размера, формата и любых необходимых преобразований.
В простейшем случае команда COPY SQL загружает данные в таблицу из текстового файла, доступного для главного экземпляра базы данных RT.Warehouse. Это не требует настройки и обеспечивает хорошую производительность для небольших объемов данных. С помощью команды COPY данные, скопированные в базу данных или из нее, передаются между одним файлом на мастер хосте и базой данных. Это ограничивает общий размер набора данных емкостью файловой системы, в которой находится внешний файл, и ограничивает передачу данных одним потоком записи файла.
Более эффективные варианты загрузки данных для больших наборов данных используют преимущества архитектуры MPP базы данных RT.Warehouse, используя сегменты базы данных RT.Warehouse для параллельной загрузки данных. Эти методы позволяют загружать данные одновременно из нескольких файловых систем через несколько сетевых адаптеров на несколько хостов, достигая очень высоких скоростей передачи данных. Внешние таблицы позволяют вам обращаться к внешним файлам из базы данных, как если бы они были обычными таблицами базы данных. При использовании с gpfdist, программой параллельного распространения файлов базы данных RT.Warehouse, внешние таблицы обеспечивают полный параллелизм, используя ресурсы всех сегментов базы данных RT.Warehouse для загрузки или выгрузки данных.
База данных RT.Warehouse использует параллельную архитектуру распределенной файловой системы Hadoop для доступа к файлам в этой системе.
Используйте команды SQL, такие как INSERT и SELECT, для запроса доступной для чтения внешней таблицы так же, как вы запрашиваете обычную таблицу базы данных. Например, чтобы загрузить данные о командировочных расходах из внешней таблицы ext_expenses в таблицу базы данных Costs_travel:
=# INSERT INTO expenses_travel
SELECT * from ext_expenses where category='travel';
Чтобы загрузить все данные в новую таблицу базы данных:
=# CREATE TABLE expenses AS SELECT * from ext_expenses;
База данных RT.Warehouse поддерживает форматы TEXT и CSV для импорта и экспорта данных через внешние таблицы. Вы можете загружать и сохранять данные в других форматах, определив пользовательский формат или пользовательский протокол или настроив преобразование с помощью параллельного файлового сервера gpfdist.
Пользовательский формат данных указывается в предложении FORMAT команды CREATE EXTERNAL TABLE.
FORMAT 'CUSTOM' (formatter=format_function, key1=val1,...keyn=valn)
Где ключевое слово CUSTOM указывает, что данные имеют пользовательский формат, а средство форматирования указывает функцию, используемую для форматирования данных, за которой следуют параметры, разделенные запятыми, для функции форматирования.
База данных RT.Warehouse предоставляет функции для форматирования данных фиксированной ширины, но вы должны создать функции форматирования для данных переменной ширины. Шаги следующие:
Укажите пользовательские форматы для данных фиксированной ширины с помощью функций базы данных RT.Warehouse fixedwith_in и fixedwidth_out. Эти функции уже существуют в файле $GPHOME/share/postgresql/cdb_external_extensions.sql. В следующем примере объявляется пользовательский формат, а затем вызывается функция fixedwidth_in для форматирования данных.
CREATE READABLE EXTERNAL TABLE students (
name varchar(20), address varchar(30), age int)
LOCATION ('file://<host>/file/path/')
FORMAT 'CUSTOM' (formatter=fixedwidth_in,
name='20', address='30', age='4');
Следующие параметры определяют, как импортировать данные фиксированной ширины:
Чтобы загрузить все поля в фиксированной строке с данными, вы должны загрузить их в их физическом порядке. Вы должны указать длину поля, но не можете указать начальную и конечную позицию. Имена полей в аргументах фиксированной ширины должны соответствовать порядку в списке полей в начале команды CREATE TABLE.
Конечные пробелы по умолчанию обрезаются. Чтобы оставить пробелы в конце, используйте параметр save_blanks=on. Вы можете сбросить параметр завершающих пробелов до значения по умолчанию с помощью параметра save_blanks=off.
Используйте параметр null='null_string_value', чтобы указать значение для нулевых символов.
Используйте параметр line_delim='line_ending', чтобы указать символ конца строки. Следующие примеры охватывают большинство случаев. E указывает константу управляющей строки.
line_delim=E'\n'
line_delim=E'\r'
line_delim=E'\r\n'
line_delim='abc'
В следующих примерах показано, как читать данные фиксированной ширины.
CREATE READABLE EXTERNAL TABLE students (
name varchar(20), address varchar(30), age int)
LOCATION ('file://<host>/file/path/')
FORMAT 'CUSTOM' (formatter=fixedwidth_in,
name=20, address=30, age=4);
CREATE READABLE EXTERNAL TABLE students (
name varchar(20), address varchar(30), age int)
LOCATION ('gpfdist://<host>:<portNum>/file/path/')
FORMAT 'CUSTOM' (formatter=fixedwidth_in,
name=20, address=30, age=4,
preserve_blanks='on',null='NULL');
CREATE READABLE EXTERNAL TABLE students (
name varchar(20), address varchar(30), age int)
LOCATION ('file://<host>/file/path/')
FORMAT 'CUSTOM' (formatter=fixedwidth_in,
name='20', address='30', age='4', line_delim='?@')
CREATE WRITABLE EXTERNAL TABLE students_out (
name varchar(20), address varchar(30), age int)
LOCATION ('gpfdist://<host>:<portNum>/file/path/students_out.txt')
FORMAT 'CUSTOM' (formatter=fixedwidth_out,
name=20, address=30, age=4, line_delim=E'\r\n');
База данных RT.Warehouse предоставляет такие протоколы, как gpfdist, http и file для доступа к данным по сети, или вы можете создать собственный протокол. Вы можете использовать стандартные форматы данных, TEXT и CSV, или пользовательский формат данных с пользовательскими протоколами.
Вы можете создать собственный протокол всякий раз, когда доступных встроенных протоколов недостаточно для конкретной потребности. Например, вы можете подключить базу данных RT.Warehouse параллельно к другой системе напрямую и передавать данные из одной системы в другую без необходимости материализовать данные на диске или использовать промежуточный процесс, такой как gpfdist. Вы должны быть суперпользователем, чтобы создать и зарегистрировать собственный протокол.
1. Создавайте функции отправки, получения и (необязательно) проверки на языке C с помощью предопределенного API. Эти функции скомпилированы и зарегистрированы в базе данных RT.Warehouse.
2. После написания и компиляции функций чтения и записи в общий объект (.so) объявите функцию базы данных, которая указывает на файл .so и имена функций.
В следующих примерах используется скомпилированный код импорта и экспорта.
CREATE FUNCTION myread() RETURNS integer
as '$libdir/gpextprotocol.so', 'myprot_import'
LANGUAGE C STABLE;
CREATE FUNCTION mywrite() RETURNS integer
as '$libdir/gpextprotocol.so', 'myprot_export'
LANGUAGE C STABLE;
Формат дополнительной функции валидатора:
CREATE OR REPLACE FUNCTION myvalidate() RETURNS void
AS '$libdir/gpextprotocol.so', 'myprot_validate'
LANGUAGE C STABLE;
3. Создайте протокол, который обращается к этим функциям. Validatorfunc является необязательным.
CREATE TRUSTED PROTOCOL myprot(
writefunc='mywrite',
readfunc='myread',
validatorfunc='myvalidate');
4. При необходимости предоставьте доступ другим пользователям.
GRANT ALL ON PROTOCOL myprot TO otheruser;
5. Используйте протокол во внешних таблицах, доступных для чтения или записи.
CREATE WRITABLE EXTERNAL TABLE ext_sales(LIKE sales)
LOCATION ('myprot://<meta>/<meta>/…')
FORMAT 'TEXT';
CREATE READABLE EXTERNAL TABLE ext_sales(LIKE sales)
LOCATION('myprot://<meta>/<meta>/…')
FORMAT 'TEXT';
Объявите пользовательские протоколы с помощью команды SQL CREATE TRUSTED PROTOCOL, а затем используйте команду GRANT, чтобы предоставить доступ своим пользователям. Например:
GRANT SELECT ON PROTOCOL <protocol name> TO <user name>;
GRANT INSERT ON PROTOCOL <protocol name> TO <user name>;
GRANT ALL ON PROTOCOL <protocol name> TO <user name>;
Читаемые внешние таблицы чаще всего используются для выбора данных для загрузки в обычные таблицы базы данных. Для запроса данных внешней таблицы используются команды CREATE TABLE AS SELECT или INSERT INTO. По умолчанию, если данные содержат ошибку, вся команда завершается неудачно, и данные не загружаются в целевую таблицу базы данных.
Предложение SEGMENT REJECT LIMIT позволяет изолировать ошибки формата в данных внешней таблицы и продолжить загрузку правильно отформатированных строк. Используйте SEGMENT REJECT LIMIT для установки порога ошибок, указывая счетчик пределов отклонения в виде количества строк (по умолчанию) или в виде PERCENT от общего количества строк (1-100).
Вся операция с внешней таблицей прерывается, и ни одна строка не обрабатывается, если количество ошибочных строк достигает предельного значения SEGMENT REJECT LIMIT. Лимит ошибочных строк устанавливается для каждого сегмента, а не для всей операции. Операция обрабатывает все хорошие строки, отбрасывает и по желанию регистрирует ошибки форматирования для ошибочных строк, если количество ошибочных строк не достигает предела SEGMENT REJECT LIMIT.
Пункт LOG ERRORS позволяет сохранить ошибочные строки для дальнейшего изучения. Информацию о пункте LOG ERRORS смотрите в команде CREATE EXTERNAL TABLE.
Когда вы устанавливаете SEGMENT REJECT LIMIT, RT.Warehouse сканирует внешние данные в режиме изоляции ошибок одной строки. Режим изоляции ошибок одной строки применяется к строкам внешних данных с ошибками формата, такими как лишние или отсутствующие атрибуты, атрибуты неправильного типа данных или неверные последовательности кодирования клиента. RT.Warehouse не проверяет ошибки ограничений, но вы можете отфильтровать ошибки ограничений, ограничив SELECT из внешней таблицы во время выполнения. Например, чтобы исключить ошибки дублирования ключей:
=# INSERT INTO table_with_pkeys
SELECT DISTINCT * FROM external_table;
Примечание. При загрузке данных с помощью команды COPY или внешней таблицы значение параметра конфигурации сервера gp_initial_bad_row_limit ограничивает начальное количество обрабатываемых строк, отформатированных неправильно. По умолчанию обработка прекращается, если первые 1000 строк содержат ошибки форматирования. |
В следующем примере ведется внутренний лог ошибок в базе данных RT.Warehouse и установлен порог ошибок в 10 ошибок.
=# CREATE EXTERNAL TABLE ext_expenses ( name text,
date date, amount float4, category text, desc1 text )
LOCATION ('gpfdist://etlhost-1:8081/*',
'gpfdist://etlhost-2:8082/*')
FORMAT 'TEXT' (DELIMITER '|')
LOG ERRORS SEGMENT REJECT LIMIT 10
ROWS;
Используйте встроенную SQL-функцию gp_read_error_log('external_table') для чтения данных лога ошибок. Этот пример команды отображает ошибки лога для ext_expenses:
SELECT gp_read_error_log('ext_expenses');
Информацию о формате лога ошибок см. в разделе Просмотр Bad Rows в логе ошибок (23.3.3).
Встроенная SQL-функция gp_truncate_error_log('external_table') удаляет данные об ошибках. В этом примере удаляются данные лога ошибок, созданные в предыдущем примере с внешней таблицей:
SELECT gp_truncate_error_log('ext_expenses');
Следующий фрагмент SQL фиксирует ошибки форматирования внутри базы данных RT.Warehouse и объявляет ограничение отклонения в 10 строк.
LOG ERRORS SEGMENT REJECT LIMIT 10 ROWS
Используйте встроенную функцию SQL gp_read_error_log() для чтения данных лога ошибок. Сведения о просмотре лога ошибок см. в разделе Просмотр Bad Rows в логе ошибок (23.3.3).
Если вы используете изоляцию ошибок одной строки (см. раздел Определение внешней таблицы с изоляцией ошибок в одной строке (23.3.1) или Запуск COPY в режиме изоляции ошибок одной строки (23.7)), все строки с ошибками форматирования регистрируются базой данных RT.Warehouse.
База данных RT.Warehouse фиксирует следующую информацию об ошибках в формате таблицы. В Таблице 132 показан формат лога ошибок.
Таблица 132. Формат лога ошибок
Столбец | Тип | Описание |
---|---|---|
cmdtime | timestamptz | Отметка времени, когда произошла ошибка. |
relname | text | Имя внешней таблицы или целевой таблицы команды COPY. |
filename | text | Имя файла загрузки, содержащего ошибку. |
linenum | int | Если использовалось COPY, номер строки в файле загрузки, где произошла ошибка. Для внешних таблиц, использующих протокол file:// или протокол gpfdist:// и формат CSV, регистрируются имя файла и номер строки. |
bytenum | int |
Для внешних таблиц с протоколом gpfdist:// и данными в формате TEXT: смещение в байтах в файле загрузки, где произошла ошибка. gpfdist анализирует файлы TEXT блоками, поэтому регистрация номера строки невозможна. CSV-файлы анализируются построчно, поэтому для CSV-файлов возможно отслеживание номеров строк. |
errmsg | text | Текст сообщения об ошибке. |
rawdata | text | Необработанные данные отклоненной строки. |
rawbytes | bytea | В случаях, когда возникает ошибка кодирования базы данных (используемая клиентская кодировка не может быть преобразована в кодировку на стороне сервера), невозможно зарегистрировать ошибку кодирования как необработанные данные. Вместо этого сохраняются необработанные байты, и вы увидите восьмеричный код для любых не семибитных символов ASCII. |
Вы можете использовать встроенную в базу данных RT.Warehouse функцию SQL gp_read_error_log() для отображения ошибок форматирования, которые регистрируются внутри. Например, эта команда отображает информацию лога ошибок для таблицы ext_expenses:
SELECT gp_read_error_log('ext_expenses');
Для получения информации об управлении ошибками форматирования, которые регистрируются во внутреннем логе, см. команду COPY или CREATE EXTERNAL TABLE.
Вы можете использовать CREATE TABLE AS или INSERT...SELECT для загрузки данных внешней и внешней веб-таблицы в другую (не внешнюю) таблицу базы данных, и данные будут загружаться параллельно в соответствии с определением внешней или внешней веб-таблицы.
Если во внешнем файле таблицы или внешнем источнике данных веб-таблицы есть ошибка, в зависимости от используемого режима изоляции произойдет одно из следующих событий:
Утилита RT.Warehouse gpload загружает данные, используя читаемые внешние таблицы и параллельный файловый сервер RT.Warehouse (gpfdist или gpfdists). Она обрабатывает настройку внешних таблиц на основе параллельных файлов и позволяет пользователям настраивать формат данных, определение внешних таблиц и настройку gpfdist или gpfdists в одном конфигурационном файле.
Примечание. Операции MERGE и UPDATE не поддерживаются, если имя столбца целевой таблицы является зарезервированным ключевым словом, содержит заглавные буквы или включает любой символ, требующий кавычек (" ") для идентификации столбца. |
1. Убедитесь, что ваша среда настроена для запуска gpload. Требуются некоторые зависимые файлы из вашей установки RT.Warehouse, такие как gpfdist и Python, а также сетевой доступ к узлам сегмента RT.Warehouse.
2. Создайте свой файл управления нагрузкой. Это файл в формате YAML, в котором указана информация о подключении к базе данных RT.Warehouse, информация о конфигурации gpfdist, параметры внешней таблицы и формат данных.
Например:
---
VERSION: 1.0.0.1
DATABASE: ops
USER: gpadmin
HOST: mdw-1
PORT: 5432
GPLOAD:
INPUT:
- SOURCE:
LOCAL_HOSTNAME:
- etl1-1
- etl1-2
- etl1-3
- etl1-4
PORT: 8081
FILE:
- /var/load/data/*
- COLUMNS:
- name: text
- amount: float4
- category: text
- descr: text
- date: date
- FORMAT: text
- DELIMITER: '|'
- ERROR_LIMIT: 25
- LOG_ERRORS: true
OUTPUT:
- TABLE: payables.expenses
- MODE: INSERT
PRELOAD:
- REUSE_TABLES: true
SQL:
- BEFORE: "INSERT INTO audit VALUES('start', current_timestamp)"
- AFTER: "INSERT INTO audit VALUES('end', current_timestamp)"
3. Запустите gpload, передав файл управления загрузкой. Например:
gpload -f my_load.yml
Параллельный файловый сервер gpfdist позволяет настраивать трансформации, которые позволяют внешним таблицам базы данных RT.Warehouse читать и записывать файлы в форматах, которые не поддерживаются с помощью условия FORMAT команды CREATE EXTERNAL TABLE. Входное преобразование считывает файл в формате внешних данных и выводит строки в gpfdist в формате CSV или другом текстовом формате, указанном в пункте FORMAT внешней таблицы. Выходное преобразование получает строки из gpfdist в текстовом формате и преобразует их в формат внешних данных.
В этом разделе описываются задачи по настройке преобразований данных, которые работают с gpfdist для чтения или записи внешних файлов данных в форматах, которые не поддерживаются базой данных RT.Warehouse.
Чтобы установить преобразование для формата данных, вы предоставляете исполняемую команду, которую gpfdist может вызвать с именем файла, содержащего данные. Например, можно написать скрипт оболочки, который запускает XSLT-преобразование XML-файла для вывода строк со столбцами, разделенными символом вертикальной полосы (|), и строк, разделенных символами перевода строки.
Преобразования настраиваются в конфигурационном файле в формате YAML, передаваемом gpfdist в командной строке.
Если вы хотите загрузить внешние данные в таблицу базы данных RT.Warehouse, вы можете использовать утилиту gpload для автоматизации задач по созданию внешней таблицы, запуску gpfdist и загрузке преобразованных данных в таблицу базы данных.
Доступ к данным во внешних XML-файлах из базы данных - распространенный пример, требующий преобразования. На следующей схеме показано, как gpfdist выполняет преобразование XML-файлов на сервере ETL.
На Рисунке 9 Показаны внешние таблицы с использованием преобразований XML.
Рисунок 9. Внешние таблицы с использованием преобразований XML
Ниже приведены общие шаги по настройке преобразования gpfdist для внешних файлов данных. Процесс проиллюстрирован примером XML.
Чтобы подготовиться к проекту преобзразования:
Например, следующий файл XML, цены.xml, представляет собой простой файл XML, содержащий записи о ценах. Каждая запись цены содержит два поля: номер товара и цену.
<?xml version="1.0" encoding="ISO-8859-1" ?>
<prices>
<pricerecord>
<itemnumber>708421</itemnumber>
<price>19.99</price>
</pricerecord>
<pricerecord>
<itemnumber>708466</itemnumber>
<price>59.25</price>
</pricerecord>
<pricerecord>
<itemnumber>711121</itemnumber>
<price>24.99</price>
</pricerecord>
</prices>
Цель этого преобразования состоит в том, чтобы импортировать все данные во внешнюю таблицу базы данных RT.Warehouse, доступную для чтения, со столбцом с целочисленным столбцом itemnumber и десятичным столбцом price.
Преобразование определяет, что именно нужно извлечь из данных. Вы можете использовать любую авторскую среду и язык, подходящие для вашего проекта. Для преобразований XML выбирайте такие технологии, как XSLT, Joost (STX), Java, Python или Perl, в зависимости от целей и масштаба проекта.
В примере с ценой следующим шагом будет преобразование данных XML в текстовый формат с разграничением на две колонки.
708421|19.99
708466|59.25
711121|24.99
Следующее преобразование STX, называемое input_transform.stx, выполняет преобразование данных.
<?xml version="1.0"?>
<stx:transform version="1.0"
xmlns:stx="http://stx.sourceforge.net/2002/ns"
pass-through="none">
<!-- declare variables -->
<stx:variable name="itemnumber"/>
<stx:variable name="price"/>
<!-- match and output prices as columns delimited by | -->
<stx:template match="/prices/pricerecord">
<stx:process-children/>
<stx:value-of select="$itemnumber"/>
<stx:text>|</stx:text>
<stx:value-of select="$price"/> <stx:text>
</stx:text>
</stx:template>
<stx:template match="itemnumber">
<stx:assign name="itemnumber" select="."/>
</stx:template>
<stx:template match="price">
<stx:assign name="price" select="."/>
</stx:template>
</stx:transform>
Это преобразование STX объявляет две временные переменные, itemnumber и price, и следующие правила.
Конфигурация gpfdist задается в виде документа YAML 1.1. Она содержит правила, которые gpfdist использует для выбора преобразования, применяемого при загрузке или извлечении данных.
Этот пример конфигурации gpfdist содержит следующие элементы, необходимые для сценария трансформации prices.xml:
Помимо обычных правил YAML, таких как начало документа с трех тире (---), конфигурация gpfdist должна соответствовать следующим ограничениям:
1. Параметр VERSION должен иметь значение 1.0.0.1.
2. Параметр TRANSFORMATIONS должен присутствовать и содержать одно или несколько отображений.
3. Каждое отображение в TRANSFORMATIONS должно содержать:
4. Каждое сопоставление в TRANSFORMATION может содержать дополнительные параметры CONTENT, SAFE и STDERR.
Следующая конфигурация gpfdist, называемая config.yaml, относится к примеру с ценами. Начальный отступ в каждой строке имеет большое значение и отражает иерархический характер спецификации. Имя преобразования price_input в следующем примере будет использоваться позже при создании таблицы в SQL.
---
VERSION: 1.0.0.1
TRANSFORMATIONS:
prices_input:
TYPE: input
COMMAND: /bin/bash input_transform.sh %filename%
Параметр COMMAND использует скрипт-обертку под названием input_transform.sh с заполнителем %filename%. Когда gpfdist запускает преобразование prices_input, он вызывает input_transform.sh с помощью /bin/bash и заменяет %filename% на путь к входному файлу для преобразования. Скрипт-обертка под названием input_transform.sh содержит логику для вызова STX-преобразования и возврата выходных данных.
Если используется Joost, необходимо установить механизм Joost STX.
#!/bin/bash
# input_transform.sh - sample input transformation,
# demonstrating use of Java and Joost STX to convert XML into
# text to load into Greenplum Database.
# java arguments:
# -jar joost.jar joost STX engine
# -nodecl don't generate a <?xml?> declaration
# $1 filename to process
# input_transform.stx the STX transformation
#
# the AWK step eliminates a blank line joost emits at the end
java \
-jar joost.jar \
-nodecl \
$1 \
input_transform.stx \
| awk 'NF>0
Файл input_transform.sh использует движок Joost STX с интерпретатором AWK. На Рисунке 10 показана диаграмма хода процесса, когда gpfdist выполняет преобразование.
Рисунок 10. Диаграмма процесса выполнения преобразования gpfdist
Создайте целевые таблицы базы данных с операторами SQL на основе соответствующей схемы.
Нет особых требований к таблицам базы данных RT.Warehouse, которые содержат загруженные данные. В примере с ценами следующая команда создает таблицу цен, в которую должны быть загружены данные.
CREATE TABLE prices (
itemnumber integer,
price decimal
)
DISTRIBUTED BY (itemnumber);
Затем используйте один из следующих подходов для преобразования данных с помощью gpfdist:
Утилита gpload базы данных RT.Warehouse управляет операцией загрузки данных, используя параллельный файловый сервер gpfdist и файл конфигурации в формате YAML. gpload автоматизирует следующие задачи:
Для преобразования данных с помощью gpload необходимо, чтобы параметры TRANSFORM и TRANSFORM_CONFIG отображались в разделе INPUT управляющего файла gpload.
---
VERSION: 1.0.0.1
DATABASE: ops
USER: gpadmin
GPLOAD:
INPUT:
- TRANSFORM_CONFIG: config.yaml
- TRANSFORM: prices_input
- SOURCE:
FILE: prices.xml
Имя преобразования должно появиться в двух местах: в параметре TRANSFORM файла конфигурации gpfdist и в разделе TRANSFORMATIONS файла, имя которого находится в разделе TRANSFORM_CONFIG.
В управляющем файле gpload необязательный параметр MAX_LINE_LENGTH указывает максимальную длину строки в данных преобразования XML, которые передаются gpload.
На следующей Рисунке 11 показана диаграмма отношений между управляющим файлом gpload, файлом конфигурации gpfdist и файлом данных XML.
Рисунок 11. Диаграмма отношений между управляющим файлом gpload, файлом конфигурации gpfdist и файлом данных XML
С помощью этого метода загрузки вы выполняете каждую из задач, которые автоматизирует gpload. Вы запускаете gpfdist, создаете внешнюю таблицу, загружаете данные и выполняете очистку, удаляя таблицу и останавливая gpfdist.
Укажите преобразование в предложении LOCATION определения CREATE EXTERNAL TABLE. Например, преобразование выделено полужирным шрифтом в следующей команде (сначала запустите gpfdist, используя команду gpfdist -c config.yaml).
CREATE READABLE EXTERNAL TABLE prices_readable (LIKE prices)
LOCATION ('gpfdist://hostname:8080/prices.xml#transform=prices_input')
FORMAT 'TEXT' (DELIMITER '|')
LOG ERRORS SEGMENT REJECT LIMIT 10;
В приведенной выше команде измените имя хоста на ваше имя хоста. Prices_input берется из конфигурационного файла gpfdist.
Затем следующий запрос загружает данные в таблицу цен.
INSERT INTO prices SELECT * FROM prices_readable;
Файл конфигурации gpfdist использует формат документа YAML 1.1 и реализует схему для определения параметров преобразования. Файл конфигурации должен быть действительным документом YAML.
Программа gpfdist обрабатывает документ по порядку и использует отступы (пробелы) для определения иерархии документа и взаимоотношений разделов друг с другом. Использование белого пространства имеет большое значение. Не используйте пробелы для форматирования и не используйте табы.
Ниже приведена базовая структура файла конфигурации.
---
VERSION: 1.0.0.1
TRANSFORMATIONS:
transformation_name1:
TYPE: input | output
COMMAND: command
CONTENT: data | paths
SAFE: posix-regex
STDERR: server | console
transformation_name2:
TYPE: input | output
COMMAND: command
...
VERSION — Необходимый. Версия схемы файла конфигурации gpfdist. Текущая версия 1.0.0.1.
TRANSFORMATIONS — Необходимый. Начинает раздел спецификации преобразования. Файл конфигурации должен иметь хотя бы одно преобразование. Когда gpfdist получает запрос на преобразование, он ищет в этом разделе запись с соответствующим именем преобразования.
TYPE — Необходимый. Задает направление преобразования. Значения являются входными или выходными.
COMMAND — Необходимый. Указывает, какую команду gpfdist будет выполнять для выполнения преобразования.
Для входных преобразований gpfdist вызывает команду, указанную в параметре CONTENT. Ожидается, что команда откроет соответствующий файл (файлы) и создаст одну строку TEXT для каждой строки для загрузки в базу данных RT.Warehouse. Входное преобразование определяет, должно ли все содержимое быть преобразовано в одну строку или в несколько строк.
Для выходных преобразований gpfdist вызывает эту команду, как указано в настройке CONTENT. Ожидается, что выходная команда откроет и запишет в базовый файл(ы) по мере необходимости. Выходное преобразование определяет окончательное размещение преобразованного вывода.
CONTENT — Необязательный. Значения — это данные и пути. Значение по умолчанию — данные.
Ниже приведен пример раздела COMMAND, показывающего замененный текст %filename%.
COMMAND: /bin/bash input_transform.sh %filename%
SAFE — Необязательный. Регулярное выражение POSIX, которому должны соответствовать пути для передачи в преобразование. Укажите SAFE, если есть опасения по поводу внедрения или неправильной интерпретации путей, переданных команде. По умолчанию нет ограничений на пути.
STDERR — Необязательный. Значения — сервер и консоль.
Этот параметр указывает, как обрабатывать стандартный вывод ошибок преобразования. Значение по умолчанию, сервер, указывает, что gpfdist будет захватывать стандартные выходные данные об ошибках преобразования во временный файл и отправлять первые 8 КБ этого файла в базу данных RT.Warehouse в качестве сообщения об ошибке. Сообщение об ошибке появится как ошибка SQL. Консоль указывает, что gpfdist не перенаправляет и не передает стандартные выходные данные об ошибках преобразования.
Следующие примеры демонстрируют полный процесс для различных типов XML-данных и STX-преобразований.
Выходные данные команды оболочки или скрипта определяют данные веб-таблицы на основе команд. Укажите команду в предложении EXECUTE команды CREATE EXTERNAL WEB TABLE. Данные актуальны на момент выполнения команды. Предложение EXECUTE запускает команду оболочки или сценарий на указанном мастер хосте и/или хосте или хостах сегмента. Команда или сценарий должны находиться на хостах, соответствующих хостам, определенным в предложении EXECUTE.
По умолчанию команда запускается на хостах сегментов, когда в активных сегментах есть выходные строки для обработки. Например, если на каждом хосте сегмента запущено четыре основных экземпляра сегмента, у которых есть выходные строки для обработки, команда выполняется четыре раза для каждого хоста сегмента. При желании вы можете ограничить количество экземпляров сегмента, которые выполняют команду веб-таблицы. Все сегменты, включенные в определение веб-таблицы в предложении ON, выполняют команду параллельно.
Команда, которую вы указываете в определении внешней таблицы, выполняется из базы данных и не может получить доступ к переменным среды из .bashrc или .profile. Задайте переменные среды в предложении EXECUTE. Например:
=# CREATE EXTERNAL WEB TABLE output (output text)
EXECUTE 'PATH=/home/gpadmin/programs; export PATH; myprogram.sh'
FORMAT 'TEXT';
Скрипты должны выполняться пользователем gpadmin и находиться в одном и том же месте на мастер хосте или хосте сегмента.
Следующая команда определяет веб-таблицу, которая запускает скрипт. Скрипт запускается на каждом хосте сегмента, где у сегмента есть выходные строки для обработки.
=# CREATE EXTERNAL WEB TABLE log_output
(linenum int, message text)
EXECUTE '/var/load_scripts/get_log_data.sh' ON HOST
FORMAT 'TEXT' (DELIMITER '|');
В этом примере демонстрируется загрузка образца налоговой декларации IRS Modernized eFile с использованием преобразования Joost STX. Данные представлены в виде сложного файла XML.
Служба внутренних доходов США (IRS) взяла на себя значительные обязательства по XML и определяет его использование в своей системе Modernized e-File (MeF). В MeF каждая налоговая декларация представляет собой XML-документ с глубокой иерархической структурой, точно отражающей конкретную форму базового налогового кодекса.
XML, XML-схема и таблицы стилей играют важную роль в представлении данных и бизнес-процессах. Фактические XML-данные извлекаются из ZIP-файла, прикрепленного к сообщению MIME «файл передачи (transmission file)».
Пример XML-документа RET990EZ_2006.xml имеет размер около 350 КБ и состоит из двух элементов:
Элемент <ReturnHeader> содержит общие сведения о налоговой декларации, такие как имя налогоплательщика, налоговый год декларации и составитель. Элемент <ReturnData> содержит несколько разделов с конкретными сведениями о налоговой декларации и связанных таблицах.
Ниже приведен сокращенный образец XML-файла.
<?xml version="1.0" encoding="UTF-8"?>
<Return returnVersion="2006v2.0"
xmlns="https://www.irs.gov/efile"
xmlns:efile="https://www.irs.gov/efile"
xsi:schemaLocation="https://www.irs.gov/efile"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ReturnHeader binaryAttachmentCount="1">
<ReturnId>AAAAAAAAAAAAAAAAAAAA</ReturnId>
<Timestamp>1999-05-30T12:01:01+05:01</Timestamp>
<ReturnType>990EZ</ReturnType>
<TaxPeriodBeginDate>2005-01-01</TaxPeriodBeginDate>
<TaxPeriodEndDate>2005-12-31</TaxPeriodEndDate>
<Filer>
<EIN>011248772</EIN>
... more data ...
</Filer>
<Preparer>
<Name>Percy Polar</Name>
... more data ...
</Preparer>
<TaxYear>2005</TaxYear>
</ReturnHeader>
... more data ..
Цель состоит в том, чтобы импортировать все данные в базу данных RT.Warehouse. Во-первых, преобразуйте XML-документ в текст с символами новой строки «экранированными» с двумя столбцами: ReturnId и одним столбцом в конце для всей налоговой декларации MeF. Например:
AAAAAAAAAAAAAAAAAAAA|<Return returnVersion="2006v2.0"...
Загрузите данные в базу данных RT.Warehouse.
В этом примере демонстрируется загрузка демонстрационных данных, описывающих нефтяную вышку, с использованием преобразования Joost STX. Данные представлены в виде сложного XML-файла, загруженного с сайта energistics.org.
Стандартный язык разметки для передачи информации о буровых площадках (WITSML™) — это инициатива нефтяной отрасли, направленная на предоставление открытых, непатентованных, стандартных интерфейсов для технологий и программного обеспечения для обмена информацией между нефтяными компаниями, сервисными компаниями, буровыми подрядчиками, поставщиками приложений и регулирующими органами.
Информация о нефтяной вышке состоит из элемента <rigs> верхнего уровня с несколькими дочерними элементами, такими как <documentInfo>, <rig> и т. д. Следующий отрывок из файла показывает тип информации в теге <rig>.
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="../stylesheets/rig.xsl" type="text/xsl" media="screen"?>
<rigs
xmlns="http://www.energistics.org/schemas/131"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.energistics.org/schemas/131 ../obj_rig.xsd"
version="1.3.1.1">
<documentInfo>
... misc data ...
</documentInfo>
<rig uidWell="W-12" uidWellbore="B-01" uid="xr31">
<nameWell>6507/7-A-42</nameWell>
<nameWellbore>A-42</nameWellbore>
<name>Deep Drill #5</name>
<owner>Deep Drilling Co.</owner>
<typeRig>floater</typeRig>
<manufacturer>Fitsui Engineering</manufacturer>
<yearEntService>1980</yearEntService>
<classRig>ABS Class A1 M CSDU AMS ACCU</classRig>
<approvals>DNV</approvals>
... more data ...
Цель состоит в том, чтобы импортировать информацию об этой буровой установке в базу данных RT.Warehouse.
Образец документа rig.xml имеет размер около 11 КБ. Входные данные не содержат вкладок, поэтому соответствующую информацию можно преобразовать в записи, разделенные вертикальной чертой (|).
W-12|6507/7-A-42|xr31|Deep Drill #5|Deep Drilling Co.|John Doe|John.Doe@example.com|
Со столбцами:
Затем загрузите данные в базу данных RT.Warehouse.
COPY FROM копирует данные из файла или стандартного ввода в таблицу и добавляет данные к содержимому таблицы. COPY является непараллельным: данные загружаются в одном процессе с использованием мастера RT.Warehouse. Использование COPY рекомендуется только для очень маленьких файлов данных.
Исходный файл COPY должен быть доступен процессу postgres на мастер хосте. Укажите имя исходного файла COPY относительно каталога данных на мастер хосте или укажите абсолютный путь.
RT.Warehouse копирует данные из STDIN или STDOUT, используя соединение между клиентом и мастер-сервером.
Команда COPY просит серверную часть postgres открыть указанный файл, прочитать его и добавить в таблицу. Чтобы иметь возможность прочитать файл, серверная часть должна иметь разрешения на чтение файла, а имя файла должно быть указано с использованием абсолютного пути на мастер хосте или относительного пути к каталогу основных данных.
COPY table_name FROM /path/to/filename;
Чтобы избежать проблемы с копированием файла данных на мастер хост перед загрузкой данных, COPY FROM STDIN использует стандартный канал ввода и передает данные непосредственно в серверную часть postgres. После запуска команды COPY FROM STDIN серверная часть будет принимать строки данных до тех пор, пока одна строка не будет содержать только точку обратной косой черты (\.).
COPY table_name FROM STDIN;
Не путайте команду psql \copy с командой COPY SQL. \copy вызывает обычную команду COPY FROM STDIN и отправляет данные из клиента psql в серверную часть. Поэтому любой файл должен находиться на хосте, на котором работает клиент psql, и должен быть доступен пользователю, запускающему клиент.
Чтобы избежать проблемы с копированием файла данных на мастер хост перед загрузкой данных, COPY FROM STDIN использует стандартный канал ввода и передает данные непосредственно в серверную часть postgres. После запуска команды COPY FROM STDIN серверная часть будет принимать строки данных до тех пор, пока одна строка не будет содержать только точку обратной косой черты (\.). psql упаковывает все это в удобную команду \copy.
\copy table_name FROM filename;
COPY FROM принимает параметр FORMAT, который указывает формат входных данных. Возможные значения: TEXT, CSV (значения, разделенные запятыми) и BINARY.
COPY table_name FROM /path/to/filename WITH (FORMAT csv);
FORMAT csv будет считывать значения, разделенные запятыми. Текст FORMAT по умолчанию использует табуляторы для разделения значений, опция DELIMITER указывает другой символ в качестве разделителя значений.
COPY table_name FROM /path/to/filename WITH (FORMAT text, DELIMITER '|');
По умолчанию используется кодировка клиента по умолчанию, ее можно изменить с помощью параметра ENCODING. Это полезно, если данные поступают из другой операционной системы.
COPY table_name FROM /path/to/filename WITH (ENCODING 'latin1');
По умолчанию COPY останавливает операцию при первой ошибке: если данные содержат ошибку, операция завершается неудачно и данные не загружаются. Если вы запустите COPY FROM в режиме изоляции ошибок одной строки, RT.Warehouse пропустит строки, содержащие ошибки формата, и загрузит правильно отформатированные строки. Режим изоляции ошибок одной строки применяется только к строкам входного файла, содержащим ошибки формата. Если данные содержат ошибку ограничения, например, нарушение ограничений NOT NULL, CHECK или UNIQUE, операция завершается неудачей, и данные не загружаются.
Указание SEGMENT REJECT LIMIT запускает операцию COPY в режиме изоляции однострочных ошибок. Укажите допустимое количество строк с ошибками в каждом сегменте, после которого вся операция COPY FROM завершается сбоем и строки не загружаются. Количество строк с ошибками относится к каждому сегменту базы данных RT.Warehouse, а не ко всей операции загрузки.
Если операция COPY не достигает предела ошибок, RT.Warehouse загружает все правильно отформатированные строки и отбрасывает строки с ошибками. Используйте предложение LOG ERRORS для регистрации ошибок форматирования данных внутри базы данных RT.Warehouse. Например:
=> COPY country FROM '/data/gpdb/country_data'
WITH DELIMITER '|' LOG ERRORS
SEGMENT REJECT LIMIT 10 ROWS;
Используйте следующие советы, чтобы оптимизировать загрузку данных и производительность последующих запросов.
Внешняя таблица с возможностью записи позволяет выбирать строки из других таблиц базы данных и выводить строки в файлы, именованные каналы, приложения или в качестве целей вывода для параллельных вычислений RT.Warehouse MapReduce. Вы можете определить файловые и сетевые внешние таблицы с возможностью записи.
В этом разделе описывается, как выгружать данные из базы данных RT.Warehouse, используя параллельную выгрузку (внешние таблицы с возможностью записи) и непараллельную выгрузку (COPY).
Доступные для записи внешние таблицы, которые выводят данные в файлы, могут использовать программу параллельного файлового сервера RT.Warehouse, gpfdist, или Platform Extension Framework (PXF), интерфейс RT.Warehouse к Hadoop.
Используйте команду CREATE WRITABLE EXTERNAL TABLE, чтобы определить внешнюю таблицу и указать расположение и формат выходных файлов.
При желании вы можете объявить политику распределения для ваших внешних таблиц, доступных для записи. По умолчанию доступные для записи внешние таблицы используют политику случайного распределения. Если исходная таблица, из которой вы экспортируете данные, имеет политику распределения хэшей, определение одного и того же столбца (столбцов) ключа распределения для доступной для записи внешней таблицы повышает производительность выгрузки за счет устранения необходимости перемещать строки по межсоединению. Если вы выгружаете данные из определенной таблицы, вы можете использовать предложение LIKE, чтобы скопировать определения столбцов и политику распределения из исходной таблицы.
=# CREATE WRITABLE EXTERNAL TABLE unload_expenses
( LIKE expenses )
LOCATION ('gpfdist://etlhost-1:8081/expenses1.out',
'gpfdist://etlhost-2:8081/expenses2.out')
FORMAT 'TEXT' (DELIMITER ',')
DISTRIBUTED BY (exp_id);
=# CREATE WRITABLE EXTERNAL TABLE unload_expenses
( LIKE expenses )
LOCATION ('pxf://dir/path?PROFILE=hdfs:text')
FORMAT 'TEXT' (DELIMITER ',')
DISTRIBUTED BY (exp_id);
Вы указываете каталог HDFS для доступной для записи внешней таблицы, которую вы создаете с помощью протокола pxf.
Вы можете определить доступные для записи внешние веб-таблицы для отправки выходных строк в приложение или скрипт. Приложение должно принимать входной поток, находиться в одном и том же месте на всех хостах сегмента RT.Warehouse и быть исполняемым пользователем gpadmin. Все сегменты в системе RT.Warehouse запускают приложение или сценарий, независимо от того, есть ли в сегменте выходные строки для обработки.
Используйте CREATE WRITABLE EXTERNAL WEB TABLE, чтобы определить внешнюю таблицу и указать приложение или скрипт для запуска на узлах сегмента. Команды выполняются из базы данных и не могут обращаться к переменным среды (таким как $PATH). Задайте переменные среды в предложении EXECUTE определения вашей доступной для записи внешней таблицы. Например:
=# CREATE WRITABLE EXTERNAL WEB TABLE output (output text)
EXECUTE 'export PATH=$PATH:/home/gpadmin
/programs;
myprogram.sh'
FORMAT 'TEXT'
DISTRIBUTED RANDOMLY;
Следующие переменные базы данных RT.Warehouse доступны для использования в командах ОС, выполняемых веб-таблицей или внешней таблицей с возможностью записи. Установите эти переменные в качестве переменных среды в оболочке, которая выполняет команды. Их можно использовать для идентификации набора запросов, сделанных оператором внешней таблицы в массиве хостов и экземпляров сегментов базы данных RT.Warehouse.
В Таблице 133 представлены переменные EXECUTE для внешней таблицы.
Таблица 133. Переменные EXECUTE для внешней таблицы
Переменная | Описание |
---|---|
$GP_CID | Счетчик команд транзакции, выполняющей оператор внешней таблицы. |
$GP_DATABASE | База данных в которой находится определение внешней таблицы. |
$GP_DATE | Дата выполнения команды внешней таблицы. |
$GP_MASTER_HOST | Имя хоста мастер хоста RT.Warehouse, с которого был отправлен оператор внешней таблицы. |
$GP_MASTER_PORT | Номер порта мастера RT.Warehouse, с которого был отправлен оператор внешней таблицы. |
$GP_QUERY_STRING | Команда SQL (запрос DML или SQL), выполняемая базой данных RT.Warehouse. |
$GP_SEG_DATADIR | Местоположение каталога данных экземпляра сегмента, выполняющего команду внешней таблицы. |
$GP_SEG_PG_CONF | Расположение файла postgresql.conf экземпляра сегмента, выполняющего команду внешней таблицы. |
$GP_SEG_PORT | Номер порта экземпляра сегмента, выполняющего команду внешней таблицы. |
$GP_SEGMENT_COUNT | Общее количество экземпляров основного сегмента в системе базы данных RT.Warehouse. |
$GP_SEGMENT_ID | Идентификационный номер экземпляра сегмента, выполняющего команду внешней таблицы (такой же, как dbid в gp_segment_configuration). |
$GP_SESSION_ID | Номер идентификатора сеанса базы данных, связанный с оператором внешней таблицы. |
$GP_SN | Порядковый номер узла сканирования внешней таблицы в плане запроса оператора внешней таблицы. |
$GP_TIME | Время выполнения команды внешней таблицы. |
$GP_USER | Пользователь базы данных, выполняющий оператор внешней таблицы. |
$GP_XID | Идентификатор транзакции оператора внешней таблицы. |
Разрешение внешним таблицам выполнять команды или скрипт ОС связано с риском для безопасности. Чтобы запретить использование EXECUTE в веб-определениях внешних таблиц с возможностью записи, установите параметр конфигурации сервера gp_external_enable_exec в значение off в главном файле postgresql.conf:
gp_external_enable_exec = off
Доступные для записи внешние таблицы допускают только операции INSERT. Вы должны предоставить разрешение INSERT для таблицы, чтобы предоставить доступ пользователям, которые не являются владельцем таблицы или суперпользователем. Например:
GRANT INSERT ON writable_ext_table TO admin;
Чтобы выгрузить данные с помощью доступной для записи внешней таблицы, выберите данные из исходных таблиц и вставьте их в доступную для записи внешнюю таблицу. Результирующие строки выводятся во внешнюю таблицу, доступную для записи. Например:
INSERT INTO writable_ext_table SELECT * FROM regular_table;
COPY TO копирует данные из таблицы в файл (или в стандартный ввод) на мастер хосте RT.Warehouse, используя один процесс на главном экземпляре RT.Warehouse. Используйте COPY для вывода всего содержимого таблицы или отфильтруйте вывод с помощью инструкции SELECT. Например:
COPY (SELECT * FROM country WHERE country_name LIKE 'A%')
TO '/home/gpadmin/a_list_countries.out';
Когда вы используете инструменты RT.Warehouse для загрузки и выгрузки данных, вы должны указать, как форматируются ваши данные. COPY, CREATE EXTERNAL TABLE и gpload имеют пункты, которые позволяют указать, как форматируются ваши данные. Данные могут быть в текстовом формате с разделителями (TEXT) или в формате значений, разделенных запятыми (CSV). Внешние данные должны быть правильно отформатированы для чтения базой данных RT.Warehouse. В этом разделе объясняется формат файлов данных, ожидаемый базой данных RT.Warehouse.
База данных RT.Warehouse ожидает, что строки данных будут разделены символом LF (перевод строки, 0x0A), CR (возврат каретки, 0x0D) или CR, за которым следует LF (CR+LF, 0x0D 0x0A). LF — это стандартное представление новой строки в UNIX или UNIX-подобных операционных системах. Операционные системы, такие как Windows или Mac OS X, используют CR или CR+LF. Все эти представления новой строки поддерживаются базой данных RT.Warehouse в качестве разделителя строк.
Разделителем столбца или поля по умолчанию является горизонтальный символ TAB (0x09) для текстовых файлов и символ запятой (0x2C) для файлов CSV. Вы можете объявить разделитель из одного символа, используя предложение DELIMITER в COPY, CREATE EXTERNAL TABLE или gpload при определении формата данных. Символ-разделитель должен стоять между любыми двумя полями значений данных. Не ставьте разделитель в начале или конце строки. Например, если символ вертикальной черты ( | ) является вашим разделителем:
data value 1|data value 2|data value 3
Следующая команда показывает использование вертикальной черты в качестве разделителя столбцов:
=# CREATE EXTERNAL TABLE ext_table (name text, date date)
LOCATION ('gpfdist://<hostname>/filename.txt)
FORMAT 'TEXT' (DELIMITER '|');
NULL представляет неизвестный фрагмент данных в столбце или поле. В ваших файлах данных вы можете назначить строку для представления нулевых значений. Строка по умолчанию — \N (обратная косая черта-N) в режиме TEXT или пустое значение без кавычек в режиме CSV. Вы также можете объявить другую строку, используя предложение NULL в COPY, CREATE EXTERNAL TABLE или gpload при определении формата данных. Например, вы можете использовать пустую строку, если не хотите отличать нули от пустых строк. При использовании инструментов загрузки базы данных RT.Warehouse любой элемент данных, соответствующий обозначенной нулевой строке, считается нулевым значением.
Есть два зарезервированных символа, которые имеют особое значение для базы данных RT.Warehouse:
Если ваши данные содержат любой из этих символов, вы должны экранировать этот символ, чтобы RT.Warehouse рассматривал его как данные, а не как разделитель полей или новую строку. По умолчанию escape-символом является \ (обратная косая черта) для файлов в текстовом формате и двойная кавычка (") для файлов в формате csv.
По умолчанию escape-символом является \ (обратная косая черта) для файлов в текстовом формате. Вы можете объявить другой escape-символ в предложении ESCAPE команд COPY, CREATE EXTERNAL TABLE или gpload. Если ваш экранирующий символ появляется в ваших данных, используйте его, чтобы экранировать себя.
Например, предположим, что у вас есть таблица с тремя столбцами, и вы хотите загрузить следующие три поля:
Ваш назначенный символ-разделитель | (вертикальная черта), а назначенный вами escape-символ — \ (обратная косая черта). Отформатированная строка в вашем файле данных выглядит следующим образом:
backslash = \\ | vertical bar = \| | exclamation point = !
Обратите внимание, как символ обратной косой черты, являющийся частью данных, экранируется другим символом обратной косой черты, а символ вертикальной черты, являющийся частью данных, экранируется символом обратной косой черты.
Вы можете использовать escape-символ для экранирования восьмеричных и шестнадцатеричных последовательностей. Экранированное значение преобразуется в эквивалентный символ при загрузке в базу данных RT.Warehouse. Например, чтобы загрузить символ амперсанда (&), используйте escape-символ, чтобы избежать его эквивалентного шестнадцатеричного (\0x26) или восьмеричного (\046) представления.
Вы можете отключить экранирование в файлах в формате TEXT, используя предложение ESCAPE в COPY, CREATE EXTERNAL TABLE или gpload следующим образом:
ESCAPE 'OFF'
Это полезно для входных данных, содержащих много символов обратной косой черты, таких как данные веб-журнала.
По умолчанию escape-символом является " (двойная кавычка) для файлов в формате CSV. Если вы хотите использовать другой escape-символ, используйте предложение ESCAPE в COPY, CREATE EXTERNAL TABLE или gpload, чтобы объявить другой escape-символ. В некоторых случаях если выбранный вами escape-символ присутствует в ваших данных, вы можете использовать его, чтобы избежать самого себя.
Например, предположим, что у вас есть таблица с тремя столбцами, и вы хотите загрузить следующие три поля:
Символом-разделителем является , (запятая), а символом-экраном - " (двойная кавычка). Отформатированная строка в вашем файле данных выглядит следующим образом:
"Free trip to A,B","5.89","Special rate ""1.79"""
Значение данных с символом запятой, которое является частью данных, заключено в двойные кавычки. Двойные кавычки, являющиеся частью данных, экранируются двойными кавычками, даже если значение поля заключено в двойные кавычки.
Включение всего поля в набор двойных кавычек гарантирует сохранение начальных и конечных пробельных символов:
"Free trip to A,B ","5.89 ","Special rate ""1.79"" "
Примечание. В режиме CSV все символы являются значимыми. Значение в кавычках, окруженное пробелами или любыми другими символами, кроме DELIMITER, включает эти символы. Это может вызвать ошибки, если вы импортируете данные из системы, которая дополняет строки CSV пробелами до некоторой фиксированной ширины. В этом случае перед импортом данных в базу данных RT.Warehouse предварительно обработайте CSV-файл, чтобы удалить пробелы в конце. |
Системы кодирования символов состоят из кода, который связывает каждый символ из набора символов с чем-то еще, например с последовательностью чисел или октетов, для облегчения передачи и хранения данных. База данных RT.Warehouse поддерживает множество наборов символов, включая однобайтовые наборы символов, такие как серия ISO 8859, и многобайтовые наборы символов, такие как EUC (расширенный код UNIX), UTF-8 и внутренний код Mule. Набор символов на стороне сервера определяется во время инициализации базы данных, UTF-8 используется по умолчанию и может быть изменен. Клиенты могут прозрачно использовать все поддерживаемые наборы символов, но некоторые из них не поддерживаются для использования на сервере в качестве кодировки на стороне сервера. При загрузке или вставке данных в базу данных RT.Warehouse, RT.Warehouse прозрачно преобразует данные из указанной кодировки клиента в кодировку сервера. При отправке данных обратно клиенту RT.Warehouse преобразует данные из кодировки символов сервера в указанную кодировку клиента.
Файлы данных должны иметь кодировку символов, распознаваемую базой данных RT.Warehouse. Файлы данных, которые содержат недопустимые или неподдерживаемые последовательности кодирования, вызывают ошибки при загрузке в базу данных RT.Warehouse.
Примечание. Для файлов данных, созданных в операционной системе Microsoft Windows, выполните системную команду dos2unix, чтобы удалить все символы, предназначенные только для Windows, перед загрузкой в базу данных RT.Warehouse. |
Примечание. Если вы измените значение ENCODING в существующем управляющем файле gpload, вы должны вручную удалить все внешние таблицы, созданные с использованием предыдущей конфигурации ENCODING. gpload не удаляет и не создает заново внешние таблицы для использования новой ENCODING, если для параметра REUSE_TABLES установлено значение true. |
Кодировка символов на стороне клиента может быть изменена для сессии путем установки параметра конфигурации сервера client_encoding.
SET client_encoding TO 'latin1';
Измените кодировку символов на стороне клиента на значение по умолчанию:
RESET client_encoding;
Показать текущую настройку кодировки символов на стороне клиента:
SHOW client_encoding;
Ниже приведен API для пользовательского протокола доступа к данным базы данных RT.Warehouse. Пример реализации протокола gpextprotocal.c написан на C и показывает, как можно использовать API. Сведения о доступе к пользовательскому протоколу доступа к данным см. в разделе Использование пользовательского протокола (23.2.2).
/* ---- Read/Write function API ------*/
CALLED_AS_EXTPROTOCOL(fcinfo)
EXTPROTOCOL_GET_URL(fcinfo)(fcinfo)
EXTPROTOCOL_GET_DATABUF(fcinfo)
EXTPROTOCOL_GET_DATALEN(fcinfo)
EXTPROTOCOL_GET_SCANQUALS(fcinfo)
EXTPROTOCOL_GET_USER_CTX(fcinfo)
EXTPROTOCOL_IS_LAST_CALL(fcinfo)
EXTPROTOCOL_SET_LAST_CALL(fcinfo)
EXTPROTOCOL_SET_USER_CTX(fcinfo, p)
/* ------ Validator function API ------*/
CALLED_AS_EXTPROTOCOL_VALIDATOR(fcinfo)
EXTPROTOCOL_VALIDATOR_GET_URL_LIST(fcinfo)
EXTPROTOCOL_VALIDATOR_GET_NUM_URLS(fcinfo)
EXTPROTOCOL_VALIDATOR_GET_NTH_URL(fcinfo, n)
EXTPROTOCOL_VALIDATOR_GET_DIRECTION(fcinfo)
Протокол соответствует примеру, описанному в разделе Использование пользовательского протокола (23.2.2). Имя файла исходного кода и общий объект — gpextprotocol.c и gpextprotocol.so.
Протокол имеет следующие свойства:
На эти функции ссылаются в операторе CREATE PROTOCOL, когда протокол создается и объявляется в базе данных.
Пример реализации gpextprotocal.c использует fopen() и fread() для имитации простого протокола, считывающего локальные файлы. Однако на практике протокол будет реализовывать такие функции, как удаленное подключение к какому-либо процессу по сети.
Чтобы использовать пример протокола внешней таблицы, вы используете компилятор C cc для компиляции и компоновки исходного кода для создания общего объекта, который может динамически загружаться базой данных RT.Warehouse. Команды для компиляции и компоновки исходного кода в системе Linux аналогичны этой:
cc -fpic -c gpextprotocal.c cc -shared -o gpextprotocal.so gpextprotocal.o
Параметр -fpic определяет создание кода, независимого от позиции (PIC), а параметр -c компилирует исходный код без компоновки и создает объектный файл. Объектный файл должен быть создан как позиционно-независимый код (PIC), чтобы его можно было загрузить в любое произвольное место в памяти с помощью базы данных RT.Warehouse.
Флаг -shared указывает на создание общего объекта (разделяемой библиотеки), а параметр -o указывает имя файла общего объекта gpextprotocal.so. Обратитесь к руководству GCC для получения дополнительной информации об опциях cc.
Файлы заголовков, объявленные как включаемые файлы в gpextprotocal.c, расположены в подкаталогах $GPHOME/include/postgresql/.
Дополнительные сведения о компиляции и компоновке динамически загружаемых функций и примеры компиляции исходного кода C для создания разделяемой библиотеки в других операционных системах см. в документации PostgreSQL.
Страницы руководства для компилятора C cc и редактора ссылок ld для вашей операционной системы также содержат информацию о компиляции и компоновке исходного кода в вашей системе.
Скомпилированный код (общий объектный файл) для пользовательского протокола должен быть размещен в одном и том же месте на каждом хосте в массиве базы данных RT.Warehouse (мастер и все сегменты). Это расположение также должно быть в LD_LIBRARY_PATH, чтобы сервер мог найти файлы. Рекомендуется размещать общие библиотеки либо относительно $libdir (который находится в $GPHOME/lib), либо через динамический путь к библиотеке (задается параметром конфигурации сервера dynamic_library_path) во всех экземплярах основного сегмента в массиве базы данных RT.Warehouse. Вы можете использовать утилиты базы данных RT.Warehouse gpssh и gpscp для обновления сегментов.
#include "postgres.h"
#include "fmgr.h"
#include "funcapi.h"
#include "access/extprotocol.h"
#include "catalog/pg_proc.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/memutils.h"
/* Our chosen URI format. We can change it however needed */
typedef struct DemoUri
{
char *protocol;
char *path;
} DemoUri;
static DemoUri *ParseDemoUri(const char *uri_str);
static void FreeDemoUri(DemoUri* uri);
/* Do the module magic dance */
PG_MODULE_MAGIC;
PG_FUNCTION_INFO_V1(demoprot_export);
PG_FUNCTION_INFO_V1(demoprot_import);
PG_FUNCTION_INFO_V1(demoprot_validate_urls);
Datum demoprot_export(PG_FUNCTION_ARGS);
Datum demoprot_import(PG_FUNCTION_ARGS);
Datum demoprot_validate_urls(PG_FUNCTION_ARGS);
/* A user context that persists across calls. Can be
declared in any other way */
typedef struct {
char *url;
char *filename;
FILE *file;
} extprotocol_t;
/*
* The read function - Import data into GPDB.
*/
Datum
myprot_import(PG_FUNCTION_ARGS)
{
extprotocol_t *myData;
char *data;
int datlen;
size_t nread = 0;
/* Must be called via the external table format manager */
if (!CALLED_AS_EXTPROTOCOL(fcinfo))
elog(ERROR, "myprot_import: not called by external
protocol manager");
/* Get our internal description of the protocol */
myData = (extprotocol_t *) EXTPROTOCOL_GET_USER_CTX(fcinfo);
if(EXTPROTOCOL_IS_LAST_CALL(fcinfo))
{
/* we're done receiving data. close our connection */
if(myData && myData->file)
if(fclose(myData->file))
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not close file \"%s\": %m",
myData->filename)));
PG_RETURN_INT32(0);
}
if (myData == NULL)
{
/* first call. do any desired init */
const char *p_name = "myprot";
DemoUri *parsed_url;
char *url = EXTPROTOCOL_GET_URL(fcinfo);
myData = palloc(sizeof(extprotocol_t));
myData->url = pstrdup(url);
parsed_url = ParseDemoUri(myData->url);
myData->filename = pstrdup(parsed_url->path);
if(strcasecmp(parsed_url->protocol, p_name) != 0)
elog(ERROR, "internal error: myprot called with a
different protocol (%s)",
parsed_url->protocol);
FreeDemoUri(parsed_url);
/* open the destination file (or connect to remote server in
other cases) */
myData->file = fopen(myData->filename, "r");
if (myData->file == NULL)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("myprot_import: could not open file \"%s\"
for reading: %m",
myData->filename),
errOmitLocation(true)));
EXTPROTOCOL_SET_USER_CTX(fcinfo, myData);
}
/* ==========================================
* DO THE IMPORT
* ========================================== */
data = EXTPROTOCOL_GET_DATABUF(fcinfo);
datlen = EXTPROTOCOL_GET_DATALEN(fcinfo);
/* read some bytes (with fread in this example, but normally
in some other method over the network) */
if(datlen > 0)
{
nread = fread(data, 1, datlen, myData->file);
if (ferror(myData->file))
ereport(ERROR,
(errcode_for_file_access(),
errmsg("myprot_import: could not write to file
\"%s\": %m",
myData->filename)));
}
PG_RETURN_INT32((int)nread);
}
/*
* Write function - Export data out of GPDB
*/
Datum
myprot_export(PG_FUNCTION_ARGS)
{
extprotocol_t *myData;
char *data;
int datlen;
size_t wrote = 0;
/* Must be called via the external table format manager */
if (!CALLED_AS_EXTPROTOCOL(fcinfo))
elog(ERROR, "myprot_export: not called by external
protocol manager");
/* Get our internal description of the protocol */
myData = (extprotocol_t *) EXTPROTOCOL_GET_USER_CTX(fcinfo);
if(EXTPROTOCOL_IS_LAST_CALL(fcinfo))
{
/* we're done sending data. close our connection */
if(myData && myData->file)
if(fclose(myData->file))
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not close file \"%s\": %m",
myData->filename)));
PG_RETURN_INT32(0);
}
if (myData == NULL)
{
/* first call. do any desired init */
const char *p_name = "myprot";
DemoUri *parsed_url;
char *url = EXTPROTOCOL_GET_URL(fcinfo);
myData = palloc(sizeof(extprotocol_t));
myData->url = pstrdup(url);
parsed_url = ParseDemoUri(myData->url);
myData->filename = pstrdup(parsed_url->path);
if(strcasecmp(parsed_url->protocol, p_name) != 0)
elog(ERROR, "internal error: myprot called with a
different protocol (%s)",
parsed_url->protocol);
FreeDemoUri(parsed_url);
/* open the destination file (or connect to remote server in
other cases) */
myData->file = fopen(myData->filename, "a");
if (myData->file == NULL)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("myprot_export: could not open file \"%s\"
for writing: %m",
myData->filename),
errOmitLocation(true)));
EXTPROTOCOL_SET_USER_CTX(fcinfo, myData);
}
/* ========================================
* DO THE EXPORT
* ======================================== */
data = EXTPROTOCOL_GET_DATABUF(fcinfo);
datlen = EXTPROTOCOL_GET_DATALEN(fcinfo);
if(datlen > 0)
{
wrote = fwrite(data, 1, datlen, myData->file);
if (ferror(myData->file))
ereport(ERROR,
(errcode_for_file_access(),
errmsg("myprot_import: could not read from file
\"%s\": %m",
myData->filename)));
}
PG_RETURN_INT32((int)wrote);
}
Datum
myprot_validate_urls(PG_FUNCTION_ARGS)
{
List *urls;
int nurls;
int i;
ValidatorDirection direction;
/* Must be called via the external table format manager */
if (!CALLED_AS_EXTPROTOCOL_VALIDATOR(fcinfo))
elog(ERROR, "myprot_validate_urls: not called by external
protocol manager");
nurls = EXTPROTOCOL_VALIDATOR_GET_NUM_URLS(fcinfo);
urls = EXTPROTOCOL_VALIDATOR_GET_URL_LIST(fcinfo);
direction = EXTPROTOCOL_VALIDATOR_GET_DIRECTION(fcinfo);
/*
* Dumb example 1: search each url for a substring
* we don't want to be used in a url. in this example
* it's 'secured_directory'.
*/
for (i = 1 ; i <= nurls ; i++)
{
char *url = EXTPROTOCOL_VALIDATOR_GET_NTH_URL(fcinfo, i);
if (strstr(url, "secured_directory") != 0)
{
ereport(ERROR,
(errcode(ERRCODE_PROTOCOL_VIOLATION),
errmsg("using 'secured_directory' in a url
isn't allowed ")));
}
}
/*
* Dumb example 2: set a limit on the number of urls
* used. In this example we limit readable external
* tables that use our protocol to 2 urls max.
*/
if(direction == EXT_VALIDATE_READ && nurls > 2)
{
ereport(ERROR,
(errcode(ERRCODE_PROTOCOL_VIOLATION),
errmsg("more than 2 urls aren't allowed in this protocol ")));
}
PG_RETURN_VOID();
}
/* --- utility functions --- */
static
DemoUri *ParseDemoUri(const char *uri_str)
{
DemoUri *uri = (DemoUri *) palloc0(sizeof(DemoUri));
int protocol_len;
uri->path = NULL;
uri->protocol = NULL;
/*
* parse protocol
*/
char *post_protocol = strstr(uri_str, "://");
if(!post_protocol)
{
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("invalid protocol URI \'%s\'", uri_str),
errOmitLocation(true)));
}
protocol_len = post_protocol - uri_str;
uri->protocol = (char *)palloc0(protocol_len + 1);
strncpy(uri->protocol, uri_str, protocol_len);
/* make sure there is more to the uri string */
if (strlen(uri_str) <= protocol_len)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("invalid myprot URI \'%s\' : missing path",
uri_str),
errOmitLocation(true)));
/* parse path */
uri->path = pstrdup(uri_str + protocol_len + strlen("://"));
return uri;
}
static
void FreeDemoUri(DemoUri *uri)
{
if (uri->path)
pfree(uri->path);
if (uri->protocol)
pfree(uri->protocol);
pfree(uri);
}
Темы в этом разделе охватывают управление производительностью базы данных RT.Warehouse, в том числе способы мониторинга производительности и настройки рабочих нагрузок для определения приоритетов использования ресурсов.
Управление производительностью системы включает измерение производительности, выявление причин проблем с производительностью и применение доступных вам инструментов и методов для устранения проблем.
RT.Warehouse измеряет производительность базы данных на основе скорости, с которой система управления базами данных (СУБД) предоставляет информацию запрашивающим сторонам.
Несколько ключевых факторов производительности влияют на производительность базы данных. Понимание этих факторов помогает определить возможности производительности и избежать проблем:
Производительность базы данных в значительной степени зависит от дискового ввода-вывода и использования памяти. Для точного определения ожидаемой производительности необходимо знать базовую производительность аппаратного обеспечения, на котором развернута ваша СУБД. Производительность таких аппаратных компонентов, как процессоры, жесткие диски, дисковые контроллеры, оперативная память и сетевые интерфейсы, существенно влияет на скорость работы вашей базы данных.
Примечание. Не устанавливайте антивирусное программное обеспечение на хосты базы данных RT.Warehouse, так как это программное обеспечение может вызвать дополнительную нагрузку на ЦП и ввод-вывод, что помешает работе базы данных RT.Warehouse. |
Рабочая нагрузка равна общей потребности СУБД, и она изменяется во времени. Общая рабочая нагрузка - это комбинация запросов пользователей, приложений, пакетных заданий, транзакций и системных команд, направленных через СУБД в любой момент времени. Например, она может увеличиваться при выполнении отчетов в конце месяца или уменьшаться в выходные дни, когда большинство пользователей не работают. Рабочая нагрузка сильно влияет на производительность базы данных. Знание рабочей нагрузки и времени пикового спроса помогает планировать наиболее эффективное использование ресурсов системы и позволяет обрабатывать максимально возможную рабочую нагрузку.
Пропускная способность системы определяет ее общую способность обрабатывать данные. Пропускная способность СУБД измеряется количеством запросов в секунду, транзакций в секунду или средним временем отклика. Пропускная способность СУБД тесно связана с вычислительной мощностью базовых систем (дисковый ввод-вывод, скорость ЦП, пропускная способность памяти и т. д.), поэтому важно знать пропускную способность вашего оборудования при установке целей пропускной способности СУБД.
Конфликт (Contention) - это состояние, при котором два или более компонента рабочей нагрузки пытаются использовать систему противоречивым образом - например, несколько запросов, которые пытаются обновить один и тот же фрагмент данных одновременно, или несколько больших рабочих нагрузок, которые конкурируют за ресурсы системы. При увеличении количества конфликтов пропускная способность снижается.
Оптимизация СУБД может повлиять на общую производительность системы. Формулировка SQL, параметры конфигурации базы данных, дизайн таблиц, распределение данных и т. д. позволяют оптимизатору запросов к базе данных создавать наиболее эффективные планы доступа.
Приступая к инициативе по настройке производительности, вы должны знать ожидаемый уровень производительности вашей системы и определить измеримые требования к производительности, чтобы вы могли точно оценить производительность вашей системы. При постановке целей производительности учитывайте следующее:
Большинство проблем с производительностью базы данных вызваны не самой базой данных, а базовыми системами, на которых работает база данных. Узкие места ввода-вывода, проблемы с памятью и проблемы с сетью могут заметно снизить производительность базы данных. Знание базовых возможностей вашего оборудования и операционной системы (ОС) поможет вам выявлять и устранять проблемы, связанные с оборудованием, прежде чем приступить к настройке на уровне базы данных или на уровне запроса.
Чтобы поддерживать хорошую производительность или устранять проблемы с производительностью, вы должны знать возможности вашей СУБД при определенной рабочей нагрузке. Эталонный тест — это предопределенная рабочая нагрузка, которая дает известный набор результатов. Периодически запускайте одни и те же тесты производительности, чтобы выявить ухудшение производительности системы с течением времени. Используйте эталонные тесты для сравнения рабочих нагрузок и определения запросов или приложений, которые нуждаются в оптимизации.
Многие сторонние организации, такие как Совет по производительности обработки транзакций (Transaction Processing Performance Council, TPC), предоставляют эталонные инструменты для индустрии баз данных. TPC предоставляет TPC-H, систему поддержки принятия решений, которая анализирует большие объемы данных, выполняет запросы высокой степени сложности и дает ответы на важные бизнес-вопросы. Для получения дополнительной информации о TPC-H перейдите по ссылке: http://www.tpc.org/tpch
В этом разделе описаны процессы поиска и устранения неисправностей для распространенных проблем производительности и возможные решения этих проблем.
Производительность базы данных RT.Warehouse зависит от оборудования и ИТ-инфраструктуры на которых она работает. База данных RT.Warehouse состоит из нескольких серверов (хостов), работающих вместе как единая система (массив); в качестве первого шага в диагностике проблем с производительностью убедитесь, что все сегменты базы данных RT.Warehouse находятся в сети. Производительность базы данных RT.Warehouse будет такой же, как и у самого медленного хоста в массиве. Проблемы с использованием ЦП, управлением памятью, обработкой ввода-вывода или сетевой нагрузкой влияют на производительность. Общие проблемы, связанные с оборудованием:
Система базы данных имеет ограниченную мощность ЦП, память и дисковые ресурсы ввода-вывода. Когда несколько рабочих нагрузок конкурируют за доступ к этим ресурсам, производительность базы данных снижается. Управление ресурсами максимизирует пропускную способность системы, удовлетворяя различные бизнес-требования. База данных RT.Warehouse предоставляет очереди ресурсов и группы ресурсов, чтобы помочь вам управлять этими системными ресурсами.
Очереди ресурсов и группы ресурсов ограничивают использование ресурсов и общее количество одновременных запросов, выполняемых в конкретной очереди или группе. Назначая роли базы данных соответствующей очереди или группе, администраторы могут контролировать одновременные запросы пользователей и предотвращать перегрузку системы. Дополнительные сведения об очередях ресурсов и группах ресурсов, включая выбор подходящей схемы для вашей среды базы данных RT.Warehouse, см. в разделе Управление ресурсами (24.4).
Администраторы базы данных RT.Warehouse должны запускать рабочие нагрузки по обслуживанию, такие как загрузка данных и операции VACUUM ANALYZE, в нерабочее время (не конфликтуя с пользователями базы данных за системные ресурсы). Административные задачи следует выполнять в периоды низкой нагрузки.
Конфликт возникает, когда несколько пользователей или рабочих нагрузок пытаются использовать систему противоречивым образом; например, конфликт возникает, когда две транзакции пытаются обновить таблицу одновременно. Транзакция, которая ищет блокировку на уровне таблицы или строки, будет ждать неопределенное время, пока конфликтующие блокировки будут освобождены. Приложения не должны держать транзакции открытыми в течение длительных периодов времени, например, в ожидании ввода данных пользователем.
База данных RT.Warehouse использует оптимизатор запросов на основе затрат, который опирается на статистику базы данных. Точная статистика позволяет оптимизатору запросов лучше оценить количество строк, извлеченных запросом, чтобы выбрать наиболее эффективный план запроса. Без статистики базы данных оптимизатор запросов не может оценить, сколько записей будет возвращено. Оптимизатор не предполагает, что у него достаточно памяти для выполнения определенных операций, таких как агрегирование, поэтому он предпринимает наиболее консервативные действия и выполняет эти операции путем чтения и записи с диска. Это значительно медленнее, чем делать их в памяти. ANALYZE собирает статистику о базе данных, необходимую оптимизатору запросов.
Примечание. При выполнении SQL-команды с GPORCA база данных RT.Warehouse выдает предупреждение, если производительность команды может быть улучшена путем сбора статистики по столбцу или набору столбцов, на которые ссылается команда. Предупреждение выдается в командной строке, и информация добавляется в лог файл базы данных RT.Warehouse. Для получения информации о сборе статистики по столбцам таблицы см. команду ANALYZE |
Прежде чем интерпретировать план запроса с помощью EXPLAIN или EXPLAIN ANALYZE, ознакомьтесь с данными, чтобы выявить возможные проблемы со статистикой. Проверьте план на наличие следующих показателей недостоверной статистики:
Следующие параметры конфигурации управляют объемом выборки данных для сбора статистики:
Эти параметры управляют выборкой статистики на системном уровне. Лучше выбирать только увеличенную статистику для столбцов, наиболее часто используемых в предикатах запросов. Вы можете настроить статистику для конкретного столбца с помощью команды:
Например:
ALTER TABLE sales ALTER COLUMN region SET STATISTICS 50;
Это эквивалентно изменению default_statistics_target для определенного столбца. Последующие операции ANALYZE затем соберут больше статистических данных для этого столбца и в результате создадут лучшие планы запросов.
При создании таблицы в базе данных RT.Warehouse необходимо объявить ключ распределения, позволяющий равномерно распределять данные по всем сегментам системы. Поскольку сегменты обрабатывают запрос параллельно, база данных RT.Warehouse всегда будет такой же быстрой, как и самый медленный сегмент. Если данные несбалансированы, сегменты с большим количеством данных будут возвращать свои результаты медленнее и, следовательно, замедлят работу всей системы.
Многие проблемы с производительностью могут быть решены путем проектирования базы данных. Изучите структуру своей базы данных и примите во внимание следующее:
Чтобы оптимизировать дизайн базы данных, в Таблицу 134 собраны максимальные ограничения, поддерживаемые базой данных RT.Warehouse.
Таблица 134. Максимальные ограничения, поддерживаемые базой данных RT.Warehouse
Измерение | Ограничение |
---|---|
Размер базы данных (Database Size) | Неограниченно |
Размер таблицы (Table Size) | Неограниченно, 128 ТБ на раздел на сегмент |
Размер строки (Row Size) | 1,6 ТБ (1600 столбцов * 1 ГБ) |
Размер поля (Field Size) | 1 ГБ |
Количество строк в таблице (Rows per Table) | 281474976710656 (2^48) |
Столбцов на таблицу/представление (Columns per Table/View) | 1600 |
Индексы на таблицу (Indexes per Table) | Неограниченно |
Столбцов на индекс | 32 |
Ограничения на уровне таблицы для каждой таблицы (Table-level Constraints per Table) | Неограниченно |
Длина имени таблицы (Table Name Length) | 63 байта (ограничено типом данных имени) |
Размеры, указанные как неограниченные, не имеют внутренних ограничений базы данных RT.Warehouse. Однако на практике они ограничены доступным дисковым пространством и памятью/пространством подкачки. Производительность может ухудшиться, если эти значения необычно велики.
Примечание. Существует максимальное количество объектов (таблиц, индексов и представлений, но не строк), которые могут существовать одновременно. Этот предел составляет 4 294 967 296 (2 ^ 32). |
Память является ключевым ресурсом для системы баз данных RT.Warehouse и при эффективном использовании может обеспечить высокую производительность и пропускную способность. В этом разделе описывается, как память узла сегмента распределяется между сегментами, а также параметры, доступные администраторам для настройки памяти.
Хост сегмента базы данных RT.Warehouse запускает несколько экземпляров PostgreSQL, и все они совместно используют память хоста. Сегменты имеют идентичную конфигурацию и одновременно потребляют одинаковые объемы памяти, ЦП и дискового ввода-вывода, при параллельной обработке запросов.
Для наилучшей пропускной способности запросов необходимо тщательно управлять конфигурацией памяти. В базе данных RT.Warehouse есть параметры конфигурации памяти на каждом уровне, от параметров операционной системы до управления ресурсами с помощью очередей ресурсов и групп ресурсов, до установки объема памяти, выделяемой для отдельного запроса.
На хосте сегмента базы данных RT.Warehouse доступная память хоста совместно используется всеми процессами, запущенными на компьютере, включая операционную систему, экземпляры сегмента базы данных RT.Warehouse и другие процессы приложений. Администраторы должны определить, какие процессы базы данных RT.Warehouse и процессы, не относящиеся к базе данных RT.Warehouse, совместно используют память хостов, и настроить систему для эффективного использования памяти. Не менее важно регулярно отслеживать использование памяти, чтобы обнаруживать любые изменения в том, как память хоста используется базой данных RT.Warehouse или другими процессами.
На Рисунке 12 показано, как потребляется память на хосте сегмента базы данных RT.Warehouse, когда активно управление ресурсами на основе очереди ресурсов.
Рисунок 12. Потребление памяти на хосте сегмента базы данных RT.Warehouse
Начиная с нижней части рисунка, линия, обозначенная буквой A, представляет собой общий объем памяти хоста. Строка непосредственно над строкой A показывает, что общая память хоста включает как физическую оперативную память, так и пространство подкачки.
Строка с меткой B показывает, что общая доступная память должна совместно использоваться базой данных RT.Warehouse и всеми другими процессами на хосте. Процессы базы данных, отличные от RT.Warehouse, включают операционную систему и любые другие приложения, например, агенты системного мониторинга. Некоторые приложения могут использовать значительную часть памяти, и в результате вам может потребоваться настроить количество сегментов на хост базы данных RT.Warehouse или объем памяти на сегмент.
Каждый сегмент (C) получает равную долю памяти базы данных RT.Warehouse (B).
В пределах сегмента текущая активная схема управления ресурсами, Очереди ресурсов или Группы ресурсов, управляет тем, как выделяется память для выполнения оператора SQL. Эти конструкции позволяют преобразовывать бизнес-требования в политики выполнения в вашей системе базы данных RT.Warehouse и защищаться от запросов, которые могут снизить производительность. Обзор групп ресурсов и очередей ресурсов см. в разделе Управление ресурсами (24.4).
Память хоста — это общая память, совместно используемая всеми приложениями на хосте сегмента. Вы можете настроить объем памяти хоста одним из следующих способов:
Физическая оперативная память и конфигурация ОС обычно управляются командой платформы и системными администраторами.
Объем памяти, резервируемый для операционной системы и других процессов, зависит от рабочей нагрузки. Минимальный рекомендуемый объем памяти операционной системы составляет 32 ГБ, но если в базе данных RT.Warehouse много параллелизма, может потребоваться увеличение до 64 ГБ зарезервированной памяти. Наибольшим пользователем памяти операционной системы является SLAB, который увеличивается по мере увеличения параллелизма базы данных RT.Warehouse и количества используемых сокетов.
Параметр ядра vm.overcommit_memory всегда должен быть установлен на 2, единственное безопасное значение для базы данных RT.Warehouse.
Параметр ядра vm.overcommit_ratio устанавливает процент оперативной памяти, которая используется для процессов приложений, а оставшаяся часть зарезервирована для операционной системы. Значение по умолчанию для Red Hat — 50 (50%). Установка слишком высокого значения для этого параметра может привести к недостаточному объему памяти, зарезервированной для операционной системы, что может привести к сбою узла сегмента или сбою базы данных. Оставить для параметра значение по умолчанию 50, как правило, безопасно, но консервативно. Установка слишком низкого значения снижает количество параллелизма и сложность запросов, которые вы можете выполнять одновременно, за счет уменьшения объема памяти, доступной для базы данных RT.Warehouse. При увеличении vm.overcommit_ratio важно всегда резервировать часть памяти для действий операционной системы.
Настройка vm.overcommit_ratio при активном управлении ресурсами на основе группы ресурсов
Когда управление ресурсами на основе групп ресурсов активно, настройте операционную систему vm.overcommit_ratio по мере необходимости. Если использование памяти слишком низкое, увеличьте значение, если использование памяти или подкачки слишком велико, уменьшите значение параметра.
Настройка vm.overcommit_ratio при активном управлении ресурсами на основе очереди ресурсов
Чтобы рассчитать безопасное значение для vm.overcommit_ratio, когда активно управление ресурсами на основе очереди ресурсов, сначала определите общий объем памяти, доступный для процессов базы данных RT.Warehouse, gp_vmem_rq.
gp_vmem_rq = ((SWAP + RAM) – (7.5GB + 0.05 * RAM)) / 1.7
gp_vmem_rq = ((SWAP + RAM) – (7.5GB + 0.05 * RAM)) / 1.17
где SWAP — это пространство подкачки на хосте в ГБ, а RAM — количество ГБ оперативной памяти, установленной на хосте.
Когда активно управление ресурсами на основе очереди ресурсов, используйте gp_vmem_rq для расчета значения vm.overcommit_ratio по этой формуле:
vm.overcommit_ratio = (RAM - 0.026 * gp_vmem_rq) / RAM
Память базы данных RT.Warehouse — это объем памяти, доступный для всех экземпляров сегмента базы данных RT.Warehouse.
При настройке кластера базы данных RT.Warehouse вы определяете количество основных сегментов для запуска на хосте и объем памяти, выделяемый для каждого сегмента. В зависимости от ядер ЦП, объема физической оперативной памяти и характеристик рабочей нагрузки количество сегментов обычно составляет от 4 до 8. При включенном зеркалировании сегментов важно выделить память для максимального количества основных сегментов, работающих на хосте во время сбоя. Например, если вы используете конфигурацию группового зеркала по умолчанию, сбой узла сегмента удваивает количество действующих основных узлов на узле, на котором есть зеркала отказавшего узла. Конфигурации зеркал, которые распределяют зеркала каждого хоста по нескольким другим хостам, могут снизить максимальное значение, позволяя выделить больше памяти для каждого сегмента. Например, если вы используете конфигурацию зеркалирования блоков с 4 хостами на блок и 8 первичными сегментами на хост, отказ одного хоста приведет к тому, что другие хосты в блоке будут иметь максимум 11 активных первичных сегментов, по сравнению с 16 для конфигурации группирующего зеркала по умолчанию.
Настройка сегментной памяти при активном управлении ресурсами на основе группы ресурсов
Когда управление ресурсами на основе группы ресурсов активно, объем памяти, выделенный каждому сегменту на узле сегмента, представляет собой объем памяти, доступный для базы данных RT.Warehouse, умноженный на параметр конфигурации сервера gp_resource_group_memory_limit и разделенный на количество активных первичных сегментов на узле. Используйте следующую формулу для расчета памяти сегмента при использовании групп ресурсов для управления ресурсами.
rg_perseg_mem = ((RAM * (vm.overcommit_ratio / 100) + SWAP) * gp_resource_group_memory_limit) / num_active_primary_segments
Группы ресурсов предоставляют дополнительные параметры конфигурации, которые позволяют дополнительно контролировать и уточнять объем памяти, выделяемой для запросов.
Настройка сегментной памяти при активном управлении ресурсами на основе очереди ресурсов
Когда управление ресурсами на основе очередей ресурсов активно, значение параметра конфигурации сервера gp_vmem_protect_limit определяет объем памяти, выделяемый каждому сегменту. Это значение оценивается путем вычисления памяти, доступной для всех процессов базы данных RT.Warehouse, и деления на максимальное количество первичных сегментов во время сбоя. Если для gp_vmem_protect_limit задано слишком большое значение, запросы могут завершаться ошибкой. Используйте следующую формулу для расчета безопасного значения gp_vmem_protect_limit; укажите значение gp_vmem_rq, которое вы рассчитали ранее.
gp_vmem_protect_limit = gp_vmem_rq / max_acting_primary_segments
где max_acting_primary_segments — это максимальное количество первичных сегментов, которые могут работать на хосте, когда зеркальные сегменты активированы из-за сбоя хоста или сегмента.
Примечание. Параметр gp_vmem_protect_limit применяется только тогда, когда управление ресурсами на основе очереди ресурсов активно в базе данных RT.Warehouse. RT.Warehouse игнорирует этот параметр конфигурации, когда активно управление ресурсами на основе группы ресурсов. |
Очереди ресурсов предоставляют дополнительные параметры конфигурации, которые позволяют дополнительно контролировать и уточнять объем памяти, выделяемой для запросов.
В этом разделе приведены примеры расчетов памяти для очередей ресурсов и групп ресурсов для системы базы данных RT.Warehouse со следующими характеристиками:
Когда в базе данных RT.Warehouse активно управление ресурсами на основе группы ресурсов, доступная для использования память на хосте зависит от объема ОЗУ и пространства подкачки, настроенных для системы, а также от настройки системного параметра vm.overcommit_ratio:
total_node_usable_memory = RAM * (vm.overcommit_ratio / 100) + Swap
= 256GB * (50/100) + 64GB
= 192GB
Предполагая значение gp_resource_group_memory_limit по умолчанию (.7), память, выделенная хосту базы данных RT.Warehouse с конфигурацией примера, будет следующей:
total_gp_memory = total_node_usable_memory * gp_resource_group_memory_limit
= 192GB * .7
= 134.4GB
Память, доступная для сегмента базы данных RT.Warehouse на узле сегмента, является функцией памяти, зарезервированной для RT.Warehouse на узле, и количества активных первичных сегментов на узле. При запуске кластера:
gp_seg_memory = total_gp_memory / number_of_active_primary_segments
= 134.4GB / 8
= 16.8GB
Обратите внимание, что когда 3 зеркальных сегмента переключаются на основные сегменты, объем памяти для каждого сегмента по-прежнему составляет 16,8 ГБ. Общее использование памяти на узле сегмента может приблизиться к:
total_gp_memory_with_primaries = 16.8GB * 11 = 184.8GB
Вычисление vm.overcommit_ratio для примера системы, когда управление ресурсами на основе очереди ресурсов активно в базе данных RT.Warehouse, выглядит следующим образом:
gp_vmem_rq = ((SWAP + RAM) – (7.5GB + 0.05 * RAM)) / 1.7
= ((64 + 256) - (7.5 + 0.05 * 256)) / 1.7
= 176
vm.overcommit_ratio = (RAM - (0.026 * gp_vmem_rq)) / RAM
= (256 - (0.026 * 176)) / 256
= .982
Вы должны установить vm.overcommit_ratio из примера системы на 98.
Вычисление gp_vmem_protect_limit, когда управление ресурсами на основе очереди ресурсов активно в базе данных RT.Warehouse:
gp_vmem_protect_limit = gp_vmem_rq / maximum_acting_primary_segments
= 176 / 11
= 16GB
= 16384MB
Вы должны установить для параметра конфигурации сервера gp_vmem_protect_limit в примере системы значение 16384.
База данных RT.Warehouse предоставляет функции, помогающие расставлять приоритеты и распределять ресурсы для запросов в соответствии с бизнес-требованиями, а также предотвращать запуск запросов, когда ресурсы недоступны.
Вы можете использовать функции управления ресурсами, чтобы ограничить количество одновременных запросов, объем памяти, используемый для выполнения запроса, и относительный объем ЦП, выделяемый для обработки запроса. База данных RT.Warehouse предоставляет две схемы управления ресурсами — очереди ресурсов и группы ресурсов.
Внимание. Наблюдалось значительное снижение производительности базы данных RT.Warehouse при включении управления рабочей нагрузкой на основе группы ресурсов в RedHat 6.x и CentOS 6.x. Эта проблема вызвана ошибкой ядра cgroup Linux. Эта ошибка ядра была исправлена в системах CentOS 7.x и Red Hat 7.x/8.x. |
Если вы используете RedHat 6 и производительность групп ресурсов приемлема для вашего варианта использования, обновите ядро до версии 2.6.32-696 или выше, чтобы воспользоваться другими исправлениями реализации cgroups.
В базе данных RT.Warehouse может быть активна либо очередь ресурсов, либо схема управления группами ресурсов; обе схемы не могут быть активны одновременно.
Очереди ресурсов включаются по умолчанию при установке кластера базы данных RT.Warehouse. Хотя вы можете создавать и назначать группы ресурсов, когда очереди ресурсов активны, вы должны явно включить группы ресурсов, чтобы начать использовать эту схему управления.
В Таблице 135 приведены некоторые различия между очередями ресурсов и группами ресурсов.
Таблица 135. Различия между очередями ресурсов и группами ресурсов
Метрика | Очереди ресурсов (Resource Queues) | Группы ресурсов (Resource Groups) |
---|---|---|
Concurrency | Управляется на уровне запроса | Управляется на уровне транзакции |
CPU | Укажите приоритет запроса | Укажите процент ресурсов процессора; использует группы управления Linux |
Memory | Управляется на уровне очереди и оператора; пользователи могут переподписываться | Управление на уровне транзакций с улучшенным распределением и отслеживанием; пользователи не могут переподписываться |
Memory Isolation | Нет | Память изолирована между группами ресурсов и между транзакциями в одной группе ресурсов |
Users | Ограничения применяются только к пользователям без прав администратора | Ограничения применяются как к пользователям SUPERUSER, так и к пользователям без прав администратора. |
Queueing | Очередь только тогда, когда нет свободных слотов | Очередь, когда нет свободного слота или недостаточно доступной памяти |
Query Failure | Запрос может немедленно завершиться ошибкой, если недостаточно памяти | Запрос может завершиться ошибкой после достижения предела фиксированной памяти транзакции, когда не существует общей памяти группы ресурсов, и транзакция запрашивает больше памяти |
Limit Bypass | Ограничения не применяются для ролей SUPERUSER и некоторых операторов и функций | Ограничения не применяются к командам SET, RESET и SHOW |
External Components | Нет | Управление ресурсами ЦП и памяти PL/Container |
Вы используете группы ресурсов для установки и обеспечения соблюдения ограничений ЦП, памяти и одновременных транзакций в базе данных RT.Warehouse. После определения группы ресурсов вы можете назначить группу одной или нескольким ролям базы данных RT.Warehouse или внешнему компоненту, такому как PL/Container, чтобы управлять ресурсами, используемыми этими ролями или компонентами.
Когда вы назначаете группу ресурсов роли (группе ресурсов на основе ролей), ограничения ресурсов, которые вы определяете для группы, применяются ко всем ролям, которым вы назначаете группу. Например, ограничение памяти для группы ресурсов определяет максимальное использование памяти для всех запущенных транзакций, отправленных пользователями базы данных RT.Warehouse во всех ролях, которым вы назначаете группу.
Точно так же, когда вы назначаете группу ресурсов внешнему компоненту, ограничения группы применяются ко всем запущенным экземплярам компонента. Например, если вы создаете группу ресурсов для внешнего компонента PL/Container, ограничение памяти, которое вы определяете для группы, определяет максимальное использование памяти для всех запущенных экземпляров каждой среды выполнения PL/Container, которой вы назначаете группу.
База данных RT.Warehouse поддерживает два типа групп ресурсов: группы, которые управляют ресурсами для ролей, и группы, которые управляют ресурсами для внешних компонентов, таких как PL/Container.
Наиболее распространенное применение групп ресурсов — управление количеством активных запросов, которые разные роли могут выполнять одновременно в кластере базы данных RT.Warehouse. Вы также можете управлять объемом ресурсов ЦП и памяти, которые RT.Warehouse выделяет для каждого запроса.
Группы ресурсов для ролей используют контрольные группы Linux (cgroups) для управления ресурсами ЦП. База данных RT.Warehouse осуществляет внутреннее отслеживание виртуальной памяти для этих групп ресурсов с помощью аудитора памяти, называемого vmtracker.
Когда пользователь запускает запрос, база данных RT.Warehouse оценивает запрос по набору ограничений, определенных для группы ресурсов. База данных RT.Warehouse запускает запрос немедленно, если лимиты ресурсов группы еще не достигнуты и запрос не приводит к превышению лимита одновременных транзакций группой. Если эти условия не выполняются, база данных RT.Warehouse ставит запрос в очередь. Например, если максимальное количество одновременных транзакций для группы ресурсов уже достигнуто, последующий запрос ставится в очередь и должен ждать завершения других запросов, прежде чем он будет запущен. База данных RT.Warehouse также может выполнять ожидающий запрос, когда ограничения параллелизма и памяти группы ресурсов изменены на достаточно большие значения.
В группе ресурсов для ролей транзакции оцениваются в порядке поступления. База данных RT.Warehouse периодически оценивает активную рабочую нагрузку системы, перераспределяя ресурсы и запуская/ставя в очередь задания по мере необходимости.
Вы также можете использовать группы ресурсов для управления ресурсами ЦП и памяти внешних компонентов, таких как PL/Container. Группы ресурсов для внешних компонентов используют контрольные группы Linux для управления общими ресурсами ЦП и общими ресурсами памяти для компонента.
Примечание. Контейнерные развертывания базы данных RT.Warehouse могут создать иерархический набор вложенных контрольных групп для управления ресурсами хост-системы. Вложенность cgroups влияет на ограничения группы ресурсов базы данных RT.Warehouse для процентного соотношения ЦП, ядер ЦП и памяти (за исключением внешних компонентов базы данных RT.Warehouse). Ограничение системных ресурсов группы ресурсов базы данных RT.Warehouse основано на квоте для родительской группы. |
Например, база данных RT.Warehouse работает в демо cgroup, а cgroup базы данных RT.Warehouse вложена в демо cgroup. Если демо-версия cgroup настроена с ограничением ЦП в 60 % от ресурсов ЦП системы, а ограничение ЦП для группы ресурсов базы данных RT.Warehouse установлено на 90 %, ограничение ресурсов ЦП для базы данных RT.Warehouse составляет 54 % (0,6 x 0,9).
Вложенные контрольные группы не влияют на ограничения памяти для внешних компонентов базы данных RT.Warehouse, таких как PL/Container. Ограничениями памяти для внешних компонентов можно управлять только в том случае, если контрольная группа, используемая для управления ресурсами базы данных RT.Warehouse, не является вложенной, контрольная группа настроена как контрольная группа верхнего уровня.
При создании группы ресурсов вы:
В Таблице 136 представлены атрибуты и ограничения группы ресурсов.
Таблица 136. Атрибуты и ограничения группы ресурсов
Тип ограничения | Описание |
---|---|
MEMORY_AUDITOR | Аудитор памяти, используемый для группы ресурсов. vmtracker (по умолчанию) требуется, если вы хотите назначить группе ресурсов роли. Укажите cgroup, чтобы назначить группу ресурсов внешнему компоненту. |
CONCURRENCY | Максимальное количество одновременных транзакций, включая активные и бездействующие транзакции, разрешенное в группе ресурсов. |
CPU_RATE_LIMIT | Процент ресурсов ЦП, доступных для этой группы ресурсов. |
CPUSET | Ядра ЦП, которые необходимо зарезервировать для этой группы ресурсов на главном узле и узле сегмента. |
MEMORY_LIMIT | Процент зарезервированных ресурсов памяти, доступных для этой группы ресурсов. |
MEMORY_SHARED_QUOTA | Процент зарезервированной памяти для совместного использования транзакциями, отправленными в эту группу ресурсов. |
MEMORY_SPILL_RATIO | Порог использования памяти для транзакций, интенсивно использующих память. Когда транзакция достигает этого порога, она записывается на диск. |
Примечание. Ограничения ресурсов не применяются к командам SET, RESET и SHOW. |
Атрибут MEMORY_AUDITOR указывает тип группы ресурсов, определяя аудитора памяти для группы. Группа ресурсов, в которой указан vmtracker MEMORY_AUDITOR, определяет группу ресурсов для ролей. Группа ресурсов, указывающая контрольную группу MEMORY_AUDITOR, определяет группу ресурсов для внешних компонентов.
MEMORY_AUDITOR по умолчанию — vmtracker.
В Таблице 137 MEMORY_AUDITOR, который вы указываете для группы ресурсов, определяет, использует ли RT.Warehouse атрибуты ограничения для управления ресурсами CPU и памяти:
Таблица 137. MEMORY_AUDITOR
Тип ограничения | Группа ресурсов для ролей | Группа ресурсов для внешних компонентов |
---|---|---|
CONCURRENCY | Да | Нет, должен быть равен нулю (0) |
CPU_RATE_LIMIT | Да | Да |
CPUSET | Да | Да |
MEMORY_LIMIT | Да | Да |
MEMORY_SHARED_QUOTA | Да | Специфичный для компонента |
MEMORY_SPILL_RATIO | Да | Специфичный для компонента |
Примечание. Для запросов, управляемых группами ресурсов, которые настроены на использование аудитора памяти vmtracker база данных RT.Warehouse поддерживает автоматическое завершение запросов в зависимости от объема используемой памяти. |
Ограничение CONCURRENCY контролирует максимальное количество одновременных транзакций, разрешенных для группы ресурсов для ролей.
Примечание. Ограничение CONCURRENCY не применяется к группам ресурсов для внешних компонентов и должно быть установлено равным нулю (0) для таких групп. |
Каждая группа ресурсов для ролей логически разделена на фиксированное количество слотов, равное пределу CONCURRENCY. База данных RT.Warehouse выделяет этим слотам равный фиксированный процент ресурсов памяти.
Предельное значение CONCURRENCY по умолчанию для группы ресурсов для ролей равно 20.
База данных RT.Warehouse ставит в очередь любые транзакции, отправленные после того, как группа ресурсов достигает предела CONCURRENCY. Когда текущая транзакция завершается, база данных RT.Warehouse снимает с очереди и запускает самую раннюю транзакцию из очереди, если имеется достаточно ресурсов памяти.
Вы можете установить параметр конфигурации сервера gp_resource_group_bypass, чтобы обойти ограничение параллелизма группы ресурсов.
Вы можете установить параметр конфигурации сервера gp_resource_group_queuing_timeout, чтобы указать количество времени, в течение которого транзакция остается в очереди, прежде чем база данных RT.Warehouse отменит транзакцию. Тайм-аут по умолчанию равен нулю, RT.Warehouse ставит транзакции в очередь на неопределенный срок.
Вы настраиваете долю ресурсов ЦП для резервирования для группы ресурсов на главных узлах и узлах сегмента, назначая определенные ядра ЦП группе или определяя процент ресурсов ЦП сегмента, выделяемых группе. База данных RT.Warehouse использует ограничения группы ресурсов CPUSET и CPU_RATE_LIMIT для определения режима выделения ресурсов ЦП. При настройке группы ресурсов необходимо указать только одно из этих ограничений.
Вы можете одновременно использовать оба режима распределения ресурсов ЦП в своем кластере базы данных RT.Warehouse. Вы также можете изменить режим выделения ресурсов ЦП для группы ресурсов во время выполнения.
Параметр конфигурации сервера gp_resource_group_cpu_limit определяет максимальный процент системных ресурсов ЦП, выделяемых группам ресурсов на каждом хосте базы данных RT.Warehouse. Это ограничение регулирует максимальное использование ЦП всеми группами ресурсов на главном сервере или на узле сегмента независимо от режима выделения ЦП, настроенного для группы. Оставшиеся незарезервированные ресурсы ЦП используются для ядра ОС и вспомогательных процессов демона базы данных RT.Warehouse. Значение gp_resource_group_cpu_limit по умолчанию равно 0,9 (90%).
Примечание. Значение gp_resource_group_cpu_limit по умолчанию может не оставлять достаточных ресурсов ЦП, если вы выполняете другие рабочие нагрузки на узлах кластера базы данных RT.Warehouse, поэтому обязательно настройте этот параметр конфигурации сервера соответствующим образом. |
Внимание. Не устанавливайте для gp_resource_group_cpu_limit значение выше 0,9. Это может привести к тому, что запросы с высокой рабочей нагрузкой будут занимать почти все ресурсы ЦП, что потенциально приведет к голоданию вспомогательных процессов базы данных RT.Warehouse. |
Вы определяете ядра ЦП, которые хотите зарезервировать для группы ресурсов, с помощью свойства CPUSET. Указанные вами ядра ЦП должны быть доступны в системе и не должны перекрываться с ядрами ЦП, зарезервированными для других групп ресурсов (хотя база данных RT.Warehouse использует ядра, которые вы назначаете группе ресурсов исключительно для этой группы, обратите внимание, что эти ядра ЦП также могут использоваться процессами, не относящимися к RT.Warehouse, в системе).
Укажите ядра ЦП отдельно для главного узла и узлов сегмента, разделив их точкой с запятой. Используйте разделенный запятыми список номеров отдельных ядер или интервалов номеров при настройке ядер для CPUSET. Вы должны заключить номера ядер/интервалы в одинарные кавычки, например, «1;1,3-4» использует ядро 1 на главном хосте и ядра 1, 3 и 4 на хостах сегмента.
При назначении ядер ЦП группам CPUSET учитывайте следующее:
Группы ресурсов, настроенные с помощью CPUSET, имеют более высокий приоритет ресурсов ЦП. Максимальный процент использования ресурсов ЦП для всех групп ресурсов, настроенных с помощью CPUSET на узле сегмента, равен количеству зарезервированных ядер ЦП, деленному на количество всех ядер ЦП, умноженному на 100.
Когда вы настраиваете CPUSET для группы ресурсов, база данных RT.Warehouse деактивирует CPU_RATE_LIMIT для группы и устанавливает значение -1.
Примечание. Вы должны настроить CPUSET для группы ресурсов после включения управления ресурсами на основе группы ресурсов для своего кластера базы данных RT.Warehouse. |
Процент ЦП узла базы данных RT.Warehouse делится поровну между каждым сегментом узла RT.Warehouse. Каждая группа ресурсов, которую вы настраиваете с помощью CPU_RATE_LIMIT, резервирует указанный процент ЦП сегмента для управления ресурсами.
Минимальный процент CPU_RATE_LIMIT, который вы можете указать для группы ресурсов, равен 1, максимальный — 100.
Сумма CPU_RATE_LIMIT, указанная для всех групп ресурсов, которые вы определяете в своем кластере базы данных RT.Warehouse, не должна превышать 100.
Максимальное использование ресурсов ЦП для всех групп ресурсов, настроенных с помощью CPU_RATE_LIMIT на узле сегмента, составляет минимум:
Когда вы настраиваете CPU_RATE_LIMIT для группы ресурсов, база данных RT.Warehouse деактивирует CPUSET для группы и устанавливает значение -1.
Существует два разных способа распределения ресурсов ЦП в процентах, определяемых значением параметра конфигурации gp_resource_group_cpu_ceiling_enforcement:
Этот режим активен, когда для gp_resource_group_cpu_ceiling_enforcement установлено значение false (по умолчанию). Он эластичен в том смысле, что база данных RT.Warehouse может выделять ресурсы ЦП из незанятой группы ресурсов более занятой группе (группам). В таких ситуациях ресурсы ЦП перераспределяются для ранее простаивающей группы ресурсов, когда эта группа ресурсов в следующий раз становится активной. Если несколько групп ресурсов заняты, им выделяются ресурсы ЦП любых простаивающих групп ресурсов на основе соотношения их CPU_RATE_LIMIT. Например, группе ресурсов, созданной с CPU_RATE_LIMIT, равной 40, будет выделено в два раза больше дополнительных ресурсов ЦП, чем группе ресурсов, созданной с CPU_RATE_LIMIT, равной 20.
Этот режим активен, когда для gp_resource_group_cpu_ceiling_enforcement установлено значение true. Группа ресурсов принудительно не использует больше ресурсов ЦП, чем определенное значение CPU_RATE_LIMIT, избегая использования функции резкого увеличения ЦП.
Когда группы ресурсов включены, использование памяти управляется на уровне узла, сегмента и группы ресурсов базы данных RT.Warehouse. Вы также можете управлять памятью на уровне транзакций с помощью группы ресурсов для ролей.
Параметр конфигурации сервера gp_resource_group_memory_limit определяет максимальный процент ресурсов системной памяти, выделяемых группам ресурсов на каждом хосте сегмента базы данных RT.Warehouse. Значение gp_resource_group_memory_limit по умолчанию равно 0,7 (70%).
Ресурс памяти, доступный на узле базы данных RT.Warehouse, далее делится поровну между каждым сегментом узла. Когда управление ресурсами на основе группы ресурсов активно, объем памяти, выделенный каждому сегменту на хосте сегмента, представляет собой память, доступную базе данных RT.Warehouse, умноженную на параметр конфигурации сервера gp_resource_group_memory_limit и разделенную на количество активных первичных сегментов на хосте:
rg_perseg_mem = ((RAM * (vm.overcommit_ratio / 100) + SWAP) * gp_resource_group_memory_limit) / num_active_primary_segments
Каждая группа ресурсов может зарезервировать процент памяти сегмента для управления ресурсами. Вы определяете этот процент с помощью значения MEMORY_LIMIT, которое вы указываете при создании группы ресурсов. Минимальный процент MEMORY_LIMIT, который вы можете указать для группы ресурсов, равен 0, максимальный — 100. Когда MEMORY_LIMIT равен 0, база данных RT.Warehouse не резервирует память для группы ресурсов, но использует глобальную общую память (Global Shared Memory) группы ресурсов для выполнения всех запросов памяти в группе.
Сумма MEMORY_LIMIT, указанная для всех групп ресурсов, которые вы определяете в своем кластере базы данных RT.Warehouse, не должна превышать 100.
Если память группы ресурсов зарезервирована для ролей (ненулевой MEMORY_LIMIT), память дополнительно делится на фиксированные и общие компоненты. Значение MEMORY_SHARED_QUOTA, которое вы указываете при создании группы ресурсов, определяет процентную долю зарезервированной памяти группы ресурсов, которая может использоваться совместно выполняющимися в данный момент транзакциями. Эта память выделяется в порядке очереди. Выполняемая транзакция может не использовать ни одной, часть или всю MEMORY_SHARED_QUOTA.
Минимальная MEMORY_SHARED_QUOTA, которую вы можете указать, равна 0, максимальная — 100. MEMORY_SHARED_QUOTA по умолчанию — 80.
Как упоминалось ранее, CONCURRENCY определяет максимальное количество одновременно выполняемых транзакций, разрешенных в группе ресурсов для ролей. Если фиксированная память зарезервирована группой ресурсов (ненулевой MEMORY_LIMIT), она делится на число CONCURRENCY слотов транзакций. Каждому слоту выделяется фиксированный равный объем памяти группы ресурсов. База данных RT.Warehouse гарантирует эту фиксированную память для каждой транзакции.
На Рисунке 13 показано распределение памяти групп ресурсов.
Рисунок 13. Распределение памяти групп ресурсов
Когда использование памяти для запроса превышает фиксированный объем памяти для каждой транзакции, база данных RT.Warehouse выделяет для запроса доступную общую память группы ресурсов. Максимальный объем памяти группы ресурсов, доступной для определенного слота транзакции, представляет собой сумму фиксированной памяти транзакции и полного выделения общей памяти группы ресурсов.
Сумма значений MEMORY_LIMIT, настроенных для всех групп ресурсов (включая группы admin_group и default_group по умолчанию), определяет процент зарезервированной памяти группы ресурсов. Если эта сумма меньше 100, база данных RT.Warehouse выделяет любую незарезервированную память глобальному пулу общей памяти группы ресурсов.
Глобальная общая память группы ресурсов доступна только для групп ресурсов, настроенных с помощью аудитора памяти vmtracker.
Когда доступно, база данных RT.Warehouse выделяет глобальную общую память для транзакции после первого выделения слота и общей памяти группы ресурсов (если применимо). База данных RT.Warehouse выделяет глобальную разделяемую память группы ресурсов для транзакций в порядке очереди.
Примечание. База данных RT.Warehouse отслеживает, но не контролирует активно использование памяти транзакций в группах ресурсов. Если использование памяти для группы ресурсов превышает ее фиксированное выделение памяти, транзакция в группе ресурсов завершается сбоем при выполнении всех следующих условий:
|
База данных RT.Warehouse более эффективно использует память группы ресурсов, если оставить часть памяти (например, 10-20%) нераспределенной для глобального пула общей памяти. Доступность глобальной разделяемой памяти также помогает смягчить сбои запросов, требующих большого объема памяти, или непредсказуемых запросов.
Большинство операторов запросов не требуют большого объема памяти, то есть во время обработки база данных RT.Warehouse может хранить их данные в выделенной памяти. Когда операторы запросов, интенсивно использующие память, такие как объединение и сортировка, обрабатывают больше данных, чем может храниться в памяти, данные сбрасываются на диск.
Параметр конфигурации сервера gp_resgroup_memory_policy управляет алгоритмом выделения и распределения памяти для всех операторов запросов. База данных RT.Warehouse поддерживает политики свободной памяти (по умолчанию) и политики автоматической памяти для групп ресурсов. Когда вы указываете автоматическую политику, база данных RT.Warehouse использует ограничения памяти группы ресурсов для распределения памяти между операторами запросов, выделяя фиксированный размер памяти операторам, не требующим большого объема памяти, а оставшуюся часть — операторам, интенсивно использующим память. Когда применяется политика нетерпеливого_свободного, база данных RT.Warehouse распределяет память между операторами более оптимально, перераспределяя память, освобожденную операторами, которые завершили свою обработку, операторам на более позднем этапе запроса.
MEMORY_SPILL_RATIO определяет порог использования памяти для операторов, активно использующих память в транзакции. При достижении этого порога транзакция сбрасывается на диск. База данных RT.Warehouse использует MEMORY_SPILL_RATIO для определения начального объема памяти, выделяемой для транзакции.
Вы можете указать целое процентное значение от 0 до 100 включительно для MEMORY_SPILL_RATIO. Значение по умолчанию MEMORY_SPILL_RATIO равно 0.
Когда MEMORY_SPILL_RATIO равно 0, база данных RT.Warehouse использует значение параметра конфигурации сервера statement_mem для управления памятью оператора начального запроса.
Примечание. Когда вы устанавливаете MEMORY_LIMIT в 0, MEMORY_SPILL_RATIO также должно быть установлено в 0. |
Вы можете выборочно установить MEMORY_SPILL_RATIO для каждого запроса на уровне сеанса с помощью параметра конфигурации сервера memory_spill_ratio.
Планировщик запросов предварительно вычисляет максимальный объем памяти, который может использовать каждый узел в дереве плана. Когда управление ресурсами на основе группы ресурсов активно и MEMORY_SPILL_RATIO для группы ресурсов не равно нулю, следующая формула приблизительно определяет максимальный объем памяти, который база данных RT.Warehouse выделяет для транзакции:
query_mem = (rg_perseg_mem * memory_limit) * memory_spill_ratio / concurrency
Где memory_limit, memory_spill_ratio и параллелизм задаются группой ресурсов, в которой выполняется транзакция.
По умолчанию база данных RT.Warehouse вычисляет максимальный объем памяти узла сегмента, выделенного для транзакции, на основе rg_perseg_mem и количества первичных сегментов, настроенных на мастер хосте.
Примечание. Если конфигурация памяти на мастер хосте и хосте сегмента базы данных RT.Warehouse различается, вы можете столкнуться с нехваткой памяти или недостаточным использованием ресурсов с конфигурацией по умолчанию. |
Если аппаратная конфигурация вашего мастер и сегментного хостов различается, установите для параметра конфигурации сервера gp_resource_group_enable_recalculate_query_mem значение true; это побуждает базу данных RT.Warehouse пересчитать максимальное выделение памяти для каждого запроса на каждом узле сегмента на основе rg_perseg_mem и количества первичных сегментов, настроенных на этом хосте сегмента.
Было показано, что низкое значение параметра statement_mem (например, в диапазоне 10 МБ) повышает производительность запросов с низкими требованиями к памяти. Используйте параметры конфигурации сервера memory_spill_ratio и statement_mem, чтобы переопределить настройку для каждого запроса. Например:
SET memory_spill_ratio=0;
SET statement_mem='10 MB';
Если вы не резервируете память для группы ресурсов (для MEMORY_LIMIT и MEMORY_SPILL_RATIO установлено значение 0):
Чтобы снизить риск OOM для запроса, выполняемого в важной группе ресурсов, рассмотрите возможность резервирования некоторой фиксированной памяти для группы. Хотя резервирование фиксированной памяти для группы уменьшает размер глобального пула общей общей памяти группы ресурсов, это может быть справедливым компромиссом для снижения риска возникновения условия OOM в запросе, выполняемом в критической группе ресурсов.
Группы ресурсов для ролей отслеживают всю память базы данных RT.Warehouse, выделенную с помощью функции palloc(). Память, которую вы выделяете с помощью функции Linux malloc(), не управляется этими группами ресурсов. Чтобы убедиться, что группы ресурсов для ролей точно отслеживают использование памяти, избегайте использования malloc() для выделения больших объемов памяти в пользовательских пользовательских функциях базы данных RT.Warehouse.
С помощью RT.Warehouse Command Center администратор может создавать группы ресурсов и управлять ими, изменять группы ресурсов ролей и создавать правила управления рабочей нагрузкой.
Правила назначения управления рабочей нагрузкой назначают транзакции различным группам ресурсов на основе определяемых пользователем критериев. Если правило назначения не совпадает, база данных RT.Warehouse назначает транзакцию группе ресурсов роли по умолчанию.
Внимание. Наблюдалось значительное снижение производительности базы данных RT.Warehouse при включении управления рабочей нагрузкой на основе группы ресурсов в системах RedHat 6.x и CentOS 6.x. Эта проблема вызвана ошибкой ядра cgroup Linux. Эта ошибка ядра была исправлена в системах CentOS 7.x и Red Hat 7.x/8.x. |
Если вы используете RedHat 6 и производительность групп ресурсов приемлема для вашего варианта использования, обновите ядро до версии 2.6.32-696 или выше, чтобы воспользоваться другими исправлениями реализации cgroups.
Группы ресурсов базы данных RT.Warehouse используют группы управления Linux (cgroups) для управления ресурсами ЦП. База данных RT.Warehouse также использует cgroups для управления памятью для групп ресурсов для внешних компонентов. С помощью cgroups RT.Warehouse изолирует использование ЦП и памяти внешних компонентов вашими процессами RT.Warehouse от других процессов на узле. Это позволяет RT.Warehouse поддерживать ограничения на использование памяти ЦП и внешних компонентов для каждой группы ресурсов.
Примечание. Redhat 8.x поддерживает две версии cgroups: cgroup v1 и cgroup v2. База данных RT.Warehouse поддерживает только cgroup v1. Выполните следующие (ниже) шаги, чтобы убедиться, что ваша система монтирует файловую систему cgroups-v1 при запуске. |
Выполните следующие задачи на каждом узле в кластере базы данных RT.Warehouse, чтобы настроить cgroups для использования с группами ресурсов:
1. Установите пакет операционной системы Control Groups на каждый узел базы данных RT.Warehouse, если он еще не установлен. Команда, которую вы запускаете для выполнения этой задачи, будет отличаться в зависимости от операционной системы, установленной на узле. Вы должны быть суперпользователем или иметь доступ sudo для запуска команды:
sudo yum install libcgroup-tools
sudo yum install libcgroup
2. Если вы используете Redhat 8.x, убедитесь, что вы настроили систему на монтирование файловой системы cgroups-v1 по умолчанию во время загрузки системы, выполнив следующую команду:
stat -fc %T /sys/fs/cgroup/
Для cgroup v1 вывод — tmpfs.
Если ваш вывод — cgroup2fs, настройте систему на монтирование cgroups-v1 по умолчанию во время загрузки системы системным и сервисным менеджером systemd:
grubby --update-kernel=/boot/vmlinuz-$(uname -r) --args="systemd.unified_cgroup_hierarchy=0 systemd.legacy_systemd_cgroup_controller"
Чтобы добавить те же параметры во все записи загрузки ядра:
grubby --update-kernel=ALL --args="systemd.unified_cgroup_hierarchy=0 systemd.legacy_systemd_cgroup_controller"
Перезагрузите систему, чтобы изменения вступили в силу.
3. Найдите файл конфигурации cgroups /etc/cgconfig.conf. Вы должны быть суперпользователем или иметь доступ sudo для редактирования этого файла:
sudo vi /etc/cgconfig.conf
4. Добавьте в файл следующую информацию о конфигурации:
group gpdb {
perm {
task {
uid = gpadmin;
gid = gpadmin;
}
admin {
uid = gpadmin;
gid = gpadmin;
}
}
cpu {
}
cpuacct {
}
cpuset {
}
memory {
}
}
Это содержимое настраивает ЦП, учет ЦП, набор ядер ЦП и группы управления памятью, управляемые пользователем gpadmin. База данных RT.Warehouse использует группу управления памятью только для тех групп ресурсов, которые созданы с помощью контрольной группы MEMORY_AUDITOR.
5. Запустите службу cgroups на каждом узле базы данных RT.Warehouse. Команда, которую вы запускаете для выполнения этой задачи, будет отличаться в зависимости от операционной системы, установленной на узле. Вы должны быть суперпользователем или иметь доступ sudo для запуска команды:
sudo cgconfigparser -l /etc/cgconfig.conf
sudo service cgconfig start
6. Определите точку монтирования каталога cgroup для узла:
grep cgroup /proc/mounts
Первая строка вывода идентифицирует точку монтирования cgroup.
7. Убедитесь, что вы правильно настроили конфигурацию контрольных групп базы данных RT.Warehouse, выполнив следующие команды. Замените <cgroup_mount_point> точкой монтирования, указанной на предыдущем шаге:
ls -l <cgroup_mount_point>/cpu/gpdb
ls -l <cgroup_mount_point>/cpuacct/gpdb
ls -l <cgroup_mount_point>/cpuset/gpdb
ls -l <cgroup_mount_point>/memory/gpdb
Если эти каталоги существуют и принадлежат gpadmin:gpadmin, вы успешно настроили контрольные группы для управления ресурсами ЦП базы данных RT.Warehouse.
8. Для автоматического воссоздания требуемых базой данных RT.Warehouse иерархий и параметров cgroup при перезапуске системы настройте свою систему для включения демона службы cgroup Linux cgconfig.service (Redhat/CentOS 7.x/8.x) или cgconfig (Redhat/CentOS 6.x) при запуске узла. Например, настройте одну из следующих команд службы cgroup в предпочтительном инструменте автозапуска службы:
sudo systemctl enable cgconfig.service
Чтобы запустить службу немедленно (без перезагрузки), введите:
sudo systemctl start cgconfig.service
sudo chkconfig cgconfig on
Вы можете выбрать другой метод для воссоздания иерархии cgroup группы ресурсов базы данных RT.Warehouse.
Чтобы использовать группы ресурсов в кластере базы данных RT.Warehouse, вы должны:
Когда вы устанавливаете базу данных RT.Warehouse, очереди ресурсов включены по умолчанию. Чтобы использовать группы ресурсов вместо очередей ресурсов, вы должны установить параметр конфигурации сервера gp_resource_manager.
1. Установите для параметра конфигурации сервера gp_resource_manager значение "group":
gpconfig -s gp_resource_manager
gpconfig -c gp_resource_manager -v "group"
2. Перезапустите базу данных RT.Warehouse:
gpstop
gpstart
После включения любая транзакция, отправленная ролью, направляется группе ресурсов, назначенной этой роли, и регулируется ограничениями параллелизма, памяти и ЦП этой группы ресурсов. Точно так же использование ЦП и памяти внешним компонентом регулируется ограничениями ЦП и памяти, настроенными для группы ресурсов, назначенной компоненту.
База данных RT.Warehouse создает две группы ресурсов по умолчанию для ролей с именами admin_group и default_group. Когда вы включаете группы ресурсов, любая роль, которой явно не назначена группа ресурсов, назначается группой по умолчанию для возможности роли. Роли SUPERUSER назначаются группе admin_group, роли без прав администратора назначаются группе с именем default_group.
В Таблице 138 показано с какими ограничениями создаются группы ресурсов по умолчанию admin_group и default_group:
Таблица 138. Ограничения при создании группы ресурсов по умолчанию
Тип ограничения | admin_group | default_group |
---|---|---|
CONCURRENCY | 10 | 20 |
CPU_RATE_LIMIT | 10 | 30 |
CPUSET | -1 | -1 |
MEMORY_LIMIT | 10 | 0 |
MEMORY_SHARED_QUOTA | 80 | 80 |
MEMORY_SPILL_RATIO | 0 | 0 |
MEMORY_AUDITOR | vmtracker | vmtracker |
Помните, что значения CPU_RATE_LIMIT и MEMORY_LIMIT для групп ресурсов по умолчанию admin_group и default_group вносят вклад в общие проценты на хосте сегмента. Вы можете обнаружить, что вам нужно настроить эти ограничения для admin_group и/или default_group по мере создания и добавления новых групп ресурсов в развертывание базы данных RT.Warehouse.
Когда вы создаете группу ресурсов для роли, вы указываете имя и режим распределения ресурсов ЦП. По желанию можно указать предел одновременных транзакций и предел памяти, общую квоту и коэффициент распыления. Для создания новой группы ресурсов используйте команду CREATE RESOURCE GROUP.
Когда вы создаете группу ресурсов для роли, вы должны указать предельное значение CPU_RATE_LIMIT или CPUSET. Эти ограничения определяют процент ресурсов ЦП базы данных RT.Warehouse, который необходимо выделить этой группе ресурсов. Вы можете указать MEMORY_LIMIT, чтобы зарезервировать фиксированный объем памяти для группы ресурсов. Если вы укажете MEMORY_LIMIT равным 0, база данных RT.Warehouse использует глобальную общую память для выполнения всех требований к памяти для группы ресурсов.
Например, чтобы создать группу ресурсов с именем rgroup1 с лимитом процессора 20, лимитом памяти 25 и коэффициентом перераспределения памяти 20:
=# CREATE RESOURCE GROUP rgroup1 WITH (CPU_RATE_LIMIT=20, MEMORY_LIMIT=25, MEMORY_SPILL_RATIO=20);
Ограничение ЦП, равное 20, используется всеми ролями, которым назначена rgroup1. Точно так же ограничение памяти, равное 25, используется всеми ролями, которым назначена rgroup1. rgroup1 использует vmtracker MEMORY_AUDITOR по умолчанию и параметр CONCURRENCY по умолчанию, равный 20.
При создании группы ресурсов для внешнего компонента необходимо указать предельные значения CPU_RATE_LIMIT или CPUSET и MEMORY_LIMIT. Вы также должны предоставить MEMORY_AUDITOR и явно установить CONCURRENCY равным нулю (0). Например, чтобы создать группу ресурсов с именем rgroup_extcomp, для которой вы зарезервируете ядро ЦП 1 на главных узлах и узлах сегмента и назначите ограничение памяти, равное 15:
=# CREATE RESOURCE GROUP rgroup_extcomp WITH (MEMORY_AUDITOR=cgroup, CONCURRENCY=0,
CPUSET='1;1', MEMORY_LIMIT=15);
Команда ALTER RESOURCE GROUP обновляет ограничения группы ресурсов. Чтобы изменить ограничения группы ресурсов, укажите новые значения, которые вы хотите применить для группы. Например:
=# ALTER RESOURCE GROUP rg_role_light SET CONCURRENCY 7;
=# ALTER RESOURCE GROUP exec SET MEMORY_SPILL_RATIO 25;
=# ALTER RESOURCE GROUP rgroup1 SET CPUSET '1;2,4';
Примечание. Вы не можете установить или изменить значение CONCURRENCY для группы admin на ноль (0). |
Команда DROP RESOURCE GROUP удаляет группу ресурсов. Чтобы удалить группу ресурсов для роли, эта группа не может быть назначена ни одной роли, а также в группе ресурсов не может быть активных или ожидающих транзакций. Удаление группы ресурсов для внешнего компонента, в котором есть запущенные экземпляры, приводит к прекращению работы этих экземпляров.
Чтобы удалить группу ресурсов:
=# DROP RESOURCE GROUP exec;
Настройка автоматического завершения запроса на основе использования памяти
Когда группы ресурсов имеют глобальный пул общей памяти, параметр конфигурации сервера runaway_detector_activation_percent задает процент используемой глобальной общей памяти, который запускает завершение запросов, управляемых группами ресурсов, настроенными на использование аудитора памяти vmtracker, например admin_group и default_group. .
Группы ресурсов имеют глобальный общий пул памяти, если сумма значений атрибута MEMORY_LIMIT, настроенных для всех групп ресурсов, меньше 100. Например, если у вас есть 3 группы ресурсов, настроенные со значениями MEMORY_LIMIT 10 , 20 и 30, то глобальная общая память 40% = 100% - (10% + 20% + 30%).
Когда вы создаете группу ресурсов с vmtracker по умолчанию MEMORY_AUDITOR, эта группа доступна для назначения одной или нескольким ролям (пользователям). Вы назначаете группу ресурсов роли базы данных с помощью предложения RESOURCE GROUP команд CREATE ROLE или ALTER ROLE. Если вы не укажете группу ресурсов для роли, роли назначается группа по умолчанию для возможностей роли. Роли SUPERUSER назначаются группе admin_group, роли без прав администратора назначаются группе с именем default_group.
Используйте команды ALTER ROLE или CREATE ROLE, чтобы назначить группе ресурсов роль. Например:
=# ALTER ROLE bill RESOURCE GROUP rg_light;
=# CREATE ROLE mary RESOURCE GROUP exec;
Вы можете назначить группе ресурсов одну или несколько ролей. Если вы определили иерархию ролей, назначение группы ресурсов родительской роли не распространяется на членов этой группы ролей.
Примечание. Вы не можете назначить роль группе ресурсов, созданной для внешнего компонента. |
Если вы хотите удалить назначение группы ресурсов из роли и назначить роль группой по умолчанию, измените назначение имени группы роли на NONE. Например:
=# ALTER ROLE mary RESOURCE GROUP NONE;
Мониторинг состояния ваших групп ресурсов и запросов может включать следующие задачи:
Просмотр ограничений группы ресурсов
В системном представлении gp_resgroup_config gp_toolkit отображаются текущие ограничения для группы ресурсов. Чтобы просмотреть лимиты всех групп ресурсов:
=# SELECT * FROM gp_toolkit.gp_resgroup_config;
Просмотр статуса запроса группы ресурсов и использования ЦП/памяти
Системное представление gp_resgroup_status gp_toolkit позволяет просматривать состояние и активность группы ресурсов. В представлении отображается количество запущенных и находящихся в очереди транзакций. Он также отображает в реальном времени использование ЦП и памяти группой ресурсов. Чтобы просмотреть эту информацию:
=# SELECT * FROM gp_toolkit.gp_resgroup_status;
Просмотр использования ЦП/памяти группой ресурсов на хост
Системное представление gp_resgroup_status_per_host gp_toolkit позволяет просматривать в реальном времени использование ЦП и памяти группой ресурсов для каждого хоста. Чтобы просмотреть эту информацию:
=# SELECT * FROM gp_toolkit.gp_resgroup_status_per_host;
Просмотр использования ЦП/памяти группой ресурсов на сегмент
Системное представление gp_resgroup_status_per_segment gp_toolkit позволяет просматривать в реальном времени использование ЦП и памяти группой ресурсов для каждого сегмента и хоста. Чтобы просмотреть эту информацию:
=# SELECT * FROM gp_toolkit.gp_resgroup_status_per_segment;
Просмотр группы ресурсов, назначенной роли
Чтобы просмотреть назначения групп ресурсов ролям, выполните следующий запрос к таблицам системного каталога pg_roles и pg_resgroup:
=# SELECT rolname, rsgname FROM pg_roles, pg_resgroup
WHERE pg_roles.rolresgroup=pg_resgroup.oid;
Просмотр запущенных и ожидающих запросов группы ресурсов
Чтобы просмотреть текущие и ожидающие запросы группы ресурсов, а также время ожидания ожидающих запросов в очереди, просмотрите таблицу системного каталога pg_stat_activity:
=# SELECT query, waiting, rsgname, rsgqueueduration
FROM pg_stat_activity;
pg_stat_activity отображает информацию о пользователе/роли, инициировавшем запрос. Запрос, который использует внешний компонент, такой как PL/Container, состоит из двух частей: оператора запроса, который выполняется в базе данных RT.Warehouse, и пользовательской функции, которая выполняется в экземпляре PL/Container. База данных RT.Warehouse обрабатывает операторы запроса в группе ресурсов, назначенной роли, инициировавшей запрос. Пользовательская функция, работающая в экземпляре PL/Container, запускается в группе ресурсов, назначенной среде выполнения PL/Container. Последний не представлен в представлении pg_stat_activity; База данных RT.Warehouse не имеет никакого представления о том, как внешние компоненты, такие как PL/Container, управляют памятью в запущенных экземплярах.
Отмена выполняющейся или поставленной в очередь транзакции в группе ресурсов
Могут быть случаи, когда вы хотите отменить выполняющуюся или стоящую в очереди транзакцию в группе ресурсов. Например, может потребоваться удалить запрос, ожидающий в очереди группы ресурсов, но еще не выполнен. Или вы можете остановить выполняющийся запрос, выполнение которого занимает слишком много времени, или запрос, который бездействует в транзакции и занимает слоты транзакций группы ресурсов, которые необходимы другим пользователям.
По умолчанию транзакции могут оставаться в очереди в группе ресурсов на неопределенный срок. Если вы хотите, чтобы база данных RT.Warehouse отменяла транзакцию в очереди по истечении определенного времени, установите параметр конфигурации сервера gp_resource_group_queuing_timeout. Если для этого параметра установлено значение (в миллисекундах) больше 0, RT.Warehouse отменяет любую транзакцию в очереди, если она ожидает дольше установленного времени ожидания.
Чтобы вручную отменить выполняющуюся транзакцию или транзакцию в очереди, необходимо сначала определить идентификатор процесса (pid), связанный с транзакцией. Получив идентификатор процесса, вы можете вызвать pg_cancel_backend(), чтобы завершить этот процесс, как показано ниже.
Например, чтобы просмотреть информацию о процессе, связанную со всеми операторами, активными или ожидающими выполнения во всех группах ресурсов, выполните следующий запрос. Если запрос не возвращает результатов, ни в одной группе ресурсов нет запущенных или поставленных в очередь транзакций.
=# SELECT rolname, g.rsgname, pid, waiting, state, query, datname
FROM pg_roles, gp_toolkit.gp_resgroup_status g, pg_stat_activity
WHERE pg_roles.rolresgroup=g.groupid
AND pg_stat_activity.usename=pg_roles.rolname;
Пример частичного вывода запроса:
rolname | rsgname | pid | waiting | state | query | datname
---------+----------+---------+---------+--------+------------------------ -+---------
sammy | rg_light | 31861 | f | idle | SELECT * FROM mytesttbl; | testdb
billy | rg_light | 31905 | t | active | SELECT * FROM topten; | testdb
Используйте этот вывод, чтобы определить идентификатор процесса (pid) транзакции, которую вы хотите отменить, а затем отмените процесс. Например, чтобы отменить ожидающий запрос, указанный в примере выше:
=# SELECT pg_cancel_backend(31905);
Вы можете предоставить необязательное сообщение во втором аргументе pg_cancel_backend(), чтобы указать пользователю, почему процесс был отменен.
Примечание. Не используйте команду KILL операционной системы для отмены любого процесса базы данных RT.Warehouse. |
Пользователь с правами суперпользователя базы данных RT.Warehouse может запустить функцию gp_toolkit.pg_resgroup_move_query(), чтобы переместить выполняющийся запрос из одной группы ресурсов в другую, не останавливая запрос. Используйте эту функцию, чтобы ускорить длительный запрос, переместив его в группу ресурсов с более высоким выделением или доступностью ресурсов.
Примечание. В новую группу ресурсов можно переместить только активный или выполняющийся запрос. Вы не можете переместить поставленный в очередь или ожидающий запрос, который находится в состоянии ожидания из-за параллелизма или ограничений памяти. |
pg_resgroup_move_query() требует идентификатор процесса (process id, pid) выполняющегося запроса, а также имя группы ресурсов, в которую вы хотите переместить запрос. Сигнатура функции следующая:
pg_resgroup_move_query( pid int4, group_name text );
Вы можете получить pid выполняющегося запроса из системного представления pg_stat_activity, как описано выше в Отмена выполняющейся или помещенной в очередь транзакции в группе ресурсов. Используйте представление gp_toolkit.gp_resgroup_status, чтобы перечислить имя, идентификатор и статус каждой группы ресурсов.
Когда вы вызываете pg_resgroup_move_query(), запрос подчиняется ограничениям, настроенным для целевой группы ресурсов:
После того как RT.Warehouse переместит запрос, невозможно гарантировать, что запрос, выполняющийся в настоящее время в целевой группе ресурсов, не превысит квоту памяти группы. В этом случае может произойти сбой одного или нескольких запущенных запросов в целевой группе, включая перемещенный запрос. Зарезервируйте достаточно глобальной общей памяти группы ресурсов, чтобы свести к минимуму возможность возникновения этого сценария.
pg_resgroup_move_query() перемещает только указанный запрос в целевую группу ресурсов. База данных RT.Warehouse назначает последующие запросы, которые вы отправляете в сеансе, исходной группе ресурсов.
В данном разделе представлены часто задаваемые вопросы.
Вы можете столкнуться с этой ситуацией, когда в группе ресурсов выполняется небольшое количество запросов и фрагментов, и эти процессы не используют все ядра в системе.
Такая ситуация может возникнуть при следующих обстоятельствах:
Транзакция, представленная в группе ресурсов, завершается со сбоем и завершается, когда использование памяти превышает ее фиксированное выделение памяти, не существует доступной общей памяти группы ресурсов и транзакция запрашивает больше памяти.
База данных RT.Warehouse автоматически настраивает транзакционную и групповую память в соответствии с новыми настройками, когда вы используете команду ALTER RESOURCE GROUP для изменения памяти группы ресурсов и/или пределов параллелизма. Ошибка «недостаточно памяти» может возникнуть, если вы недавно изменили атрибуты группы ресурсов и для выполняемого в данный момент запроса больше не имеется достаточного объема памяти.
Фактическое использование памяти группой ресурсов может превышать настроенный объем, когда одному или нескольким запросам, выполняемым в группе, выделяется память из глобального пула общей памяти (если глобальная общая память недоступна, запросы завершатся ошибкой и не повлияют на ресурсы памяти других групп ресурсов).
Когда доступна глобальная общая память, использование памяти также может превышать настроенный объем, когда транзакция переносится на диск. Операторы базы данных RT.Warehouse продолжают запрашивать память, когда они начинают передаваться на диск, потому что:
База данных RT.Warehouse учитывает доступность памяти перед выполнением транзакции и ставит транзакцию в очередь, если для ее обслуживания недостаточно памяти. Если вы используете ALTER RESOURCE GROUP для увеличения ограничения CONCURRENCY для группы ресурсов, но не настраиваете также ограничения памяти, текущие транзакции могут потреблять все ресурсы памяти, выделенные для группы. В этом состоянии база данных RT.Warehouse ставит в очередь последующие транзакции в группе ресурсов.
В группе ресурсов могут выполняться команды SET и SHOW, которые обходят проверки транзакций группы ресурсов.
Используйте очереди ресурсов базы данных RT.Warehouse для определения приоритетов и распределения ресурсов для запросов в соответствии с бизнес-требованиями, а также для предотвращения запуска запросов, когда ресурсы недоступны.
Очереди ресурсов — это один из инструментов управления степенью параллелизма в системе базы данных RT.Warehouse. Очереди ресурсов — это объекты базы данных, которые вы создаете с помощью оператора SQL CREATE RESOURCE QUEUE. Вы можете использовать их для управления количеством активных запросов, которые могут выполняться одновременно, объемом памяти, выделяемой для каждого типа запроса, и относительным приоритетом запросов. Очереди ресурсов также могут защищать от запросов, которые потребляют слишком много ресурсов и снижают общую производительность системы.
Каждая роль базы данных связана с одной очередью ресурсов; несколько ролей могут совместно использовать одну и ту же очередь ресурсов. Роли назначаются очередям ресурсов с помощью фразы RESOURCE QUEUE операторов CREATE ROLE или ALTER ROLE. Если очередь ресурсов не указана, роль связана с очередью ресурсов по умолчанию, pg_default.
Когда пользователь отправляет запрос на выполнение, запрос оценивается в соответствии с ограничениями очереди ресурсов. Если запрос не приведет к превышению лимита ресурсов очереди, то этот запрос будет выполнен немедленно. Если запрос приводит к превышению пределов очереди (например, если в настоящее время используется максимальное количество слотов активных операторов), то перед запуском запрос должен дождаться освобождения ресурсов очереди. Запросы оцениваются в порядке поступления. Если приоритезация запросов включена, активная рабочая нагрузка на систему периодически оценивается, а ресурсы обработки перераспределяются в соответствии с приоритетом запроса. На роли с атрибутом SUPERUSER не распространяются ограничения очереди ресурсов. Запросы суперпользователя всегда выполняются немедленно, независимо от ограничений, налагаемых назначенной им очередью ресурсов.
На Рисунке 14 показано использование очереди ресурсов.
Рисунок 14. Использование очереди ресурсов
Очереди ресурсов определяют классы запросов с аналогичными требованиями к ресурсам. Администраторы должны создавать очереди ресурсов для различных типов рабочих нагрузок в своей организации. Например, вы можете создать очереди ресурсов для следующих классов запросов, соответствующих различным соглашениям об уровне обслуживания:
Очередь ресурсов имеет следующие характеристики:
Объем памяти, используемый всеми запросами в очереди (на сегмент). Например, установка MEMORY_LIMIT на 2 ГБ в очереди ETL позволяет запросам ETL использовать до 2 ГБ памяти в каждом сегменте.
Количество слотов для очереди; максимальный уровень параллелизма для очереди. Когда все слоты используются, новые запросы должны ждать. Каждый запрос по умолчанию использует одинаковый объем памяти.
Например, очередь ресурсов pg_default имеет ACTIVE_STATEMENTS = 20.
Относительное использование ЦП для запросов. Это может быть один из следующих уровней: НИЗКИЙ, СРЕДНИЙ, ВЫСОКИЙ, МАКС. Уровень по умолчанию — СРЕДНИЙ. Механизм приоритезации запросов отслеживает использование ЦП всеми запросами, выполняющимися в системе, и регулирует использование ЦП для каждого из них в соответствии с его уровнем приоритета. Например, вы можете установить МАКСИМАЛЬНЫЙ приоритет для очереди исполнительных ресурсов и СРЕДНИЙ для других очередей, чтобы гарантировать, что исполнительные запросы получают большую долю ЦП.
Ограничение стоимости плана запроса.
Оптимизатор базы данных RT.Warehouse присваивает числовую стоимость каждому запросу. Если стоимость превышает значение MAX_COST, установленное для очереди ресурсов, запрос отклоняется как слишком дорогой.
Примечание. GPORCA и Postgres Planner используют разные модели расчета стоимости запросов и могут вычислять разные затраты для одного и того же запроса. Схема управления ресурсами очереди ресурсов базы данных RT.Warehouse не различает и не выравнивает затраты между GPORCA и Postgres Planner; он использует буквальное значение стоимости, возвращенное оптимизатором, для регулирования запросов. |
Когда управление ресурсами на основе очередей ресурсов активно, используйте ограничения MEMORY_LIMIT и ACTIVE_STATEMENTS для очередей ресурсов, а не настраивайте ограничения на основе затрат. Даже при использовании GPORCA база данных RT.Warehouse может вернуться к использованию планировщика Postgres для определенных запросов, поэтому использование ограничений на основе затрат может привести к неожиданным результатам.
Конфигурация по умолчанию для системы базы данных RT.Warehouse имеет одну очередь ресурсов по умолчанию с именем pg_default. Очередь ресурсов pg_default имеет параметр ACTIVE_STATEMENTS, равный 20, без MEMORY_LIMIT, со средним приоритетом и без установленного значения MAX_COST. Это означает, что все запросы принимаются и выполняются немедленно, с одинаковым приоритетом и без ограничений памяти; однако одновременно могут выполняться только двадцать запросов.
Количество одновременных запросов, разрешенных очередью ресурсов, зависит от того, установлен ли параметр MEMORY_LIMIT:
Запросу, допущенному в систему, выделяется объем памяти и для него формируется дерево плана запроса. Каждый узел дерева является оператором, таким как сортировка или хэш-соединение. Каждый оператор представляет собой отдельный поток выполнения, и ему выделяется часть общей памяти операторов, минимум 100 КБ. Если в плане большое количество операторов, минимальный объем памяти, необходимый для операторов, может превышать доступную память, и запрос будет отклонен с ошибкой нехватки памяти. Операторы определяют, могут ли они выполнять свои задачи в выделенной памяти, или они должны передавать данные на диск в рабочих файлах. Механизм, который выделяет и контролирует объем памяти, используемый каждым оператором, называется квотой памяти.
Не все операторы SQL, отправленные через очередь ресурсов, оцениваются по ограничениям очереди. По умолчанию оцениваются только операторы SELECT, SELECT INTO, CREATE TABLE AS SELECT и DECLARE CURSOR. Если параметр конфигурации сервера resource_select_only выключен, то также будут оцениваться операторы INSERT, UPDATE и DELETE.
Кроме того, оператор SQL, выполняемый во время выполнения команды EXPLAIN ANALYZE, исключается из очередей ресурсов.
Очередь ресурсов по умолчанию, pg_default, допускает не более 20 активных запросов и выделяет одинаковый объем памяти для каждого из них. Как правило, это неадекватное управление ресурсами для производственных систем. Чтобы убедиться, что система соответствует ожидаемой производительности, вы можете определить классы запросов и назначить их очередям ресурсов, настроенным для их выполнения с ресурсами параллелизма, памяти и ЦП, которые лучше всего подходят для этого класса запросов.
На Рисунке 15 показан пример конфигурации очереди ресурсов для системы базы данных RT.Warehouse с параметром gp_vmem_protect_limit равным 8 ГБ:
Рисунок 15. Пример конфигурации очереди ресурсов
В этом примере есть три класса запросов с разными характеристиками и соглашениями об уровне обслуживания (SLA). Для них настроены три очереди ресурсов. Часть памяти сегмента зарезервирована в качестве запаса прочности. В Таблице 139 показаны очереди ресурсов.
Таблица 139. Очереди ресурсов
Имя очереди ресурсов | Активные запросы | Ограничение по памяти | Память на запрос |
---|---|---|---|
ETL | 3 | 2 ГБ | 667 МБ |
Отчетные (Reporting) | 7 | 3 ГБ | 429 МБ |
Исполнительные (Executive) | 1 | 1,4 ГБ | 1,4 ГБ |
Общий объем памяти, выделенный для очередей, составляет 6,4 ГБ, или 80% от общего объема памяти сегмента, определяемого параметром конфигурации сервера gp_vmem_protect_limit. Предоставление запаса прочности в 20 % учитывает некоторые операторы и запросы, которые, как известно, используют больше памяти, чем они выделены очередью ресурсов.
Параметр MEMORY_LIMIT для очереди ресурсов устанавливает максимальный объем памяти, который все активные запросы, отправленные через очередь, могут потреблять для экземпляра сегмента. Объем памяти, выделенный запросу, равен пределу памяти очереди, деленному на предел активного оператора (используйте ограничения памяти в сочетании с очередями на основе операторов, а не очередями на основе стоимости). Например, если ограничение памяти очереди составляет 2000 МБ, а ограничение активных операторов — 10, каждому запросу, отправленному через очередь, по умолчанию выделяется 200 МБ памяти. Выделение памяти по умолчанию можно переопределить для каждого запроса с помощью параметра конфигурации сервера statement_mem (вплоть до предела памяти очереди). Как только запрос начал выполняться, он удерживает выделенную ему память в очереди до завершения, даже если во время выполнения он фактически потребляет меньше выделенного объема памяти.
Вы можете использовать параметр конфигурации сервера statement_mem, чтобы переопределить ограничения памяти, установленные текущей очередью ресурсов. На уровне сеанса вы можете увеличить statement_mem до MEMORY_LIMIT очереди ресурсов. Это позволит отдельному запросу использовать всю память, выделенную для всей очереди, не затрагивая другие очереди ресурсов.
Значение statement_mem ограничено с помощью параметра конфигурации max_statement_mem (параметр суперпользователя). Для запроса в очереди ресурсов с установленным MEMORY_LIMIT максимальное значение для оператора_mem равно min(MEMORY_LIMIT, max_statement_mem). Когда запрос принимается, выделенная ему память вычитается из MEMORY_LIMIT. Если MEMORY_LIMIT исчерпан, новые запросы в той же очереди ресурсов должны ожидать. Это происходит, даже если ACTIVE_STATEMENTS еще не достигнут. Обратите внимание, что это может произойти только в том случае, если для переопределения памяти, выделенной очередью ресурсов, используется statement_mem.
Например, рассмотрим очередь ресурсов с именем adhoc со следующими параметрами:
По умолчанию каждому оператору, отправленному в очередь, выделяется 500 МБ памяти. Теперь рассмотрим следующую серию событий:
Запросы Q1 и Q2 использовали 1300 МБ из 1500 МБ очереди. Следовательно, Q3 должен дождаться завершения Q1 или Q2, прежде чем он сможет работать.
Если MEMORY_LIMIT не установлен для очереди, запросы допускаются до тех пор, пока не будут использованы все слоты ACTIVE_STATEMENTS, и каждый запрос может установить произвольно большое значение statement_mem. Это может привести к тому, что очередь ресурсов будет использовать неограниченный объем памяти.
Было показано, что низкое значение параметра statement_mem (например, в диапазоне 1–3 МБ) повышает производительность запросов с низкими требованиями к памяти. Используйте параметр конфигурации сервера statement_mem, чтобы переопределить настройку для каждого запроса. Например:
SET statement_mem='2MB';
Параметр PRIORITY для очереди ресурсов отличается от параметров MEMORY_LIMIT и ACTIVE_STATEMENTS, которые определяют, будет ли запрос помещен в очередь и в конечном итоге запущен. Параметр PRIORITY применяется к запросам после того, как они станут активными. Активные запросы совместно используют доступные ресурсы ЦП в соответствии с настройками приоритета для своей очереди ресурсов. Когда оператор из очереди с высоким приоритетом входит в группу активно выполняющихся операторов, он может претендовать на большую долю доступного ЦП, уменьшая долю, выделенную уже выполняющимся операторам в очередях с меньшим значением приоритета.
Сравнительный размер или сложность запросов не влияет на выделение ЦП. Если простой недорогой запрос выполняется одновременно с большим сложным запросом, и их настройки приоритета совпадают, им будет выделена одинаковая доля доступных ресурсов ЦП. Когда новый запрос становится активным, доли ЦП будут пересчитаны, но запросы с одинаковым приоритетом по-прежнему будут иметь одинаковое количество ЦП.
Например, администратор создает три очереди ресурсов: adhoc для текущих запросов, отправляемых бизнес-аналитиками, reporting для запланированных заданий отчетности и executive для запросов, отправленных исполнительными ролями пользователей. Администратор хочет убедиться, что запланированные задания по составлению отчетов не подвержены сильному влиянию непредсказуемых потребностей в ресурсах, возникающих в результате специальных запросов аналитиков. Кроме того, администратор хочет убедиться, что запросам, отправленным исполнительными ролями, выделяется значительная доля ЦП. Соответственно, приоритеты очереди ресурсов устанавливаются, как показано ниже:
Во время выполнения доля ЦП активных инструкций определяется этими параметрами приоритета. Если запросы 1 и 2 из очереди отчетов выполняются одновременно, они имеют равные доли ЦП. Когда специальный запрос становится активным, он требует меньшую долю ЦП. Точная доля, используемая отчетными запросами, корректируется, но остается неизменной из-за их одинаковой настройки приоритета, как показано на Рисунке 16.
Рисунок 16. Запросы и влияние настройки приоритета
Примечание. Проценты, показанные на этих иллюстрациях, являются приблизительными. Использование ЦП между очередями с высоким, низким и максимальным приоритетом не всегда рассчитывается точно в этих пропорциях. |
Когда исполнительный (executive) запрос входит в группу запущенных операторов, загрузка ЦП корректируется с учетом его максимального приоритета. Это может быть простой запрос по сравнению с запросами аналитики и отчетов, но до тех пор, пока он не будет завершен, он потребует наибольшей доли ЦП, как показано на Рисунке 17.
Рисунок 17. Влияние исполнительного запроса на приоритет
Включение и использование управления ресурсами в базе данных RT.Warehouse включает следующие задачи высокого уровня:
Планирование ресурсов включается по умолчанию при установке базы данных RT.Warehouse и требуется для всех ролей. Очередь ресурсов по умолчанию, pg_default, имеет ограничение на количество активных операторов, равное 20, без ограничений по памяти и со средним приоритетом.
Настройка управления ресурсами.
1. Следующие параметры предназначены для общей конфигурации очередей ресурсов:
2. Следующие параметры связаны с использованием памяти:
В базе данных RT.Warehouse алгоритм распределения await_free использует тот факт, что не все операторы выполняются одновременно. План запроса разделен на этапы, и база данных RT.Warehouse с готовностью освобождает память, выделенную для предыдущей стадии, в конце выполнения этой стадии, а затем выделяет с готовностью освобожденную память для новой стадии.
3. Следующие параметры связаны с приоритезацией запросов. Обратите внимание, что все следующие параметры являются локальными параметрами, то есть они должны быть установлены в файлах postgresql.conf мастера и всех сегментов:
Каждый хост RT.Warehouse проверяет значение этого параметра в собственном файле postgresql.conf. Этот параметр также влияет на главный хост, где для него должно быть установлено значение, отражающее более высокое соотношение ядер ЦП. Например, в кластере с 10 ядрами ЦП на узел сегмента и 4 первичными сегментами на узел вы должны указать следующие значения для gp_resqueue_priority_cpucores_per_segment:
Если значение параметра задано неправильно, ЦП может быть загружен не полностью, или приоритизация запросов может работать не так, как ожидалось. Например, если кластер базы данных RT.Warehouse имеет менее одного экземпляра сегмента на ядро ЦП на хостах вашего сегмента, убедитесь, что вы соответствующим образом настроили это значение.
Фактическое использование ядра ЦП основано на способности базы данных RT.Warehouse распараллеливать запрос и ресурсах, необходимых для выполнения запроса.
Примечание. Включите любое ядро ЦП, доступное для операционной системы, в число ядер ЦП, включая виртуальные ядра ЦП. |
4. Если вы хотите просмотреть или изменить какие-либо значения параметров управления ресурсами, вы можете использовать утилиту gpconfig.
5. Например, чтобы увидеть настройку того или иного параметра:
$ gpconfig --show gp_vmem_protect_limit
6. Например, чтобы установить одно значение для всех экземпляров сегмента и другое значение для главного:
$ gpconfig -c gp_resqueue_priority_cpucores_per_segment -v 2 -m 8
7. Перезапустите базу данных RT.Warehouse, чтобы изменения конфигурации вступили в силу:
$ gpstop -r
Создание очереди ресурсов включает присвоение ей имени, установку лимита активных запросов и, при необходимости, приоритета запроса в очереди ресурсов. Используйте команду CREATE RESOURCE QUEUE для создания новых очередей ресурсов.
Очереди ресурсов с настройкой ACTIVE_STATEMENTS ограничивают количество запросов, которые могут выполняться ролями, назначенными этой очереди. Например, чтобы создать очередь ресурсов с именем adhoc с ограничением активных запросов, равным трем:
=# CREATE RESOURCE QUEUE adhoc WITH (ACTIVE_STATEMENTS=3);
Это означает, что для всех ролей, назначенных в очередь ресурсов adhoc, в системе в любой момент времени могут быть запущены только три активных запроса. Если в этой очереди запущены три запроса, а четвертый запрос отправлен ролью из этой очереди, этот запрос должен подождать, пока освободится слот, прежде чем он сможет быть запущен.
Очереди ресурсов с настройкой MEMORY_LIMIT управляют объемом памяти для всех запросов, отправляемых через очередь. Общий объем памяти не должен превышать объема физической памяти, доступной для каждого сегмента. Установите MEMORY_LIMIT на 90% доступной памяти для каждого сегмента. Например, если хост имеет 48 ГБ физической памяти и 6 экземпляров сегментов, то доступная память на экземпляр сегмента составляет 8 ГБ. Вы можете рассчитать рекомендуемый MEMORY_LIMIT для одной очереди как 0,90 * 8 = 7,2 ГБ. Если в системе создано несколько очередей, их общий лимит памяти также должен составлять до 7,2 ГБ.
При использовании в сочетании с ACTIVE_STATEMENTS объем памяти по умолчанию, выделяемый для каждого запроса, составляет: MEMORY_LIMIT``/``ACTIVE_STATEMENTS. При использовании в сочетании с MAX_COST объем памяти по умолчанию, выделяемый для каждого запроса, составляет: MEMORY_LIMIT * (query_cost/MAX_COST). Используйте MEMORY_LIMIT в сочетании с ACTIVE_STATEMENTS, а не с MAX_COST.
Например, чтобы создать очередь ресурсов с ограничением активных запросов 10 и ограничением общей памяти 2000 МБ (каждому запросу будет выделено 200 МБ памяти узла сегмента во время выполнения):
=# CREATE RESOURCE QUEUE myqueue WITH (ACTIVE_STATEMENTS=20,
MEMORY_LIMIT='2000MB');
Выделение памяти по умолчанию можно переопределить для каждого запроса с помощью параметра конфигурации сервера statement_mem при условии, что MEMORY_LIMIT или max_statement_mem не превышены. Например, чтобы выделить больше памяти для конкретного запроса:
=> SET statement_mem='2GB';
=> SELECT * FROM my_big_table WHERE column='value' ORDER BY id;
=> RESET statement_mem;
Как правило, MEMORY_LIMIT для всех ваших очередей ресурсов не должен превышать объем физической памяти хоста сегмента. Если рабочие нагрузки распределены по нескольким очередям, может быть допустимо превысить выделение памяти, имея в виду, что запросы могут быть отменены во время выполнения, если превышен лимит памяти хоста сегмента (gp_vmem_protect_limit).
Чтобы контролировать потребление очередью ресурсов доступных ресурсов ЦП, администратор может назначить соответствующий уровень приоритета. Когда высокий параллелизм вызывает конкуренцию за ресурсы ЦП, запросы и операторы, связанные с очередью ресурсов с высоким приоритетом, будут требовать большей доли доступного ЦП, чем запросы и операторы с более низким приоритетом.
Настройки приоритета создаются или изменяются с помощью параметра WITH команд CREATE RESOURCE QUEUE и ALTER RESOURCE QUEUE. Например, чтобы указать параметры приоритета для очередей adhoc и отчетов, администратор может использовать следующие команды:
=# ALTER RESOURCE QUEUE adhoc WITH (PRIORITY=LOW);
=# ALTER RESOURCE QUEUE reporting WITH (PRIORITY=HIGH);
Чтобы создать executive очередь с максимальным приоритетом, администратор должен использовать следующую команду:
=# CREATE RESOURCE QUEUE executive WITH (ACTIVE_STATEMENTS=3, PRIORITY=MAX);
Когда функция определения приоритетов запросов включена, очереди ресурсов по умолчанию получают СРЕДНИЙ приоритет, если он не назначен явно.
Внимание. Чтобы уровни приоритета очереди ресурсов применялись к рабочей нагрузке активных запросов, необходимо включить функцию приоритизации запросов, задав соответствующие параметры конфигурации сервера. |
После создания очереди ресурсов необходимо назначить роли (пользователей) соответствующей очереди ресурсов. Если роли явно не назначены очереди ресурсов, они перейдут в очередь ресурсов по умолчанию, pg_default. Очередь ресурсов по умолчанию имеет ограничение на количество активных операторов, равное 20, без ограничений по стоимости и со средним приоритетом.
Используйте команды ALTER ROLE или CREATE ROLE, чтобы назначить роль очереди ресурсов. Например:
=# ALTER ROLE `name` RESOURCE QUEUE `queue_name`;
=# CREATE ROLE `name` WITH LOGIN RESOURCE QUEUE `queue_name`;
Роль может быть назначена только одной очереди ресурсов в любой момент времени, поэтому вы можете использовать команду ALTER ROLE для первоначального назначения или изменения очереди ресурсов роли.
Очереди ресурсов должны назначаться для каждого пользователя отдельно. Если у вас есть иерархия ролей (например, роль на уровне группы), то назначение очереди ресурсов группе не распространяется на пользователей в этой группе.
Суперпользователи всегда освобождаются от ограничений очереди ресурсов. Запросы суперпользователя всегда будут выполняться независимо от ограничений, установленных для назначенной им очереди.
Все пользователи должны быть назначены очереди ресурсов. Если явно не назначена конкретная очередь, пользователи перейдут в очередь ресурсов по умолчанию, pg_default. Если вы хотите удалить роль из очереди ресурсов и поместить их в очередь по умолчанию, измените назначение очереди роли на none. Например:
=# ALTER ROLE `role_name` RESOURCE QUEUE none;
После создания очереди ресурсов вы можете изменить или сбросить ограничения очереди с помощью команды ALTER RESOURCE QUEUE. Вы можете удалить очередь ресурсов с помощью команды DROP RESOURCE QUEUE. Чтобы изменить роли (пользователей), назначенные очереди ресурсов, см. Назначение ролей (пользователей) очереди ресурсов (24.4.2.6).
Команда ALTER RESOURCE QUEUE изменяет ограничения очереди ресурсов. Чтобы изменить ограничения очереди ресурсов, укажите новые значения для очереди. Например:
=# ALTER RESOURCE QUEUE <adhoc> WITH (ACTIVE_STATEMENTS=5);
=# ALTER RESOURCE QUEUE <exec> WITH (PRIORITY=MAX);
Чтобы сбросить активные операторы или ограничение памяти до безлимитного, введите значение -1. Чтобы сбросить максимальную стоимость запроса до неограниченных, введите значение -1,0. Например:
=# ALTER RESOURCE QUEUE <adhoc> WITH (MAX_COST=-1.0, MEMORY_LIMIT='2GB');
Вы можете использовать команду ALTER RESOURCE QUEUE для изменения приоритета запросов, связанных с очередью ресурсов. Например, чтобы установить для очереди минимальный уровень приоритета:
ALTER RESOURCE QUEUE <webuser> WITH (PRIORITY=MIN);
Команда DROP RESOURCE QUEUE удаляет очередь ресурсов. Чтобы удалить очередь ресурсов, ей не должны быть назначены какие-либо роли, а также не может быть никаких операторов, ожидающих в очереди. Чтобы удалить очередь ресурсов:
=# DROP RESOURCE QUEUE <name>;
Проверка состояния очереди ресурсов включает следующие задачи:
Представление gp_toolkit.gp_resqueue_status позволяет администраторам просматривать состояние и активность очереди ресурсов. Он показывает, сколько запросов ожидает выполнения и сколько запросов в настоящее время активно в системе из определенной очереди ресурсов. Чтобы увидеть очереди ресурсов, созданные в системе, атрибуты их лимита и текущий статус:
=# SELECT * FROM gp_toolkit.gp_resqueue_status;
Если вы хотите отслеживать статистику и производительность очередей ресурсов с течением времени, вы можете включить сбор статистики для очередей ресурсов. Это делается путем установки следующего параметра конфигурации сервера в главном файле postgresql.conf:
stats_queue_level = on
Как только это будет включено, вы сможете использовать системное представление pg_stat_resqueues для просмотра статистики, собранной по использованию очереди ресурсов. Обратите внимание, что включение этой функции приводит к небольшому снижению производительности, поскольку необходимо отслеживать каждый запрос, отправленный через очередь ресурсов. Может оказаться полезным включить сбор статистики по очередям ресурсов для первоначальной диагностики и административного планирования, а затем отключить эту функцию для дальнейшего использования.
Дополнительную информацию о сборе статистики в базе данных RT.Warehouse см. в разделе Сборщик статистики в документации PostgreSQL.
Чтобы просмотреть роли, назначенные очереди ресурсов, выполните следующий запрос к таблицам системного каталога pg_roles и gp_toolkit.``gp_resqueue_status:
=# SELECT rolname, rsqname FROM pg_roles,
gp_toolkit.gp_resqueue_status
WHERE pg_roles.rolresqueue=gp_toolkit.gp_resqueue_status.queueid;
Вы можете создать представление этого запроса, чтобы упростить будущие запросы. Например:
=# CREATE VIEW role2queue AS
SELECT rolname, rsqname FROM pg_roles, pg_resqueue
WHERE pg_roles.rolresqueue=gp_toolkit.gp_resqueue_status.queueid;
Затем вы можете запросить представление:
=# SELECT * FROM role2queue;
Когда слот используется для очереди ресурсов, он записывается в таблицу системного каталога pg_locks. Здесь вы можете увидеть все текущие активные и ожидающие запросы для всех очередей ресурсов. Чтобы проверить, что операторы поставлены в очередь (даже операторы, которые не ожидают выполнения), вы также можете использовать представление gp_toolkit.gp_locks_on_resqueue. Например:
=# SELECT * FROM gp_toolkit.gp_locks_on_resqueue WHERE lorwaiting='true';
Если этот запрос не возвращает результатов, это означает, что в настоящее время в очереди ресурсов нет операторов, ожидающих выполнения.
В некоторых случаях может потребоваться удалить оператор ожидания из очереди ресурсов. Например, вы можете удалить запрос, ожидающий в очереди, но еще не выполненный. Вы также можете остановить запущенный запрос, если он выполняется слишком долго или бездействует в транзакции и занимает слоты очереди ресурсов, которые нужны другим пользователям. Для этого вы должны сначала указать оператор, который хотите очистить, определить его идентификатор процесса (pid), а затем использовать pg_cancel_backend с идентификатором процесса, чтобы завершить этот процесс, как показано ниже. Необязательное сообщение процессу может быть передано в качестве второго параметра, чтобы указать пользователю, почему процесс был отменен.
Например, чтобы просмотреть информацию о процессе обо всех инструкциях, которые в настоящее время активны или ожидают во всех очередях ресурсов, выполните следующий запрос:
=# SELECT rolname, rsqname, pg_locks.pid as pid, granted, state,
query, datname
FROM pg_roles, gp_toolkit.gp_resqueue_status, pg_locks,
pg_stat_activity
WHERE pg_roles.rolresqueue=pg_locks.objid
AND pg_locks.objid=gp_toolkit.gp_resqueue_status.queueid
AND pg_stat_activity.pid=pg_locks.pid
AND pg_stat_activity.usename=pg_roles.rolname;
Если этот запрос не возвращает результатов, это означает, что в настоящее время в очереди ресурсов нет операторов. Пример очереди ресурсов с двумя операторами в ней выглядит примерно так:
rolname | rsqname | pid | granted | state | query | datname
--------+---------+-------+---------+--------+------------------------+---------
sammy | webuser | 31861 | t | idle | SELECT * FROM testtbl; | namesdb
daria | webuser | 31905 | f | active | SELECT * FROM topten; | namesdb
Используйте этот вывод, чтобы определить идентификатор процесса (pid) инструкции, которую вы хотите удалить из очереди ресурсов. Чтобы очистить оператор, вы должны открыть окно терминала (как суперпользователь базы данных gpadmin или как root) на главном хосте и отменить соответствующий процесс. Например:
=# pg_cancel_backend(31905)
Внимание. Не используйте команду KILL операционной системы. |
Административная схема gp_toolkit имеет представление под названием gp_resq_priority_statement, в котором перечислены все операторы, выполняемые в настоящее время, а также указан приоритет, идентификатор сеанса и другая информация.
Это представление доступно только через административную схему gp_toolkit.
Суперпользователи могут настроить приоритет выполняемого в данный момент оператора с помощью встроенной функции gp_adjust_priority(session_id, statement_count, priority). С помощью этой функции суперпользователи могут повышать или понижать приоритет любого запроса. Например:
=# SELECT gp_adjust_priority(752, 24905, 'HIGH')
Чтобы получить идентификатор сеанса и параметры счетчика операторов, требуемые этой функцией, суперпользователи могут использовать представление административной схемы gp_toolkit, gp_resq_priority_statement. Из представления используйте эти значения для параметров функции:
Примечание. Функция gp_adjust_priority() влияет только на указанный оператор. Последующие операторы в той же очереди ресурсов выполняются с использованием обычно назначаемого очереди приоритета. |
В этом разделе приведены рекомендации по выявлению и устранению проблем с производительностью в системе базы данных RT.Warehouse.
Далее будут перечислены шаги, которые можно предпринять, чтобы определить причину проблемы с производительностью. Если проблема затрагивает определенную рабочую нагрузку или запрос, вы можете сосредоточиться на настройке этой конкретной рабочей нагрузки. Если проблема с производительностью носит общесистемный характер, причиной могут быть проблемы с оборудованием, системные сбои или конфликты ресурсов.
Используйте утилиту gpstate, чтобы идентифицировать сбойные сегменты. Система базы данных RT.Warehouse подвергнется снижению производительности, когда экземпляры сегментов не работают, потому что другие хосты должны взять на себя обязанности по обработке отключенных сегментов.
Неработающие сегменты могут указывать на аппаратный сбой, например, на неисправный диск или сетевую карту. База данных RT.Warehouse предоставляет инструмент проверки оборудования gpcheckperf, который помогает идентифицировать хосты сегмента с аппаратными проблемами.
Проверка активности БД состоит из:
Представление системного каталога pg_stat_activity показывает одну строку для каждого процесса сервера; он показывает OID базы данных, имя базы данных, идентификатор процесса, OID пользователя, имя пользователя, текущий запрос, время начала выполнения текущего запроса, время запуска процесса, адрес клиента и номер порта. Чтобы получить максимум информации о текущей нагрузке на систему, запросите это представление от имени суперпользователя базы данных. Например:
SELECT * FROM pg_stat_activity;
Обратите внимание, что информация не обновляется мгновенно.
Представление системного каталога pg_locks позволяет просматривать информацию об незавершенных блокировках. Если транзакция удерживает блокировку объекта, любые другие запросы должны дождаться освобождения этой блокировки, прежде чем они смогут продолжить выполнение. Пользователю может показаться, что запрос завис.
Проверьте pg_locks на наличие непредоставленных блокировок, чтобы выявить конфликты между сеансами клиентов базы данных. pg_locks обеспечивает глобальное представление всех блокировок в системе баз данных, а не только тех, которые относятся к текущей базе данных. Вы можете соединить его столбец отношений с pg_class.oid, чтобы идентифицировать заблокированные отношения (например, таблицы), но это правильно работает только для отношений в текущей базе данных. Вы можете присоединить столбец pid к pg_stat_activity.pid, чтобы увидеть больше информации о сеансе, удерживающем или ожидающем удержания блокировки. Например:
SELECT locktype, database, c.relname, l.relation,
l.transactionid, l.pid, l.mode, l.granted,
a.query
FROM pg_locks l, pg_class c, pg_stat_activity a
WHERE l.relation=c.oid AND l.pid=a.pid
ORDER BY c.relname;
Если вы используете группы ресурсов, ожидающие запросы также будут отображаться в pg_locks. Чтобы узнать, сколько запросов ожидает выполнения в группе ресурсов, используйте представление системного каталога gp_resgroup_status. Например:
SELECT * FROM gp_toolkit.gp_resgroup_status;
Точно так же, если вы используете очереди ресурсов, запросы, ожидающие в очереди, также отображаются в pg_locks. Чтобы узнать, сколько запросов ожидает выполнения из очереди ресурсов, используйте представление системного каталога gp_resqueue_status. Например:
SELECT * FROM gp_toolkit.gp_resqueue_status;
Вы можете использовать утилиты мониторинга системы, такие как ps, top, iostat, vmstat, netstat и т. д., для мониторинга активности базы данных на хостах в вашем массиве базы данных RT.Warehouse. Эти инструменты могут помочь идентифицировать процессы базы данных RT.Warehouse (процессы postgres), которые в настоящее время выполняются в системе, и наиболее ресурсоемкие задачи в отношении ЦП, памяти, дискового ввода-вывода или сетевой активности. Просмотрите эту системную статистику, чтобы определить запросы, которые снижают производительность базы данных, перегружая систему и потребляя чрезмерные ресурсы. Инструмент управления базой данных RT.Warehouse gpssh позволяет запускать эти команды мониторинга системы на нескольких хостах одновременно.
Вы можете создать и использовать представление session_level_memory_consumption базы данных RT.Warehouse, которое предоставляет информацию о текущем использовании памяти и времени простоя для сеансов, выполняющих запросы к базе данных RT.Warehouse.
Вы можете включить выделенную базу данных gpperfmon, в которой агенты сбора данных, работающие на каждом узле сегмента, сохраняют запросы и показатели использования системы.
Дополнительный пользовательский веб-интерфейс RT.Warehouse Command Center графически отображает запросы и показатели использования системы.
Если запрос работает плохо, просмотрите план запроса, чтобы выявить его проблемы. Команда EXPLAIN показывает план запроса для данного запроса.
Когда во время выполнения запроса возникает событие нехватки памяти, структура учета памяти базы данных RT.Warehouse предоставляет подробный отчет о потреблении памяти для каждого запроса, выполняющегося во время события. Информация записывается в сегментные логи базы данных RT.Warehouse.
Сообщения лога базы данных RT.Warehouse записываются в файлы в каталоге лога в каталоге данных мастера или сегмента. Поскольку основной лог файл содержит больше всего информации, его всегда следует проверять в первую очередь. Файлы логов обновляются ежедневно и используют соглашение об именах: gpdb-YYYY-MM-DD_hhmmss.csv. Чтобы найти файлы лога на мастер хосте:
$ cd $MASTER_DATA_DIRECTORY/log
Строки лога имеют формат:
<timestamp> | <user> | <database> | <statement_id> | <con#><cmd#>
|:-<LOG_LEVEL>: <log_message>
Вы можете сосредоточить свой поиск на сообщениях уровня журнала WARNING, ERROR, FATAL или PANIC. Вы можете использовать утилиту gplogfilter RT.Warehouse для поиска в лог файлах базы данных RT.Warehouse. Например, когда вы запускаете следующую команду на мастер хосте, она проверяет наличие сообщений лога проблем в стандартных местах регистрации:
$ gplogfilter -t
Для поиска связанных записей лога в лог файлах сегмента можно запустить gplogfilter на узлах сегмента с помощью gpssh. Вы можете идентифицировать соответствующие записи лога по statement_id или con# (идентификатор сеанса). Например, чтобы найти сообщения лога в файлах лога сегмента, содержащих строку con6, и сохранить вывод в файл:
gpssh -f seg_hosts_file -e 'source
/usr/local/greenplum-db/greenplum_path.sh ; gplogfilter -f
con6 /gpdata/*/log/gpdb*.csv' > seglog.out