前天维护数据库,在执行某个的脚本时,发现结果与实际情况有所出入。对该脚本进行检查,发现其中包含有一条子查询语句:
SELECT sys_context('userenv','ip_address') from DUAL;
在sqlplus内执行,返回结果为空。经翻阅资料,发现该语句的作用是返回一个上下文的参数值。其中sys_context是一个oracle关键字,用于查询一个命名空间(namespace)中某个参数(parameter)的值。Oracle默认建立的命名空间为“userenv”。该命名空间包含以下参数(摘自Docs Oracle ):
ACTION:模块中的位置标识(应用程序名),通过DBMS_APPLICATION_INFO包或者OCI设置。
AUDITED_CURSORID:返回触发审计的SQL游标标识。该参数在高精度审计环境下总是返回NULL。
AUTHENTICATED_IDENTITY:返回认证用的标识。下列列表中各种用户类型返回其后的值:
- Kerberos认证企业用户:kerberos主体名
- Kerberos认证外部用户:kerberos主体名;等同于Schema名
- SSL认证企业用户:用户PKI证书的识别号
- SSL认证外部用户:用户PKI证书的识别号
- 密钥认证企业用户:昵称;等同于登陆名
- 密钥认证数据库用户:数据库用户名,等同于Schema名
- OS认证外部用户:外部操作系统用户名
- Radius/DCE认证外部用户:方案名
- 携带DN代理:OID识别号
- 携带证书代理:客户的证书识别号
- 携带用户名代理:客户若为本地数据库用户则为数据库用户名,若为企业用户则返回昵称。
- 以密码文件登陆SYSDBA/SYSOPER:登陆名
- 以操作系统认证登陆SYSDBA/SYSOPER:操作系统用户名
AUTHENTICATION_DATA:用于认证登录用户的数据。
AUTHENTICATION_METHOD:认证方式,包括PASSWORD、KERBEROS、SSL、RADIUS、OS、DCE或者NONE。在Password, Kerberos或者SSL认证模式下可用关键字IDENTIFICATION_TYPE区分外部用户和企业用户。
BG_JOB_ID:若本会话是由Oracle数据库后台进程创建的,返回其任务ID;否则返回NULL。
CLIENT_IDENTIFIER:返回某应用通过DBMS_SESSION.SET_IDENTIFIER程序、OCI特性OCI_ATTR_CLIENT_IDENTIFIER或者JAVA类Oracle.jdbc.OracleConnection.setClientIdentifier所设置的标识。该特性用于各类数据库组件,鉴别认证为同一数据库用户的不同轻量级应用用户。
CLIENT_INFO:返回最多64字节的用户会话信息,当应用使用到DBMS_APPLICATION_INFO包时该信息可被保存下来。
CURRENT_BIND:返回高精度审计的绑定变量。
CURRENT_SCHEMA:返回当前Schema中的默认Schema名。该值随着会话中所使用ALTERSESSION SET CURRENT_SCHEMA声明而改变。
CURRENT_SCHEMAID:本会话所使用的默认Schema标识。
CURRENT_SQL/CURRENT_SQLn:CURRENT_SQL返回当前触发高精度审计事件的SQL前4k个字节。CURRENT_SQLn特性返回接下来以4k字节递增的序列,n可以是整数1-7。CURRENT_SQL1返回4k-8k字节;CURRENT_SQL2返回4k至8k字节,以此往下推。该属性只能在高精度审记特性的事件处理器中被指定。
CURRENT_SQL_LENGTH:当前触发高精度审记、行级安全策略功能或事件处理器的SQL声明长度。仅在功能或者事件处理器内有效。
DB_DOMAIN:由初始化参数DB_DOMAIN所指定的数据库域。
DB_NAME:由初始化参数DB_NAME所指定的数据库名。
DB_UNIQUE_NAME:由初始化参数DB_UNIQUE_NAME所指定的数据库名。
ENTRYID:当前审计项号码。高精度审记记录与一般审计记录共享该审计项标识序列。该属性不能用于分布式SQL声明。在一个标准或高精度审记的审计处理器中才能看到准确的审计项标识。
ENTERPRISE_IDENTITY:返回用户在企业范围内的标识:
- 对于企业用户: OID识别号。
- 对于外部用户:外部标识,例如Kerberos主体名,Radius和DCE方案名,操作系统用户名,证书辨析名等。
- 对于本地用户和SYSDBA/SYSOPER登陆:NULL。
下列属性值根据代理方式而有所变化:
- 对于带有识别号的代理:客户的OID识别号。
- 对于带有证书的代理:对于外部用户为客户证书识别号;全局用户为OID识别号。
- 对于带有用户名的代理:若客户为企业用户则为OID识别号;若客户为本地数据库用户则为NULL。
FG_JOB_ID:若当前会话是由客户前台进程创建的,返回任务标识;否则返回NULL。
GLOBAL_CONTEXT_MEMORY:返回在全局访问的情况下SGA中所使用的号码。
GLOBAL_UID:在企业用户安全登陆情况下,返回从Oracle因特网目录中全局用户标识,其余的登陆返回NULL。
HOST:客户端用于连接本服务器的设备名称。
IDENTIFICATION_TYPE:返回数据库中创建该用户Schema的方式。特别指出,其将显示出CREATE/ALTERUSER语法中的IDENTIFIED字句。下述列表中,对于不同Schema创建的语法返回其随后的标识:
- 密码标识: LOCAL
- 外部标识: EXTERNAL
- 全局标识: GLOBAL SHARED
- 使用分辨号作为全局标识: GLOBAL PRIVATE
INSTANCE:本实例的实例ID数目。
INSTANCE_NAME:实例名。
IP_ADDRESS:客户端用于连接本服务器的设备IP地址。
ISDBA:若用户通过操作系统或者密码文件的形式通过DBA特权认证,则返回TURE。
LANG:语言ISO名,是已有参数‘Language’的缩写。
LANGUAGE:你的会话当前的语言和范围,随后是数据库字符集,格式如下:语言_范围.字符集。
MODULE:由DBMS_APPLICATION_INFO包或者OCI设置的应用名(模块)。
NETWORK_PROTOCOL:客户端与服务器之间用来创建连接的连接字符串中'PROTOCOL='所指定的协议名。
NLS_CALENDAR:本会话的日程表。
NLS_CURRENCY:本会话的货币单位。
NLS_DATE_FORMAT:本会话的日期格式。
NLS_DATE_LANGUAGE:用于表示日期的语言。
NLS_SORT:二进制或者语义排序基础。
NLS_TERRITORY:本会话的范围。
OS_USER:初始化该数据库会话的客户端进程,其操作系统用户名。
POLICY_INVOKER:行级别的安全策略功能调用者。
PROXY_ENTERPRISE_IDENTITY:当代理用户为企业用户时,返回OID的用户辨识名(Distinguished Name)。
PROXY_GLOBAL_UID:对于企业用户安全(EUS)代理用户,从Oracle英特网目录中返回的全局用户ID。对其他代理用户返回NULL。
PROXY_USER:为session_user打开当前会话的数据库用户名(代理用户名)。
PROXY_USERID:为session_user打开当前会话的数据库用户ID(代理用户ID)。
SERVER_HOST:返回本实例所在服务器的hostname;
SERVICE_NAME:返回本会话的service_name;
SESSION_USER:对企业用户而言,返回该user的schema. 对于其他用户,返回通过数据库认证的该用户的username。整个会话中该值保持不变。
SESSION_USERID:当前被认证用户的user ID。
SESSIONID:审计会话标识。该属性无法用于分布式SQL声明。
SID:会话号(与会话标识不同)。
STATEMENTID:审计声明标识。STATEMENTID表示给定会话中被审计的SQL声明号码。该特性不能用于分布式SQL声明。只有在标准或高精度审计中通过审计处理器才能看到正确的审计声明标识。
TERMINAL:当前会话客户的操作系统标识。分布式SQL声明中,该特性返回本地会话的标识。在分布式环境中,本特性只支持远程SELECT声明,不支持远程INSERT、UPDATE或者DELETE操作。(该参数返回长度随着不同操作系统而改变。)
其中上述语句的返回值应为客户端连接的IP地址。由于该脚本应在服务器上执行,因此发现该处应返回服务器IP地址,而返回空值意味着无法获取该IP地址。事实上,只要把该语句改为直接填写的IP地址,则脚本顺利通过。
进一步检查,发现该脚本在登陆数据库时使用的命令为conn user/passwd。而其它没有问题的服务器上使用conn user/passwd@IP进行连接。经过上网搜索,发现两种登陆形式确实影响了脚本的执行。
使用TCP连接可以使程序在本地或远程连接Oracle数据库。TCP连接需要对Listener进行配置,主要参数为服务器名(需要DNS)或者服务器IP地址。
LISTENER=
(DESCRIPTION=
(ADDRESS_LIST=
(ADDRESS=(PROTOCOL=tcp)(HOST=ServerName/ServerIP)(PORT=1521))
)
)
以下述方式进行连接:
conn user/passwd@OracleServer
使用IPC可以使程序在本地直接连接Oracle数据库,可以与Oracle共享服务器同时配置。IPC同样需要Listener进行连接。IPC的参数主要为Service Name或者SID。
LISTENER=
(DESCRIPTION=
(ADDRESS_LIST=
(ADDRESS=(PROTOCOL=ipc)(KEY=ServiceName/SID))
)
)
以下述方式进行连接:
conn user/passwd@ServiceName
若只有一个Oracle服务开启,可省略服务名:
conn user/passwd
另外,据Docs Oracle介绍,还有第三种连接模式:Bequeath Protocol。这是一种SQL*NET的协议,无需使用Listener,只能连接到本地独占模式的Oracle数据库。
结论:
本地使用客户端连接服务器时,使用IPC,程序之间直接递交查询申请和结果反馈;相比之下,TCP连接需要经过TCP/IP4层封装解封装。因此,一般情况下,使用IPC约有7%到8%的性能提升。但这样由于不经过TCP/IP协议栈的处理,sys_context无法获取连接客户端的IP地址,从而导致该脚本的无法执行。
解决方法:不使用
sys_context(‘userenv’,’ip_address’) from dual;
改用
select utl_inaddr.get_host_address from dual;