PostgreSQL分布式架构之——PL/Proxy
- 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 API
SQL/MED
1.3 PL/Proxy特性介绍
PL/Proxy把需要对数据库SQL访问转换为对PostgreSQL函数调用。
PL/Proxy后端数据库节点数必须是2的N次方。
- PL/Proxy安装
2.1 编译安装
执行“source /home/postgres/.bashrc”加载环境变量;目的确保来自postgresql bin目录的pgconfig在您的路径中
tar -zxvf plproxy-2.7.tar.gz
cd plproxy-2.7
source /home/postgres/.bashrc
make
make install
2.2 创建PL/Proxy扩展
在这里我选“proxy”数据库作为路由代理数据库。
复制代码
[postgres@Postgres201 ~]$ psql
psql (9.6.0)
Type "help" for help.
postgres=# create database proxy;
CREATE DATABASE
postgres=# c proxy
You are now connected to database "proxy" as user "postgres".
proxy=# create extension plproxy;
CREATE EXTENSION
proxy=# 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)
复制代码
- PL/Proxy的配置
本实验的配置环境如下:
主机名 IP 角色 数据库名 用户
PostgreSQL201 192.168.1.201 proxy node proxy lottu
PostgreSQL202 192.168.1.202 data node pl_db0 lottu
PostgreSQL202 192.168.1.202 data node pl_db1 lottu
PostgreSQL202 192.168.1.202 data node pl_db2 lottu
PostgreSQL202 192.168.1.202 data node pl_db3 lottu
修改数据节点的pg_hba.conf
要确保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 lottu
psql (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 plproxy
proxy-# 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 SERVER
proxy=# 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 MAPPING
proxy=# deu
List of user mappings
Server | User name
cluster_srv1 | lottu |
(1 row)
复制代码
配置完成!在"CLUSTER"模式中;才需要上述配置;在"CONNECT"模式中是不需要的。
- 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 allowed
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$# 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”;用法很简单;作用很鸡肋。
- 总结
PL/Proxy的语法本文差不多都涉及到了。至于通过“集群configuration API”方式配置集群,本文不讲解了;其实配置也很简单。 - 参考文档
https://yq.aliyun.com/articles/59372?spm=a2c4e.11153940.blogcont59345.17.46039916yDaqtq