即使重新创建数据库链接,Oracle 11g DB-Link连接仍保持打开状态

时间:2015-02-19 20:32:31

标签: sql oracle oracle11g connection dblink

我遇到Oracle DB-Link连接问题(Oracle 11g)。让我们考虑以下情况:

  1. 我们使用用户USER1
  2. 连接到数据库DATABASE_A
  3. 我们创建了新的私有数据库链接到DATABASE_B,用户名为connecion:USER2
  4. CREATE DATABASE LINK "CHECK_CONNECTION"
    CONNECT TO USER2
    IDENTIFIED BY "password1"
    USING 'DATABASE_B';
    
    1. 测试连接失败 - 密码或用户名不正确
    2. SELECT * FROM DUAL@CHECK_CONNECTION
      
      Error at line 1
      ORA-01017: invalid username/password; logon denied
      
      1. 我们正在更改密码:
      2. DROP DATABASE LINK CHECK_CONNECTION
        
        CREATE DATABASE LINK "CHECK_CONNECTION"
        CONNECT TO USER2
        IDENTIFIED BY "password2"
        USING 'DATABASE_B';
        
        1. 连接测试成功
        2. SELECT * FROM DUAL@CHECK_CONNECTION
          
          DUMMY
          -----
          X   
          1 row selected.
          
          1. 我们正在再次更改密码:
          2. DROP DATABASE LINK CHECK_CONNECTION
            
            CREATE DATABASE LINK "CHECK_CONNECTION"
            CONNECT TO USER2
            IDENTIFIED BY "password1"
            USING 'DATABASE_B';
            
            1. 尽管密码错误,但连接仍然是正确的:
            2. SELECT * FROM DUAL@CHECK_CONNECTION
              
              DUMMY
              -----
              X   
              1 row selected.
              
              1. 仅创建具有更改名称的新DB-Link会检测到错误连接。
              2. CREATE DATABASE LINK "CHECK_CONNECTION_2"
                CONNECT TO USER2
                IDENTIFIED BY "password1"
                USING 'DATABASE_B';
                
                SELECT * FROM DUAL@CHECK_CONNECTION_2
                
                Error at line 1
                ORA-01017: invalid username/password; logon denied
                

                如果密码错误,为什么连接正确?

3 个答案:

答案 0 :(得分:1)

从命令alter session close database link上的手册:

  

当您发出使用数据库链接的语句时,Oracle数据库   使用该链接在远程数据库上为您创建会话。该   连接保持打开状态,直到您结束本地会话...

每次使用数据库链接时,Oracle都不会重新连接。但即使数据库链接发生变化,保持连接仍然存在似乎是一个小错误。我已经证实这仍然发生在12c。

这应该不是什么大问题,因为数据库链接应该保持相当静态。就像应用程序不应该为每个查询重新连接到数据库的方式一样,数据库会话不应经常更改链接。

许多奇怪的事情发生在数据库链接上。让您的远程过程尽可能简单。

答案 1 :(得分:1)

您需要检查通话计划。有可能是第一次从数据库中获取值,但是当第二次调用它时,它是从缓存中读取的。举个小例子:

SQL> set autotrace traceonly;
SQL> select * from dual;


Execution Plan
----------------------------------------------------------
Plan hash value: 272002086

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |     1 |     2 |     2   (0)| 00:00:01 |
|   1 |  TABLE ACCESS FULL| DUAL |     1 |     2 |     2   (0)| 00:00:01 |
--------------------------------------------------------------------------


Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
          3  consistent gets
          2  physical reads
          0  redo size
        522  bytes sent via SQL*Net to client
        523  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

SQL> select * from dual;


Execution Plan
----------------------------------------------------------
Plan hash value: 272002086

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |     1 |     2 |     2   (0)| 00:00:01 |
|   1 |  TABLE ACCESS FULL| DUAL |     1 |     2 |     2   (0)| 00:00:01 |
--------------------------------------------------------------------------


Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
          3  consistent gets
          0  physical reads
          0  redo size
        522  bytes sent via SQL*Net to client
        523  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

SQL>

从迹象中可以明显看出,在第一次调用期间,有2次物理读取。但是当再次调用相同的语句时,有0个物理读取,这意味着结果是从缓存中读取的。

在你的情况下,虽然db链接的定义是更改但基本的sql保持不变,因此发生了缓存命中。

答案 2 :(得分:0)

@Jon Heller

您的回答很有帮助 - 如果将在步骤6中使用,则会在删除和创建正确的数据库链接之间使用会话。如果在丢弃不正确的数据库链接之后使用它,它就无法工作 - 只是因为没有创建连接。

但是你告诉我,也许我解决问题的概念不正确。

  

