Postgresql配置pl/proxy实现集群

PL/Proxy可以在CONNECT模式或CLUSTER模式下使用。
在“连接”模式下,PL/Proxy充当到另一个数据库的传递代理。每个PL/Proxy函数都包含一个libpq连接字符串,用于连接到将要代理请求的数据库。
PL/Proxy也可以在CLUSTER模式下使用,它提供了对基于聚类功能的多个数据库的数据分区支持。
在CONNECT模式下使用PL/Proxy时,不需要特殊配置。但是,在CLUSTER模式下使用PL/Proxy需要通过群集配置API或SQL/MED定义群集配置。

pg_plproxy install --通过群集配置API

plproxy 参考网址
plproxy 参考网址2
plproxy 参考网址3
plproxy 参考网址4


创建三个容器,每一个容器都安装了postgresql数据库软件,并且每一个数据库都可以远程连接。其中一个数据库作为proxy节点 install plproxy,称为P1,剩下的两个数据库作为数据节点,称为D1,D1。每一个容器都有一个testduliyan的数据库。每一个数据库都要创建plpgsql过程语言--createlang plpgsql testduliyan, 这个过程是相对于物理机而言的。

配置总结
pg_cluster0 ip 172.17.0.11 P1 testduliyan install plproxy
pg_cluster1 ip 172.17.0.12 D1 testduliyan
pg_cluster2 ip 172.17.0.13 D2 testduliyan

plproxy的配置是通过三个函数(过程)实现的。get_cluster_version()函数在每个请求上被调用,它应该返回特定集群当前配置的版本号。
如果这个函数返回的版本号高于一个plproxy缓存的版本号,那么配置和分区信息将通过调用get_cluster_config()和get_cluster_partitions()函数被重载。如果以上三个函数配置完成了,基本的配置就结束了。之后就是根据需要在proxy节点或者数据节点创建函数。

特性;
plproxy是使用长连接的, 会话会复用连接。 如果修改了server, 那么这些连接会断开, 重新连接,所以不需要担心修改server带来的连接cache问题。如果业务为短连接的形式, 那么需要1层连接池, 在应用程序和plproxy数据库之间,而不是plproxy和数据节点之间。在应用程序和plproxy之间加连接池后, 其实对于plproxy来说就是长连接了, 所以在plproxy和数据节点之间也就不需要连接池了。

注意事项:

  1. proxy节点不需要存在数据。
  2. 数据节点的数量必须是2的幂。
  3. 需要创建一个plproxy的schema -- create schema plproxy ,因为配置的三个函数使用这个schema。
  4. 数据节点可以是一个物理机,也可以是一个物理机中的一个数据库。可以通过get_cluster_partitions动态的添加和删除数据节点,但是需要重启数据库。
  5. proxy节点和数据节点具备相同的schema,或者使用 target func_name(schema.数据节点的函数名)。
  6. 在proxy节点执行函数时,所有数据节点必须具有与proxy节点同名的函数,或者使用 target func_name(数据节点的函数名) 。
  7. 修改后需要重启数据库
  8. 数据节点的函数仅仅需要同名或者使用target指向,与使用什么语言没有关系(plpgsql和sql都可以)

已经配置的proxy集群如何添加和删除节点

  1. 创建一个新的物理机或者在已经的物理机创建一个新的数据库都可以。 如果在一个原有的物理机上添加一个数据库,没有创建plpgsql过程语言--createlang plpgsql xxx的过程。
  2. 新添加的节点和原来的旧节点的总和必须是2的幂,2,4,8……。不然会报错,显示invalida part count.
  3. 然后修改proxy节点的get_cluster_partitions 添加或者移除数据节点。

##################以下配置都是在P1上做的配置,D2,D1不需要。

install postgresql

复制 plproxy-2.8.tar.gz到容器内部

  • gunzip plproxy-2.8.tar.gz
  • tar xf plproxy-2.8.tar
  • cd plproxy-2.8
  • find / -name pg_config-- /usr/pgsql-9.6/bin/pg_config

对于bash: make: command not found错误

  • yum -y install gcc automake autoconf libtool make

  • export PATH=/usr/pgsql-9.6/bin:$PATH

对于Makefile:66: /usr/pgsql-9.6/lib/pgxs/src/makefiles/pgxs.mk: No such file or directory错误

  • yum install postgresql96-devel

对于/usr/bin/flex -osrc/scanner.c --header-file=src/scanner.h src/scanner.l
make: /usr/bin/flex: Command not found
make: *** [src/scanner.c] Error 127

  • yum install flex

