postgresql分布式数据库方案stado

http://xmarker.blog.163.com/blog/static/22648405720131125560959/

最近一直在寻找postgresql的分布式解决方案,试过pgpool-ii,plproxy,但都不太满意,昨天意外发现了一种开源的分布式postgresql解决方案,stado,前身是enterprise公司开发的gridsql(开源,但现在已经停止更新),今天测试后发现和阿里的cobar有点类似,在此分享下安装和初步试用。

 
(一).下载stado软件,地址为:http://www.stormdb.com/community/stado
 
(二).规划实验环境:
主机: pgtest4 pgtest5 pgtest6
ip地址 10.1.1.13 10.1.1.14 10.1.1.15
操作系统 centos6.4 centos6.4 centos6.4
其中pgtest4、pgtest5、pgtest6都是做数据节点,其中pgtest4还做stado节点。
 
(三).创建stado用户和相关安装目录:
[stado@pgtest4 ~]$ tar -zxvf stado_2_5.tar.gz 
[stado@pgtest4 ~]$ chown -R stado:stadogrp stado
[stado@pgtest4 ~]$ chmod 700  stado/bin/*.sh
[stado@pgtest4 ~]$ chmod 775  stado/log
[stado@pgtest4 ~]$ chmod 755  stado/bin/gs-cmdline.sh 
[stado@pgtest4 ~]$ chmod 600  stado/config/*
因为stado代理节点只在pgtest4上安装就行了,因此只需在pgtest4上安装,我把stado安装在/home/stado下了,当然也可以安装在别处。
 
(四).在每个节点安装postgresql软件及数据库(略)
 
(五)在每个节点数据库中创建用户

创建用户(每个节点):


createuser –d –E stado –U postgres -P

创建.pgpass文件,这样可以不用输密码就能直接连接

/home/postgres@pgtest4$cat .pgpass 
*:*:*:stado:123456
chmod 600 .pgpass

 

(六).在stado节点(pgtest4)上修改配置文件
 
[stado@pgtest4 stado]$ cd /home/stado/stado/config/
[stado@pgtest4 config]$ ls
stado_agent.config  stado.config
[stado@pgtest4 config]$ vim stado.config
xdb.port=6453
xdb.maxconnections=10
xdb.default.dbusername=stado
xdb.default.dbpassword=123456
xdb.default.dbport=5432
xdb.default.threads.pool.initsize=5
xdb.default.threads.pool.maxsize=10
xdb.metadata.database=XDBSYS
xdb.metadata.dbhost=127.0.0.1
xdb.nodecount=3
xdb.node.1.dbhost=pgtest4
xdb.node.2.dbhost=pgtest5
xdb.node.3.dbhost=pgtest6
xdb.coordinator.node=1
修改以上配置即可,其他的可以不用修改。
 
(七).在stado节点(pgtest4)上创建metadata数据库
 
[stado@pgtest4 bin]$ ./gs-createmddb.sh -u admin -p secret
Executed Statement: create table xsystablespaces ( tablespaceid serial, tablespacename varchar(255) not null, ownerid int not null, primary key(tablespaceid))
Executed Statement: create unique index idx_xsystablespaces_1 on xsystablespaces (tablespacename)
Executed Statement: create table xsystablespacelocs ( tablespacelocid int not null, tablespaceid int not null, filepath varchar(1024) not null, nodeid int not null, primary key(tablespacelocid))
Executed Statement: create unique index idx_xsystablespacelocs_1 on xsystablespacelocs (tablespaceid, nodeid)
....
Executed Statement: create unique index idx_xsyschecks_1 on xsyschecks (constid, seqno)
Executed Statement: alter table xsyschecks add foreign key (constid) references xsysconstraints (constid)
User admin is created
 
(八).启动stado
 
[stado@pgtest4 bin]$ ./gs-server.sh
Starting....
 
(九).创建用户数据库
 
[stado@pgtest4 bin]$ ./gs-createdb.sh -d xtest -u admin -p secret -n 1,2,3 
OK
 
登陆各个节点后应该能看到xtest数据库,如
__xtest__N2=# \l
                                      List of databases
      Name      |    Owner     | Encoding |   Collate   |    Ctype    |   Access privileges   
----------------+--------------+----------+-------------+-------------+-----------------------
 __xtest__N2    | stado        | UTF8     | en_US.UTF-8 | en_US.UTF-8 | 
 
(十).登陆数据库
第八步创建用户数据库后,stado会自动启动,这时登陆stado管理界面即可做数据操作:
[stado@pgtest4 bin]$ ./gs-cmdline.sh -d xtest -u admin -p secret
Stado -> show databases;
+----------------------------+
| DATABASE |  STATUS | NODES |
+----------------------------+
| xtest    | Started | 1,2,3 |
+----------------------------+
1 row(s).
创建表:
Stado -> CREATE TABLE mytable1 (col1 INT) PARTITIONING KEY col1 ON ALL;
OK
 
Stado -> INSERT INTO mytable1 VALUES (1);
1 row(s) affected
 
Stado -> INSERT INTO mytable1 VALUES (2);
1 row(s) affected
 
Stado -> SELECT * FROM mytable1;              
+------+
| col1 |
+------+
|    2 |
|    1 |
+------+
2 row(s).
 
Stado -> INSERT INTO mytable1 VALUES (3);
1 row(s) affected
 
Stado ->  SELECT * FROM mytable1;    
+------+
| col1 |
+------+
|    2 |
|    1 |
|    3 |
+------+
3 row(s).
Stado -> show tables;
+----------------------------------------------------+
|  TABLE   | TABLE_PARTITIONING_COLUMN | TABLE_NODES |
+----------------------------------------------------+
| mytable1 | col1                      | 1,2,3       |
+----------------------------------------------------+
1 row(s).
 
以上就是简单的安装及测试,下一篇将会介绍常用命令和操作。
 
http://xmarker.blog.163.com/blog/static/22648405720131126350993/
上篇介绍了stado的安装及配置,本篇简单介绍下常用命令和表分区用法
(一).停止stado :
如我们在pgtest5这个数据节点上查看stado进程,会发现如下进程在连接到真实的数据库上:
[root@pgtest5 ~]# ps -ef|grep stado
postgres  1942  1815  0 10:59 ?        00:00:00 postgres: stado __xtest__N2 10.1.1.13(41465) idle
postgres  1943  1815  0 10:59 ?        00:00:00 postgres: stado __xtest__N2 10.1.1.13(41470) idle
postgres  1944  1815  0 10:59 ?        00:00:00 postgres: stado __xtest__N2 10.1.1.13(41471) idle
postgres  1945  1815  0 10:59 ?        00:00:00 postgres: stado __xtest__N2 10.1.1.13(41472) idle
postgres  1946  1815  0 10:59 ?        00:00:00 postgres: stado __xtest__N2 10.1.1.13(41473) idle
root      2085  2064  0 11:23 pts/1    00:00:00 grep stado
 
关闭stado进程:
[stado@pgtest4 bin]$ ./gs-dbstop.sh -d xtest -u admin -p secret
Database(s) xtest stopped.
 
[root@pgtest5 ~]# ps -ef|grep stado
root      4085  2064  0 18:38 pts/1    00:00:00 grep stado
 
(二).启动stado:
开启stado进程:
[stado@pgtest4 bin]$ ./gs-dbstart.sh -d xtest -u admin -p secret
Database(s) xtest started.
 
[root@pgtest5 ~]# ps -ef|grep stado
postgres  4101  1815  0 18:41 ?        00:00:00 postgres: stado __xtest__N2 10.1.1.13(41496) idle in transaction
postgres  4102  1815  0 18:41 ?        00:00:00 postgres: stado __xtest__N2 10.1.1.13(41499) idle
postgres  4103  1815  0 18:41 ?        00:00:00 postgres: stado __xtest__N2 10.1.1.13(41502) idle
postgres  4104  1815  0 18:41 ?        00:00:00 postgres: stado __xtest__N2 10.1.1.13(41505) idle
postgres  4105  1815  0 18:41 ?        00:00:00 postgres: stado __xtest__N2 10.1.1.13(41507) idle
root      4112  2064  0 18:42 pts/1    00:00:00 grep stado
 
(三)删除一个数据库连接:
[stado@pgtest4 bin]$ ./gs-dbstop.sh -d abc -u admin -p secret
Database(s) abc stopped.
[stado@pgtest4 bin]$ ./gs-dropdb.sh -d abc -u admin -p secret
OK
删除数据库及其链接需要先停止数据库的连接进程,然后才能在数据库中删除。
 
(四)创建一个stado数据库
[stado@pgtest4 bin]$ ./gs-createdb.sh -d abc  -u admin -p secret -n 1,2,3
OK
 
(五)创建一个复制模式的表
Stado -> create table t (id int primary key,name text)  REPLICATED ;
OK
 
Stado -> insert into t values(1,'abc');
1 row(s) affected
 
Stado -> insert into t values(2,'bcd');
1 row(s) affected
 
在各个节点上查看数据:
__xtest__N1=# select * from t;
 id | name 
----+------
  1 | abc
  2 | bcd
(2 rows)
 
__xtest__N2=# select * from t;
 id | name 
----+------
  1 | abc
  2 | bcd
(2 rows)
 
(六)创建一个partition模式的表
Stado -> create table t2 (area_id int,area_name varchar(20),descs text)  partitioning key area_id on all;
OK
 
插入数据:
insert into t2 values(10002,'xishan','test');
insert into t2 values(10002,'xishan','fasdasdf');
insert into t2 values(10002,'xishan','testfasdf');
insert into t2 values(10001,'jiangyin','testfasdf');
insert into t2 values(10003,'yichang','test22');
insert into t2 values(10003,'yichang','test22');
insert into t2 values(10003,'yichang','test2yichang');
insert into t2 values(10003,'yichang','test22fasdfas');
 
Stado -> select * from t2;
+-------------------------------------+
| area_id | area_name |     descs     |
+-------------------------------------+
|   10001 | jiangyin  | test          |
|   10002 | xishan    | test          |
|   10001 | jiangyin  | testfasdf     |
|   10002 | xishan    | test          |
|   10003 | yichang   | test22        |
|   10002 | xishan    | fasdasdf      |
|   10003 | yichang   | test22        |
|   10002 | xishan    | testfasdf     |
|   10003 | yichang   | test2yichang  |
|   10003 | yichang   | test22fasdfas |
+-------------------------------------+
10 row(s).
 
在各个节点查看数据分布:
pgtest4:
__xtest__N1=# select * from t2;
LOG:  statement: select * from t2;
 area_id | area_name |     descs     
---------+-----------+---------------
   10001 | jiangyin  | test
   10001 | jiangyin  | testfasdf
   10003 | yichang   | test22
   10003 | yichang   | test22
   10003 | yichang   | test2yichang
   10003 | yichang   | test22fasdfas
(6 rows)
 
pgtest5:
__xtest__N2=# select * from t2;
 area_id | area_name |   descs   
---------+-----------+-----------
   10002 | xishan    | test
   10002 | xishan    | test
   10002 | xishan    | fasdasdf
   10002 | xishan    | testfasdf
(4 rows)
 
pgtest6:
__xtest__N3=# select * from t2;
 area_id | area_name | descs 
---------+-----------+-------
(0 rows)
发现按area_id分区也没用完全均衡的分区,这个还需要进一步研究。
 
(七)创建一个ROUND ROBIN模式的表
Stado -> create table t3 (area_id int ,name varchar2(30)) ROUND ROBIN ON all;
OK
 
Stado -> insert into t3 values(1001,'jiangyi');
1 row(s) affected
 
Stado ->  insert into t3 values(1002,'xishan');
1 row(s) affected
 
Stado -> insert into t3 values(1003,'yichang');
1 row(s) affected
 
Stado -> select * from t3;
+-------------------+
| area_id |  name   |
+-------------------+
|    1003 | yichang |
|    1001 | jiangyi |
|    1002 | xishan  |
+-------------------+
3 row(s).
在pgtest4上查看:
__xtest__N1=# select * from t3;
LOG:  statement: select * from t3;
 area_id |  name   
---------+---------
    1003 | yichang
(1 row)
__xtest__N2=# select * from t3;
 area_id |  name   
---------+---------
    1001 | jiangyi
(1 rows)
__xtest__N3=# select * from t3;
 area_id |  name  
---------+--------
    1002 | xishan
(1 row)
可以看到,这个是轮训方式进行插入数据的。
 
参考:
 
http://xmarker.blog.163.com/blog/static/2264840572013112105159991/
上两节分别对stado的安装配置及常用命令进行了实验,本篇将进一步对表关联、分库方式进行实验。
(一)创建stado支持的三种类型的表:
根据列值hash分区(partitioning)、轮换分区(roundrobin)、复制模式(replicated),注意,不支持range分区,也就说要按某个列的某些范围值分区是不支持的。
 
Stado -> create table t_partition (id int,name varchar(30)) partitioning key id on all;
OK
 
Stado ->  insert into t_partition  select generate_series(1,100)::int,'mcl'::varchar(30);
100 row(s) affected
 
Stado -> create table t_replicate (id int,name varchar(30)) replicated;
OK
 
Stado -> insert into t_replicate select generate_series(1,100)::int,'mcl'::varchar(30);
100 row(s) affected
 
Stado -> create table t_roundrobin (id int,name varchar(30)) round robin on all;
OK
 
Stado -> insert into t_roundrobin select generate_series(1,100)::int,'mcl'::varchar(30);
100 row(s) affected
 
以上分别建了三种类型的表,都分别插入100条数据
 
(二)三种表关联查询:
Stado -> select * from t_partition a,t_replicate b where a.id=b.id;
+-------------------------+
|  id | name |  id | name |
+-------------------------+
|  11 | mcl  |  11 | mcl  |
|  13 | mcl  |  13 | mcl  |
|  14 | mcl  |  14 | mcl  |
|  22 | mcl  |  22 | mcl  |
....
| 100 | mcl  | 100 | mcl  |
+-------------------------+
100 row(s).
我们看下执行计划:
Stado -> explain select * from t_partition a,t_replicate b where a.id=b.id;
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|                                                                                                    Query Plan                                                                                                    |
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|                                                                                                                                                                                                                  |
|  Step: 0                                                                                                                                                                                                         |
|  -------                                                                                                                                                                                                         |
|  Target: CREATE UNLOGGED TABLE "TMPTT23_1" ( "id" INT, "name" VARCHAR (30))  WITHOUT OIDS                                                                                                                        |
|  Select: SELECT "a"."id" AS "id","a"."name" AS "name" FROM "t_partition" "a"                                                                                                                                     |
|                                                                                                                                                                                                                  |
|                                                                                                                                                                                                                  |
|  Step: 1                                                                                                                                                                                                         |
|  -------                                                                                                                                                                                                         |
|  Select: SELECT "TMPTT23_1"."id" AS "id","TMPTT23_1"."name" AS "name","b"."id" AS "EXPRESSION1","b"."name" AS "EXPRESSION2" FROM "TMPTT23_1"  CROSS JOIN "t_replicate" "b"   WHERE ("TMPTT23_1"."id" = "b"."id") |
|   Drop:                                                                                                                                                                                                          |
|  TMPTT23_1                                                                                                                                                                                                       |
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
12 row(s).
可以看到,join操作分两步,第一步建unlogged 表TMPTT23_1,并插入t_partition的数据,第二步根据TMPTT23_1数据关联t_replicate得到最终数据。
 
同样的,partition表和roundrobin表也可以关联查到同样数据,看下执行计划(和上一步类似):
Stado -> explain select * from t_partition a,t_roundrobin b where a.id=b.id;
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|                                                                                                    Query Plan                                                                                                     |
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|                                                                                                                                                                                                                   |
|  Step: 0                                                                                                                                                                                                          |
|  -------                                                                                                                                                                                                          |
|  Target: CREATE UNLOGGED TABLE "TMPTT25_1" ( "id" INT, "name" VARCHAR (30))  WITHOUT OIDS                                                                                                                         |
|  Select: SELECT "a"."id" AS "id","a"."name" AS "name" FROM "t_partition" "a"                                                                                                                                      |
|                                                                                                                                                                                                                   |
|                                                                                                                                                                                                                   |
|  Step: 1                                                                                                                                                                                                          |
|  -------                                                                                                                                                                                                          |
|  Select: SELECT "TMPTT25_1"."id" AS "id","TMPTT25_1"."name" AS "name","b"."id" AS "EXPRESSION1","b"."name" AS "EXPRESSION2" FROM "TMPTT25_1"  CROSS JOIN "t_roundrobin" "b"   WHERE ("TMPTT25_1"."id" = "b"."id") |
|   Drop:                                                                                                                                                                                                           |
|  TMPTT25_1                                                                                                                                                                                                        |
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
12 row(s).
 
同样的,roundrobin和replicate也可以查出同样的数据:
Stado -> explain  select * from t_roundrobin a,t_replicate b where a.id=b.id;
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|                                                                                                    Query Plan                                                                                                    |
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|                                                                                                                                                                                                                  |
|  Step: 0                                                                                                                                                                                                         |
|  -------                                                                                                                                                                                                         |
|  Target: CREATE UNLOGGED TABLE "TMPTT27_1" ( "id" INT, "name" VARCHAR (30))  WITHOUT OIDS                                                                                                                        |
|  Select: SELECT "a"."id" AS "id","a"."name" AS "name" FROM "t_roundrobin" "a"                                                                                                                                    |
|                                                                                                                                                                                                                  |
|                                                                                                                                                                                                                  |
|  Step: 1                                                                                                                                                                                                         |
|  -------                                                                                                                                                                                                         |
|  Select: SELECT "TMPTT27_1"."id" AS "id","TMPTT27_1"."name" AS "name","b"."id" AS "EXPRESSION1","b"."name" AS "EXPRESSION2" FROM "TMPTT27_1"  CROSS JOIN "t_replicate" "b"   WHERE ("TMPTT27_1"."id" = "b"."id") |
|   Drop:                                                                                                                                                                                                          |
|  TMPTT27_1                                                                                                                                                                                                       |
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
12 row(s).
 
从上可以看出,三种分区方式都支持关联查询
 
(三)group by查询
Stado -> select a.id,count(*) from t_partition a,t_partition2 b where a.id=b.id and b.id<30 group by a.id order by 1;
+---------------+
| id | count(*) |
+---------------+
|  1 |        1 |
|  2 |        1 |
|  3 |        1 |
|  4 |        1 |
|  5 |        1 |
|  6 |        1 |
|  7 |        1 |
|  8 |        1 |
|  9 |        1 |
| 10 |        1 |
| 11 |        1 |
| 12 |        1 |
| 13 |        1 |
| 14 |        1 |
| 15 |        1 |
| 16 |        1 |
| 17 |        1 |
| 18 |        1 |
| 19 |        1 |
| 20 |        1 |
| 21 |        1 |
| 22 |        1 |
| 23 |        1 |
| 24 |        1 |
| 25 |        1 |
| 26 |        1 |
| 27 |        1 |
| 28 |        1 |
| 29 |        1 |
+---------------+
29 row(s).
 
partition表和partition表的关联后group by(注意,t_partition2表和t_partition1表结构一样,数据是后者的一半),最终分成3步完成:
Stado -> explain select a.id,count(*) from t_partition a,t_partition2 b where a.id=b.id and b.id<30 group by a.id order by 1;
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|                                                                                             Query Plan                                                                                              |
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|                                                                                                                                                                                                     |
|  Step: 0                                                                                                                                                                                            |
|  -------                                                                                                                                                                                            |
|  Target: CREATE UNLOGGED TABLE "TMPTT34_1" ( "id" INT)  WITHOUT OIDS                                                                                                                                |
|  Select: SELECT "a"."id" AS "id" FROM "t_partition" "a"                                                                                                                                             |
|                                                                                                                                                                                                     |
|                                                                                                                                                                                                     |
|  Step: 1                                                                                                                                                                                            |
|  -------                                                                                                                                                                                            |
|  Target: CREATE UNLOGGED TABLE "TMPTT34_2" ( "XCOL1" INT, "XCOL2" INT)  WITHOUT OIDS                                                                                                                |
|  Select: SELECT "TMPTT34_1"."id" AS "XCOL1",count(*) AS "XCOL2" FROM "TMPTT34_1"  CROSS JOIN "t_partition2" "b"   WHERE ("b"."id" < 30) AND ("TMPTT34_1"."id" = "b"."id") group by "TMPTT34_1"."id" |
|   Drop:                                                                                                                                                                                             |
|  TMPTT34_1                                                                                                                                                                                          |
|                                                                                                                                                                                                     |
|                                                                                                                                                                                                     |
|  Step: 2                                                                                                                                                                                            |
|  -------                                                                                                                                                                                            |
|  Select: SELECT "XCOL1" AS "id",SUM("XCOL2") AS "EXPRESSION67" FROM "TMPTT34_2"  group by "XCOL1", "XCOL1"                                                                                          |
|   Drop:                                                                                                                                                                                             |
|  TMPTT34_2                                                                                                                                                                                          |
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
20 row(s).
 
(四)怎样实现根据range分库
很多时候我们可能不希望通过roundrobin或者partition 的hash分区方式分库,但由于stado没有提供这种分区,如果我们又有比较明显的分区条件,比如无锡区域的用户都会插入到无锡来,苏州区域的用户都会插入到苏州,这样我们可以用应用程序之间插入到各自对应的后台数据节点,查询时通过stado汇总各个地区的结果集,一样可以实现:
 
 Stado -> create table t_area_record(area_id int,area_name varchar(30),name varchar(30)) partitioning key area_id on all;
OK
 
第一个节点作为江阴的数据库:
__xtest__N1=# insert into t_area_record select 1001,'jiangyin'::varchar(30),generate_series(1,50)||'dba'::varchar(30);
INSERT 0 50
 
第二个节点作为无锡的数据库:
__xtest__N2=# insert into t_area_record select 1002,'wuxi'::varchar(30),generate_series(1,50)||'dba'::varchar(30);
INSERT 0 50
 
第三个节点作为宜兴的数据库:
__xtest__N3=# insert into t_area_record select 1003,'yixing'::varchar(30),generate_series(1,50)||'dba'::varchar(30);
INSERT 0 50
 
然后在总的stado节点查询:
Stado -> select area_id,count(*) from t_area_record group by area_id order by 2;
+--------------------+
| area_id | count(*) |
+--------------------+
|    1003 |       50 |
|    1002 |       50 |
|    1001 |       50 |
+--------------------+
3 row(s).
 
Stado ->   select * from t_area_record order by area_id limit 5;
+----------------------------+
| area_id | area_name | name |
+----------------------------+
|    1001 | jiangyin  | 2dba |
|    1001 | jiangyin  | 3dba |
|    1001 | jiangyin  | 4dba |
|    1001 | jiangyin  | 5dba |
|    1001 | jiangyin  | 1dba |
+----------------------------+
5 row(s).
 
可以看出,这样分库写入,而总库查询正好弥补了stado没有partition by range的缺点,而基于mysql的cobar的分区规则则更丰富和灵活,但cobar不能整个汇总,如在cobar查询select count(*) from  t_area_record会产生三条数据,因为cobar只是简单的把sql语句发到所在的数据节点,然后结果分别返回而不做进一步处理,但stado显然是做了处理的:
Stado -> select count(*) from t_area_record;
+----------+
| count(*) |
+----------+
|      150 |
+----------+
1 row(s).
 
Stado -> explain select count(*) from t_area_record;
+-------------------------------------------------------------------------+
|                               Query Plan                                |
+-------------------------------------------------------------------------+
|                                                                         |
|  Step: 0                                                                |
|  -------                                                                |
|  Target: CREATE UNLOGGED TABLE "TMPTT56_1" ( "XCOL1" INT)  WITHOUT OIDS |
|  Select: SELECT count(*) AS "XCOL1" FROM "t_area_record"                |
|                                                                         |
|                                                                         |
|  Step: 1                                                                |
|  -------                                                                |
|  Select: SELECT SUM("XCOL1") AS "EXPRESSION120" FROM "TMPTT56_1"        |
|   Drop:                                                                 |
|  TMPTT56_1                                                              |
+-------------------------------------------------------------------------+
12 row(s).
 
不过还有更好的方式,结合postgres自带的分区表功能,优化器还能做Constraint Exclusion,也就是约束排除,可以让优化器更聪明的知道哪些字表需要扫描哪个数据节点,不需要扫描的不会再扫那个分区,下次再写,该睡觉了。
 
http://xmarker.blog.163.com/blog/static/2264840572013101062120936/
本文简单介绍postgresql的分库方案plproxy的安装及使用,实际分库以后再深入学习后更新。本实验使用三个centos6.4虚拟机做服务器,ip分别为10.1.1.2、10.1.1.11、10.1.1.12,其中10.1.1.12位plproxy节点,其他两个为数据节点。
1.plproxy的原理(参考德哥的相关文章):
 
postgresql分布式方案plproxy使用 - xmarker - x-marker的博客
 
2.下载软件:
选择最新版本下载即可,目前最新版本为2.5。
 
3.解压软件并进入目录(在plproxy节点安装即可):
/postgres/plproxy-2.5@pgtest4$pwd
/postgres/plproxy-2.5
/postgres/plproxy-2.5@pgtest4$ls
AUTHORS  config  COPYRIGHT  debian  doc  Makefile  META.json  NEWS  plproxy.control  plproxy.so  README  sql  src  test 
 
4.安装:
注意需要用root用户安装,并且还要执行postgres用户的.bash_profile 

 

source /home/postgres/.bash_profile
 
/postgres/plproxy-2.5@pgtest4$make
bison -b src/parser -d src/parser.y
gcc -O2 -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fpic -I/usr/local/pgsql/include/server -I/usr/local/pgsql/include -DNO_SELECT=0 -I. -I. -I/usr/local/pgsql/include/server -I/usr/local/pgsql/include/internal -D_GNU_SOURCE   -c -o src/scanner.o src/scanner.c
gcc -O2 -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fpic -I/usr/local/pgsql/include/server -I/usr/local/pgsql/include -DNO_SELECT=0 -I. -I. -I/usr/local/pgsql/include/server -I/usr/local/pgsql/include/internal -D_GNU_SOURCE   -c -o src/parser.tab.o src/parser.tab.c
....
gcc -O2 -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fpic -shared -o plproxy.so src/scanner.o src/parser.tab.o src/cluster.o src/execute.o src/function.o src/main.o src/query.o src/result.o src/type.o src/poll_compat.o src/aatree.o -L/usr/local/pgsql/lib -Wl,--as-needed -Wl,-rpath,'/usr/local/pgsql/lib',--enable-new-dtags  -L/usr/local/pgsql/lib -lpq
echo "create extension plproxy;" > sql/plproxy.sql
cat sql/plproxy_lang.sql sql/plproxy_fdw.sql > sql/plproxy--2.5.0.sql
touch sql/plproxy--2.3.0--2.5.0.sql
touch sql/plproxy--2.4.0--2.5.0.sql
cat sql/ext_unpackaged.sql > sql/plproxy--unpackaged--2.5.0.sql
/postgres/plproxy-2.5@pgtest4$make install
/bin/mkdir -p '/usr/local/pgsql/lib'
/bin/mkdir -p '/usr/local/pgsql/share/extension'
/bin/mkdir -p '/usr/local/pgsql/share/extension'
/usr/bin/install -c -m 755  plproxy.so '/usr/local/pgsql/lib/plproxy.so'
/usr/bin/install -c -m 644 ./plproxy.control '/usr/local/pgsql/share/extension/'
/usr/bin/install -c -m 644  sql/plproxy--2.5.0.sql sql/plproxy--2.3.0--2.5.0.sql sql/plproxy--2.4.0--2.5.0.sql sql/plproxy--unpackaged--2.5.0.sql'/usr/local/pgsql/share/extension/'

 

 
5.plproxy配置:
在三个节点上分别创建三个库(此库用来做代理,sql分发函数等都在这个库里定义,其中第三个节点的db_plproxy用作plproxy路由):

/postgres/plproxy-2.5@testpg$createdb db_plproxy
postgres=# \l
                                  List of databases
    Name    |  Owner   | Encoding |   Collate   |    Ctype    |   Access privileges  
------------+----------+----------+-------------+-------------+-----------------------
db_plproxy | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | ...

 
创建extension,使其支持plproxy语言:

/home/postgres@pgtest4$psql -f /postgres/plproxy-2.5/sql/plproxy.sql db_plproxy
CREATE EXTENSION

在数据库中创建plpgsql语言:

/home/postgres@pgtest4$createlang plpgsql db_plproxy
createlang: language "plpgsql" is already installed in database "db_plproxy"

按照软件时默认已经安装,说明不需要再创建了
 
在第三个节点的db_plproxy库中创建模式:

create schema plproxy;

 
在第三个节点创建plproxy函数(public模式下):
/home/postgres@pgtest4$psql db_plproxy
psql (9.3.1)
Type "help" for help.
 

CREATE OR REPLACE FUNCTION plproxy.get_cluster_partitions(cluster_name text) RETURNS SETOF text AS $$ IF cluster_name = 'cluster' THEN RETURN NEXT 'dbname=db_plproxy host=10.1.1.2'; RETURN NEXT 'dbname=db_plproxy host=10.1.1.11'; RETURN; END IF; RAISE EXCEPTION 'Unknown cluster'; END; $$ LANGUAGE plpgsql;

此函数是为了得到集群的分区

CREATE OR REPLACE FUNCTION plproxy.get_cluster_version(cluster_name text) RETURNS integer AS $$ BEGIN     IF cluster_name = 'cluster' THEN         RETURN 1;     END IF;     RAISE EXCEPTION 'Unknown cluster'; END; $$ LANGUAGE plpgsql;

此函数是为了得到集群版本

CREATE OR REPLACE FUNCTION plproxy.get_cluster_config(IN cluster_name text, OUT key text, OUT val text) returns setof record as $$ begin key := 'statement_timeout'; val := 60; return next; return; end; $$ language plpgsql;

此函数是为了得到集群配置,以上三个函数都是plproxy内部调用
 
在plproxy节点创建如下函数:

CREATE OR REPLACE FUNCTION ddlexec(query text)   RETURNS SETOF integer AS $BODY$ CLUSTER 'cluster'; RUN ON ALL; $BODY$   LANGUAGE plproxy VOLATILE   COST 100   ROWS 1000; ALTER FUNCTION ddlexec(text)   OWNER TO postgres;

CREATE OR REPLACE FUNCTION dmlexec(query text) 

RETURNS SETOF integer AS
$BODY$
CLUSTER 'cluster';
RUN ON ANY;
$BODY$
  LANGUAGE plproxy VOLATILE
  COST 100
  ROWS 1000;
ALTER FUNCTION dmlexec(text)
  OWNER TO postgres;

   CREATE OR REPLACE FUNCTION dqlexec(query text)
 RETURNS SETOF record AS
$BODY$
CLUSTER 'cluster';
RUN ON ALL;
$BODY$
  LANGUAGE plproxy VOLATILE
  COST 100
  ROWS 1000;
ALTER FUNCTION dqlexec(text)
  OWNER TO postgres;

  上面这三个函数是plproxy内部函数,会调用数据节点同名的函数。
 
在两个数据节点的数据库中创建如下函数:
 
 

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;

====================================测试===================================
测试:在plproxy节点登陆数据库,并执行如下:

 

/home/postgres@pgtest4$psql db_plproxy
 
select ddlexec('create table usertable(id integer)');
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)');
 
节点1数据:

db_plproxy=# select * from usertable;
id
----
  0
  1
  3
  5
  7
  8
  9
10
(8 rows)

节点2数据:

 

db_plproxy=# select * from usertable;
id
----
  2
  4
  6
(3 rows)
但查询复杂sql时还是比较麻烦的,要把所有返回的列声明其类型,当字段多时也是挺繁琐的,比如:

proxy=> select * from dqlexec('select cast(count(userid) as int) as cnt  from user_info ') as (cnt int) ;
  cnt   
--------
125001
125000
(2 rows)

proxy=> select * from dqlexec('select * from user_info where userid=5') as (userid  integer, engname text, cnname text , occupation text, birthday  date , signname text , email text , qq  numeric , crt_time timestamp without time zone , mod_time  timestamp without time zone );
userid | engname | cnname | occupation |  birthday  | signname | email |    qq     |          crt_time          | mod_time 
--------+---------+--------+------------+------------+----------+-------+-----------+----------------------------+----------
      5 | mcl     | test   | DBA        | 1970-01-01 | test     | test  | 276732431 | 2013-11-10 22:59:17.627482 | 
(1 row)

注意上面的count 那条sql,我本意是想查出两个节点总的数据量,结果返回了两条记录,分别输出各自节点的数据量
 
参考:
 
 
http://my.oschina.net/zhangjiawen/blog/180637
最近在玩Hadoop、Hive、PostgreSql,因此结合上一篇文档记录一下本周工作内容,主要是环境的搭建和配置以及数据的生成与导入导出:在VirtualBox中安装CentOS6.4,并且搭建Hadoop1.2.1和Hive0.12.0,然后在CentOS 6.4中安装Postgresql9.3,在Windows7下安装pgAdmin,然后生成一千万条数据记录导入到HDFS上并且能够在Hive进行查询,最后安装Sqoop,将数据从HDFS导入到postgresql中。上篇文档已经记录了如何搭建hadoop平台,这篇将接着记录后一部分工作内容。

1 在CentOS 6.4上安装PostgreSql 9.3

1.1 用yum安装PostgreSql Server:

CentOS 6.4用yum默认安装的PostgreSql版本是8.x,比目前最新的9.3差了一代,需要更新yum repository之后,才能用yum安装。用yum安装的好处是方便、模块化,而且以后比较好卸载。

首先到http://yum.postgresql.org/repopackages.php找到对应操作系统的合适的yum repository版本,我使用的服务器环境是CentOS 6.4 x86_64,找到该版本对用的rpm url后,用rpm安装yum repository:

sudo rpm –i http://yum.postgresql.org/9.3/redhat/rhel-6-x86_64/pgdg-centos93-9.3-1.noarch.rpm

然后就可以通过yum安装PostgreSql 9.3了:

sudo yum install postgresql93-server postgresql93 postgresql93-contrib postgresql93-devel

这几个安装包分别对应的是:

  • postgresql93-server:PostgreSql的服务器端;
  • postgresql93:PostgreSql的客户端程序和库;
  • postgresql93-contrib:一些外部扩展;
  • postgresql93-devel:库和头文件,对于一般使用,这个是可选的;但是如果后面需要以编译源文件的形式安装一些PostgreSql的扩展(例如Multicorn),它们在编译过程中需要这些文件,那么这个一定要有。

安装程序会创建postgres用户和postgres组,为了安全起见,建议将这个用户禁止从远程登录(具体方法请自行查找)。然后设置postgres用户的密码:

sudo passwd postgres

至此,postgresql9.3安装过程完成。

提醒:这个postgres用户是操作系统的用户,和后面将会讲到的数据库登录用户postgres不是一个东西,千万不要混淆。

1.2 设置PostgreSql Server:

1.2.1 首先初始化数据库:

sudo service postgresql-9.3 initdb

对于使用yum安装的PostgreSql Server,默认配置是服务器会将数据库的数据文件放在/var/lib/pgsql/9.3/data目录下,初始化数据库后,这个目录下保存有最基本的数据和配置文件;通常这个目录都是在磁盘的系统分区中,如果服务器有可靠性更高、速度更快的存储空间(例如磁盘阵列),最好把数据文件目录移到那里,移动的方法是:假设有一个磁盘阵列已经挂载在了/dwdata下,我们想将数据文件放在/dwdata/data/postgresql/目录下,则我们需要做如下移动:

sudo mkdir –p /dwdata/data

sudo cp –r /var/lib/pgsql/9.3/data/ /dwdata/data

sudo mv /dwdata/data/data /dwdata/data/postgresql

设置新数据目录及其所有内容的owner和group为postgres:

sudo chowm –r postgres /dwdata/data/postgresql

sudo chgrp –r postgres /dwdata/data/postgresql

1.2.2 然后修改PostgreSql Server的启动配置文件/etc/init.d/postgresql-9.3:

Sudo vi /etc/init.d/postgresql-9.3

找到如下字样的行:

# Set defaults for configuration variables

PGENGINE=/usr/pgsql-9.3/bin

PGPORT=5432

PGDATA=/var/lib/pgsql/9.3/data

PGLOG=/var/lib/pgsql/9.3/pgstartup.log

# Log file for pg_upgrade

PGUPLOG=/var/lib/pgsql/$PGMAJORVERSION/pgupgrade.log

这里面值得改的几个参数及其含义是:

  • PGPORT:PostgreSql Server蹲守的TCP端口,一般用默认值即可;
  • PGDATA:数据文件目录,对于DW生产来说,要指定到磁盘阵列上;
  • PGLOG:PostgreSql Server启动日志的位置,是一个全路径文件名;
  • PGUPLOG:PostgreSql升级日志的位置,也是一个全路径文件名;

这里我需要将其中几个参数修改为新的数据文件目录:

PGDATA=/dwdata/data/postgresql

PGLOG=/dwdata/data/postgresql/pgstartup.log

PGUPLOG=/dwdata/data/postgresql/pgupgrade.log

PostgreSql就会将数据文件和关键的log文件放到创建的数据目录中。

1.2.3 启动和停止PostgreSql Server:

上述设置完成后,就可以用下述命令启动PostgreSql Server了:

sudo service postgresql-9.3 start

下述命令停止PostgreSql Server:

sudo service postgresql-9.3 stop

下述命令检查PostgreSql Server的运行状态:

sudo service postgresql-9.3 status

下述命令重新启动PostgreSql Server:

sudo service postgresql-9.3 restart

1.2.4 配置PostgreSql Server的网络参数:

PostgreSql Server的配置文件在数据文件目录中(在这里即为/dwdata/data/postgresql目录)。最主要的配置文件是postgresql.conf,里面有关于网络、性能、缓存大小等各类设置,对于具备多块网卡的服务器来说,最主要的设置是服务器监听的地址,在配置文件中可以找到如下条目:

#listen_addresses = ‘localhost’ # what IP address(es) to listen on;

# comma-separated list of addresses;

# defaults to ‘localhost’; use ‘*’ for all

# (change requires restart)

一般情况况下,要求PostgreSql Server在所有网卡上均监听,可以将此条目前的“#”注释符去掉,并改为:

listen_addresses=’*’即可监听来自所有网卡的请求。如果需要监听特定的网卡,则可以用网卡的IP地址替换“*”字符。

如需修改监听的端口,则可以找到#port=5432这一条目,去掉“#”注释符并按需要修改之。对于不直接暴露在公网上的服务器,不需要修改这个端口。

上述配置修改后,需要重新启动PostgreSql Server才能生效。

1.2.5 控制PostgreSql Server的访问权限:

为了保证安全,PostgreSql Server在安装后,仅允许来自服务器本机的连接,如果需要对PostgreSql Server进行网络访问(比如用另一台服务器访问,或者用图形化的管理工具从远程管理服务器),就需要对访问权限进行设置。

控制网络访问权限的配置文件是pg_hba.conf,在文件末尾会有如下条目:

# TYPE DATABASE USER ADDRESS METHOD

# “local” is for Unix domain socket connections only

local all all peer

# IPv4 local connections:

host all all 127.0.0.1/32 ident

# IPv6 local connections:

……

为了打开PostgreSql Server的外部访问,需要向文件末尾添加内容。例如:假如我们安装PostgreSql Server的服务器IP地址是192.168.66.23,我们希望该子网内(即IP为192.168.66.*)所有计算机都能连接这台PostgreSql Server,并且都需要用户名/密码登录,则可以添加如下行:

host all all 192.168.66.1/24 md5

这个例子中,“192.168.66.1/24”表示允许所有IP为“192.168.66.*”的计算机对服务器进行访问,“24”即IP的掩码有24个二进制的“1”,就是子网掩码的概念;例如,如果我们希望所有IP为“192.168.*.*”的计算机对服务器进行访问,则可以写为“192.168.1.1/16”。

第一个all表示允许访问所有数据库,第二个all表示允许所有用户名的用户登录;这两个域的设置可以参阅该文件前面长长的说明内容,本文不再赘述。

最后的md5表示用加密后的密码进行用户名/密码匹配的认证,这里的“用户名”是指数据库的用户,而不是操作系统的用户。常用的几种其他认证方式有ident(与操作系统的用户名做映射,采用操作系统的认证,数据库用户与操作系统用户之间的映射在pg_ident.conf文件中配置)、password(在网络传送明文密码认证,不安全)、trust(完全信任)、peer(取客户端操作系统的用户名,一般仅用来做本地连接)。

通常,采用数据库用户的用户名和密码进行认证,即md5。

1.2.6 使用psql命令行界面设置postgres用户的密码:

PostgreSql Server安装后,数据库中会有一个默认的用户叫postgres(注意这里是数据库登陆用户postgres,不是上面提到的操作系统用户postgres),具备管理员级别的权限。如果我们想通过图形化的管理界面登录到PostgreSql Server上对服务器进行管理,就需要先设置这个用户的密码,首先要切换到操作系统的postgres用户,该用户具有使用psql进行数据库管理的权限:

su postgres

然后执行psql,进入PostgreSql Server的命令行界面,提示符是“postgres=#”,这时我们就可以键入PostgreSql的命令了,例如用来修改用户密码的ALTER USER命令,我们将数据库用户postgres的密码修改为abcdefg:

psql

postgres=#ALTER USER postgres with password ‘abcdefg‘;

ALTER ROLE

postgres=#\q

exit

上面一段命令中,粗体蓝色表示我们要键入的操作系统命令,灰色表示psql的提示与输出,蓝色表示我们要键入的PostgreSql命令。psql界面用\q命令退出。

于是PostgreSql Server具备了一个超级用户postgres,其密码是abcdefg。可以从远程用各种客户端工具连接这台服务器了。

1.2.7 在客户端安装图形化管理界面并连接PostgreSql Server:

目前PostgreSql比较成熟的图形化管理界面是PgAdmin,具有Windows、Mac、Linux等多种版本,网站是http://www.pgadmin.org/,支持PostgreSql Server 9.3需要PgAdmin 1.18.0或更高的版本。我们可以用自己的笔记本作为客户端,安装管理界面,前提是笔记本与服务器之间的网络是直接连通的,5432端口能够正常通信。

下载安装都是图形化的。安装完成并运行,由于尚未连接过任何服务器,所以Server Groups中是空的。

点击左上角的“Add a connection to a server”按钮:

 

在弹出的对话框中,填入PostgreSql Server的地址和用户信息:

Name一栏可以自行填写易于识别的名字;Host是PostgreSql Server的机器名或IP地址;Port是Server的端口(默认是5432);Service一栏留空;Maintenance DB留默认的postgres即可;Username填入postgres,这是上面被设定过密码的数据库用户;Password填入上面设定的密码,并勾选Store password。由于我是在Windows下安装PgAdmin来连接虚拟机中CentOS中的postgresql,因此需要利用VirtualBox的端口转发规则:Host是我在Windows下的IP地址(cmd->ipconfig);然后在VirtualBox中设置->网络->端口转发->插入新规则,如下图所示添加端口转发规则,自定义主机端口,子系统IP是CentOS(ifconfig)中的IP地址,子系统端口是postgresql server端口,默认是5432。

 

完成后点击OK按钮,即可看到Server Groups列表中多了一台服务器,点击它,即可一级一级的展开各种数据库对象:

         在使用pgAdmin连接postgresql数据库的过程中,总是出现“端口监听未开启”等的问题连接不上,经过各种排错,我把CentIOS中的防火墙关闭,并且关闭了SELinux功能,同时修改了postgresql的配置文件pg-hba.conf,添加host all all 10.0.2.2/24 md5,这样才最后成功连接。

至此,在CentOSPostgreSql 6上安装PostgreSql 9.3的过程就成功完成了,而且也具有了能够管理该PostgreSql Server的图形化管理界面,可以进行任何服务器管理、监控、用户增删、数据查询、存储过程编写和运行等各类工作了。

 

2 将文件放到HDFS上并使用Hive查询:

现有一份一千万条数据记录的文件FakeData,需要部署到HDFS上,先创建一个文件夹命名为FakeDataDir:hadoop dfs –mkdir FakeDataDir

然后使用命令将文件放到HDFS上:hadoop dfs –put FakeData FakeDataDir,

使用命令查看HDFS下的文件:hadoop dfs –ls

 

         进入hive的所在路径,我的是/usr/local/hive/hive-0.12.0/bin,输入以下命令:

./hive

 

创建外部表:

create external table if not exists fakedata(JULIAN_DATE string, PLATFORM string,…, LOG_ID double) ROW FORMAT DELIMITED FIELDS TERMINATED BY ‘,’ location ‘/user/root/FakeDataDir’

注意由于LOG_ID字段是一个20位整随机数,所以使用double类型。

由于数据文件是由逗号分隔,所以TERMINATED BY ‘,’

最后定位数据文件所在位置,被放在HDFS路径/user/root/FakeDataDir下,注意location只要写文件所在目录即可。

         这样就可以通过hive对数据文件进行查询了,如下图所示:

 

 

3 安装Sqoop1.3.0并将数据从HDFS导入到Postgresql数据库:

首先需要在postgresql中创建表fakedata;然后在CentOS 6.4上安装Sqoop:

yum install sqoop

并且下载postgresql-9.3-1100.jdbc41.jar和hadoop-0.20.2-CDH3B4.tar.gz,放入sqoop所在路径的lib文件夹下/usr/lib/sqoop/lib。同时需要修改配置文件configure-sqoop,将hbase和zookeeper注释掉检查:(除非需要使用HBase等Hadoop组件)

 
 

并且确保sqoop所需的HADOOP_HOME正确指向hadoop安装目录,执行echo $HADOOP_HOME查看HADOOP_HOME是否正确配置。

然后使用命令将之前部署在HDFS上的数据文件导入postgresql:

./sqoop export --connect jdbc:postgresql://localhost:5432/postgres --username postgres --password 123456 --table fakedata--fields-terminated-by ',' --export-dir /user/root/FakeDataDir

这样就可以使用pgAdmin在postgresql中进行查询了。

需要注意的是,sqoop与hadoop可能会出现连接失败,提示:ident authenation failed for user postgres,这时需要修改postgresql的配置文件pg_hba,conf,修改postgresql的认证方式为trust:

local all all trust

host all all 127.0.0.1/32 trust

这样终于能够将postgresql连接到hadoop。

 

       然后我对fakedata文件中的数据进行计算,并需要把计算结果导出到postgresql数据库中,按照如下步骤进行:

1、通过pgAdmin在postgresql中创建表,建表的字段和数据类型要符合计算结果的字段和数据类型。

2、然后在hive中创建相应的表作为存储计算结果的临时表。在浏览器输入localhost:50070,browse the filesystem,hive内部表默认位置在hive安装路径conf下的hive-site.xml中规定,设置为/user/hive/warehouse。输入下面命令建表:

create table success(pv bigint, uv bigint) row format delimited fields terminated by ‘,’;(不设置行分隔符默认为\001,是8进制的ASCII码序始字符SOH,start of header)

show tables;

desc success;

insert into success select sum(pv), count(distinct uv) from testdata group by julian_date;

3、然后通过sqoop将hive的表中的数据导出到postgresql数据库中:

./sqoop export –connect jdbc:postgresql://localhost:5432/postgres –username postgres –password 123456 –table testdata –fields-terminated-by ‘,’ –export-dir /user/hive/warehouse/success

注意:由于是初学者,我在实践过程中在分隔符上栽了大跟头,还是要注意这些命令的细节!这篇文档也不完全是自己完成的,当然要感谢提供我材料和解决方案的技术人员!!!

 
 
 
http://download.csdn.net/detail/machen_smiling/8426217

高阶技术postgreSQL+pgpool+Ubuntu 实现分布式流复制模式

 https://files.cnblogs.com/files/taosim/postgreSQL_pgpool_Ubuntu%E5%AE%9E%E7%8E%B0%E5%88%86%E5%B8%83%E5%BC%8F%E6%B5%81%E5%A4%8D%E5%88%B6%E6%A8%A1%E5%BC%8F.pdf

 

http://www.ithao123.cn/content-244813.html

标题:postgresql分布式方案plproxy使用 

本文简单介绍postgresql的分库方案plproxy的安装及使用,实际分库以后再深入学习后更新。本实验使用三个centos6.4虚拟机做服务器,ip分别为10.1.1.2、10.1.1.11、10.1.1.12,其中10.1.1.12位plproxy节点,其他两个为数据节点。
1.plproxy的原理(参考德哥的相关文章):
 
postgresql分布式方案plproxy使用 - xmarker - x-marker的博客
 
2.下载软件:
http://pgfoundry.org/projects/plproxy/
选择最新版本下载即可,目前最新版本为2.5。
 
3.解压软件并进入目录(在plproxy节点安装即可):
/postgres/plproxy-2.5@pgtest4$pwd
/postgres/plproxy-2.5
/postgres/plproxy-2.5@pgtest4$ls
AUTHORS  config  COPYRIGHT  debian  doc  Makefile  META.json  NEWS  plproxy.control  plproxy.so  README  sql  src  test 
 
4.安装:
注意需要用root用户安装,并且还要执行postgres用户的.bash_profile 

 

source /home/postgres/.bash_profile
 
/postgres/plproxy-2.5@pgtest4$make
bison -b src/parser -d src/parser.y
gcc -O2 -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fpic -I/usr/local/pgsql/include/server -I/usr/local/pgsql/include -DNO_SELECT=0 -I. -I. -I/usr/local/pgsql/include/server -I/usr/local/pgsql/include/internal -D_GNU_SOURCE   -c -o src/scanner.o src/scanner.c
gcc -O2 -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fpic -I/usr/local/pgsql/include/server -I/usr/local/pgsql/include -DNO_SELECT=0 -I. -I. -I/usr/local/pgsql/include/server -I/usr/local/pgsql/include/internal -D_GNU_SOURCE   -c -o src/parser.tab.o src/parser.tab.c
....
gcc -O2 -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fpic -shared -o plproxy.so src/scanner.o src/parser.tab.o src/cluster.o src/execute.o src/function.o src/main.o src/query.o src/result.o src/type.o src/poll_compat.o src/aatree.o -L/usr/local/pgsql/lib -Wl,--as-needed -Wl,-rpath,'/usr/local/pgsql/lib',--enable-new-dtags  -L/usr/local/pgsql/lib -lpq
echo "create extension plproxy;" > sql/plproxy.sql
cat sql/plproxy_lang.sql sql/plproxy_fdw.sql > sql/plproxy--2.5.0.sql
touch sql/plproxy--2.3.0--2.5.0.sql
touch sql/plproxy--2.4.0--2.5.0.sql
cat sql/ext_unpackaged.sql > sql/plproxy--unpackaged--2.5.0.sql
/postgres/plproxy-2.5@pgtest4$make install
/bin/mkdir -p '/usr/local/pgsql/lib'
/bin/mkdir -p '/usr/local/pgsql/share/extension'
/bin/mkdir -p '/usr/local/pgsql/share/extension'
/usr/bin/install -c -m 755  plproxy.so '/usr/local/pgsql/lib/plproxy.so'
/usr/bin/install -c -m 644 ./plproxy.control '/usr/local/pgsql/share/extension/'
/usr/bin/install -c -m 644  sql/plproxy--2.5.0.sql sql/plproxy--2.3.0--2.5.0.sql sql/plproxy--2.4.0--2.5.0.sql sql/plproxy--unpackaged--2.5.0.sql '/usr/local/pgsql/share/extension/'

 

 
5.plproxy配置:
在三个节点上分别创建三个库(此库用来做代理,sql分发函数等都在这个库里定义,其中第三个节点的db_plproxy用作plproxy路由):

/postgres/plproxy-2.5@testpg$createdb db_plproxy
postgres=# l
                                  List of databases
    Name    |  Owner   | Encoding |   Collate   |    Ctype    |   Access privileges  
------------+----------+----------+-------------+-------------+-----------------------
db_plproxy | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | ...

 
创建extension,使其支持plproxy语言:

/home/postgres@pgtest4$psql -f /postgres/plproxy-2.5/sql/plproxy.sql db_plproxy
CREATE EXTENSION

在数据库中创建plpgsql语言:

/home/postgres@pgtest4$createlang plpgsql db_plproxy
createlang: language "plpgsql" is already installed in database "db_plproxy"

按照软件时默认已经安装,说明不需要再创建了
 
在第三个节点的db_plproxy库中创建模式:

create schema plproxy;

 
在第三个节点创建plproxy函数(public模式下):
/home/postgres@pgtest4$psql db_plproxy
psql (9.3.1)
Type "help" for help.
 

CREATE OR REPLACE FUNCTION plproxy.get_cluster_partitions(cluster_name text) RETURNS SETOF text AS $$ IF cluster_name = 'cluster' THEN RETURN NEXT 'dbname=db_plproxy host=10.1.1.2'; RETURN NEXT 'dbname=db_plproxy host=10.1.1.11'; RETURN; END IF; RAISE EXCEPTION 'Unknown cluster'; END; $$ LANGUAGE plpgsql;

此函数是为了得到集群的分区

CREATE OR REPLACE FUNCTION plproxy.get_cluster_version(cluster_name text) RETURNS integer AS $$ BEGIN     IF cluster_name = 'cluster' THEN         RETURN 1;     END IF;     RAISE EXCEPTION 'Unknown cluster'; END; $$ LANGUAGE plpgsql;

此函数是为了得到集群版本

CREATE OR REPLACE FUNCTION plproxy.get_cluster_config(IN cluster_name text, OUT key text, OUT val text) returns setof record as $$ begin key := 'statement_timeout'; val := 60; return next; return; end; $$ language plpgsql;

此函数是为了得到集群配置,以上三个函数都是plproxy内部调用
 
在plproxy节点创建如下函数:

CREATE OR REPLACE FUNCTION ddlexec(query text)   RETURNS SETOF integer AS $BODY$ CLUSTER 'cluster'; RUN ON ALL; $BODY$   LANGUAGE plproxy VOLATILE   COST 100   ROWS 1000; ALTER FUNCTION ddlexec(text)   OWNER TO postgres;

CREATE OR REPLACE FUNCTION dmlexec(query text) 

RETURNS SETOF integer AS
$BODY$
CLUSTER 'cluster';
RUN ON ANY;
$BODY$
  LANGUAGE plproxy VOLATILE
  COST 100
  ROWS 1000;
ALTER FUNCTION dmlexec(text)
  OWNER TO postgres;

   CREATE OR REPLACE FUNCTION dqlexec(query text)
 RETURNS SETOF record AS
$BODY$
CLUSTER 'cluster';
RUN ON ALL;
$BODY$
  LANGUAGE plproxy VOLATILE
  COST 100
  ROWS 1000;
ALTER FUNCTION dqlexec(text)
  OWNER TO postgres;

  上面这三个函数是plproxy内部函数,会调用数据节点同名的函数。
 
在两个数据节点的数据库中创建如下函数:
 
 

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;

====================================测试===================================
测试:在plproxy节点登陆数据库,并执行如下:

 

/home/postgres@pgtest4$psql db_plproxy
 
select ddlexec('create table usertable(id integer)');
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)');
 
节点1数据:

db_plproxy=# select * from usertable;
id
----
  0
  1
  3
  5
  7
  8
  9
10
(8 rows)

节点2数据:

 

db_plproxy=# select * from usertable;
id
----
  2
  4
  6
(3 rows)
但查询复杂sql时还是比较麻烦的,要把所有返回的列声明其类型,当字段多时也是挺繁琐的,比如:

proxy=> select * from dqlexec('select cast(count(userid) as int) as cnt  from user_info ') as (cnt int) ;
  cnt   
--------
125001
125000
(2 rows)

proxy=> select * from dqlexec('select * from user_info where userid=5') as (userid  integer, engname text, cnname text , occupation text, birthday  date , signname text , email text , qq  numeric , crt_time timestamp without time zone , mod_time  timestamp without time zone );
userid | engname | cnname | occupation |  birthday  | signname | email |    qq     |          crt_time          | mod_time 
--------+---------+--------+------------+------------+----------+-------+-----------+----------------------------+----------
      5 | mcl     | test   | DBA        | 1970-01-01 | test     | test  | 276732431 | 2013-11-10 22:59:17.627482 | 
(1 row)

注意上面的count 那条sql,我本意是想查出两个节点总的数据量,结果返回了两条记录,分别输出各自节点的数据量
 
参考:
http://pgfoundry.org/projects/plproxy/
http://blog.163.com/digoal@126/blog/static/163877040201041111304328/
 
 
http://blog.chinaunix.net/uid-15145533-id-2775928.html
目前在数据库的分布方面,有两个不同的方向,一种是数据存储到数据分布算法都完全由自己实现分布式引擎,另一个方向是在底层使用现有成熟的关系型数据库,上层再实现一套分布式引擎的系统。
前一种中,Cassandra就是其突出代表,而后一种,我们公司的mysql+corbar方案就是其突出代表,其他以PostgreSQL数据库为基础的分布式方案有plproxy、pgpool、greenplum、pgmaster等等。
这两种分布式方案各有优缺点:
第一种方案: 
优点: 
在特定的场景上,性能可能更高一些。
缺点:
1. 不够成熟。例如Digg公司就是因为把技术架构从原先成熟的LAMP迁移到Cassandra上去,由于Cassandra技术的不稳定,Digg的技术人员
无法解决这些问题,网站遭遇到了重大的打击,导致了技术副总裁John Quinn的为此而卷铺盖走人。见http://www.dbthink.com/?p=639&cpage=1
2. 由于所有都是由自己来实现,所以程序的实现相当复杂,程序的可维护性不好,需要很高的维护成本。
3. 部分功能不完善。不象传统的关系型数据库有各种各样成熟的备份方案,数据的可靠性不高。
 
第二种方案: 
优点: 
1. 方案成熟。由于底层是使用传统的关系型数据库,而仅是实现上上层做数据分布的部分,所以一般来说这种方案的实现成本低,程序相对简单,所以bug也比较少,运行也比较稳定。
2. 维护成本低。由于低层是传统的关系型数据库,底层的维护与原先的数据库维护完全一样,传统数据库的备份方案可以直接使用。
 
从我个人来讲,虽然第二种方案看起来创新成度不如第一种,但我是比较倾向于使用第二种方案。原因是第二种方案比较成熟,维护成本低。
另外说一说NoSQL,现在NoSQL很火,有点象病毒一样在开发人员中传播,这也很容易理解,因为生活在科技前沿,对于大部分开发人员来讲,是令人兴奋的,但是企业在实施时必须非常谨慎,不要重蹈Digg的覆辙。
 
 
http://www.2cto.com/database/201302/191606.html
postgresql分布式数据库
 
1 分布式事务所用到的两阶段提交协议 
两阶段提交的过程涉及到协调者和参与者。协调者可以看做成事务的发起者,同时也是事务的一个参与者。对于一个分布式事务来说,一个事务是涉及到多个参与者的。具体的两阶段提交的过程如下: 
  www.2cto.com  
第一阶段: 
首先,协调者在自身节点的日志中写入一条的日志记录,然后所有参与者发送消息prepare T,询问这些参与者(包括自身),是否能够提交这个事务; 
参与者在接受到这个prepare T 消息以后,会根据自身的情况,进行事务的预处理,如果参与者能够提交该事务,则会将日志写入磁盘,并返回给协调者一个ready T信息,同时自身进入预提交状态状态;如果不能提交该事务,则记录日志,并返回一个not commit T信息给协调者,同时撤销在自身上所做的数据库改动;参与者能够推迟发送响应的时间,但最终还是需要发送的。 
 
第二阶段: 
协调者会收集所有参与者的意见,如果收到参与者发来的not commit T信息,则标识着该事务不能提交,协调者会将Abort T 记录到日志中,并向所有参与者发送一个Abort T 信息,让所有参与者撤销在自身上所有的预操作。 
如果协调者收到所有参与者发来prepare T信息,那么协调者会将Commit T日志写入磁盘,并向所有参与者发送一个Commit T信息,提交该事务。若协调者迟迟未收到某个参与者发来的信息,则认为该参与者发送了一个VOTE_ABORT信息,从而取消该事务的执行。 
  www.2cto.com  
参与者接收到协调者发来的Abort T信息以后,参与者会终止提交,并将Abort T 记录到日志中;如果参与者收到的是Commit T信息,则会将事务进行提交,并写入记录一般情况下,两阶段提交机制都能较好的运行,当在事务进行过程中,有参与者宕机时,他重启以后,可以通过询问其他参与者或者协调者,从而知道这个事务到底提交了没有。当然,这一切的前提都是各个参与者在进行每一步操作时,都会事先写入日志。 
唯一一个两阶段提交不能解决的困境是:当协调者在发出commit T消息后宕机了,而唯一收到这条命令的一个参与者也宕机了,这个时候这个事务就处于一个未知的状态,没有人知道这个事务到底是提交了还是未提交,从而需要数据库管理员的介入,防止数据库进入一个不一致的状态。当然,如果有一个前提是:所有节点或者网络的异常最终都会恢复,那么这个问题就不存在了,协调者和参与者最终会重启,其他节点最终也会收到commit T的信息。 
2使用两阶段提交注意事项(德哥一篇文章中的建议) 
2.1. 不要使2PC时间过长,因为有2PC存在的话vacuum不能回收垃圾空间(这个我在之前的博客也有写到,哪怕是begin;开着不放都不行)。 
2.2. 2PC时间过长还可能造成强制数据库SHUTDOWN,如 transaction ID wraparound. 
2.3. 2PC时间过长也可能带来锁时间过长的问题。 
2.4. 因此没必要的话建议不要开启prepared transaction,由应用来实现2PC也是不错的选择。 
3 分布式事务到数据文件支持 
Data/fxdb_twophase 
3.1 prepare transaction 
在 prepare transaction 的时候,在数据库的目录的 pg_twophase 文件夹生成state file,文件名为事务的XID.要生成state file的主要原因是,在这一过程中,已完成了资源的释放,把不能释放的记录下来,以便2 commit时候释放. 
3.2 commit prepared 
把state file读出来解析,接着释放资源,之后就是记录日志,并把state file删除. 
 
3.3 总结fxdb_twophase的作用 
当在prepare transaction成功,之后系统挂掉,这时state file已创建成功,保留在硬盘上,当系统重启后,会根据日志和state file重构XA事物,在系统启动完成后,可以接着 commit prepared 或 rollback prepared 这个事物。 
 
postgresql中两阶段提交实现原理 
TwoPhaseStateData 
/* 
* Two Phase Commit shared state.  Access to this struct is protected 
* by TwoPhaseStateLock. 
*/ 
typedef struct TwoPhaseStateData 
/* Head of linked list of free GlobalTransactionData structs */ 
GlobalTransaction freeGXacts; 
 
/* Number of valid prepXacts entries. */ 
int  numPrepXacts; 
 
/* 
* There are max_prepared_xacts items in this array, but C wants a 
* fixed-size array. 
*/ 
GlobalTransaction prepXacts[1];  /* VARIABLE LENGTH ARRAY */ 
} TwoPhaseStateData;  /* VARIABLE LENGTH STRUCT */ 
GlobalTransactionData 
typedef struct GlobalTransactionData 
PGPROC  proc;  /* dummy proc */ 
BackendId dummyBackendId; /* similar to backend id for backends */ 
TimestampTz prepared_at; /* time of preparation */ 
XLogRecPtr prepare_lsn; /* XLOG offset of prepare record */ 
Oid  owner;  /* ID of user that executed the xact */ 
TransactionId locking_xid; /* top-level XID of backend working on xact */ 
bool  valid;  /* TRUE if fully prepared */ 
char  gid[GIDSIZE]; /* The GID assigned to the prepared xact */ 
#ifdef FOUNDER_XDB_SE 
TransactionId xid; 
#endif 
}GlobalTransactionData; 
TwoPhaseFileHeader 
typedef struct TwoPhaseFileHeader 
uint32  magic;  /* format identifier */ 
uint32  total_len;  /* actual file length */ 
TransactionId xid;  /* original transaction XID */ 
Oid  database;  /* OID of database it was in */ 
TimestampTz prepared_at; /* time of preparation */ 
Oid  owner;  /* user running the transaction */ 
int32  nsubxacts;  /* number of following subxact XIDs */ 
int32  ncommitrels; /* number of delete-on-commit rels */ 
int32  nabortrels;  /* number of delete-on-abort rels */ 
int32  ninvalmsgs;  /* number of cache invalidation messages */ 
bool  initfileinval; /* does relcache init file need invalidation? */ 
char  gid[GIDSIZE]; /* GID for transaction */ 
} TwoPhaseFileHeader; 
Variable 
static THREAD_LOCAL TwoPhaseStateData *TwoPhaseState; 
 
4 分布式事务创建 
4.1 Where the transcation id is come from? 
Each global transaction is associated with a global transaction 
identifier (GID). The client assigns a GID to a postgres transaction with the PREPARE TRANSACTION command. 
4.2 Where the transaction is stored in server? 
We keep all active global transactions in a shared memory array.When the PREPARE TRANSACTION command is issued, the GID is reserved for the transaction in the array. This is done before a WAL entry is made, because the reservation checks for duplicate GIDs and aborts the transaction if there already is a global transaction in prepared state with the same GID. 
4.3 global transaction has a dummy PGPROC 
A global transaction (gxact) also has a dummy PGPROC that is entered 
into the ProcArray array; this is what keeps the XID considered 
running by TransactionIdIsInProgress.  It is also convenient as a 
PGPROC to hook the gxact's locks to. 
5 分布式事务Commit 
recptr = XLogInsert(RM_XACT_ID, XLOG_XACT_ABORT_PREPARED, rdata); 
 
/* Always flush, since we're about to remove the 2PC state file */ 
XLogFlush(recptr); 
/* 
* Mark the transaction aborted in clog.  This is not absolutely necessary 
* but we may as well do it while we are here. 
*/ 
TransactionIdAbortTree(xid, nchildren, children); 
 
5分布式事务Recovery 
In order to survive crashes and shutdowns, all prepared transactions must be stored in permanent storage. This includes locking information, pending notifications etc. All that state information is written to the per-transaction state file in the pg_twophase directory. 
5.1RecoverPreparedTransactions 
In order to survive crashes and shutdowns, all prepared transactions must be stored in permanent storage. This includes locking information, pending notifications etc. All that state information is written to the per-transaction state file in the pg_twophase directory. 
5.2RecoverPreparedTransactions 
Scan the pg_twophase directory and reload shared-memory state for each 
prepared transaction 
5.3lock_twophase_standby_recover 
Re-acquire a lock belonging to a transaction that was prepared, when starting up into hot standby mode. 
 
 
http://www.oschina.net/question/109945_13806

支付宝(杭州)正在招聘以下职位,有意向请联系: qq:30673044,

简历请发至邮箱:ivan.li328@gmail.com

Postgresql DBA (15W - 25W)

工作地点: 杭州

职位描述:

1、负责Postgresql数据库架构设计;

2、负责数据库管理维护,监控及性能优化;

3、负责数据库运维标准化,规范化;

4、数据库规范化文档编写及管理;

5、配合公司产品和项目数据库设计;

6、前瞻性数据库技术与解决方案研究。

任职要求:

1、三年以上数据库开发或DBA工作经验

2、精通Postgresql数据库体系架构设计及高可用性解决方案;

3、精通Postgresql数据库管理,备份及性能调优;

4、精通Postgresql事务处理机制;

5、精通Linux系统管理与维护;

6、熟悉Shell/Perl等脚本语言;

7、有海量数据库构架经验者优先。

 

分布式数据库开发工程师(Hadoop)—P6/P7 (20W - 35W)

工作地点:杭州
职位描述:

开发支付宝分布式数据库系统 
协助部门制定产品计划,持续改善产品 


职位要求:
1.有扎实的计算机理论基础, 对数据结构及算法有较强的功底 
2.熟练掌握C++/Java编程语言 
3.对分布式原理有较深的理解 

4.熟悉一种开源关系型数据库的实现(mysql/postgres等) 
5.熟悉MapReduce及Hadoop的使用,能解决Hadoop的复杂问题 
6.熟悉Hive的实现 

 
 
 
 
 
 
posted @ 2015-03-30 18:35  陳聽溪  阅读(2177)  评论(0编辑  收藏  举报