February 23, 2010

Выполнение произвольных запросов с помощью PL/Proxy

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

https://developer.skype.com/SkypeGarage/DbProjects/PlProxy

Допустим необходимо сделать шардинг на основе PL/Proxy. Также есть приложение, типа ORM генерирующее SQL-запросы, которое тяжело переделать на работу с БД через хранимые функции, что является необходимым для PL/Proxy. Это приложение позволяет с определёнными усилиями в обозримые сроки переработать места инициации запросов, указав как дополнительный параметр критерии шардинга, а структура БД допускает существование таких критериев для каждой выборки.

В этом случае можно воспользоваться следующим трюком распределения запросов по нескольким серверам. Смысл его в создании PL/Proxy функции, куда можно передать текст SQL-запроса, а она в свою очередь нициирует выполнение этого запроса на сервере, определённом критериями шардинга и вернёт результат. В данном примере я не указываю критерии, а просто обращаюсь к серверу напрямую.

Функция на стороне прокси-базы:

CREATE OR REPLACE FUNCTION plproxy_run_sql(text /* , place_for_shrding_criteria */)
RETURNS SETOF text AS
$BODY$
CLUSTER 'cluster1';
RUN ON 1;
$BODY$
LANGUAGE 'plproxy' VOLATILE
COST 100;


Функция на стороне партиций:

CREATE OR REPLACE FUNCTION plproxy_run_sql(text)
RETURNS SETOF text AS
$BODY$
DECLARE
t text;
BEGIN
FOR t IN EXECUTE $1 LOOP
RETURN NEXT t;
END LOOP;
RETURN;
END;
$BODY$
LANGUAGE 'plpgsql' VOLATILE
COST 10
ROWS 1000;


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

SELECT (t::table1).*
FROM plproxy_run_sql(
$$ SELECT table1::text
FROM table1
LIMIT 1 $$
) AS t;


Если же запрос возвращает не типизированный набор полей, то его (набр полей) просто надо типизировать:

CREATE TYPE query_type1 AS (
table1_id bigint,
table2_id bigint
);

SELECT (t::query_type1).*
FROM plproxy_run_sql(
$$ SELECT (t1.id, t2.id)::text
FROM table2 t2
JOIN table1 t1 ON
t1.some_field = t2.some_field
LIMIT 1 $$
) AS t;


Как-то так.

No comments:

Post a Comment