PostgreSQL使用oracle_fdw与Oracle查询性能对比
在PostgreSQL中使用oracle_fdw
安装oracle_fdw插件
- 安装前要求:
- 全部功能要求PostgreSQL9.3及以上
- Oracle client要求10.1以上
- 确保PostgreSQL配置了
--without-ldap - 确保
pg_config在PATH中(用pg_config --pgxs测试) - 设置了ORACLE_HOME环境变量
下载oracle_fdw源代码,
并解压到某个目录下,进入文件目录,执行:
1 | make |
安装完毕后,使用psql连接到PostgreSQL;1
psql -d testdb;
使用PostgreSQL的超级用户配置oralce_fdw:
1 | testdb=# create extension oracle_fdw; |
简单查询,无索引
生成测试数据,创建一个表,并同时添加1百万条数据
1 | create table test_table as select rownum as id,to_char(sysdate + rownum/24/3600, 'yyyy-mm-dd hh24:mi:ss') as inc_datetime,trunc(dbms_random.value(0, 100)) as random_id,dbms_random.string('x', 20) random_string from dual connect by level <= 1000000; |
创建完表后,在原来表的基础上追加记录1
2insert into test_table (ID, INC_DATETIME,RANDOM_ID,RANDOM_STRING) select rownum as id, to_char(sysdate + rownum, 'yyyy-mm-dd hh24:mi:ss') as inc_datetime,trunc(dbms_random.value(0, 100)) as random_id, dbms_random.string('x', 20) random_string from dual connect by level <= 1000000;
commit;
上面SQL是利用了Oracle数据库语法的几个实用小技巧实现的:
利用Oracle特有的“connect by”树形连接语法生成测试记录,“level <= 10”表示要生成10记录;
利用rownum虚拟列生成递增的整数数据;
利用sysdate函数加一些简单运算来生成日期数据,本例中是每条记录的时间加1秒;
利用dbms_random.value函数生成随机的数值型数据,本例中是生成0到100之间的随机整数;
利用dbms_random.string函数生成随机的字符型数据,本例中是生成长度为20的随机字符串,字符串中可以包括字符或数字。
to_char(sysdate + rownum, ‘yyyy-mm-dd hh24:mi:ss’) 这里是转换为字符串,如果该字段的类型为TimeStamp时间戳,那这里可以改写一下方法,转换为时间戳 to_timestamp(sysdate + rownum, ‘yyyy-mm-dd hh24:mi:ss’)
PostgreSQL 创建相应的外部表
1 | testdb=# create foreign table test_table(id int, inc_datetime varchar(19), random_id int, random_string varchar(4000)) server oracle_121_56 options (schema 'DBUSER', table 'TEST_TABLE'); |
- 查询表的行数
- sqlplus
1 | sqlplus dbuser/test@//192.168.121.56:1521/orcl |
1 | SQL> select count(*) from test_table; |
- oracle_fdw
1 | testdb=# select count(*) from test_table; |
查看oracle session中真正执行的sql语句,红框为oracle_fdw执行时,oracle session中执行的sql:
在psql中查看select count(*) from test_table;的执行计划:
可见oracle处理的sql即是查询计划中的oracle query后面的sql
查看网络流量
oracle_fdw的网络流量明显比sqlplus要高出很多。
- 查询某条记录
- sqlplus
1 | SQL> select * from test_table where id= 50; |
- oracle_fdw
1 | testdb=# select * from test_table where id= 50; |
- 使用orderby id获取前5行记录
- sqlplus
1 | SQL> select * from test_table order by id offset 0 rows fetch next 5 rows only; |
- oracle_fdw
1 | testdb=# select * from test_table order by id offset 0 rows fetch next 5 rows only; |
- 使用orderby random_string获取前5行记录
- sqlplus
1 | select * from test_table order by random_string offset 0 rows fetch next 5 rows only; |
- oracle_fdw
1 | testdb=# select * from test_table order by random_string offset 0 rows fetch next 5 rows only; |
oracle_fdw执行时,oracle session中执行的sql

