Oracle 11g DB-Link 即使在重新创建数据库后连接仍保持打开状态 link
Oracle 11g DB-Link connection stays open even after re-creating database link
我遇到 Oracle DB-Link 连接问题 (Oracle 11g)。让我们考虑以下情况:
- 我们与用户 USER1DATABASE_A 连接到数据库,
- 我们创建了新的私有数据库 link 到 DATABASE_B,连接用户名:USER2
CREATE DATABASE LINK "CHECK_CONNECTION"
CONNECT TO USER2
IDENTIFIED BY "password1"
USING 'DATABASE_B';
- 测试连接失败 - 密码或用户名不正确
SELECT * FROM DUAL@CHECK_CONNECTION
Error at line 1
ORA-01017: invalid username/password; logon denied
- 我们正在更改密码:
DROP DATABASE LINK CHECK_CONNECTION
CREATE DATABASE LINK "CHECK_CONNECTION"
CONNECT TO USER2
IDENTIFIED BY "password2"
USING 'DATABASE_B';
- 连接测试成功
SELECT * FROM DUAL@CHECK_CONNECTION
DUMMY
-----
X
1 row selected.
- 我们再次更改密码,改成旧密码:
DROP DATABASE LINK CHECK_CONNECTION
CREATE DATABASE LINK "CHECK_CONNECTION"
CONNECT TO USER2
IDENTIFIED BY "password1"
USING 'DATABASE_B';
- 尽管密码错误,连接仍然正确:
SELECT * FROM DUAL@CHECK_CONNECTION
DUMMY
-----
X
1 row selected.
- 仅使用更改后的名称创建新的 DB-Link 检测到不正确的连接。
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
知道为什么密码错误但连接正确吗?
来自命令手册 alter session close database link:
When you issue a statement that uses a database link, Oracle Database
creates a session for you on the remote database using that link. The
connection remains open until you end your local session...
Oracle 不会在每次使用数据库 link 时都重新连接,这很有用。但是,即使数据库 link 已更改,保持连接仍然存在似乎是一个小错误。我已经证实这在 12c 中仍然会发生。
这应该没什么大不了的,因为数据库 links 应该保持相当静态。就像应用程序不应为每个查询重新连接到数据库的方式一样,数据库会话不应频繁更改 links。
数据库 link 发生了很多奇怪的事情。使您的远程过程尽可能简单。
您需要检查通话计划。有可能第一次是从数据库中取值,第二次调用时是从缓存中读取。举个小例子:
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 link 的定义发生了变化,但基本的 sql 保持不变,因此发生了缓存命中。
@乔恩海勒
您的回答很有帮助 - 改变会话有效,如果将在步骤 6 中使用,在删除和创建适当的数据库之间 link。如果在删除不正确的数据库后使用它将不起作用 link - 仅仅是因为未创建连接。
不管你怎么暗示我,也许我解决问题的想法是不正确的。
It shouldn't be a big deal because database links should remain fairly
static. Just like the way an application should not re-connect to the
database for each query, database sessions should not be changing
links frequently.
我想从级别 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.
我遇到 Oracle DB-Link 连接问题 (Oracle 11g)。让我们考虑以下情况:
- 我们与用户 USER1DATABASE_A 连接到数据库,
- 我们创建了新的私有数据库 link 到 DATABASE_B,连接用户名:USER2
CREATE DATABASE LINK "CHECK_CONNECTION"
CONNECT TO USER2
IDENTIFIED BY "password1"
USING 'DATABASE_B';
- 测试连接失败 - 密码或用户名不正确
SELECT * FROM DUAL@CHECK_CONNECTION
Error at line 1
ORA-01017: invalid username/password; logon denied
- 我们正在更改密码:
DROP DATABASE LINK CHECK_CONNECTION
CREATE DATABASE LINK "CHECK_CONNECTION"
CONNECT TO USER2
IDENTIFIED BY "password2"
USING 'DATABASE_B';
- 连接测试成功
SELECT * FROM DUAL@CHECK_CONNECTION
DUMMY
-----
X
1 row selected.
- 我们再次更改密码,改成旧密码:
DROP DATABASE LINK CHECK_CONNECTION
CREATE DATABASE LINK "CHECK_CONNECTION"
CONNECT TO USER2
IDENTIFIED BY "password1"
USING 'DATABASE_B';
- 尽管密码错误,连接仍然正确:
SELECT * FROM DUAL@CHECK_CONNECTION
DUMMY
-----
X
1 row selected.
- 仅使用更改后的名称创建新的 DB-Link 检测到不正确的连接。
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
知道为什么密码错误但连接正确吗?
来自命令手册 alter session close database link:
When you issue a statement that uses a database link, Oracle Database creates a session for you on the remote database using that link. The connection remains open until you end your local session...
Oracle 不会在每次使用数据库 link 时都重新连接,这很有用。但是,即使数据库 link 已更改,保持连接仍然存在似乎是一个小错误。我已经证实这在 12c 中仍然会发生。
这应该没什么大不了的,因为数据库 links 应该保持相当静态。就像应用程序不应为每个查询重新连接到数据库的方式一样,数据库会话不应频繁更改 links。
数据库 link 发生了很多奇怪的事情。使您的远程过程尽可能简单。
您需要检查通话计划。有可能第一次是从数据库中取值,第二次调用时是从缓存中读取。举个小例子:
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 link 的定义发生了变化,但基本的 sql 保持不变,因此发生了缓存命中。
@乔恩海勒
您的回答很有帮助 - 改变会话有效,如果将在步骤 6 中使用,在删除和创建适当的数据库之间 link。如果在删除不正确的数据库后使用它将不起作用 link - 仅仅是因为未创建连接。
不管你怎么暗示我,也许我解决问题的想法是不正确的。
It shouldn't be a big deal because database links should remain fairly static. Just like the way an application should not re-connect to the database for each query, database sessions should not be changing links frequently.
我想从级别 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.