假设 现在假设我们有这样一个环境: Node_proxy,上面是plproxy 的运行环境;IP: 192.168.1.253, postgresql 数据库端口 5432,数据库是testproxy Node_db1,是第一个数据库节点;IP:192.168.1.1, postgresql数据库端口 5432,上面数据库名是 test1 Node_db1,是第二个数据库节点;IP:192.168.1.2, postgresql数据库端口 5432,上面数据库名是 test2 我们假设我们下面的所有脚本都保存在plproxy节点上,并且也在plproxy节点上执行所有的命令。 需求我们希望Node_proxy是plproxy的节点,而Node_db1和Node_db2是底层的数据库节点。我门的目标是希望在 Node_proxy 上头执行查询,能够同时查询 Node_db1和Node_db2上面的数据并返回结果。 那么这个工作可以利用下面的步骤来实现。 配置前提首先,我们要保证 Node_proxy 这个节点上的 plproxy /plpgsql语言已经正确安装,如果用我们的制作包制作,那么只要是用 createdb 创建出来的数据库,都已经内置了plproxy/plpgsql了,也就是说,你只要 /usr/local/pgsql/bin/createdb testproxy # testproxy 是测试用数据库名称 这样出来的 testdb 里面就已经有plproxy/plpgsql了,我们只要用下面的简单语句在psql中测试一下即可: psql testproxy # 连接数据库
create or replace function plproxyTest()returns integer as$$begin return 1 end;$$language plsql; #测试SQL
语句返回成功,表明plpgsql是正常的,我们首先要保证plpgsql已经安装,plproxy会在后面进行测试。 配置过程首先为 plproxy 创建一个 schema: create schema plproxy;
plproxy 的配置是通过三个函数(过程)实现的,这三个函数的标准模版如下: CREATE OR REPLACE FUNCTION plproxy.get_cluster_partitions(cluster_name text)
RETURNS SETOF text AS $$
BEGIN
IF cluster_name = 'MyCluster' THEN
RETURN NEXT 'dbname=test1 host=192.168.1.1';
RETURN NEXT 'dbname=test2 host=192.168.1.2';
RETURN;
END IF;
RAISE EXCEPTION 'Unknown cluster';
END;
$$ LANGUAGE plpgsql;
上面这个函数是让plproxy可以找到对应的集群,那么这里的 MyCluster 是集群的名称,根据自己的需要指定,这个名称在后面的查询的时候就有用了;这里的dbname, host 等参数,就是PostgreSQL标准的数据库连接串的配置方法,注意我们这里和上面的Node_db1以及Node_db2对应; 第二个函数是: CREATE OR REPLACE FUNCTION plproxy.get_cluster_version(cluster_name text)
RETURNS int4 AS $$
BEGIN
IF cluster_name = 'MyCluster' THEN
RETURN 1;
END IF;
RAISE EXCEPTION 'Unknown cluster';
END;
$$ LANGUAGE plpgsql;
这个函数其实是plproxy用于判断是否给前端返回已经cache过的结果用的,这样,因为函数本身可以动态更新(无需down机),那么我们可以通过重新创建函数,返回不同RETURN的值,实现cache的失效控制。 第三个函数是: create or replace function plproxy.get_cluster_config(cluster_name text, out key text, out val text)
returns setof record as $$
begin
key := 'statement_timeout';
val := 60;
return next;
return;
end; $$ language plpgsql;
这个函数其实是获取不同的集群的配置,我们这里可以给不同的集群(比如MyCluster等)不同的类似超时时间、长短连接等的设置,具体参数我们会在用户手册里详细给出,我们上面这个例子缺省认为所有的集群都用一个配置,因为这个函数也可以运行时动态create or replace,所以问题不大。
psql -f MyClusterInit.sql -d testproxy
这样,plproxy的设置就完成了。然后,我们继续一个很重要的工作:我们希望在plproxy节点上执行SQL查询,获取两个数据库节点(Node_db1和Node_db2)的数据(因为我们的数据很可能已经分散在那两个数据库上头了,所以需要获取两个数据库对的同样查询的返回)。 数据库节点的设置OK,我们给每个数据库节点都创建一个函数,这个函数是这样的: create or replace function public.dquery(query text) returns setof record as $$
declare
ret record;
begin
for ret in execute query loop
return next ret;
end loop;
return;
end;
$$ language plpgsql;
create or replace function public.ddlExec(query text) returns integer as $$
declare
ret integer;
begin
execute query;
return 1;
end;
$$ language plpgsql;
create or replace function public.dmlExec(query text) returns integer as $$
declare
ret integer;
begin
execute query;
return 1;
end;
$$ language plpgsql;
这几个函数实际上是非常通用的函数,它可以接收一个SQL语句,然后执行之、返回这个SQL语句的结果。区别只是一个是数据定义操作,一个是数据更新操作,一个是假设,我们把这个函数保存在 MyClusterNodesInit.sql,那么我们可以这样创建它: psql -f MyClusterNodesInit.sql -h 192.168.1.1 -d test1
psql -f MyClusterNodesInit.sql -h 192.168.1.2 -d test2
host all all 192.168.1.253/32 trust
执行完上面的东西之后,就可以在 proxy 节点上创建一个同名的函数,用于进行集群检索,这个函数是这样的: CREATE OR REPLACE FUNCTION public.dquery(query text) RETURNS setof
record AS $$
CLUSTER 'MyCluster';
RUN ON ALL;
$$ LANGUAGE plproxy;
CREATE OR REPLACE FUNCTION public.ddlexec(query text) RETURNS setof integer
AS $$
CLUSTER 'MyCluster';
RUN ON ALL;
$$ LANGUAGE plproxy;
CREATE OR REPLACE FUNCTION public.dmlexec(query text) RETURNS setof integer
AS $$
CLUSTER 'MyCluster';
RUN ON ANY;
$$ LANGUAGE plproxy;
我们把它保存在 MyClusterProxyExec.sql 文件里,用下面的命令执行之: psql -f MyClusterProxyExec.sql -d testproxy
实例OK,有了上面的基本的脚本,我们举一个例子,假如我们在数据节点上有这么一个表: create tabel userTable(
id serial primary key,
userName varchar(250),
userAge smallint,
userAlias varcahr(250)
);
那么,我们可以在proxy节点上,执行下面的操作:
select * from public.ddlexec('
create table userTable( id serial primary key, userName varchar(250), userAge smallint, userAlias varcarh(250))'
);
select * from public.dmlexec('insert into userTable(userName, userAge) values(''laser'', 30)');
select * from public.dmlexec('insert into userTable(userName, userAge) values(''henry'', 20)');
...
select * from public.dquery('select * from userTable where userAge >=20 and userAge<=30') as
(id integer, name varchar(250), age smallint, alias varchar(250)) ; |