psql中查看执行计划
网络流量对比
简单查询,带索引
创建id索引
1 | create index test_id on test_table(id); |
- 查询某条记录
- sqlplus
1 | SQL> select * from test_table where id = 8222; |
- oracle_fdw
1 | testdb=# select * from test_table where id = 8222; |
- 使用orderby id获取前5行记录
- sqlplus
1 | SQL> select * from test_table order by id offset 0 rows fetch next 5 rows only; |
- oracle_fdw
1 | testdb=# select * from test_table order by id offset 0 rows fetch next 5 rows only; |
创建random_string索引
1 | create index test_string on test_table(random_string); |
- 查询某条记录
- sqlplus
1 | SQL> select * from test_table where random_string='GMX0VD9WY4YPN8T4XDML'; |
- oracle_fdw
1 | testdb=# select * from test_table where random_string='GMX0VD9WY4YPN8T4XDML'; |
- 使用order by random_string获取前5条记录
- sqlplus
1 | SQL> select * from test_table order by random_string offset 0 rows fetch next 5 rows only; |
- oracle_fdw
1 | testdb=# select * from test_table order by random_string offset 0 rows fetch next 5 rows only; |
创建random_id,random_string复合索引
1 | create index test_id_string on test_table(random_id,random_string); |
- 查询某条记录
- sqlplus
1 | SQL> select * from test_table where random_id=74 and random_string='6PP2R3M0K4ARK411VFC3'; |
- oracle_fdw
1 | testdb=# select * from test_table where random_id=74 and random_string='6PP2R3M0K4ARK411VFC3'; |
- orderby
- sqlplus
1 | SQL> select * from test_table order by random_id,random_string offset 0 rows fetch next 5 rows only; |
- oracle_fdw
1 | testdb=# select * from test_table order by random_id,random_string offset 0 rows fetch next 5 rows only; |
关联查询
- sqlplus
1 | select a.user_name,b.fee from users a,phone_fee b where a.user_id=b.user_id and a.user_id=4999998; |
- oracle_fdw
1 | testdb=# select a.user_name,b.fee from users a,phone_fee b where a.user_id=b.user_id and a.user_id=4999998; |
psql的执行计划1
2
3
4
5
6
7
8
9
10
11
12
13
14testdb=# explain select a.user_name,b.fee from users a,phone_fee b where a.user_id=b.user_id and a.user_id=4999998;
QUERY PLAN
-----------------------------------------------------------------------------------------------
-------------------------------------------------------------
Nested Loop (cost=20000.00..52502.50 rows=1000000 width=134)
-> Foreign Scan on users a (cost=10000.00..20000.00 rows=1000 width=134)
Oracle query: SELECT /*f121f12a8cbc233ce7f3a1c56794af92*/ r1."USER_ID", r1."USER_NAME"
FROM "DBUSER"."USERS" r1 WHERE (r1."USER_ID" = 4999998)
-> Materialize (cost=10000.00..20005.00 rows=1000 width=32)
-> Foreign Scan on phone_fee b (cost=10000.00..20000.00 rows=1000 width=32)
Oracle query: SELECT /*d1bc90927d98c625ddda179fef4df315*/ r2."USER_ID", r2."FEE"
FROM "DBUSER"."PHONE_FEE" r2 WHERE (r2."USER_ID" = 4999998)
(6 rows)
orderby
- sqlplus
1 | SQL> select users.user_id,phone_fee.fee,phone_fee.fee_date from users ,phone_fee where users.user_id=phone_fee.user_id order by users.user_id desc offset 0 rows fetch next 5 rows only; |
- oracle_fdw
1 | testdb=# select users.user_id,phone_fee.fee,phone_fee.fee_date from users ,phone_fee where users.user_id=phone_fee.user_id order by users.user_id desc offset 0 rows fetch next 5 rows only; |
psql的执行计划1
2
3
4
5
6
7
8
9
10
11
12
13
14
15testdb=# explain select users.user_id,phone_fee.fee,phone_fee.fee_date from users ,phone_fee where users.user_id=phone_fee.user_id order by users.user_id desc offset 0 rows fetch next 5 rows only;
QUERY PLAN
-----------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------
----------------
Limit (cost=20016.61..20016.62 rows=5 width=36)
-> Sort (cost=20016.61..20019.11 rows=1000 width=36)
Sort Key: users.user_id DESC
-> Foreign Scan (cost=10000.00..20000.00 rows=1000 width=36)
Oracle query: SELECT /*c6de86645f2edcce90957e3283ade08d*/ r1."USER_ID", r2."FEE"
, r2."FEE_DATE" FROM ("DBUSER"."USERS" r1 INNER JOIN "DBUSER"."PHONE_FEE" r2 ON (r1."USER_ID" =
r2."USER_ID"))
(5 rows)
oracle session中执行的sql

流量情况

可见oracle_fdw实质上发送的是连接sql,拿到oracle返回的结果,再进行排序的。因此中间消耗的时间里,网络传输数据的消耗是占很大部分的。
结论
oracle_fdw的查询效率主要受发送给oracle的sql查询结果集的数据量影响的,结果集越大,网络IO消耗越多,对查询的影响越大。