воскресенье, 29 марта 2009 г.

[Перевод] PostgreSQL кластер: партишинг с plproxy (часть 1)

оригинал в блоге Kristo Kaiv

мой перевод на http://translated.by/you

Skype разработал много полезных утилит для создания кластера баз данных и эта серия публикаций прольет свет на ранее недокументированный функционал. В основе - plproxy. Лучшая фраза описать функциональность - "dblink на стероидах". Это короткое пособие объяснит, как установить plproxy, написать простые запросы к БД и установить простое горизонтальное масштабирование.

Партишинг для чайников.
Партишинг позволяет вам распределить загрузку БД и сами данные между несколькими серверами. Принцип прост. Допустим вы имеете одну таблицу, которая содержит данные на пользователей. Но проблема - миллионы пользователей ежедневно входят по своим аккаунтам в систему. Это создает большую загрузку сервера, не говоря о огромных размерах БД. Первое - нужно определить критерий, по которому мы будем выбирать на каком сервере содержатся данные. Мы можем сделать это по первому символу и имени пользователя. Пользователи a-j попадают в первый сервер, а k-z во второй. Это будет работать. Но скорее всего нагрузка на серверы будет не равномерной. Распространенное решение - использовать партишинг основываясь на хэше первичного ключа, в нашем случае - имени пользователя. Использование хэша позволит размещать записи более равномерно между серверами. То, что вам нужно знать про функцию хэша - это то, что она просто вычисляет номер, основанный на любом введённом значении.

Пример:
select hashtext('kristokaiv1') = 1116512480
select hashtext('kristokaiv2') = 1440348351
select hashtext('kristokaiv3') = -219299073

Объяснение, как это работает - выходит за рамки этого пособия.
Итак, допустим мы имеем 2 партиции, так что мы можем получить номер партиции основываясь на хэше имени пользователя.
Пример: partition nr = hashtext($1) & 1

&1 даст нам последний бит номера, который может быть 0 или 1. Это номер, которым мы можем выбрать партицию, на которой будут сохранены данные. Если 0 - данные попадут на партицию 0 и если 1 - на партицию 1.
select hashtext('kristokaiv1') & 1 = 0 -> partition 0
select hashtext('kristokaiv2') & 1 = 1 -> partition 1
select hashtext('kristokaiv3') & 1 = 1 -> partition 1

Как работает plproxy.
Концепция очень проста - plproxy это новый язык для запросов в PostgreSQL БД, который позволяет делать запросы к удаленным БД также как это делает dblink. Синтаксис очень прост - следующий запрос создает новую plproxy-функцию в БД, которая при запуске подключится в БД remotedb, выполнит функцию get_user_email(text) и вернет результаты.

localdb=#
CREATE FUNCTION get_user_email(username text) RETURNS text AS $$
CONNECT 'dbname=remotedb host=123.456.123.21 user=myuser';
$$ LANGUAGE plproxy;

Создадим функцию-пустышку в remotedb, которая будет отвечать на запрос

remotedb=#
create function get_user_email(text)
returns text as $$
select 'me@somewhere.com'::text;
$$ language sql;

При выполнении мы получим те же результаты, которые получаем по запросу на remotedb

localdb=#
select * from get_user_email('tere');
get_user_email
------------------
me@somewhere.com
(1 row)

Конечно это только простой пример и я вернусь к более сложному синтаксу позже. Давайте посмотрим, как установить plproxy-язык.

Установка plproxy.
Загрузить код можно с http://pgfoundry.org/projects/plproxy/ но я советую взять свежую версию из CVS (системы контроля версий).
Должен быть установлен набор разработки PostgreSLQ. Каталог, где расположена утилита конфигурации постгреса (pg_config), должен быть включен в переменную $PATH.
При этих условиях установка сводится к:

$ make
$ make install
$ make installcheck

Если у вас не получается заставить это работать - вы всегда можете попросить помощи. Финальный шаг - установить язык в БД. Это нельзя сделать как с другими языками (пользуясь утилитой createlang). Вы должны выполнить файл plproxy.sql, который создаст обработчик языка.

Файл plproxy.sql должен быть где-то в каталоге contrib.
$ psql -f /usr/local/pgsql/share/contrib/plproxy.sql queries
CREATE FUNCTION
CREATE LANGUAGE

