昨天接到一个开发的需求,内容看起来非常简单。
申请数据库192.168.1.118:1522:TEST下用户APP_TE_FLOW_128赋予对表testore_log的查询权限。。。
貌似这个语句也就几秒钟就可以搞定,直接赋予对象权限,或者角色都可以,
类似grant select on test.testore_log to APP_TE_FLOW_128;
但是这个看似简单的案例,我想用两篇日志来总结,因为里面有许多的内容量,中间的过程也是异常曲折,而且最开始的推论很可能是错误的,然后还可以上升到业务层面。
所以我会按照真实的分析思路来说这个问题,看完之后大家就会明白了。原谅我分析中的错误先。
我直接使用dba账号登录,然后直接使用alter session set current_schema=APP_TE_FLOW_128;然后触发了下面的sql语句。
sys@TEST> select count(*) from testore_log;
select count(*) from testore_log
*
ERROR at line 1:
ORA-00942: table or view does not exist
ORA-02063: preceding line from GCDB
从这个错误可以看出,是使用了db link,但是访问的时候貌似没有访问到。
整个访问的流程类似下面的形式,在ip为118和128都存在一个test用户,两个test用户的数据不同,118库中的APP_TE_FLOW_128访问的一部分数据是118中的test用户,另外一部分是128总的test用户,当然是间接通过flow0这个用户以db link的形式来访问。
在118服务器端做检查
sys@TEST> select * from dba_synonyms where synonym_name=upper('testore_log');
OWNER SYNONYM_NAME TABLE_OWNER TABLE_NAME DB_LINK
------------------------------ ------------------------------ ------------------------------ ------------------------------ ---------
APP_TE_FLOW_128 testore_log TEST testore_log GCDB
可以看到这个同义词很明显是使用了db link为gcdb
进一步查看db link的情况,发现存在这一些和gcdb相关的db link,都是位于不同的用户下。
sys@TEST> SELECT * FROM DBA_DB_LINKS WHERE DB_LINK='GCDB';
OWNER DB_LINK USERNAME HOST CREATED
------------------- -------- --------- ------------------ ------------
PUBLIC GCDB TLCS0 TEST_TEST_CENTER 23-MAY-11
TEST GCDB TEST0 TEST_TEST_CENTER 09-JUN-10
APP_TEST_QUERY_128 GCDB QUERY0 TEST_TEST_CENTER 09-JUN-10
APP_TE_FLOW_128 GCDB FLOW0 TEST_TEST_CENTER 09-JUN-10
APP_TE_SDE_128 GCDB SDE0 TEST_TEST_CENTER 02-JUL-10
然后来得到db link最终的服务信息,最终得到服务器ip即128的服务器
$ tnsping TEST_TEST_CENTER
Attempting to contact (DESCRIPTION = (ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP)(HOST = TESTcenter.cyou.com)(PORT = 1525))) (CONNECT_DATA = (SERVICE_NAME = GCDB)))
$ cat /etc/hosts|grep TESTcenter.cyou.com
192.168.1.128 TESTcenter.cyou.com
好了,简单的检查完成,我们继续测试。
切换到APP_TE_FLOW_128下。
sys@TEST> alter session set current_schema=APP_TE_FLOW_128;
Session altered.
然后查看all_synonyms没有得到任何结果,这是一个疑点。
sys@TEST> select * from all_synonyms where owner='APP_TE_FLOW_128' and synonym_name=upper('testore_log');
no rows selected
尝试得到表结构信息,竟然报错了,说明还是可以访问,只是最终访问不通。
sys@TEST> desc testore_log
ERROR:
ORA-04043: object "TEST"."testore_log" does not exist
ORA-02063: preceding line from GCDB
在128服务器端,切换到flow0这个用户
sys@GCDB> alter session set current_schema=FLOW0;
Session altered.
查看权限都没有问题,都是存在的。
sys@GCDB> SELECT * FROM DBA_TAB_PRIVS WHERE TABLE_NAME=upper('testore_log');
GRANTEE OWNER TABLE_NAME GRANTOR PRIVILEGE GRA HIE
------------------ ------- ---------------- --------- ------------ --- ---
FLOW0 TEST testore_log TEST SELECT NO NO
这是一个疑问,
然后下面的情况就更奇怪了。
在118服务器端,我尝试通过db link来查看gcdb中的数据表的情况。cat基本类似于user_tables
发现118的服务器中存在一个同义词。
sys@TEST> select table_name,table_type from cat@gcdb;
TABLE_NAME TABLE_TYPE
------------------------------ -----------
USER_POINT SYNONYM
然后我通过dba_synonyms去查看这个同义词,竟然又没有db link的关联,着实奇怪。
sys@TEST> select * from dba_synonyms where synonym_name='USER_POINT';
OWNER SYNONYM_NAME TABLE_OWNER TABLE_NAME DB_LINK
------------------------------ ------------------------------ ------------------------------ ------------------------------ ------------------------------
APP_TE_FLOW_128 USER_POINT TEST USER_POINT
为了更进一步验证,直接查看gcdb中的表user_point的rowid,也没有任何问题,如果想进一步验证,其实会发现还是在128库中的。
sys@TEST> select rowid from user_point@gcdb where rownum<2;
ROWID
------------------
AAAPNRAAHAABdzUAAw
那么这个问题就很奇怪了,看起来解释不通啊。所以这个关系理不清楚,压根没法去赋权限。
继续检查。
在128服务器端继续查看,发现确实有对应的这个表,而且赋予了基本的查询权限。
SQL>SELECT * FROM DBA_TAB_PRIVS WHERE TABLE_NAME='USER_POINT';
GRANTEE OWNER TABLE_NAME GRANTOR PRIVILEGE GRA HIE
------------------------------ ------------------------------ -------------------- ------------------------------ ---------------------------------------- --- ---
FLOW0 TEST USER_POINT TEST SELECT NO NO
那么这个问题怎么解释呢,看起来确实是很费神。如果仔细查看前面的线索,其实就会发现一个public的db link其实在暗中操作。
就是最开始我们给出的检查结果。在118的库中确实存在一个public的db link为gcdb.
gc端sys@TEST> SELECT * FROM DBA_DB_LINKS WHERE DB_LINK='GCDB';
OWNER DB_LINK USERNAME HOST CREATED
------------------- -------- --------- ------------------ ------------
PUBLIC GCDB
TLCS0 TEST_TEST_CENTER 23-MAY-11
当然我们就会发现那个用户TLCS0在128的库中也确实存在,一切手续都齐全。
sys@GCDB SELECT * FROM DBA_SYNONYMS WHERE OWNER='TLCS0';
OWNER SYNONYM_NAME TABLE_OWNER TABLE_NAME DB_LINK
------------------------------ ------------------------------ ------------------------------ -------------------- ------------------------------
TLCS0 USER_POINT TEST USER_POINT
所以大体通过这个Public的db link我们基本明白了,为什么会出现这种奇怪的现象。
那么问题来了,为什么APP_TE_FLOW_128中的db link没有起作用呢,一种很大的可能性就是这个db link有问题。
首选从dba_users中查到加密后的密码。
sys@GCDB select username,password from dba_users where username='FLOW0';
USERNAME PASSWORD
------------------------------ ------------------------------
FLOW0 BCF5E83CF6EF0269
因为这个db link创建的时间确实很早了,我也压根没法得最终的密码,所以有一种看似不错的方案,那就是使用values的方式来重新创建一个db link来验证一下。这样也不用重新动原来的密码了。
CREATE DATABASE LINK
APP_TE_FLOW_128.GC_NEW_LINK CONNECT TO FLOW0
IDENTIFIED BY VALUES 'BCF5E83CF6EF0269' USING 'TEST_TEST_CENTER'
自认为已经解决问题在望,但是做了一个简单的查询,马上让我有些措手不及。持续了十多秒没有反应,我感觉有些问题,马上终止,然后就收到一个600错误。
sys@TEST> select count(*)from test_20151208@APP_TE_FLOW_128.GC_NEW_LINK;
select count(*)from test_20151208@APP_TE_FLOW_128.GC_NEW_LINK
*
ERROR at line 1:
ORA-00600: internal error code, arguments: [kzdlk_zt2 err], [18446744073709551603], [], [], [], [], [], []
这个问题在mos上查看了一番,发现是一个bug,对于values的方式还是存在一定的问题,也是有惊无险。
ORA-00600: [Kzdlk_zt2 Err] While Selecting Using a Database Link (Doc ID 456320.1)
所以使用values的方式创建db link不通,那么我们只能DIY,重新在128的服务器上创建一个用户,做权限分配,然后链接到118的库中。
假设128中创建的用户为flow
sys@GCDB> grant connect to flow0_new;
Grant succeeded.
然后创建了一个新的db link
sys@TEST> conn cydba/cydba
Connected.
cydba@TEST> create database link flow0_128 connect to flow0_new identified by flow0_new using 'TEST_TEST_CENTER';
Database link created.
但是访问有些问题
select count(*) from test_20151208@flow0_128
就马上调整为了public 的db link
create public database link flow0_128 connect to flow0_new identified by flow0_new using 'TEST_TEST_CENTER';
然后再次验证。这次就没有问题了。
alter session set current_schema=APP_TE_FLOW_128;
cydba@TEST> select count(*)from TEST.testore_log@flow0_128 where rownum<2;
COUNT(*)
----------
1
这个过程的问题明天再来解读。