PostgreSQL分布式架構之——PL/Proxy


1. PL/Proxy的介紹

1.1 PL/Proxy概述

  PL/Proxy是一款能在PostgreSQL數據庫實現數據庫水平拆分的軟件;可以理解分布式架構(shared nothing);但是不是真正的分布式數據庫軟件;也是一款能在PostgreSQL數據庫實現SQL語言復制(replication)
  分布式架構圖如下:

1.2 PL/Proxy集群配置

  PL/Proxy既能配置成“CONNECT”模式又能配置成“CLUSTER”模式

  1. 在"CONNECT"模式中;PL/Proxy直接把請求路由(run on n)到指定的數據庫。
  2. 在"CLUSTER"模式中;PL/Proxy可以支持數據水平分區,即shared nothing。也可以實現SQL語言復制(run on all)。

  在配置“CLUSTER”模式有兩種方式:

  1. 集群configuration API
  2. SQL/MED

1.3 PL/Proxy特性介紹

  1. PL/Proxy把需要對數據庫SQL訪問轉換為對PostgreSQL函數調用。
  2. PL/Proxy后端數據庫節點數必須是2的N次方。

2. 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)

3. 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"模式中是不需要的

4. PL/Proxy測試

  PL/Proxy把需要對數據庫SQL訪問轉換為對PostgreSQL函數調用;這就需要使用者有良好的編程功底。
  在數據節點創建測試樣本表

create table users(userid int, name text);

4.1 "CLUSTER"模式測試

4.1.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
  1. 在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"。

  1. 在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)測試

  1. 選擇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
  1. 在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
  1. 執行之后trunc_user();數據已經清理了。
proxy=# SELECT TRUNC_USER();
 trunc_user 
------------
          1
          1
          1
          1
(4 rows)

  其實在這里我們已經驗證數據復制(replication)測試。為了更好解釋;我們選擇insert函數接口來。

  1. 在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');
  1. 我們看看每個數據節點的數據。
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)測試。

  1. 我們在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”;用法很簡單;作用很雞肋。

5. 總結

  PL/Proxy的語法本文差不多都涉及到了。至於通過“集群configuration API”方式配置集群,本文不講解了;其實配置也很簡單。

6. 參考文檔

  https://yq.aliyun.com/articles/59372?spm=a2c4e.11153940.blogcont59345.17.46039916yDaqtq


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM