读CLOB列很慢

时间:2009-01-23 17:54:29

标签: database oracle clob

希望有人可以对我目前使用Oracle数据库的问题有所了解 - 我确信这很简单!!

我已经设法在一个示例中重新创建了这个,所以这里是数据库结构:

CREATE TABLE MyTable(
    ixMyTable NUMBER,
    clobData CLOB
)
/

CREATE OR REPLACE PACKAGE PKGTEST
AS
    PROCEDURE DoSomething(
        cur_OUT OUT SYS_REFCURSOR
        );
END PKGTEST;
/

CREATE OR REPLACE PACKAGE BODY PKGTEST
AS

PROCEDURE DoSomething(
    cur_OUT OUT SYS_REFCURSOR
)
AS
BEGIN
    OPEN cur_OUT FOR 
        SELECT ixMyTable, clobData
        FROM MyTable;
END;

END PKGTEST;
/

GRANT EXECUTE ON PKGTEST TO TEST_ROLE
/

BEGIN
    FOR i IN 1 .. 7000 LOOP
        insert into mytable values (i, TO_CLOB('123456'));
    END LOOP;
END;
/

额外信息:

架构所有者是TEST_SCHEMA

用户是CARL

CARL具有TEST_ROLE角色

鉴于上面的数据库设置,我有一个C#测试应用程序,它使用标准的System.Data.OracleClient.OracleCommand等来执行PKGTEST.DoSomething并将结果抛出到数据网格(DevExpress)。

非常确定网格在这里是不相关的,因为我们使用开源OTL(不幸的是我的部门)通过c ++遇到了同样的问题。

好的,问题....

从开始到网格填充的时间是~35-40s,哎哟。

但是,如果我执行以下操作:

GRANT SELECT ON MyTable TO TEST_ROLE
/

然后再次执行查询,需要5-6秒。

在我看来,这与特权等有关,但我不太确定为什么它仍然可以双向工作?

如果我将程序更改为

,只需将其他内容扔进底池
SELECT ixMyTable, TO_CLOB(TO_NCLOB(clobData))
FROM MyTable;

然后时间是〜5-6s,有或没有额外的SELECT权限。

非常感谢任何指针或直接解决方案!

编辑:

操作系统是Vista x86 Business

Oracle Server是10.2.0.1

Oracle客户端是10.2.0.3

编辑:

正如所建议的那样,我尝试从MS OracleClient更改为ODP.NET,这确实可以根据需要加快速度。

不幸的是,受影响的C#应用​​程序只是一个内部应用程序,用于查看表/运行SPROCS等。