它不应该是一个大问题,因为数据库链接应该保持公平   静态的。就像应用程序不应该重新连接到的方式一样   对于每个查询的数据库,数据库会话不应该改变   经常链接。

我希望从级别USER1使用PL / SQL过程验证与其他数据库的连接。

CREATE OR REPLACE PROCEDURE USER1.CHECK_CONNECTION_PROC (
   P_ID         IN     NUMBER,
   P_USERNAME   IN     VARCHAR2,
   P_PASSWORD   IN     VARCHAR2,
   P_DATABASE   IN     VARCHAR2,
   P_RESULT        OUT VARCHAR2)
IS
   M_DBLINK_NAME   VARCHAR2 (100) DEFAULT 'CHECK_CONNECTION';
   M_IS_EXISTS     NUMBER;
BEGIN
   EXECUTE IMMEDIATE
         'CREATE DATABASE LINK "'
      || M_DBLINK_NAME
      || '" CONNECT TO '
      || P_USERNAME
      || ' IDENTIFIED BY "'
      || P_PASSWORD
      || '" USING '''
      || P_DATABASE
      || '''';

   EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM DUAL@' || M_DBLINK_NAME;

   P_RESULT := 'PASSED';

   EXECUTE IMMEDIATE 'DROP DATABASE LINK ' || M_DBLINK_NAME;
EXCEPTION
   WHEN LOGIN_DENIED
   THEN
      P_RESULT := 'LOGIN_DENIED';

      EXECUTE IMMEDIATE 'DROP DATABASE LINK ' || M_DBLINK_NAME;
   WHEN OTHERS
   THEN
      P_RESULT := SUBSTR (SQLERRM, 1, 200);

      SELECT SIGN (COUNT (*))
        INTO M_IS_EXISTS
        FROM ALL_DB_LINKS
       WHERE DB_LINK = M_DBLINK_NAME;

      IF M_IS_EXISTS = 1
      THEN
         EXECUTE IMMEDIATE 'DROP DATABASE LINK ' || M_DBLINK_NAME;
      END IF;
END CHECK_CONNECTION_PROC;

DECLARE
   M_RESULT   VARCHAR2 (4000);
BEGIN
   EXECUTE IMMEDIATE 'TRUNCATE TABLE TEMPORARY_TABLE';

   INSERT ALL
     INTO TEMPORARY_TABLE (NUMERIC_VALUE1,
                           VARCHAR_VALUE1,
                           VARCHAR_VALUE2,
                           VARCHAR_VALUE3)
   VALUES (1,
           'USER2',
           'password2',
           'DATABASE_B')
     INTO TEMPORARY_TABLE (NUMERIC_VALUE1,
                           VARCHAR_VALUE1,
                           VARCHAR_VALUE2,
                           VARCHAR_VALUE3)
   VALUES (2,
           'USER3',
           'password3',
           'DATABASE_C')
     INTO TEMPORARY_TABLE (NUMERIC_VALUE1,
                           VARCHAR_VALUE1,
                           VARCHAR_VALUE2,
                           VARCHAR_VALUE3)
   VALUES (3,
           'USER4',
           'password4',
           'DATABASE_D')
      SELECT * FROM DUAL;

   FOR CNT IN (  SELECT TMP.NUMERIC_VALUE1 ID,
                        TMP.VARCHAR_VALUE1 USERNAME,
                        TMP.VARCHAR_VALUE2 PASSWORD,
                        TMP.VARCHAR_VALUE3 DATABASE
                   FROM TEMPORARY_TABLE TMP
               ORDER BY 1)
   LOOP
      CHECK_CONNECTION_PROC (CNT.ID,
                             CNT.USERNAME,
                             CNT.PASSWORD,
                             CNT.DATABASE,
                             M_RESULT);

      UPDATE TEMPORARY_TABLE
         SET VARCHAR_VALUE4 = M_RESULT
       WHERE NUMERIC_VALUE1 = CNT.ID;
   END LOOP;
END;

SELECT TMP.NUMERIC_VALUE1 ID,
       TMP.VARCHAR_VALUE1 USERNAME,
       TMP.VARCHAR_VALUE2 PASSWORD,
       TMP.VARCHAR_VALUE3 DATABASE,
       TMP.VARCHAR_VALUE4 RESULT
  FROM TEMPORARY_TABLE TMP;

ID  USERNAME    PASSWORD     DATABASE     RESULT    
---------------------------------------------------
1   USER2       password2    DATABASE_B   PASSED
2   USER3       password3    DATABASE_C   PASSED
3   USER4       password4    DATABASE_D   PASSED

3 rows selected.

至于现在,除了创建临时DB-Link之外,我还没有找到其他方法来验证其他数据库的用户名和密码的正确性。