任意选择使用 cx_Oracle 与即时客户端失败
Arbitrary selects failing using cx_Oracle with instant client
我正在尝试使用 cx_Oracle 从 ORACLE 12c 数据库中 select 数据,但出现异常:"cx_Oracle.OperationalError: ORA-03113: end-of-file on communication channel".
我的查询使用 Pycharm(jdbc:oracle:瘦驱动程序)时表现良好。然而,在 python 3.6 中使用 cx_Oracle,查询将失败,除非我将 IN 子句中的 ID 数量从 500 减少到大约 250。Cursor.fetchall() 函数是引发异常的原因.我没有访问数据库的特权来检查锁或负载等事情,但这些可能是问题的原因吗?根据我们的 DBA 的说法,Oracle 数据库服务器没有任何问题,而且由于查询在其他方面工作正常,我倾向于相信它。我也搞砸了客户端 sqlnet.ora,它允许异常最终被抛出而不是永远挂起,但我仍然无法获取数据。
def select(self, query, *args):
cur = self.dbh.cursor()
cur.prepare(query)
try:
cur.execute(None, args)
return cur.fetchall()
# my attempt to handle the issue
except (cx_Oracle.OperationalError, cx_Oracle.DatabaseError) as e:
# cx_Oracle.OperationalError: ORA-03113: end-of-file on communication channel
self.logger.error('Oracle Error: {}'.format(traceback.format_exc()))
raise e
代码这样调用select。为简洁起见,我省略了完整的字符串 ID
ids = ['1', '2', '3', ...]
query = """\
select * from my_table where id in(:0,:1,:2,:3,:4, ...)
"""
self.select(query, *ids)
如果没有占位符(ID 直接放在查询中),查询也会失败。
我希望能够 运行 使用最多 1000 个 ID 的 IN 子句进行任何 select 查询,而不会收到 ORA-03113 异常。
编辑:
我在 ubuntu 18.04.2 上安装了 oracle-instantclient18.5-basic-18.5.0.0.0-3.x86_64.rpm*,cx_Oracle 版本 7.1.2,我正在连接到甲骨文 12.1.0.2.0.
查询在 BMC Software 的 ARS 的基础 table 上。我将开始尝试使用本地 table 结构来复制该问题,但这是一团糟,需要一些时间。如果我能够创建 tables 的本地副本,我不确定我是否能够重现该问题,因为具有不同 ID 的相同查询工作正常。这让它看起来像是数据驱动的,但是,在我将查询减少到 250 个 ID 之后,我将 250 从前半部分交换到后半部分,并获得了相同的成功结果,所以它似乎不仅仅是一个坏行.
我可以在客户端启用更有帮助的日志记录以获取更多信息吗?
Edit2:我还应该补充一点,这个问题不仅仅出现在一个查询中。我已经看到 select 查询到完全不同的 tables 的相同问题。
Edit3:我刚刚发现,通过注释掉我正在 selecting 的一些列也可以使查询工作。像这样的列:
to_char(to_date('1970-01-01','YYYY-MM-DD') + numtodsinterval(EventStart,'SECOND'),'YYYY-MM-DD HH24:MI:SS')
这可能表示达到了某种超时,我的 sqlnet.ora 中可能配置也可能未配置:
DISABLE_OOB=on
SQLNET.RECV_TIMEOUT=60
SQLNET.SEND_TIMEOUT=60
TCP.CONNECT_TIMEOUT=300
SQLNET.OUTBOUND_CONNECT_TIMEOUT=300
ENABLE=BROKEN
TRACE_LEVEL_CLIENT=ADMIN
TRACE_FILE_CLIENT=sqlnet
编辑 4:我尝试了更多东西。
我安装了相同版本的即时客户端,除了在 windows 7 机器上,并且 运行 针对相同数据库实例的相同查询。查询成功。
我还缩小了对于这个特定查询的范围,它将接受 499 个 ID,但接受 500 个 ID 失败。我从查询中注释掉哪个 ID 并不重要。
我还尝试通过使用子 select 来欺骗查询,使其认为 ID 更少:
IN(
select regexp_substr(:0,'[^,]+', 1, level) from dual connect by regexp_substr(:0, '[^,]+', 1, level) is not null
)
我收到错误 "cx_Oracle.DatabaseError: ORA-01460: unimplemented or unreasonable conversion requested",之后我意识到这是有道理的,因为 Oracle 只允许一个字符串最长为 4000 字节。
我想我终于找到了让一切正常运行的方法。我终于遇到了这个 link:
https://ardentperf.com/2010/09/08/mysterious-oracle-net-errors/
事实证明,这解决了我的问题。我仍然无法让 cx_Oracle 接受与 tnsnames.ora 文件格式相同的连接字符串,但我更改了我的代码以暂时引用 tnsnames.ora 如下:
connection_info = {
'user': self.config.get(self.db, 'user'),
'pass': self.config.get(self.db, 'password')
}
connection_string = '{user}/{pass}@TEST'\
.format(**connection_info)
connection = cx_Oracle.connect(connection_string)
我的 tnsnames.ora 包含以下内容:
TEST =
(DESCRIPTION =
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = TCP)(Host = myhost.com)(Port = 1521))
)
(SDU=1024)
(CONNECT_DATA =
(SID=mysid)
)
)
这里的关键是SDU=1024,莫名其妙修复了这个问题。
https://docs.oracle.com/cd/B28359_01/network.111/b28317/sqlnet.htm#NETRF184
上面的文档 link 表明 SDU 的默认值是 8192 字节 (8 KB),我的理解是应该自动协商这个值。这似乎不是这种情况,我不知道过去的默认值是多少。
我正在尝试使用 cx_Oracle 从 ORACLE 12c 数据库中 select 数据,但出现异常:"cx_Oracle.OperationalError: ORA-03113: end-of-file on communication channel".
我的查询使用 Pycharm(jdbc:oracle:瘦驱动程序)时表现良好。然而,在 python 3.6 中使用 cx_Oracle,查询将失败,除非我将 IN 子句中的 ID 数量从 500 减少到大约 250。Cursor.fetchall() 函数是引发异常的原因.我没有访问数据库的特权来检查锁或负载等事情,但这些可能是问题的原因吗?根据我们的 DBA 的说法,Oracle 数据库服务器没有任何问题,而且由于查询在其他方面工作正常,我倾向于相信它。我也搞砸了客户端 sqlnet.ora,它允许异常最终被抛出而不是永远挂起,但我仍然无法获取数据。
def select(self, query, *args):
cur = self.dbh.cursor()
cur.prepare(query)
try:
cur.execute(None, args)
return cur.fetchall()
# my attempt to handle the issue
except (cx_Oracle.OperationalError, cx_Oracle.DatabaseError) as e:
# cx_Oracle.OperationalError: ORA-03113: end-of-file on communication channel
self.logger.error('Oracle Error: {}'.format(traceback.format_exc()))
raise e
代码这样调用select。为简洁起见,我省略了完整的字符串 ID
ids = ['1', '2', '3', ...]
query = """\
select * from my_table where id in(:0,:1,:2,:3,:4, ...)
"""
self.select(query, *ids)
如果没有占位符(ID 直接放在查询中),查询也会失败。
我希望能够 运行 使用最多 1000 个 ID 的 IN 子句进行任何 select 查询,而不会收到 ORA-03113 异常。
编辑: 我在 ubuntu 18.04.2 上安装了 oracle-instantclient18.5-basic-18.5.0.0.0-3.x86_64.rpm*,cx_Oracle 版本 7.1.2,我正在连接到甲骨文 12.1.0.2.0.
查询在 BMC Software 的 ARS 的基础 table 上。我将开始尝试使用本地 table 结构来复制该问题,但这是一团糟,需要一些时间。如果我能够创建 tables 的本地副本,我不确定我是否能够重现该问题,因为具有不同 ID 的相同查询工作正常。这让它看起来像是数据驱动的,但是,在我将查询减少到 250 个 ID 之后,我将 250 从前半部分交换到后半部分,并获得了相同的成功结果,所以它似乎不仅仅是一个坏行.
我可以在客户端启用更有帮助的日志记录以获取更多信息吗?
Edit2:我还应该补充一点,这个问题不仅仅出现在一个查询中。我已经看到 select 查询到完全不同的 tables 的相同问题。
Edit3:我刚刚发现,通过注释掉我正在 selecting 的一些列也可以使查询工作。像这样的列:
to_char(to_date('1970-01-01','YYYY-MM-DD') + numtodsinterval(EventStart,'SECOND'),'YYYY-MM-DD HH24:MI:SS')
这可能表示达到了某种超时,我的 sqlnet.ora 中可能配置也可能未配置:
DISABLE_OOB=on
SQLNET.RECV_TIMEOUT=60
SQLNET.SEND_TIMEOUT=60
TCP.CONNECT_TIMEOUT=300
SQLNET.OUTBOUND_CONNECT_TIMEOUT=300
ENABLE=BROKEN
TRACE_LEVEL_CLIENT=ADMIN
TRACE_FILE_CLIENT=sqlnet
编辑 4:我尝试了更多东西。
我安装了相同版本的即时客户端,除了在 windows 7 机器上,并且 运行 针对相同数据库实例的相同查询。查询成功。
我还缩小了对于这个特定查询的范围,它将接受 499 个 ID,但接受 500 个 ID 失败。我从查询中注释掉哪个 ID 并不重要。
我还尝试通过使用子 select 来欺骗查询,使其认为 ID 更少:
IN(
select regexp_substr(:0,'[^,]+', 1, level) from dual connect by regexp_substr(:0, '[^,]+', 1, level) is not null
)
我收到错误 "cx_Oracle.DatabaseError: ORA-01460: unimplemented or unreasonable conversion requested",之后我意识到这是有道理的,因为 Oracle 只允许一个字符串最长为 4000 字节。
我想我终于找到了让一切正常运行的方法。我终于遇到了这个 link:
https://ardentperf.com/2010/09/08/mysterious-oracle-net-errors/
事实证明,这解决了我的问题。我仍然无法让 cx_Oracle 接受与 tnsnames.ora 文件格式相同的连接字符串,但我更改了我的代码以暂时引用 tnsnames.ora 如下:
connection_info = {
'user': self.config.get(self.db, 'user'),
'pass': self.config.get(self.db, 'password')
}
connection_string = '{user}/{pass}@TEST'\
.format(**connection_info)
connection = cx_Oracle.connect(connection_string)
我的 tnsnames.ora 包含以下内容:
TEST =
(DESCRIPTION =
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = TCP)(Host = myhost.com)(Port = 1521))
)
(SDU=1024)
(CONNECT_DATA =
(SID=mysid)
)
)
这里的关键是SDU=1024,莫名其妙修复了这个问题。
https://docs.oracle.com/cd/B28359_01/network.111/b28317/sqlnet.htm#NETRF184
上面的文档 link 表明 SDU 的默认值是 8192 字节 (8 KB),我的理解是应该自动协商这个值。这似乎不是这种情况,我不知道过去的默认值是多少。