【问题标题】:Reading CLOB column is slow读取 CLOB 列很慢
【发布时间】:2009-01-23 17:54:28
【问题描述】:

希望有人能对我目前在使用 Oracle DB 时遇到的问题有所了解 - 我相信这很简单!

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

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 具有 TEST_ROLE 角色

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

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

好的,解决问题....

从开始到填充网格的时间约为 35-40 秒,哎哟。

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

GRANT SELECT ON MyTable TO TEST_ROLE
/

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

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

只是把别的东西扔进锅里,如果我把程序改成

SELECT ixMyTable, TO_CLOB(TO_NCLOB(clobData))
FROM MyTable;

那么时间大约是 5-6 秒,不管有没有额外的 SELECT 权限。

任何指针或直接的解决方案将不胜感激!

编辑:

操作系统是 Vista x86 商业版

Oracle 服务器是 10.2.0.1

Oracle 客户端是 10.2.0.3

编辑:

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

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

我们的主要交付物是使用 OTL (http://otl.sourceforge.net/otl3_intro.htm) 进行数据库访问的 C++ 应用程序。目前这不是真的可以改变的东西,所以我真的很想了解造成这种差异的原因,而不必随意抛出无端的 GRANT SELECTs。

如果缺少 SELECT 权限导致完全失败,那么我可能会忍受这种情况,但缺少 SELECT 似乎会导致访问 CLOB 数据的路径变慢。

我已经标记了 3 个答案 - 谢谢那些 - 但我真的可以这样做,所以我会为此添加一个赏金。

附:我们真的很想在一开始就为我们的 C++ 选择 OCCI,但由于 Oracle 一直支持在当前版本之前的 IDE,我们无法让它与我们的 Visual Studio 2008 很好地配合。

【问题讨论】:

    标签: database oracle clob


    【解决方案1】:

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

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

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

    编辑:或者更好:每次都删除并重新创建表。您可能正在这样做,但没有提及。

    【讨论】:

    • 是的,我们之前在进行性能测试时遇到过缓存问题,但这是一个始终如一的速度问题。即使是使用 TO_CLOB(TO_NCLOB()) 技术的第一次调用也很快,而且我不希望这会在第一次运行时命中缓存。谢谢。
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

      按照上述建议,您能否尝试不同的 ODBC 驱动程序或客户端软件。 TO_CLOB(TO_NCLOB()) 工作得如此之快这一事实似乎并不表明问题所在。

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

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

      【讨论】:

        【解决方案4】:

        我知道这是一个很老的问题

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

        1. 从具有 blob/clob 列的表中进行选择会阻止预取。您可以在 sqlplus 中对此进行试验,方法是将 set arraysize 1set 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?)

        【讨论】:

          【解决方案5】:

          老实说,我认为这里的问题是 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(),因为这似乎也有效?

          【讨论】:

          • 不幸的是,返回一个强光标并没有什么不同。可以更改存储过程,但 TO_CLOB() 本身不起作用 - 它必须是 TO_CLOB(TO_NCLOB())。我不确定这些电话实际上做了什么,也就是说,它们有多贵?
          • TO_CLOB(TO_NCLOB()) 的费用取决于数据库的字符集。以下查询中的 NLS_CHARACTERSET 和 NLS_NCHAR_CHARACTERSET 值是什么? select * from nls_database_parameters;
          【解决方案6】:

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

          select * from nls_database_parameters;
          

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

          这可能会更深入地了解问题,并有助于回答 TO_CLOB(TO_NCLOB()) 调用有多昂贵的问题。

          【讨论】:

          • NLS_CHARACTERSET 是 AL32UTF8 而 NLS_NCHAR_CHARACTERSET 是 AL16UTF16 - 我相信这些是在基于 Win 的服务器上安装的默认设置。我什至不确定是否/如何更改 ADO.NET 的 NLS_LANG
          • to_NCLOB() 转换为 UTF-16 字符集,然后 TO_CLOB() 将其转换回 UTF-8 字符集。因此,调用不会对性能产生巨大影响。我仍然没有很好的理由,但如果它有效......
          【解决方案7】:

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

          大约一年前,我在 Oracle 10g 中遇到了有关 clob 性能的问题。一旦我们得到了很棒的 dba 的帮助,我们就绕过了他们中的大多数。大约需要 2 个月的时间才能使性能达到足够的速度。

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

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

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

          如果您在处理 clob 的过程中发现性能问题(内存相关?),我们发现我们会将对象重新创建为新字符串。即使数据较小,驱动程序也会预先创建 32K 大小的字符串。

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

          另外,当您存储 clob 时,它们不会存储到 Oracle 中的一个大文件中吗?如果我没记错的话,您必须小心碎片化;存储不会被释放以供重复使用。

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

          【讨论】:

            猜你喜欢
            • 2015-03-02
            • 2021-02-17
            • 1970-01-01
            • 2015-08-15
            • 1970-01-01
            • 1970-01-01
            • 2017-09-17
            • 2021-04-11
            • 1970-01-01
            相关资源
            最近更新 更多