Всё. Можно проверить установку простой plproxy-функцией (см. в синтаксических примерах).

Установка первого кластера.

Давайте создадим простой кластер, который будет состоять из 3 БД (в моем примере они все используют один и тот же вариант PostgreSQL). Одна прокси-БД вызывает запросы и две разделенные БД (партиции) с данными. Горизантальное разделение сделано на основе имени пользователя. Это часто используемый способ, т.к. большинство данных в БД - это связанные с пользователем: логин, заказы, платы, настройки...

Настройка Кластера.

Настройки БД кластеров хранятся внутри plpgsql-функций, которые вызывает плпрокси.

Существует 3 функции, которые вы должны создать для правильной конфигурации. Давайте создадим их в БД прокси.

1) plproxy.get_cluster_version(cluster_name text)
Эта функция вызывается при каждом запросе и она используется для отслеживания изменений конфигурации кластера (если возвращаемый ей номер больше, чем закешированное значение - конфигурация перезагружается). Давайте начнем с первой версии конфигурации. Пример:
CREATE OR REPLACE FUNCTION plproxy.get_cluster_version(cluster_name text) RETURNS int AS $$
BEGIN
IF cluster_name = 'queries' THEN
RETURN 1;
END IF;
END;
$$ LANGUAGE plpgsql;

2) plproxy.get_cluster_partitions(cluster_name text)
Эта функция должна возвращать строки для соединения для всех партиций в правильном порядке.
Из-за необоснованного ограничения общее кол-во должно быть кратно 2. Это необоснованное ограничение можно легко обойти, но это будет объяснено в другой раз.

CREATE OR REPLACE FUNCTION plproxy.get_cluster_partitions(cluster_name text) RETURNS SETOF text AS $$
BEGIN
IF cluster_name = 'queries' THEN
RETURN NEXT 'host=127.0.0.1 dbname=queries_0000';
RETURN NEXT 'host=127.0.0.1 dbname=queries_0001';
RETURN;
END IF;
RAISE EXCEPTION 'no such cluster: %', cluster_name;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

Если в строке соединения не указан логин для БД, будет использован текущий (CURRENT_USER). Так как плпрокси не знает ни одного пароля, разделенные БД должны разрешать соединения от БД прокси.

3)plproxy.get_cluster_config(cluster_name text)
Это эквивалент инициализационного файла. Функция должна возвращать параметры как пары ключь-значение. Все они - не обязательные, но нужно определить функцию-пустышку:
CREATE OR REPLACE FUNCTION plproxy.get_cluster_config (cluster_name text, out key text, out val text)
RETURNS SETOF record AS $$
BEGIN
RETURN;
END;
$$ LANGUAGE plpgsql;

Более подробно о параметрах и что они делают - читайте в документации plproxy.

Теперь установка завершена и можно начать "играть" с нашим новым кластером.

Давайте создадим новую таблицу для хранения имен пользователей (логинов) на обеих партициях.

#queries_0000=# CREATE TABLE users (username text PRIMARY KEY);
#queries_0001=# CREATE TABLE users (username text PRIMARY KEY);

Также нужно создать новую функцию, которая будет добавлять новые имена пользователей в таблицу:

CREATE OR REPLACE FUNCTION insert_user(i_username text) RETURNS text AS $$
BEGIN
PERFORM 1 FROM users WHERE username = i_username;
IF NOT FOUND THEN
INSERT INTO users (username) VALUES (i_username);
RETURN 'user created';
ELSE
RETURN 'user already exists';
END IF;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

Теперь давайте создадим прокси-функцию в БД прокси, которая будет вызывать запросы на партициях.

queries=#
CREATE OR REPLACE FUNCTION insert_user(i_username text) RETURNS TEXT AS $$
CLUSTER 'queries'; RUN ON hashtext(i_username);
$$ LANGUAGE plproxy;

Заполнение партиций случайными данными:
SELECT insert_user('user_number_'||generate_series::text) FROM generate_series(1,10000);

Теперь, если мы посмотрим во все БД мы увидим, что обе заполнены и заполнение почти равномерное.
queries_0001 count(*) -> 5071
queries_0000 count(*) -> 4930

Продолжение следует...

Комментариев нет: