利用PostgreSQL外部表实现数据库sharding 3ff8
2017-06-26 11:46
253 查看
前言
PostgreSQL9.3开始推出postgres_fdw功能,9.5又实现了外部表可以继承的特性。那么我们就可以像实现分区表一样,利用"外部表继承+约束+规则"来实现数据库的sharding。
原理
传统的普通表之间的继承特性可以实现数据库级别的表分区,而外部表可以继承普通表的特性,则可以实现跨数据库,甚至跨物理节点的表分区。下图是实现数据库sharding的一个逻辑图。
应用
测试场景有4台机器,一台作为本地机用于管理访问外部节点分区,另外3台作为存储表分区的机器。
1. 先在3台外部节点机器上,创建3个表结构相同的普通表,并添加相关约束。
表名后面的数字127,136,144表示节点IP的后三位。
2. 为了让本地机器可以访问外部节点,还需要对这3个外部节点的配置文件postgresql.conf,pg_hba.conf配置一下。
下面操作都是在本地机上进行。
3. 先创建postgres_fdw扩展。
4. 创建可以映射到外部节点普通表的外部表。
5. 创建本地父表,并让上一步创建的外部表继承于父表。
6. 查看外部表的信息。
外部表已经继承了父表test_master。
7. 对父表创建规则,以便向父表中插入数据时,数据能分发到外部表中。
8. 向父表中插入数据测试。
9. 查看select父表的执行计划。
10. 查看3个节点的表的数据存储。
插入的数据已经通过规则和外部表,分发到下面三个节点中去。
性能方面
写入方面,由于采用了规则,性能自然不会很好。这里重点说一下读的性能,对父表做一次条件检索。
可以看出,执行计划是对所有的外部表做的扫描,性能上肯定会大打折扣。传统的普通表实现的分区可以利用继承表的查询优化,对主表进行条件检索时,只对目标子分区进行顺序扫描。所以目前利用外部表实现的数据库的sharding,查询优化还需要做下优化工作。
PostgreSQL9.3开始推出postgres_fdw功能,9.5又实现了外部表可以继承的特性。那么我们就可以像实现分区表一样,利用"外部表继承+约束+规则"来实现数据库的sharding。
原理
传统的普通表之间的继承特性可以实现数据库级别的表分区,而外部表可以继承普通表的特性,则可以实现跨数据库,甚至跨物理节点的表分区。下图是实现数据库sharding的一个逻辑图。
应用
测试场景有4台机器,一台作为本地机用于管理访问外部节点分区,另外3台作为存储表分区的机器。
1. 先在3台外部节点机器上,创建3个表结构相同的普通表,并添加相关约束。
postgres=# create table test_partition_127(id int check (id >= 1 AND id < 100), tt text, tm timestamp); CREATE TABLE
postgres=# create table test_partition_136(id int check (id >= 100 AND id < 200), tt text, tm timestamp); CREATE TABLE
postgres=# create table test_partition_144(id int check (id >= 200 AND id < 300), tt text, tm timestamp); CREATE TABLE
表名后面的数字127,136,144表示节点IP的后三位。
2. 为了让本地机器可以访问外部节点,还需要对这3个外部节点的配置文件postgresql.conf,pg_hba.conf配置一下。
下面操作都是在本地机上进行。
3. 先创建postgres_fdw扩展。
postgres=# create extension postgres_fdw; CREATE EXTENSION
4. 创建可以映射到外部节点普通表的外部表。
postgres=# CREATE SERVER server_remote_127 FOREIGN DATA WRAPPER postgres_fdw OPTIONS(host '192.168.100.127', port '5432', dbname 'postgres'); CREATE SERVER postgres=# CREATE USER MAPPING FOR postgres SERVER server_remote_127 OPTIONS(user 'highgo'); CREATE USER MAPPING postgres=# CREATE FOREIGN TABLE test_partition_foreign_127(id int, tt text, tm timestamp) SERVER server_remote_127 OPTIONS(schema_name 'public', table_name 'test_partition_127'); CREATE FOREIGN TABLE postgres=# postgres=# CREATE SERVER server_remote_136 FOREIGN DATA WRAPPER postgres_fdw OPTIONS(host '192.168.100.136', port '5432', dbname 'postgres'); CREATE SERVER postgres=# CREATE USER MAPPING FOR postgres SERVER server_remote_136 OPTIONS(user 'highgo'); CREATE USER MAPPING postgres=# CREATE FOREIGN TABLE test_partition_foreign_136(id int, tt text, tm timestamp) SERVER server_remote_136 OPTIONS(schema_name 'public', table_name 'test_partition_136'); CREATE FOREIGN TABLE postgres=# postgres=# CREATE SERVER server_remote_144 FOREIGN DATA WRAPPER postgres_fdw OPTIONS(host '192.168.100.144', port '5432', dbname 'postgres'); CREATE SERVER postgres=# CREATE USER MAPPING FOR postgres SERVER server_remote_144 OPTIONS(user 'highgo'); CREATE USER MAPPING postgres=# CREATE FOREIGN TABLE test_partition_foreign_144(id int, tt text, tm timestamp) SERVER server_remote_144 OPTIONS(schema_name 'public', table_name 'test_partition_144'); CREATE FOREIGN TABLE
5. 创建本地父表,并让上一步创建的外部表继承于父表。
postgres=# create table test_master(id int, tt text, tm timestamp); CREATE TABLE postgres=# alter table test_partition_foreign_127 inherit test_master; ALTER TABLE postgres=# alter table test_partition_foreign_136 inherit test_master; ALTER TABLE postgres=# alter table test_partition_foreign_144 inherit test_master; ALTER TABLE
6. 查看外部表的信息。
postgres=# \d test_partition_foreign_127 引用的外部表 "public.test_partition_foreign_127" 栏位 | 类型 | 修饰词 | FDW选项 ------+-----------------------------+--------+--------- id | integer | | tt | text | | tm | timestamp without time zone | | Server: server_remote_127 FDW Options: (schema_name 'public', table_name 'test_partition_127') 继承: test_master postgres=# \d test_partition_foreign_136 引用的外部表 "public.test_partition_foreign_136" 栏位 | 类型 | 修饰词 | FDW选项 ------+-----------------------------+--------+--------- id | integer | | tt | text | | tm | timestamp without time zone | | Server: server_remote_136 FDW Options: (schema_name 'public', table_name 'test_partition_136') 继承: test_master postgres=# \d test_partition_foreign_144 引用的外部表 "public.test_partition_foreign_144" 栏位 | 类型 | 修饰词 | FDW选项 ------+-----------------------------+--------+--------- id | integer | | tt | text | | tm | timestamp without time zone | | Server: server_remote_144 FDW Options: (schema_name 'public', table_name 'test_partition_144') 继承: test_master
外部表已经继承了父表test_master。
7. 对父表创建规则,以便向父表中插入数据时,数据能分发到外部表中。
postgres=# CREATE OR REPLACE RULE insert_rule_test_partition_foreign_127 AS ON INSERT TO test_master WHERE id >= 1 AND id < 100 DO INSTEAD INSERT INTO test_partition_foreign_127 VALUES(NEW.*); CREATE RULE postgres=# postgres=# CREATE OR REPLACE RULE insert_rule_test_partition_foreign_136 AS ON INSERT TO test_master WHERE id >= 100 AND id < 200 DO INSTEAD INSERT INTO test_partition_foreign_136 VALUES(NEW.*); CREATE RULE postgres=# postgres=# CREATE OR REPLACE RULE insert_rule_test_partition_foreign_144 AS ON INSERT TO test_master WHERE id >= 200 AND id < 300 DO INSTEAD INSERT INTO test_partition_foreign_144 VALUES(NEW.*); CREATE RULE
8. 向父表中插入数据测试。
postgres=# insert into test_master select generate_series(1, 299), 'highgo', now(); INSERT 0 0
9. 查看select父表的执行计划。
postgres=# explain analyze select * from test_master; QUERY PLAN --------------------------------------------------------------------------------------------------------- --------------------------------- Append (cost=0.00..438.36 rows=3613 width=44) (actual time=6.364..34.279 rows=299 loops=1) -> Seq Scan on test_master (cost=0.00..0.00 rows=1 width=44) (actual time=0.016..0.016 rows=0 loops= 1) -> Foreign Scan on test_partition_foreign_127 (cost=100.00..146.12 rows=1204 width=44) (actual time= 6.345..6.378 rows=99 loops=1) -> Foreign Scan on test_partition_foreign_136 (cost=100.00..146.12 rows=1204 width=44) (actual time= 21.258..21.707 rows=100 loops=1) -> Foreign Scan on test_partition_foreign_144 (cost=100.00..146.12 rows=1204 width=44) (actual time= 5.327..6.054 rows=100 loops=1) Planning time: 0.617 ms Execution time: 40.256 ms
10. 查看3个节点的表的数据存储。
postgres=# select * from test_partition_127 limit 5; id | tt | tm ----+--------+---------------------------- 1 | highgo | 2017-06-26 11:07:11.816957 2 | highgo | 2017-06-26 11:07:11.816957 3 | highgo | 2017-06-26 11:07:11.816957 4 | highgo | 2017-06-26 11:07:11.816957 5 | highgo | 2017-06-26 11:07:11.816957 (5 行记录) postgres=# explain analyze select * from test_partition_127 ; QUERY PLAN --------------------------------------------------------------------------------------------------------------- Seq Scan on test_partition_127 (cost=0.00..1.99 rows=99 width=19) (actual time=0.026..0.107 rows=99 loops=1) Planning time: 0.094 ms Execution time: 0.238 ms (3 行记录)
postgres=# select * from test_partition_136 limit 5; id | tt | tm -----+--------+---------------------------- 100 | highgo | 2017-06-26 11:07:11.816957 101 | highgo | 2017-06-26 11:07:11.816957 102 | highgo | 2017-06-26 11:07:11.816957 103 | highgo | 2017-06-26 11:07:11.816957 104 | highgo | 2017-06-26 11:07:11.816957 (5 行记录) postgres=# explain analyze select * from test_partition_136; QUERY PLAN ----------------------------------------------------------------------------------------------------------------- Seq Scan on test_partition_136 (cost=0.00..2.00 rows=100 width=19) (actual time=0.022..0.067 rows=100 loops=1) Planning time: 0.088 ms Execution time: 0.152 ms (3 行记录)
postgres=# select * from test_partition_144 limit 5; id | tt | tm -----+--------+---------------------------- 200 | highgo | 2017-06-26 11:07:11.816957 201 | highgo | 2017-06-26 11:07:11.816957 202 | highgo | 2017-06-26 11:07:11.816957 203 | highgo | 2017-06-26 11:07:11.816957 204 | highgo | 2017-06-26 11:07:11.816957 (5 行记录) postgres=# explain analyze select * from test_partition_144; QUERY PLAN ----------------------------------------------------------------------------------------------------------------- Seq Scan on test_partition_144 (cost=0.00..2.00 rows=100 width=19) (actual time=0.039..0.144 rows=100 loops=1) Planning time: 0.083 ms Execution time: 0.403 ms (3 行记录)
插入的数据已经通过规则和外部表,分发到下面三个节点中去。
性能方面
写入方面,由于采用了规则,性能自然不会很好。这里重点说一下读的性能,对父表做一次条件检索。
postgres=# explain analyze select * from test_master where id = 123; QUERY PLAN --------------------------------------------------------------------------------------------------------- -------------------------- Append (cost=0.00..375.51 rows=19 width=44) (actual time=7.614..11.620 rows=1 loops=1) -> Seq Scan on test_master (cost=0.00..0.00 rows=1 width=44) (actual time=0.002..0.002 rows=0 loops= 1) Filter: (id = 123) -> Foreign Scan on test_partition_foreign_127 (cost=100.00..125.17 rows=6 width=44) (actual time=3.3 59..3.359 rows=0 loops=1) -> Foreign Scan on test_partition_foreign_136 (cost=100.00..125.17 rows=6 width=44) (actual time=4.2 48..4.251 rows=1 loops=1) -> Foreign Scan on test_partition_foreign_144 (cost=100.00..125.17 rows=6 width=44) (actual time=4.0 00..4.000 rows=0 loops=1) Planning time: 0.464 ms Execution time: 19.298 ms
可以看出,执行计划是对所有的外部表做的扫描,性能上肯定会大打折扣。传统的普通表实现的分区可以利用继承表的查询优化,对主表进行条件检索时,只对目标子分区进行顺序扫描。所以目前利用外部表实现的数据库的sharding,查询优化还需要做下优化工作。
相关文章推荐
- 利用PostgreSQL 9.5的外部表的继承功能,实现类似数据分片的方法
- TimesTen 应用层数据库缓存学习:18. 利用TimesTen实现Sharding或数据分区
- asp中利用xmlhttp实现远程管理数据库
- 利用数据库的order by实现无限分类
- 利用快照实现两地数据库的同步
- 利用微软WebService技术实现远程数据库存取 利用web服务在不同站点间共享同一数据库
- Delphi利用数据库实现文件打包
- asp中利用数组实现数据库记录的批量录入方法
- 利用配置文件实现SQL-Server与Oralce数据库访问类的转换
- [转]数据库存储过程中利用extproc调用共享程序库/动态链接库,间接可以实现调用操作系统命令
- 利用WebService实现远程数据库存取及不同站点间共享同一数据库
- 利用外部命令Oralce数据库导入导出
- asp中利用数组实现数据库记录的批量录入方法
- .:::::利用Delphi实现两层C/S数据库应用程序设计简介 :::::.
- 利用ISAPI实现向数据库中添加记录
- 利用timestamp实现数据库处理同一记录的时间差异问题
- 在c#中利用dataGrid实现数据库的多表查询,修改,删除
- 利用JOB和SQL邮件功能实现对数据库的定时备分
- 利用JDBC显示数据库详细信息---JSP实现
- Delphi利用数据库实现文件打包