数据库集群:N个数据库堆到一起,找一个当个头头,管理所有的数据库并让它们协同工作。当然了,要不要找个头头,找几个头头,如何协作等等问题这些都可以商量和约定,因此,也就形成了不同的数据库集群。
如果数据库系统是PostgreSQL,这个集群就是PostgreSQL数据库集群。
PostgreSQL数据库管理集群的方法有很多,有人提出了PL/Proxy方式的集群(这才是本文的重点)。这个PL/Proxy方式的集群是这样的:有很多安装了PostgreSQl数据库的计算机,有台计算机是头头,我们把这个头头叫做proxy,其他的叫做database0,database1……。
以三台机器的集群为例子,看看PostgreSQL集群的架构是什么。
proxy节点:proxy节点实际上也是一个PostgreSQL数据库 节点,但是所有数据均不存放到proxy节点上,主要做三件事情:
1.接受用户的sql查询;
2.分析用户的sql查询并转换成集群上执行的SQL语句;
3.合并集群执行sql的结果,然后返回给用户。
说白了,就是把用户的sql语句交给database0,database1去执行,然后合并执行结果返回给用户。
database1节点和 database2节点:
就是普通的数据库节点,接收proxy节点的sql查询请求并返回结果给proxy节点,是真正存放数据的节点。
集群结构图,结构更清晰一些:
开始创建一个数据库集群吧,需求如下:
1.创建一个如集群结构图所示的集群,数据库集群中均操作同一个数据库“arvindb”;
2.用户通过客户端操作proxy节点在集群中创建一个表user(说白了就是要通过集群的方法在Database0和Database1的arvindb中创建一个叫做user的表);
3.用户通过客户端操作proxy节点在集群上插入多个记录信息到集群的user表上(需要把插入操作分布到两个Database节点上);
4.用户通过客户端操作proxy节点在集群上查询集群上的所有user记录;
(版本号需要留意一下,版本不同的话,可能会导致情况大不相同)
操作性较强的步骤:
第一步:创建三个空的虚拟机器并把这三台虚拟机的网卡设置搞定(比如,是否桥接,是否NAT之类)(有钱的话,自己买三台电脑,3条网线和一个集线器)。
可以创建虚拟机的软件如VMWare, VirtualPC, Xen等等,本文的实验用的是VMWare.
第二步:三个虚拟机全部安装Ubuntu-12.04
第三步:三个虚拟机全部安装PostgreSQL数据库系统
如何安装和配置请参见Ubuntu下 Postgresql-9.1安装及配置
第四步:将每台服务器的设定成允许任何机器使用任何账号访问任何数据库
root@ubuntuserver:~# vi /etc/postgresql/9.1/main/pg_hba.conf
—>改变行:host all all 0.0.0.0 0.0.0.0 md5
—>修改为:host all all 0.0.0.0 0.0.0.0 trust
如果没有此行则添加此行,具体说明参见Ubuntu下 Postgresql-9.1安装及配置。
第五步:三个虚拟机全部创建数据库Arvindb:
root@ubuntuserver:~# psql –U postgres -h 127.0.0.1
postgres=# create database “arvindb”;
创建数据库的详细方法请参见Ubuntu下 Postgresql-9.1安装及配置
PS:现在的三台机器都能提供数据库服务了,而且在任何一台机器使用客户端都可以自由的访问其他数据库服务器中的”arvindb”数据库,不过他们还不是集群,仅仅是可以相互访问的数据库服务器而已,他们之间没有任何协作关系。下面创建PL/Proxy方式的协作关系(创建PostgreSQL 数据库集群)。
第六步:在 proxy 节点上安装过程语言plproxy(安装 plproxy 必须使用 root 用户,否则不能正常安装)
root@ubuntuserver:~# sudo apt-get install postgresql-9.1-plproxy
下面是解释,不想了解的话,可以直接跳过。
这一步的目的是:使proxy节点知道怎样指挥集群(实验中,就是指挥database0和database1),换句话就是说,让prxoy知道三件事情:
怎样把用户的SQL请求发送给集群的节点;
怎样接受集群的处理结果;
怎样将处理后的结果返回给用户。
这一步的实质是:复制plproxy.so文件到目录”/user/lib/postgresql/9.1lib”的下面;复制plproxy.sql文件到目录”/usr/share/postgresql-9.1-plproxy”下面(这个目录是安装程序创建的,plproxy.sql文件是给下一步创建plproxy语言使用的,不是必须的,完全可以在需要的时候自己写相应的SQL语句)。这两个文件的存在是这一步安装成功的一个标志。
需要注意的是:安装过程语言是对数据库系统而言的,可以认为仅仅是复制了一些需要的文件而已,之后使用这个过程语言的之前,还要分别为每一个需要使用此过 程语言的数据库创建这个语言(说白了,就是还要告诉每个数据库到那里找相应的文件plproxy.so文件,下一步有解释)。
执行后在文件浏览器中的效果是:
第七步:为proxy节点上的数据库”arvindb “创建plproxy过程语言(前提是你之前安装过这个语言):
root@ubuntuserver:~# psql -U postgres -h 127.0.0.1 -f /usr/share/postgresql/9.1/contrib /plproxy.sql Arvindb
这条语句的实质是:在数据库系统表pg_language中添加一行记录。
执行后在pgAdmin3客户端观看到的效果是:
当然要是你嫌每次都要创建语言麻烦,你可以将过程语言创建到template1数据库中,这样每次创建数据库的时候,所的语言都自动创建好了(创建数据库 的过程实际上就是复制templated1模板数据苦的过程)。
下面是解释,不想了解的话,可以直接跳过。
这一步的目的是告诉数据库” arvindb”遇到plproxy的过程函数时候怎么处理。
文件plproxy.sql的内容如下:
– handler function
CREATE FUNCTION plproxy_call_handler () RETURNS language_handler
AS ‘$libdir/plproxy’ LANGUAGE C;
– language
CREATE LANGUAGE plproxy HANDLER plproxy_call_handler;
解释一下:
–handler function之下的代码的作用是创建一个函数,名字叫做plproxy_call_handler,返回类型是language_handler,他的定义在文件夹”$libdir”之下的plproxy.so文件中plproxy.so是一个由C语言编译而成的.so文件;
– language之下的代码的作用是创建一个过程语言,过程语言的名称是plproxy,处理这个过程语言的函数是plproxy_call_handler,过程语言的源代码就是这个函数的参数。
可见,实际上过程语言就是一个“C”语言函数的字符串参数。
这里有必要提及一下过程语言的前世今生(过程语言的官方解释见http://www.pgsqldb.org/pgsqldoc-cvs/xplang.html 。:
在PostgresSQL数据库系统里,除了使用系统自带的函数(如count(), max()等)外,还可以使用你自己编写的函数。编写函数的语言可以是sql或C语言,也可以是指定的过程语言(比如plproxy过程语言,plpgsql过程语言,你也可以自己写一个过程语言)。编写sql语言/C语言/plproxy过程语言的函数的例子如下:
CREATE OR REPLACE FUNCTION fun_sql_test()
RETURNS INTEGER AS
$BODY$
SELECT 1;
$BODY$
LANGUAGE‘sql‘ VOLATILE
—————————–
CREATE OR REPLACE FUNCTION fun_c_test()
RETURNS INTEGER AS
‘$libdir/c_test’, ‘fun_c_test’
LANGUAGE ‘c‘ VOLATILE
—————————–
CREATE OR REPLACE FUNCTION fun_plproxy_test()
RETURNS integer AS
$BODY$
CLUSTER ‘jiwan’;
RUN ON ALL;
$BODY$
LANGUAGE ‘plproxy‘ VOLATILE
—————————–
这样就创建了三个函数fun_sql_test(),fun_c_test()和fun_plproxy_test()。
对于fun_sql_test(),执行很简单:
0.用户执行fun_sql_test()函数;
1.执行$BODY$语句包围的sql语句;
2.返回一个整形的数值1。
对于fun_c_test(),执行的顺序是:
0.用户执行fun_c_test()函数;
1.到$libdir目录(这段文字会被PostgreSQL替换成PostgreSQL的库目录:“/user/lib/postgresql/8.5/lib“)加载c_test.so文件(.so文件是linux系统的共享文件,相当于windows下的.dll文件);
2.执行c_test.so中的fun_c_test()函数。
3.返回一个整形的数值。
对于fun_plproxy_test(),执行的顺序是(第1步加载.so文件的执行时间不一定正确,仅仅是为了说明过程语言的执行原理,下同):
0.用户执行fun_plproxy_test()函数;
1.根据plproxy过程语言的配置加载plproxy.so文件( 根据之前“create language”语句中的参数’$libdir/plproxy’);
2.提取$BODY$语句包围的语句 (这个就是所谓的过程语言源代码):CLUSTER ‘jiwan’; RUN ON ALL;
3.根据plproxy过程语言的配置调用过程语言的语言句柄函数plproxy_call_handler并把上一步中提取的过程语句作为它的参数;
4.返回一个整形的数值。
可见,实际上过程语言就是一个“C”语言函数的字符串参数。不管是谁写的过程语言, PostgreSQL中的函数的处理方式大致都是这样的:
0.用户执行某种过程语言的函数;
1.根据过程语言的配置加载相应的.so文件;
2.将函数体中的源代码(简单的说,就是一段文本)抽取出来(函数体中的文本需要根据相应的.so文件的约定去书写,你不按照它的约定,它就会状告你说:编译错误);
3.根据过程语言的配置调用语言句柄函数并把之前提取的过程语句作为它的参数。
目前在标准的 PostgreSQL 发布里有四种过程语言可用: PL/pgSQL, PL/Tcl, PL/Perl和 PL/Python等等过程函数。
当然,你也可以自己写一个模块用于解析函数体中的源代码,简单的说,就是你自己写一个.so文件,把这个.so文件复制到目录“/user/lib/postgresql/8.5/lib“的下面(这个不是必须的,不过呢,plpgsql.so在这个目录下面,plproxy.so文件也在这个目录下面,就跟一下风吧!)。我们把复制.so文件的 这一步操作叫做安装过程语言(多玄乎啊,吓唬小白必须学会这个词!)这个.so文件中应该包含语言句柄函数和验证器函数(这个可选)。
第八步:为proxy节点的”arvindb”数据库创建plpgsql过程语言:
root@ubuntuserver:~# sudo -u postgres psql Arvindb
Arvindb=# create language plpgsql;
或者:sudo -u postgres createlang plpgsql “Arvindb”
下面是解释,不想了解的话,可以直接跳过。
关于过程语言plpgsql和plproxy之间的关系:
之所以创建plpgsql语言,是因为plproxy过程语言本身需要调用plproxy目录下的三个由plpgsql过程语言定义的三个函数。
plpgsql 是postgresql自带的过程语言(含义是,安装 postgresql数据库的时候plpgsq.so文件就会被安装到postggresql的lib文件夹下,对于apt-get install postgresql方式安装的9.1版本数据库系统的lib文件夹是/usr/lib/postgresql/9.1)。
自带的含义就是“HANDLER plpgsql_call_handler”; 和“VALIDATOR plpgsql_validator;” 这两个函数应该是数据库系统为plpgsql过程语言默认设置的handler函数和validator函数(这个是我的猜测,还没有证实),所以创建plpgsql的时候不需要指定handler函数和validator函数(当 然,validator函数本省就是可选的)。有图有真相:
如果数据库系统中的模板数据template1没有创建plpgsql过程语言的话,每一个数据库要使用plpgsql语言之前,都需要创建该语言,以数据库arvindb为例的语句是:
root@ubuntuserver:~#sudo -u postgres createlang plpgsql “Arvindb”
plproxy 是用于集群的过程语言,定义了RUN ON ALL/ANY/0/1/2/3….等等函数,安装该语言的语句:
CREATE PROCEDURAL LANGUAGE ‘plproxy’ HANDLER plproxy_call_handler;
当然了,plproxy不是系统自带的过程语言,是通过apt-get install postgresql-9.1-plproxy安装的过程语言(实际上主要就是下载了plproxy.so文件),为了告诉数据库过程语言的情况,所以需 要编写plproxy_call_handler:
CREATE OR REPLACE FUNCTION plproxy_call_handler() RETURNS language_handler AS
‘$libdir/plproxy’, ‘plproxy_call_handler’
LANGUAGE ‘c’ VOLATILE
plproxy 过程语言在定义plproxy.get_cluster_config, plproxy.get_cluster_partitions, plproxy.get_cluster_version三个函数的时候使用的也是plpgsql过程语言。
第九步:在proxy节点上创建名字为plproxy的schema:
root@ubuntuserver:~# sudo -u postgres psql -d “Arvindb”
Arvindb=# create schema plproxy;
下面是解释,不想了解的话,可以直接跳过。
关于schema:schema的中文含义就是一个目录(可能不准确),是用来组织数据库中的各个对象的(PostgreSQL是ORDBMS,O就是对象的意思,比如数据表,函数等等),系统默认有一个public的schema,所有对象默认创建的位置都是public下,使用public下的对象也不需要前缀(比如public下有一个user的表,那么public.user等价于user)。plproxy这个schema是proxy过程语言所需要的schema(proxy语言需要调用这个schema下的三函数,具体原因下面会说到)。其实创建schema的实质是:在系统的schemata数据表中添加一行数据。
执行后在pgAdmin3客户端观看到的效果是:
第十步:在proxy节点上的plproxy目录下创建三个函数:
get_cluster_config
get_cluster_partitions
get_cluster_version
root@ubuntuserver:~# sudo -u postgres psql Arvindb
创建函数的SQL代码如下:
CREATE OR REPLACE FUNCTION plproxy.get_cluster_config(IN cluster_name text, OUT "key" text, OUT val text)
RETURNS SETOF record AS
$BODY$
BEGIN
key := 'statement_timeout'; //就是给 key 变量赋值,赋的值为’statement_timeout’
val := 60; //就是给 val 变量赋值,赋的值为 60
RETURN NEXT;
RETURN;
END;
$BODY$
LANGUAGE 'plpgsql' VOLATILE
COST 100
ROWS 1000;
CREATE OR REPLACE FUNCTION plproxy.get_cluster_partitions(cluster_name text)
RETURNS SETOF text AS
$BODY$
BEGIN
IF cluster_name = 'arvincluster' THEN //cluster_name 是群集的名字
RETURN NEXT 'dbname=hldb host=10.3.12.2'; //数据库节点的数据库名和 IP 地址
RETURN NEXT 'dbname=hldb host=10.3.12.3'; //数据库节点的数据库名和 IP 地址
RETURN;
END IF;
RAISE EXCEPTION 'Unknown cluster'; //–如果群集名不存在,抛出异常,这个是在数据库内部处理的,最终会写入日志中。‘Unknown cluster’是报错信息
END;
$BODY$
LANGUAGE 'plpgsql' VOLATILE
COST 100
ROWS 1000;
CREATE OR REPLACE FUNCTION plproxy.get_cluster_version(cluster_name text)
RETURNS integer AS
$BODY$
BEGIN
IF cluster_name = ' arvincluster ' THEN
RETURN 1;
END IF;
RAISE EXCEPTION 'Unknown cluster';
END;
$BODY$
LANGUAGE 'plpgsql' VOLATILE
COST 100;
下面是解释,不想了解的话,可以直接跳过。
三个函数的作用:
plproxy.get_cluster_config: 这个函数其实是获取不同的集群的配置,我们这里可以给不同的集群(比如Arvindb等)不同的类似超时时间、长短连接等的设置。
plproxy.get_cluster_partitions: 这个函数是让plproxy可以找到对应的集群,“Arvindb ”是集群的名称,根据自己的需要指定,这个名称在后面查询的时候要用到;”dbname”, “host” 等参数就是PostgreSQL标准的数据库连接串的配置方法,本试验中就是database0节点和database1节点的信息告诉proxy节点。
plproxy.get_cluster_version: 这个函数其实是plproxy用于判断是否给前端返回已经cache过的结果用的,这样,因为函数本身可以动态更新(无需down机),那么我们可以通过重新创建函数,返回不同RETURN的值,实现cache的失效控制。
plproxy过程语言设计的目的是实现数据库集群,集群受proxy节点管理,proxy获取集群的信息(比如节点的IP地址等等信息)的方法就 是调用上面创建的三个函数,所以配置集群信息的方法就是创建/修改这三个函数。
使用plproxy过程语言的用户不需要显式的调用这三个函数,但是在调用plproxy过程语言定义的函数的时候,plproxy过程自己会在内部调用目录plproxy下的这三个函数:
I.对于plproxy.get_cluster_config和plproxy.get_cluster_partitions函数,内部调用一次后就 不再调用;
以plproxy.get_cluster_partitions为例子的实验是:
a.执行plproxy 过程语言定义的函数–>成功;
b.删除plproxy.get_cluster_partitions函 数,调用plproxy过程语言定义的函数,执行成功。
一个特殊情况:如果删除plproxy.get_cluster_partitions函数之前,数据库系统从未执行过任何plproxy过程语言定义的 函数,执行plproxy过程语言定义的函数将会失败).
另外一个实验是:
a.执行plproxy 过程语言定义的函数–>成功;(集群信息为“dbname=Arvindb host=10.13.19.55”和dbname=Arvindb host=10.13.19.70)
b.修改plproxy.get_cluster_partitions函 数(集群信息为“dbname=Arvindb host=10.13.19.55”和dbname=Arvindb host=10.13.19.71, 调用plproxy过程语言定义的函数,执行成功。但是使用的集群信息仍然是:“dbname=Arvindb host=10.13.19.55”和dbname=Arvindb host=10.13.19.70)。
II.对于plproxy.get_cluster_version函数,执行plproxy过程语言定义的函数的时候,内部每次都会调用plproxy.get_cluster_version函数。 实验的方法可与参考前面的方法。
总结一下:如果修改了plproxy.get_cluster_config和plproxy.get_cluster_partitions函 数,想立即生效 的话,就需要重启postgresql服务(其他方法还没有深究过):/etc/init.d/postgresql-8.4 restart
从网上参考的资料,以及相关pdf中参看的代码会有一些问题:字符不对 ,同样为单引号,但是pdf中的单引号是‘(看清楚:不是正确的’),等等之类很难发现的错误,严重影响了工作进度。
再补充一下:下面的实验表明 postgresql下的函数定义/修改/删除是及时生效的:
创建一个函数:
CREATE OR REPLACE FUNCTION fun_test() RETURNS integer AS
$BODY$ SELECT 33; $BODY$
LANGUAGE ‘sql’ VOLATILE COST 100;
执行这个函数:
select fun_test();———> 正常执行
删除这个函数:
DROP FUNCTION fun_test;
执行这个函数:
select fun_test(); ——–>执行失败:找不到函数fun_test();
第十一步:在proxy节点上创建三个函数:
public.ddlexec(sql_request text)
public.dmlexec(sql_request text)
public.dqlexec(sql_request text)
root@ubuntuserver:~# sudo -u postgres psql Arvindb
创建的sql代码是:
CREATE OR REPLACE FUNCTION ddlexec(query text)
RETURNS SETOF integer AS
$BODY$
CLUSTER 'arvincluster';
RUN ON ALL;
$BODY$
LANGUAGE 'plproxy' VOLATILE
COST 100
ROWS 1000;
CREATE OR REPLACE FUNCTION dmlexec(query text)
RETURNS SETOF integer AS
$BODY$
CLUSTER 'arvincluster';
RUN ON ANY;
$BODY$
LANGUAGE 'plproxy' VOLATILE
COST 100
ROWS 1000;
CREATE OR REPLACE FUNCTION dqlexec(query text)
RETURNS SETOF record AS
$BODY$
CLUSTER 'arvincluster';
RUN ON ALL;
$BODY$
LANGUAGE 'plproxy' VOLATILE
COST 100
ROWS 1000; ROWS 1000;执行后在pgAdmin3客户端观看到的效果是:
下面是解释,不想了解的话,可以直接跳过。
这三个函数的作用就是:用户通过调用者三个函数操纵集群(比如select ddlexec(‘create table usertable’)就会在集群的database0和database1上同时创建一个表usertable)。
这三个函数都是plproxy过程语言的函数,函数中的RUN指令会调用集群节点上的同名函数,所以还需为集群上的数据库节点database0和database1创建同名的函数(只要同名,不需要也是plproxy过程语言定义)。
RUN ON指令之后的的ALL表示在集群上的所有数据库节点上运行(本试验就是database0和databse1都要执行)
RUN ON指令之后的的ANY表示在集群上的任取一个数据库节点上运行(本试验就是database0和databse1中任意取一个执行)
RUN ON 之后也可以是一个数字: 表示在几号节点上运行,比如 RUN ON 0/RUN ON 1。也可以自己写一个算法决定如何分配任务: 比如RUN ON hashtext(表的主键值)&1。表示将表的主键(文本类型)计算出一个hash值,然和1做与运算。就可以根据主键分配数据库节点。
第十二步:为database0和database1节点的”Arvindb”数据库创建plpgsql过程语言
root@ubuntuserver:~# sudo -u postgres psql Arvindb
Arvindb=# create language plpgsql;
或者:sudo -u postgres createlang plpgsql “Arvindb”
创建的原因是:database0和database1节点需要创建一些函数,这些函数是plpgsql过程语言定义的。
第十三步:在database0和database1节点上创建下面三个函数
public.ddlexec(sql_request text)
public.dmlexec(sql_request text)
public.dqlexec(sql_request text)
root@ubuntuserver:~# sudo -u postgres psql Arvindb
创建的SQL语句如下:
CREATE OR REPLACE FUNCTION ddlexec(query text)
RETURNS integer AS
$BODY$
declare
ret integer;
begin
execute query;
return 1;
end;
$BODY$
LANGUAGE 'plpgsql' VOLATILE
COST 100;
CREATE OR REPLACE FUNCTION dmlexec(query text)
RETURNS integer AS
$BODY$
declare
ret integer;
begin
execute query;
return 1;
end;
$BODY$
LANGUAGE 'plpgsql' VOLATILE
COST 100;
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' VOLATILE
COST 100
ROWS 1000;执行后在pgAdmin3客户端观看到的效果是:
第十四步:集群创建user表,在proxy上执行:
root@ubuntuserver:~# sudo -u postgres psql Arvindb
Arvindb=# select ddlexec('create table usertable(id integer)');
第十五步:向集群的user表中插入一些数据,在proxy上执行:
root@ubuntuserver:~# sudo -u postgres psql Arvindb
select dmlexec(‘insert into usertable values(0)’);
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)');
可以通过pgAdmin3看到创建的结果(数据存到那个节点取决于分配算法):
第十六步:查询集群的user表的记录,在proxy上执行:
select * from dqlexec('select * from usertable') as (id integer);
注意:必须要有as之后的内容,原因是, plpgsql过程语言的record返回类型需要有列定义。
可以通过pgAdmin3看到创建的结果:
续:
可能出现的问题 |
解决方案 |
用apt-get安装时,无法找到安装包 |
(1) 更新安装源地址,建议用百度安装源 输入命令 :sudo vim /etc/apt/sources.list(安装源自己找,删除原有代码,复制到里面就可以了) (2) 查看网络是否连接,ip、netmask、getway、dns是否正确配置 输入命令:sudo vim /etc/network/interfaces (3) 更新apt-get 输入命令:apt-get update |
运行引用代码时,提示出现错误 |
(1) 双引号,或单引号使用错误,手动更改为符合要求的。 (2) 丢失分号(;)应特别注意。 |
apt-get安装pgadmin3时,没有出现图形界面 |
(1)建议独立下载pgadmin3并安装 |
……. |
………. |
著:经过作者实践检验,可以正常实施.若有疑问,请加QQ;505024705
或e-mail:hl_linux@yahoo.cn
来源:oschina
链接:https://my.oschina.net/u/588294/blog/66751