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閱讀 229,362評論 6 537
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,013評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,346評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,421評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,146評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,534評論 1 325
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,585評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,767評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,318評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,074評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,258評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,828評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,486評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,916評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,156評論 1 290
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,993評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,234評論 2 375

推薦閱讀更多精彩內容