对于/usr/bin/flex -osrc/scanner.c --header-file=src/scanner.h src/scanner.l
/usr/bin/bison -b src/parser -d src/parser.y
make: /usr/bin/bison: Command not found
make: *** [src/parser.tab.c] Error 127

  • yum install bison
  • make USE_PGXS=1
  • make USE_PGXS=1 install

对于psql: FATAL: role "root" does not exist
command failed: "/usr/pgsql-9.6/bin/psql" -X -c "DROP DATABASE IF EXISTS "regression"" "postgres"

  • 进入数据库create user root with superuser password '123456';

对于ERROR: PL/Proxy function public.get_random_number_from_each_server(0): [p1] PQconnectPoll: fe_sendauth: no password supplied

  • 修改host all all 127.0.0.1/32 peer or md5 -->trust
  • localhost all all peer or md5 -->trust

ERROR: PL/Proxy function public.test_connect1(0): [test_part] PQconnectPoll:
FATAL: Peer authentication failed for user "root"

  • make installcheck-- 全部通过
  • find / -name plproxy.sql -- /var/plproxy-2.8/sql/plproxy.sql
    创建plproxy
  • su postgres
  • psql -h 127.0.0.1 -U postgres -f /var/plproxy-2.8/sql/plproxy.sql testduliyan;
    为proxy节点的数据库创建plpgsql过程语言
  • su postgres
  • createlang plpgsql testduliyan;--select * from pg_language;

在proxy节点上创建名字为plproxy的schema

  • su postgres
  • psql
  • \c testduliyan
  • create schema plproxy;

plproxy的配置是通过三个函数(过程)实现的,这三个函数的标准模版如下:

这个函数是让plproxy可以找到对应的集群

CREATE OR REPLACE FUNCTION plproxy.get_cluster_partitions(cluster_name text)
RETURNS SETOF text AS $$
BEGIN
IF cluster_name ='testcluster' THEN
RETURN NEXT 'dbname=testduliyan host=172.17.0.12';
RETURN NEXT 'dbname=testduliyan host=172.17.0.13';
RETURN;
END IF;
RAISE EXCEPTION 'Unknown cluster';
END;
$$ LANGUAGE plpgsql;

这个函数是plproxy用于判断是否给前端返回已经cache过的结果用的

CREATE OR REPLACE FUNCTION plproxy.get_cluster_version(cluster_name text)
RETURNS int4 AS $$
BEGIN
IF cluster_name = 'testcluster' THEN
RETURN 1;
END IF;
RAISE EXCEPTION 'Unknown cluster';
END;
$$ LANGUAGE plpgsql;

这个函数是获取不同的集群的配置

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;


在D1,D2 节点数据库创建plpgsql过程语言

  • su postgres
  • createlang plpgsql testduliyan;--select * from pg_language;

---------------------配置结束,以下是测试过程

在P1proxy代理上执行

CREATE OR REPLACE FUNCTION public.dqlexec(query text) RETURNS setof
record AS $$
CLUSTER 'testcluster';
RUN ON ALL;
TARGET dig.dqlexec2;---- 如果数据节点函数与proxy节点的函数不同名,使用target标注
$$ LANGUAGE plproxy;

CREATE OR REPLACE FUNCTION public.dqlexec(query text) RETURNS setof
record AS $$
CLUSTER 'testcluster';
RUN ON ALL;
$$ LANGUAGE plproxy;
CREATE OR REPLACE FUNCTION public.ddlexec(query text) RETURNS setof integer
AS $$
CLUSTER 'testcluster';
RUN ON ALL;
$$ LANGUAGE plproxy;
CREATE OR REPLACE FUNCTION public.dmlexec(query text) RETURNS setof integer
AS $$
CLUSTER 'testcluster';
RUN ON ANY;
$$ LANGUAGE plproxy;

在D1,D2上执行

CREATE OR REPLACE FUNCTION public.ddlexec(query text)
RETURNS integer AS
$BODY$
declare
ret integer;
begin
execute query;
return 1;
end;
$BODY$
LANGUAGE 'plpgsql';

CREATE OR REPLACE FUNCTION public.dmlexec(query text)
RETURNS integer AS
$BODY$
declare
ret integer;
begin
execute query;
return 1;
end;
$BODY$
LANGUAGE 'plpgsql';

CREATE OR REPLACE FUNCTION public.dqlexec(query text)
RETURNS SETOF record AS
$BODY$
declare
ret record;
begin
for ret in execute query loop
return next ret;
end loop;
return;
end;
$BODY$
LANGUAGE 'plpgsql';

测试在P1节点

select ddlexec('create table usertable(id integer)');