我们的主要交付成果是使用OTL(http://otl.sourceforge.net/otl3_intro.htm)进行数据库访问的C ++应用程序。这不是真正可以在这个时候改变的东西,所以我真的想了解差异的原因,而不必在willy-nilly周围抛出无偿的GRANT SELECT。

如果没有SELECT权限导致完全失败,那么我可能会接受这一点,但是缺少SELECT似乎会导致访问CLOB数据的路径变慢。

我已经标记了3个答案 - 感谢那些 - 但我确实有理由这样做,所以我会为此添加一笔赏金。

P.S。我们真的很想在OCCI开始时使用OCCI,但是由于Oracle始终支持IDE的一个版本,因此我们无法在Visual Studio 2008中很好地使用它。

7 个答案:

答案 0 :(得分:6)

您是否确定每次都从磁盘读取blob,而不是从磁盘缓存中读取第二个及以下内容?

我已经在性能测试中看到了这个问题,特别是在Oracle上,第一次测试运行非常糟糕。然后,有一个小的(并且看似微不足道的变化),性能突然显着提高。但实际上发生的事情是,您查询的数据已加载到缓存中,并且可以以10倍或20倍的速率访问(内存与磁盘)。

执行此测试的正确方法是在查询运行之间退回数据库。如果DBA不允许您为此测试退回测试生产服务器,请在您的计算机上加载Oracle XE的副本。

编辑:或者更好:每次都删除并重新创建表格。你可能会这样做,但没有提到它。

答案 1 :(得分:1)

我会尝试使用odp.net http://www.oracle.com/technology/tech/windows/odpnet/index.html而不是System.Data.OracleClient。

答案 2 :(得分:1)

根据上面的建议,您可以尝试不同的ODBC驱动程序或客户端软件。 TO_CLOB(TO_NCLOB())工作得如此之快的事实,直的似乎并不表明问题存在的地方。

首先,我将采用两个查询并通过SQLDeveloper运行它们并获得解释计划。这应该为您提供在Oracle端执行的基准。添加转换应该对执行路径没有影响。交替运行查询并计算时间以查看它们的速度。如果没有区别,我建议客户端软件是你的问题。

如果我的假设是正确的,这也可以解释GRANT SELECT的变化。客户端软件正在对CLOB进行某种昂贵的转换。授权和/或显式转换允许客户端避免这种情况。我不知道为什么。

答案 3 :(得分:1)

我知道这是一个非常古老的问题

我是根据我使用 Oracle OCCI 库的经验发言的,但我相信这适用于任何 Oracle 客户端。 blob/clob 读操作慢的原因如下

  1. 从具有 blob/clob 列的表中进行选择可防止预取。您可以在 sqlplus 中尝试使用 set arraysize 1 & set arraysize 5000 来处理带有 & 不带 blob/clob 列的表。对于具有大量记录且没有 blob/clob 列的表,arraysize 会产生很大的不同。对于带有 blob/clob 列的表,它不会有太大区别。因此读取每条记录将往返于服务器。在较慢的网络(例如 WAN)上,这会产生很大影响
  2. 获取 blob/clob 列需要分别获取定位器和数据。所以这是到服务器的 2 次往返。现在将它与 #1 结合起来,你就会明白为什么这很慢

请看这里。 https://docs.oracle.com/cd/E18283_01/appdev.112/e10646/oci07lob.htm#CHDDHFAB

您可以通过为 LOB 启用行预取来缓解这种情况。 https://docs.oracle.com/en/database/oracle/oracle-database/12.2/jajdb/index.html?oracle/jdbc/OracleStatement.html

我从来没有让这个为 OCCI 工作,但实现了这里提到的解决方法 (Is there a way to prefetch LOBs data in occi?)

答案 4 :(得分:0)

老实说,我认为问题在于OTL驱动程序和OracleClient在处理来自SYS_REFCURSOR的CLOB时遇到问题。 SYS_REFCURSOR很弱,这意味着游标本身可以返回任何类型的数据,这意味着驱动程序需要不断返回数据库并查询游标的元数据(即获取游标中的数据类型),然后加载CLOB流,然后加载数据。显然,如果执行查询的用户可以访问正在查询的表,则驱动程序可以更好地检索元数据并返回正确的数据。

更改存储过程以返回强大的ref_cursor工作吗?

CREATE OR REPLACE PACKAGE PKGTEST
    AS
    TYPE C_DoSomething IS REF CURSOR RETURN MyTable%ROWTYPE;
    PROCEDURE DoSomething(
        cur_OUT OUT c_DoSomething
     );
END PKGTEST;

您是否可以更改存储过程以执行TO_CLOB(),因为这似乎也有效?

答案 5 :(得分:0)

您的数据库的NLS_CHARACTERSET和NLS_NCHAR_CHARACTERSET是什么。运行

select * from nls_database_parameters;

获得结果。您的客户端软件的NLS_LANG设置是什么?

这可以更深入地了解问题,并有助于回答TO_CLOB(TO_NCLOB())调用的成本。

答案 6 :(得分:0)

这些并非都必然是直接相关的,但你可能应该检查每一个是否相关。我怀疑这是缓存相关的:一旦你完成了第一个查询,你就应用select然后它会很快。在中间你必须反弹服务器,以摆脱缓存,如果你想要正确地进行性能测试。如果您执行此测试并且突然执行得更好,请尝试将表固定到缓存中。请参阅下面围绕clob存储内联,因为这可能是相关的。

我在Oracle 10g中一年左右就出现了clob性能方面的问题。一旦我们获得了令人敬畏的dba帮助,我们就能解决大部分问题。花了大约2个月才使表演达到足够的速度。

您使用的是哪个版本的Oracle? 在Oracle 10g(早期版本)中,clob性能存在大量问题。实际上,在某些情况下,实际上只使用两个表和一个varchar列(将varchars连接在一起,然后使用clob)实际上更快。我们升级到更高版本,它更好

您的数据存储在哪里? 还有一个选项可以将clob存储在表中。根据您的数据大小,您可能会发现这有助于提高性能。 如果您已将数据存储在SAN上,那么还应该查看SAN上的缓存大小以及块大小。当缓存大小不正确时,Oracle + SAN可能会有点滑稽。

另一种解决方法: 如果您发现持久性很慢或甚至访问速度很慢且您没有CPU限制,请压缩数据并将其存储在blob中。我们在这里也看到了很大的性能优势。

如果你在处理clobs的任何事情上看到性能问题(内存相关?),我们发现我们会将对象重新创建为新字符串。即使数据较小,驱动程序也预先创建了32K大小的字符串。

我确实想知道系统表是否可能碎片化了?有很多表/模式吗?同义词怎么样?

此外,当您存储clobs时,它们是否存储在Oracle中的一个大型文件中?如果我没记错的话你必须小心分裂;存储不会被释放以供重用。

也许您可以将.NET Web服务放在数据库前面?如果您无法解决性能问题,那么这可能是一种选择。