Теперь создадим реплицированную таблицу:
CREATE TABLE users_replicated (id SERIAL, type INT, ...) DISTRIBUTE by REPLICATION TO NODE dn1, dn2;
Естественно данные идентичны на всех обработчиках данных:
# SELECT id, type from users_replicated ORDER BY id; id | type -------+------- 1 | 75 2 | 262 3 | 458 4 | 779 5 | 357 6 | 51 7 | 249 8 | 444 9 | 890 10 | 810 11 | 809 12 | 166 13 | 605 14 | 401 15 | 58
Рассмотрим как выполняются запросы для таблиц. Выберем все записи из распределенной таблицы:
# EXPLAIN VERBOSE SELECT * from users_with_modulo ORDER BY id; QUERY PLAN -------------------------------------------------------------------------------------- Sort (cost=49.83..52.33 rows=1000 width=8) Output: id, type Sort Key: users_with_modulo.id -> Result (cost=0.00..0.00 rows=1000 width=8) Output: id, type -> Data Node Scan on users_with_modulo (cost=0.00..0.00 rows=1000 width=8) Output: id, type Node/s: dn1, dn2 Remote query: SELECT id, type FROM ONLY users_with_modulo WHERE true (9 rows)
Как видно на листинге [lst:postgres-xc6] координатор собирает данные из обработчиков данных, а потом собирает их вместе.
Подсчет суммы с группировкой по полю из распределенной таблицы:
# EXPLAIN VERBOSE SELECT sum(id) from users_with_modulo GROUP BY type; QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------------------------ HashAggregate (cost=5.00..5.01 rows=1 width=8) Output: pg_catalog.sum((sum(users_with_modulo.id))), users_with_modulo.type -> Materialize (cost=0.00..0.00 rows=0 width=0) Output: (sum(users_with_modulo.id)), users_with_modulo.type -> Data Node Scan on "__REMOTE_GROUP_QUERY__" (cost=0.00..0.00 rows=1000 width=8) Output: sum(users_with_modulo.id), users_with_modulo.type Node/s: dn1, dn2 Remote query: SELECT sum(group_1.id), group_1.type FROM (SELECT id, type FROM ONLY users_with_modulo WHERE true) group_1 GROUP BY 2 (8 rows)
JOIN между и с участием реплицированных таблиц, а также JOIN между распределенными по одному и тому же полю в таблицах будет выполняются на обработчиках данных. Но JOIN с участием распределенных таблиц по другим ключам будут выполнены на координаторе и скорее всего это будет медленно (листинг [lst:postgres-xc8]).
# EXPLAIN VERBOSE SELECT * from users_with_modulo, users_with_hash WHERE users_with_modulo.id = users_with_hash.id; QUERY PLAN -------------------------------------------------------------------------------------------------- Nested Loop (cost=0.00..0.01 rows=1 width=16) Output: users_with_modulo.id, users_with_modulo.type, users_with_hash.id, users_with_hash.type Join Filter: (users_with_modulo.id = users_with_hash.id) -> Data Node Scan on users_with_modulo (cost=0.00..0.00 rows=1000 width=8) Output: users_with_modulo.id, users_with_modulo.type Node/s: dn1, dn2 Remote query: SELECT id, type FROM ONLY users_with_modulo WHERE true -> Data Node Scan on users_with_hash (cost=0.00..0.00 rows=1000 width=8) Output: users_with_hash.id, users_with_hash.type Node/s: dn1, dn2 Remote query: SELECT id, type FROM ONLY users_with_hash WHERE true (11 rows)
Пример выборки данных из реплицированной таблицы:
# EXPLAIN VERBOSE SELECT * from users_replicated; QUERY PLAN ---------------------------------------------------------------------------- Data Node Scan on "__REMOTE_FQS_QUERY__" (cost=0.00..0.00 rows=0 width=0) Output: users_replicated.id, users_replicated.type Node/s: dn1 Remote query: SELECT id, type FROM users_replicated (4 rows)
Как видно из запроса для выборки данных используется один обработчик данных, а не все (что и логично).
Высокая доступность (HA)
По архитектуре у Postgres-XC всегда
есть согласованность данных. По теореме CAP в такой системе тяжело обеспечить высокую доступность. Для достижения высокой доступности в распределенных системах требуется избыточность данных, резервные копии и автоматическое восстановление. В Postgres-XC избыточность данных может быть достигнута с помощью PostgreSQL потоковой (streaming) репликации с hot-standby для обработчиков данных. Каждый координатор способен записывать и читать данные независимо от другого, поэтому координаторы способны заменять друг друга. Поскольку GTM отдельный процесс и может стать точкой отказа, лучше создать GTM-standby как резервную копию. Ну а вот для автоматического восстановления придется использовать сторонние утилиты.
Ограничения
Postgres-XC базируется на PostgreSQL 9.1 (9.2 в разработке);
Нет системы репартиционирования при добавлении или удалении нод (в разработке);
Нет глобальных UNIQUE на распределенных таблицах;
Не поддерживаются foreign keys между нодами поскольку такой ключ должен вести на данные расположенные на том же обработчике данных;
Не поддерживаются курсоры (в разработке);
Не поддерживается INSERT ... RETURNING (в разработке);
Невозможно удаление и добавление нод в кластер без полной реинициализации кластера (в разработке).
Заключение
Postgres-XC очень перспективное решение для создание кластера на основе PostgreSQL. И хоть это решение имеет ряд недостатков, нестабильно (очень часты случаи падения координаторов при тяжелых запросах) и еще очень молодое, со временем это решение может стать стандартом для масштабирования систем на PostgreSQL.