由于毕业论文的需要,半个月前,本人开始研究oralce与其他数据库的异构连接。这次从机器选择开始到软件配置,基本是自己一条龙包办了。本人用的是旧的IBM x445服务器,磁盘柜也是旧的IBM EXP400。服务器的两个硬盘做了RAID1,磁盘柜5个硬盘做了RAID5EE。装的操作系统是RHEL5。数据库是ORALCE 11g R2。由于这些与主题关系不大,就不详细说了。本次的oracle数据库主要与三台装mssql和一台装mysql的连接。其实类似的网上不少,但真正能一篇就解决问题的没有,因为完成这个事情,我找了几十个贴,说明这方面的资料不是太全,也没人整理。我觉得作为技术人员,既要有钻研技术的热情和能力,更要有累积知识和帮助他人的热情和能力。
在开始之前,我把我自己连接的oracle数据网关的工作方式描述一遍,因为在这段时间里,本人碰到过两种问题:一种是操作问题,一种是原理问题。其中,操作问题是可以在网上查找得到的,比较外露和容易被发现的问题;而原理问题是要自己领悟的,一般搜索不到的问题,比如在连接mysql的时侯遇到的。而很多时候,我们的一些问题之所以不能解决,是因为知其然,而不知其所以然造成的。
Oracle数据网关,就像一个桥梁,贯通oracle数据库和non-oracle数据库。在配置过程中,我们经常讲到的三个重要文件:第一、tnsnames.ora;第二、lisener.ora;第三、init<sid>.ora。他们是如何工作呢?比如对一个连接数据库的查询,select * from “tablename”@linkdbname;oracle首先从linkdbname开始,通过dba_db_links表查到建表时所属于的tnsname;然后在tnsnames.ora中找到tnsname对应的sid;接着在lisener.ora中找该SID所对应的应用程序(如:PROGRAM=dg4msql或PROGRAM=dg4odbc),而且这个程序到哪找,就要注明oracle_home(ORACLE_HOME=/opt/oracle/product/gw);找到程序之后,oracle会在程序所在目录的admin子目录下找到init<sid>.ora文件,读取里面的连接信息;然后通过连接信息与non-oracle数据库通信。希望我以上的这段,能对大家在异构连接配置中起到蓝图的作用,因为只有明白这个原理,下面的配置就简单多了。
1、oracle与mssql连接
11g R2下载的时候,说是含了gateway,我以为含了dg4msql了,但其实没有。当时很彷徨,但后来在oracle站点上有得下载,真的有点弱。在安装时,我没有把dg4msql装在同一个home里,而是把它安装在另一个home里。网上的没有这么做过,我自己尝试的。这就造成了有两套dg4odbc,就是hs的目录。其实这里没所谓,只要之后的配置(后面再说)的路径选对就可以了,电脑的东西,说白了,就是读写字符串,没什么好神秘的。
主要配置参考了这里:http://guyuanli.itpub.net/post/37743/495409 How to Setup DG4MSQL (Oracle Database Gateway for MS SQL Server) Release 11 on Linux [转帖]。这里简单总结一下,其实是四部曲:
(1)配置数据库所在home里的lisener.ora;
(2)配置数据库所在home里的tnsnames.ora
(3)配置dg4msql所在home(在我这里,跟数据库所在home不一样)的init<sid>.ora
(4)建立数据库连接。
只要细心点,一般都不会有问题,这里提一下,很多网站的写ora文件时,不知道是他的问题还是网站上编辑的问题,每一个分段都是齐头并进的,我试过这种写法,结果是通不过的,因为oracle对ora文件的检测比较严格,大家如果不在行,请参考里面的sample。另外,很多网站,都把init<sid>.ora里的HS_FDS_TRACE_LEVEL设置为off。当然,如果连接成功,是没问题的;但一旦出现错误,尤其是出现:
ORA-28500: connection from ORACLE to a non-Oracle system returned this message:
ORA-02063: preceding line from <dblinkname>
试问这种错误,你能看出问题来吗,只有开了trace功能,看log里的*.trc文件,才能明白个究竟。下面马上告诉你我为什么有这种感想。
2、oracle与mysql连接
我觉得在这次配置过程中,兑现了90%和10%的理论。因为最后的10%的工作量真的用了我90%的时间来研究。
首先要明确,mysql与oracle的连接,使用odbc连接的。但连接的原理,和基本步骤跟上面与mssql是一样的。只不过oracle在找到init<sid>.ora之后,还多了一步,就是找odbc的配置。
大家一定要确保linux的odbc已经与mysql数据库连通后才做下一步,测试语句是:isql <dns>。具体配置我参考了很多网页,包括下面的连接中,都有提到。
还有一点,init<sid>.ora是在hs目录里。我一开始傻到要找一个dg4odbc的目录,结果当然是徒劳的。大家可能半信半疑,但我觉得它的原理是,只要在lisener.ora里写着PROGRAM=dg4odbc,oracle就会自动到hs目录里找。可以这么说,我一开始是连不通的。我一开始的情况跟网上的这个贴一样:
http://topic.csdn.net/u/20091117/16/FEB93486-B6E0-4F80-B5F7-6FA649FE9156.html。oracle 11g与mysql的异构连接(未解决的问题)
怎么搞都不行,反正它就是出:
ORA-28500: connection from ORACLE to a non-Oracle system returned this message:
ORA-02063: preceding line from <dblinkname>
搞死人。谁知道什么错误呢。直到我看到了这篇:
http://www.pythian.com/news/892/3-tips-on-using-dg4odbc-on-64-bit-linux/
3 Tips on Using dg4odbc on 64-bit Linux
说实在,这篇里的tips对我一点用都没有,但它让我想起了开通trace。太伟大了。这里要说明一点,在这个过程中,我的oracle用户一度权限受限,不知道为什么,后来重新赋予权限,trace才出来,上面的贴子里提到他开通后trace出不来,原因可能在权限。因为trace的写入是要有对目录的写权限的。一般只要在安装oracle时,认真建好用户,再安装,并亲切步骤正确,都可以达到这种效果。但我却出现了这么一个小插曲。搞了1天。晕。
话说我开通了trace之后,看到了hs/log下的日志。原来是:
HOSGIP for "HS_FDS_SHAREABLE_NAME" returned "/usr/lib/libmyodbc3.so"
Failed to load ODBC library symbol: /usr/lib/libmyodbc3.so(SQLAllocHandle)。这里说明一下,一开始,我以为我的错误跟上面的贴的错误是同一回事,其实不是,因为他是在64位系统下与32位程序的冲突而造成的,而我这个纯粹是瞎搞造成的(后面会说到)。所以大家要注意了,同一个报错,可能是由多种原因造成的。作为一名技术人员,这个时候一定要清醒。
后来我又发现了这个贴:
http://swik.net/dg4odbc+MySQL
HOW TO ACCESS MYSQL FROM ORACLE WITH ODBC AND SQL
这个贴是我在网上看到的dg4odbc与mysql最全面,对“成功”的文章。为什么“成功”要加双引用,因为对他是成功,但对我,照他的做,仍然不成功。这个贴,对我的启发有两个:一个是mysql-connection-3.51似乎不支持dg4odbc,要升级到mysql-connection-5.16;另一个是mysql与oracle存在字符集的bug。
结果我升级到了libmyodbc5,还是不行,看了日志:
HOSGIP for "HS_FDS_SHAREABLE_NAME" returned "/usr/lib/libmyodbc5.so"
Failed to load ODBC library symbol: /usr/lib/libmyodbc5.so(SQLAllocHandle)。还是错误,为什么,看来这个跟升级不升级是没有关系了,就这样过了一天。晚上在床上慢慢的想连接的过程:oracle——dg4odbc——odbc——mysql,现在oracle——dg4odbc是监听到了,odbc——mysql也成功了,那现在的问题很有可能是出现在dg4odbc——odbc里,而事实上,报错的地方也确实是在init<sid>.ora(这个文件就是dg4odbc——odbc的中介)。而现在老说驱动无法装载,肯能是驱动错。我马上把思路转到了对libmyodbc5.so的身份确认来。其实也是受启发于一个blog主页,里面的HS_FDS_SHAREABLE_NAME=libodbc.so。我一下子有点醒悟过来了,现在是dg4odbc跟odbc的连接,又不是跟mysql的连接。怎么会用libmyodbc.so呢,对,一定是这样。按照这个思路,我修改了init<sid>.ora,HS_FDS_SHAREABLE_NAME=libodbc.so,结果冲出了多天的阴影。
但情况仍然不乐观。Select出错:
[MySQL][ODBC 5.1 Driver][mysqld-5.0.51b-community-log]You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near云云。
但很奇怪,desc (看表结构)却可以,我心中忧喜参半。
我马上想起了mysql与oracle存在字符集的bug。网上多数都说是utf-8的问题。但在我这里,似乎不是,因为我按照上面http://swik.net/dg4odbc+MySQL所说的把mysql数据库的字符集改为latin1,还是不行。前面说过,我把trace On了,但我不知道在哪里看到,原来trace=debug,更能看出问题。那我试试,果不其然,详细多了。我把部分的错误信息在google上一贴(google现在在香港,经常打不开,鄙视一下)。结果出来两个贴,最终达成了我的愿望:
http://database.itags.org/oracle/50220/ Oracle: Database Gateway 11g ODBC connection to mysql problem
http://forums.oracle.com/forums/thread.jspa?messageID=3658618 Thread: Problem with Double Quotes arround Column and Table 
我这里说明一下,我的这个问题,是由于oracle在database link中需要使用双引号作为字段和表名的标识,而在mysql中,却是不允许的(果然是个bug)。结果造成了查询时出问题。不信,大家可以在mysql中是双引号括住字段或表名,看有没有出错信息。
如第一个贴所说的,进入mysql后,输入SET SQL_MODE='ANSI_QUOTES';,再用双引号括住字段或表名,结果是可以查询的,但一旦断开连接,再连上,又不行了。结果有人能出了一个针对session的方面:
DECLARE
ret integer;
c integer;
BEGIN
c := DBMS_HS_PASSTHROUGH.OPEN_CURSOR...mysql;
DBMS_HS_PASSTHROUGH.PARSE...mysql(c, 'SET SESSION SQL_MODE=''ANSI_QUOTES'';'));
ret := DBMS_HS_PASSTHROUGH.EXECUTE_NON_QUERY...mysql(c);
dbms_output.put_line(ret ||' passthrough output');
DBMS_HS_PASSTHROUGH.CLOSE_CURSOR...mysql(c);
END;
不瞒大家说,这段的思路是对的,但结果是错的,…mysql其实是@mysql。这就是为什么我介绍大家看第二个贴的原因,请看:
DECLARE 
ret integer; 
c integer; 
BEGIN 
c := DBMS_HS_PASSTHROUGH.OPEN_CURSOR@mysql; 
DBMS_HS_PASSTHROUGH.PARSE@mysql(c, 'SET SESSION SQL_MODE=''ANSI_QUOTES'';'); 
ret := DBMS_HS_PASSTHROUGH.EXECUTE_NON_QUERY@mysql(c); 
dbms_output.put_line(ret ||' passthrough output'); 
DBMS_HS_PASSTHROUGH.CLOSE_CURSOR@mysql(c); 
END; 
/
(replace mysql with your DB link name)
这个才是正确的!大家可以把它做成一个过程和函数使用,具体请参考
http://cn.forums.oracle.com/forums/thread.jspa?threadID=870261&tstart=114 HSODBC with MySQL large tables  为什么要这么做,正如第一个贴所说的,这样做可以不必改变原数据库以及客户端等字符集。从而做到最低的影响。这里要强调的是,作为一名程序员,一个成熟的程序开发人员,很多时不应该只从自己的可用性出发,而缺少去考虑对周边环境的影响。我也是这样过来的。这就是为什么现在很多系统,在交付客户使用之后,大部分都存在这样那样的小问题。就是这个原因。
我觉得提取数据在session层面上就够了,因为数据仓库的核心不是提取数据,而是分析数据。
我的心路历程在这里就告一段落了,希望能给在这条路上摸索的同行们一点启发。
请大家给点分吧