1. PL/Proxy的介绍
1.1 PL/Proxy概述
PL/Proxy是⼀款能在PostgreSQL数据库实现数据库⽔平拆分的软件;可以理解分布式架构(shared nothing);但是不是真正的分布式数据库软件;也是⼀款能在PostgreSQL数据库实现SQL语⾔复制(replication) 分布式架构图如下:
1.2 PL/Proxy集群配置
PL/Proxy既能配置成“CONNECT”模式⼜能配置成“CLUSTER”模式
在\"CONNECT\"模式中;PL/Proxy直接把请求路由(run on n)到指定的数据库。
在\"CLUSTER\"模式中;PL/Proxy可以⽀持数据⽔平分区,即shared nothing。也可以实现SQL语⾔复制(run on all)。 在配置“CLUSTER”模式有两种⽅式:集群configuration APISQL/MED
1.3 PL/Proxy特性介绍
PL/Proxy把需要对数据库SQL访问转换为对PostgreSQL函数调⽤。PL/Proxy后端数据库节点数必须是2的N次⽅。
2. PL/Proxy安装
2.1 编译安装
执⾏“source /home/postgres/.bashrc”加载环境变量;⽬的确保来⾃postgresql bin⽬录的pgconfig在您的路径中
tar -zxvf plproxy-2.7.tar.gzcd plproxy-2.7
source /home/postgres/.bashrcmake
make install
2.2 创建PL/Proxy扩展
在这⾥我选“proxy”数据库作为路由代理数据库。
[postgres@Postgres201 ~]$ psqlpsql (9.6.0)
Type \"help\" for help.
postgres=# create database proxy;CREATE DATABASEpostgres=# \\c proxy
You are now connected to database \"proxy\" as user \"postgres\".proxy=# create extension plproxy;CREATE EXTENSIONproxy=# \\dx
List of installed extensions
Name | Version | Schema | Description ---------+---------+------------+---------------------------------------------------------- plpgsql | 1.0 | pg_catalog | PL/pgSQL procedural language
plproxy | 2.7.0 | public | Database partitioning implemented as procedural language(2 rows)
3. PL/Proxy的配置
本实验的配置环境如下:
主机名
PostgreSQL201PostgreSQL202PostgreSQL202PostgreSQL202PostgreSQL202
修改数据节点的pg_hba.conf
IP
192.168.1.201192.168.1.202192.168.1.202192.168.1.202192.168.1.202
⾓⾊proxy nodedata nodedata nodedata nodedata node
数据库名proxypl_db0pl_db1pl_db2pl_db3
⽤户lottulottulottulottulottu
要确保PL/Proxy节点能访问所有数据库。
host all all 192.168.1.0/24 trust当然在线上数据库⼤家可以这样配置,例如:
host all lottu 192.168.1.201/24 md5
采⽤SQL/MED⽅式配置集群【在PL/Proxy节点操作】
创建⼀个使⽤plproxy FDW的服务器来完成的。服务器的选项是PL/Proxy配置设置和集群分区列表。
[postgres@Postgres201 ~]$ psql proxy lottupsql (9.6.0)
Type \"help\" for help.
proxy=# \\c
You are now connected to database \"proxy\" as user \"lottu\".
proxy=# CREATE SERVER cluster_srv1 FOREIGN DATA WRAPPER plproxyproxy-# OPTIONS (
proxy(# connection_lifetime '1800',proxy(# disable_binary '1',
proxy(# p0 'dbname=pl_db0 host=192.168.1.202',proxy(# p1 'dbname=pl_db1 host=192.168.1.202',proxy(# p2 'dbname=pl_db2 host=192.168.1.202',proxy(# p3 'dbname=pl_db3 host=192.168.1.202'proxy(# );CREATE SERVERproxy=# \\des
List of foreign servers
Name | Owner | Foreign-data wrapper --------------+-------+---------------------- cluster_srv1 | lottu | plproxy(1 row)
proxy=# grant usage on FOREIGN server cluster_srv1 to lottu; GRANT
#创建⽤户映射
proxy=# create user mapping for lottu server cluster_srv1 options (user 'lottu');CREATE USER MAPPINGproxy=# \\deu
List of user mappings Server | User name --------------+----------- cluster_srv1 | lottu(1 row)
配置完成!在\"CLUSTER\"模式中;才需要上述配置;在\"CONNECT\"模式中是不需要的。
4. PL/Proxy测试
PL/Proxy把需要对数据库SQL访问转换为对PostgreSQL函数调⽤;这就需要使⽤者有良好的编程功底。 在数据节点创建测试样本表
create table users(userid int, name text);
4.1 \"CLUSTER\"模式测试
4.1.1 数据⽔平拆分测试
在每个数据节点创建insert函数接⼝
pl_db0=> CREATE OR REPLACE FUNCTION insert_user(i_id int, i_name text)pl_db0-> RETURNS integer AS $$
pl_db0$> INSERT INTO users (userid, name) VALUES ($1,$2);pl_db0$> SELECT 1;pl_db0$> $$ LANGUAGE SQL;CREATE FUNCTION
在PL/Proxy数据库创建同名的insert函数接⼝
proxy=# CREATE OR REPLACE FUNCTION insert_user(i_id int, i_name text)proxy-# RETURNS integer AS $$proxy$# CLUSTER 'cluster_srv1';proxy$# RUN ON ANY;
proxy$# $$ LANGUAGE plproxy;CREATE FUNCTION
为什么要同名的函数呢?若不是同名的话;需要在函数⾥⾯添加⼀个\"TRAGET INSERT_USER\";表明从数据节点调⽤函数\"INSERT_USER\"。在PL/Proxy数据库创建读的函数get_user_name()
proxy=# CREATE OR REPLACE FUNCTION get_user_name()RETURNS TABLE(userid int, name text) AS $$ CLUSTER 'cluster_srv1'; RUN ON ALL ;
SELECT userid,name FROM users;$$ LANGUAGE plproxy;CREATE FUNCTION
Ok;现在函数接⼝开发完成;我现在来调⽤函数插⼊10条记录
SELECT insert_user(1001, 'Sven');SELECT insert_user(1002, 'Marko');SELECT insert_user(1003, 'Steve');SELECT insert_user(1004, 'lottu');SELECT insert_user(1005, 'rax');SELECT insert_user(1006, 'ak');SELECT insert_user(1007, 'jack');SELECT insert_user(1008, 'molica');SELECT insert_user(1009, 'pg');SELECT insert_user(1010, 'oracle');
由于函数执⾏的是\"RUN ON ANY\";表明插⼊数据是随机选取数据节点。我们看看每个数据节点的数据。
pl_db0=> select * from users; userid | name --------+-------- 1005 | rax 1006 | ak 1008 | molica 1009 | pg(4 rows)
pl_db1=> select * from users; userid | name --------+------- 1002 | Marko 1004 | lottu(2 rows)
pl_db2=> select * from users; userid | name --------+-------- 1007 | jack 1010 | oracle(2 rows)
pl_db3=> select * from users; userid | name --------+------- 1001 | Sven 1003 | Steve(2 rows)
可以看出10条数据已经切分到每个数据节点。(10条取样太少,导致数据不均匀)。我们在proxy节点查询下。
proxy=# SELECT USERID,NAME FROM GET_USER_NAME(); userid | name --------+-------- 1005 | rax
1006 | ak 1008 | molica 1009 | pg 1002 | Marko 1004 | lottu 1007 | jack 1010 | oracle 1001 | Sven
1003 | Steve(10 rows)
4.1.2数据复制(replication)测试
选择users表作为实验对象;我们先清理表users数据;在数据节点创建truncatet函数接⼝
pl_db0=> CREATE OR REPLACE FUNCTION trunc_user()pl_db0-> RETURNS integer AS $$pl_db0$> truncate table users;pl_db0$> SELECT 1;pl_db0$> $$ LANGUAGE SQL;CREATE FUNCTION
在PL/Proxy数据库创建同名的truncate函数接⼝
proxy=# CREATE OR REPLACE FUNCTION trunc_user()proxy-# RETURNS SETOF integer AS $$proxy$# CLUSTER 'cluster_srv1';proxy$# RUN ON ALL;
proxy$# $$ LANGUAGE plproxy;CREATE FUNCTION
执⾏之后trunc_user();数据已经清理了。
proxy=# SELECT TRUNC_USER(); trunc_user ------------ 1 1 1 1(4 rows)
其实在这⾥我们已经验证数据复制(replication)测试。为了更好解释;我们选择insert函数接⼝来。在PL/Proxy数据库创建函数接⼝ insert_user_2
proxy=# CREATE OR REPLACE FUNCTION insert_user_2(i_id int, i_name text)proxy-# RETURNS SETOF integer AS $$proxy$# CLUSTER 'cluster_srv1';proxy$# RUN ON ALL;
proxy$# TARGET insert_user;proxy$# $$ LANGUAGE plproxy;CREATE FUNCTION
我们选择这⼏条语句
proxy=# SELECT insert_user_2(1004, 'lottu');proxy=# SELECT insert_user_2(1005, 'rax');proxy=# SELECT insert_user_2(1006, 'ak');proxy=# SELECT insert_user_2(1007, 'jack');
我们看看每个数据节点的数据。
pl_db0=> select * from users; userid | name --------+------- 1004 | lottu 1005 | rax 1006 | ak 1007 | jack(4 rows)
pl_db1=> select * from users; userid | name --------+------- 1004 | lottu 1005 | rax 1006 | ak 1007 | jack(4 rows)
pl_db2=> select * from users; userid | name
--------+------- 1004 | lottu 1005 | rax 1006 | ak 1007 | jack(4 rows)
pl_db3=> select * from users; userid | name --------+------- 1004 | lottu 1005 | rax 1006 | ak 1007 | jack(4 rows)
每个节点的数据都是⼀样的。完成了数据复制(replication)测试。
我们在proxy节点查询下。只要在任意数据节点读取数据即可;我们先编辑函数。
proxy=# CREATE OR REPLACE FUNCTION get_user_name_2()proxy-# RETURNS TABLE(userid int, name text) AS $$proxy$# CLUSTER 'cluster_srv1';proxy$# RUN ON ANY ;
proxy$# SELECT userid,name FROM users;proxy$# $$ LANGUAGE plproxy; CREATE FUNCTION
proxy=# SELECT USERID,NAME FROM GET_USER_NAME_2(); userid | name --------+------- 1004 | lottu 1005 | rax 1006 | ak 1007 | jack(4 rows)
4.2 \"CONNECT\"模式测试
使⽤\"CONNECT\"模式;PL/Proxy不需要上述的配置;直接使⽤即可。
proxy=# CREATE OR REPLACE FUNCTION get_user_name_3()proxy-# RETURNS TABLE(userid int, name text) AS $$
proxy$# CONNECT 'dbname=pl_db0 host=192.168.1.202';proxy$# CONNECT 'dbname=pl_db1 host=192.168.1.202';proxy$# SELECT userid,name FROM users;proxy$# $$ LANGUAGE plproxy;
ERROR: PL/Proxy function lottu.get_user_name_3(0): Compile error at line 3: Only one CONNECT statement allowedproxy=# CREATE OR REPLACE FUNCTION get_user_name_3()proxy-# RETURNS TABLE(userid int, name text) AS $$
proxy$# CONNECT 'dbname=pl_db0 host=192.168.1.202';proxy$# SELECT userid,name FROM users;proxy$# $$ LANGUAGE plproxy; CREATE FUNCTION
proxy=# SELECT USERID,NAME FROM GET_USER_NAME_3(); userid | name --------+------- 1004 | lottu 1005 | rax 1006 | ak 1007 | jack(4 rows)
只允许⼀个“CONNECT statement”;⽤法很简单;作⽤很鸡肋。
5. 总结
PL/Proxy的语法本⽂差不多都涉及到了。⾄于通过“集群configuration API”⽅式配置集群,本⽂不讲解了;其实配置也很简单。
6. 参考⽂档
因篇幅问题不能全部显示,请点此查看更多更全内容