plproxy能夠在PostgreSQL上啟動並執行一種過程語言,能夠完成對遠端資料庫的調用,並能夠完成資料切片的功能。資料流處理過程如所示: 首先需要明確的是plproxy只能對使用者自定的方法才有小,如果想達到對sql語句的無條件轉寄,plproxy是做不到的。比如希望所有的select * from tablename 都轉寄到cluster1上,所有的update語句都轉寄到cluster2上,是不能通過plproxy做到的。
plproxy能做到的是:在plproxy cluster上定義foo()函數,在cluster1和cluster2上也定義foo()函數,cluster1和cluster2上的foo()函數的定義是完全相同的,包括函數參數都是相同的;二者和plproxy cluster上的foo()定義均不同。
1. plproxy的關鍵函數
a. plproxy.get_cluster_version(cluster_name) 舉例如下:
- CREATE OR REPLACE FUNCTION plproxy.get_cluster_version(cluster_name text)
- RETURNS int4 AS $$
- BEGIN
- IF cluster_name = 'a_cluster' THEN
- RETURN 1;
- END IF;
- RAISE EXCEPTION 'Unknown cluster';
- END;
- $$ LANGUAGE plpgsql;
該函數返回的是當前plproxy的配置版本號碼,每當有請求時此函數都會調用。如果該函數的傳回值和plproxy的緩衝的配置版本號碼不同,則plproxy會認為配置已經更新,就會調用下面兩個函數重新讀取配置。
b. plproxy.get_cluster_partitions(cluster_name text) 舉例如下:
- CREATE OR REPLACE FUNCTION plproxy.get_cluster_partitions(cluster_name text)
- RETURNS SETOF text AS $$
- BEGIN
- IF cluster_name = 'a_cluster' THEN
- RETURN NEXT 'dbname=part00 host=127.0.0.1';
- RETURN NEXT 'dbname=part01 host=127.0.0.1';
- RETURN;
- END IF;
- RAISE EXCEPTION 'Unknown cluster';
- END;
- $$ LANGUAGE plpgsql;
該函數返回plproxy所設定的內容,當所訪問的cluster名稱為a_cluster時,返回RETURN NEXT所對應的遠程主機配置。
c. plproxy.get_cluster_config(cluster) 舉例如下:
- CREATE OR REPLACE FUNCTION plproxy.get_cluster_config(
- in cluster_name text,
- out key text,
- out val text)
- RETURNS SETOF record AS $$
- BEGIN
- -- lets use same config for all clusters
- key := 'connection_lifetime';
- val := 30*60; -- 30m
- RETURN NEXT;
- RETURN;
- END;
- $$ LANGUAGE plpgsql;
該函數返回plproxy與cluser1和cluster2連結時的一些參數,如生命週期,逾時時間,是否使用binary IO等。
2. foo()函數的定義:
在plproxy cluster上定義foo的內容如下:
- CREATE OR REPLACE FUNCTION foo(i_username text)
- RETURNS text AS
- $BODY$
- CLUSTER 'a_cluster'; RUN ON hashtext(i_username) & 1;
- $BODY$
- LANGUAGE 'plproxy' VOLATILE
- COST 100;
在cluster1和cluster2上定義foo的內容如下:
- CREATE OR REPLACE FUNCTION foo(i_username text)
- RETURNS text AS
- $BODY$
- BEGIN
- RETURN 'user already exists';
- END;
- $BODY$
- LANGUAGE 'plpgsql' VOLATILE SECURITY DEFINER
- COST 100;
可以看到,在plproxy上定義的foo()函數的language是“plproxy”,在cluster1和2上的foo()函數定義的language是“plpgsql”,這是最主要的區別。
3. 調用過程
a. 用戶端發送select foo()的請求到plproxy cluster;
b. plproxy cluster發現foo()是使用者自訂的函數,尋找到該函數定義內容發現該函數是通過plproxy language定義的
c. plproxy cluster把控制權轉交到plproxy language的handler,
d. 該handler執行foo()的內容,發現該函數需要使用名為"a_cluster"的配置
e. handler調用plproxy.get_cluster_version("a_cluster"),發現配置版本號碼相符;
則繼續調用 plproxy.get_cluster_partitions("a_cluster")獲得四項遠程cluster的連結配置;
然後繼續調用 plproxy.get_cluster_config(in cluster_name text,out key text,out val text)獲得連結時的配
置資訊
f. 最後根據RUN ON hashtext(i_username) & 1 把"select foo()"請求轉寄到cluster1或者2上。
hashtext(i_username) & 2 會返回一個0-1之間的值,根據這個值確定應該轉寄請求到哪個具體的cluster上.
g. 在cluster上執行"select foo()",把結果"user already exists"返回給plproxy cluster
h. plproxy cluster再把收到的結果返回給client.
4. plproxy的局限
plproxy並不能完成無條件的轉寄,只能在自訂的函數上實現此功能。這就要求在實際的應用中,需要把大量的商務邏輯放到PostgreSQL伺服器端來完成,降低了靈活度。
一點體會:使用PostgreSQL,需要改變思路,需要適應把商務邏輯通過伺服器端編程實現的過程。這是與MySQL的很大不同。
plproxy官方連結
例子:PostgreSQL cluster: partitioning with plproxy (part I)
PostgreSQL cluster: partitioning with plproxy (part II)