select dmlexec('insert into usertable values(0)');
select dmlexec('insert into usertable values(1)');
select dmlexec('insert into usertable values(2)');
select dmlexec('insert into usertable values(3)');
select dmlexec('insert into usertable values(4)');
select dmlexec('insert into usertable values(5)');
select dmlexec('insert into usertable values(6)');
select dmlexec('insert into usertable values(7)');
select dmlexec('insert into usertable values(8)');
select dmlexec('insert into usertable values(9)');
select dmlexec('insert into usertable values(10)');

select * from dqlexec('select * from usertable') as (id integer);

在 plproxy 节点上创建一个同名的插入函数,用于进行集群检索

CREATE OR REPLACE FUNCTION insert_user(i_username text, i_emailaddress text)
RETURNS integer AS $$
CLUSTER 'testcluster';
RUN ON hashtext(i_username);
$$ LANGUAGE plproxy;

在 plproxy 节点上创建一个查询函数,用于进行集群检索

CREATE OR REPLACE FUNCTION get_user_email(i_username text)
RETURNS text AS $$
CLUSTER 'testcluster';
RUN ON hashtext(i_username) ;
SELECT email FROM users WHERE username = i_username;
$$ LANGUAGE plproxy;

plproxy代理数据库,可以在代理数据库上执行这个脚本,他可以查看远程的数据据节点。

CREATE FUNCTION get_user_email(i_username text)
RETURNS SETOF text AS $$
CONNECT 'dbname=part00';
SELECT email FROM users WHERE username = $1;
$$ LANGUAGE plproxy;

SELECT * from get_user_email($1);

D1,D2 创建表

CREATE TABLE users (
username text,
email text
);

CREATE OR REPLACE FUNCTION insert_user(i_username text, i_emailaddress text)
RETURNS integer AS $$
INSERT INTO users (username, email) VALUES ($1,$2);
SELECT 1;
$$ LANGUAGE SQL;


P1数据库上没有数据,但是可以通过p1查看其他D1,D2 的数据,以下语句在P1执行,但是P1没有users 表
SELECT insert_user('Sven','sven@somewhere.com');
SELECT insert_user('Marko', 'marko@somewhere.com');
SELECT insert_user('Steve','steve@somewhere.cm');
SELECT get_user_email('Sven');
SELECT get_user_email('Marko');
SELECT get_user_email('Steve');


在proxy运行后,继续在此基础上添加新的数据节点,不过要求必须2的幂。在两个数据节点的基础上又添加了两个数据节点。

D3,D4数据节点配置:
创建plpgsql过程语言

  • su postgres
  • createlang plpgsql testduliyan;--select * from pg_language;

要添加和proxy的调用的同名的function.
  • create table usertable(id integer);

CREATE OR REPLACE FUNCTION ddlexec(query text)
RETURNS integer AS
$BODY$
declare
ret integer;
begin
execute query;
return 1;
end;
$BODY$
LANGUAGE 'plpgsql';

CREATE OR REPLACE FUNCTION dmlexec(query text)
RETURNS integer AS
$BODY$
declare
ret integer;
begin
execute query;
return 1;
end;
$BODY$
LANGUAGE 'plpgsql';

CREATE OR REPLACE FUNCTION dqlexec(query text)
RETURNS SETOF record AS
$BODY$
declare
ret record;
begin
for ret in execute query loop
return next ret;
end loop;
return;
end;
$BODY$
LANGUAGE 'plpgsql';

proxy节点的修正,修正函数添加了两个数据节点,然后重启数据库。

CREATE OR REPLACE FUNCTION plproxy.get_cluster_partitions(cluster_name text)
RETURNS SETOF text AS $$
BEGIN
IF cluster_name ='testcluster' THEN
RETURN NEXT 'dbname=testduliyan host=172.17.0.12';
RETURN NEXT 'dbname=testduliyan host=172.17.0.13';
RETURN NEXT 'dbname=testduliyan host=172.17.0.14';
RETURN NEXT 'dbname=testduliyan host=172.17.0.15';
RETURN;
END IF;
RAISE EXCEPTION 'Unknown cluster';
END;
$$ LANGUAGE plpgsql;

在此基础上做测试,再次插入10条记录,发现原来插入的数据不会改变,新插入的10条记录被4个数据节点均分了。

CREATE OR REPLACE FUNCTION plproxy.get_cluster_partitions(cluster_name text)
RETURNS SETOF text AS $$
BEGIN
IF cluster_name ='testcluster' THEN
RETURN NEXT 'dbname=testduliyan host=172.17.0.12';
RETURN NEXT 'dbname=testduliyan host=172.17.0.13';
RETURN NEXT 'dbname=testduliyan host=172.17.0.14';
RETURN NEXT 'dbname=testduliyan_db host=172.17.0.11';
RETURN;
END IF;
RAISE EXCEPTION 'Unknown cluster';
END;
$$ LANGUAGE plpgsql;

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容