【问题标题】:Performance of SUBSTR on CLOBSUBSTR 在 CLOB 上的表现
【发布时间】:2012-05-07 02:19:19
【问题描述】:

我有一个 PL/SQL 过程,它在 VARCHAR2 参数上做了很多 SUBSTRs。我想取消长度限制,所以我尝试将其更改为CLOB

工作正常,但性能受到影响,所以我做了一些测试(基于 2005 年的 these tests)。


更新:我可以在具有不同 Oracle 版本和不同硬件的多个不同实例上重现此问题,dbms_lob.substr 总是明显比substr(CLOB) 慢,并且比SUBSTR(VARCHAR2) 慢很多。

Bob 的结果和上面链接中的测试讲述了一个不同的故事。

谁能解释一下,或者至少重现 Bob 或我的结果?谢谢!


测试结果:

+000000000 00:00:00.004000000 (VARCHAR2)
+000000000 00:00:00.298000000 (CLOB SUBSTR)
+000000000 00:00:00.356000000 (DBMS_LOB.SUBSTR)

测试代码:

DECLARE
  l_text   VARCHAR2(30) := 'This is a test record';
  l_clob   CLOB := l_text;
  l_substr VARCHAR2(30);
  t TIMESTAMP;
BEGIN
  t := SYSTIMESTAMP;
  FOR i IN 1..100000 LOOP
    l_substr := SUBSTR(l_text,1,14);
  END LOOP;
  dbms_output.put_line( SYSTIMESTAMP - t || ' (VARCHAR2)');

  t := SYSTIMESTAMP;
  FOR i IN 1..100000 LOOP
    l_substr := SUBSTR(l_clob,1,14);
  END LOOP;
  dbms_output.put_line( SYSTIMESTAMP - t || ' (CLOB SUBSTR)');

  t := SYSTIMESTAMP;
  FOR i IN 1..100000 LOOP
    l_substr := DBMS_LOB.SUBSTR(l_clob,14,1);
  END LOOP;
  dbms_output.put_line( SYSTIMESTAMP - t || ' (DBMS_LOB.SUBSTR)');
END;

【问题讨论】:

  • 注意:测试三是14,1,其他是1,14。我还会测试10000, 5000 之类的东西,因为关键是您希望打破 VARCHAR 的 4k 限制。此外,由于非 VARCHAR 的结果大约慢 75 倍,您是否能够查看处理多个 VARCHAR 的算法? [比如一个规范化的表,其中一个字段是'sequence_id',显示这个字符串的相对位置,另一个是VARCHAR]。最后,虽然相对差异很大,但绝对差异很小。那么,有关系吗? [预优化]
  • @Dems:感谢您的意见! 14,11,14 是正确的(感谢 Oracle 提供一致的 API)。我试图打破32767 字节限制(PL/SQL,而不是SQL),当使用具有该长度(LPAD('X', 32767, 'X')) 的文本时,结果或多或少相同。我已经想到了那个多变量表解决方案,但我想避免它:) 这确实很重要,因为该过程确实经常被调用,但最重要的是我很好奇是否有替代方案......
  • 在我的机器上 DBMS_LOB.SUBSTR 比 CLOB_SUBSTR(20%) 慢一点。两者都比 varchar2 慢(慢 70 倍)。我在 11gR2 上运行。
  • @FlorinGhita:谢谢!您使用的是哪个操作系统,以防万一?
  • AIX 6(不知道次要...2 或 3)。这是一台 128 核的 IBM 机器。你有什么操作系统和机器?

标签: sql oracle plsql oracle11g clob


【解决方案1】:

我知道这已经很老了,但可能仍然适用于使用旧系统的人。这看起来像一个数据类型转换问题。根据我注意到@bernhard.weingartner 看到的效果,offset 和 amount 参数的数据类型似乎有很大的不同。

这是在 Linux (OEL 5.6) 上的 11.2.0.3 上运行的,并且为了使差异更加明显而增加到一百万次迭代:

DECLARE
  l_text   VARCHAR2(30) := 'This is a test record';
  l_clob   CLOB := l_text;
  l_substr VARCHAR2(30);
  t TIMESTAMP;
BEGIN
  t := SYSTIMESTAMP;
  FOR i IN 1..1000000 LOOP
    l_substr := SUBSTR(l_text,1,14);
  END LOOP;
  dbms_output.put_line( SYSTIMESTAMP - t || ' (VARCHAR2)');

  t := SYSTIMESTAMP;
  FOR i IN 1..1000000 LOOP
    l_substr := SUBSTR(l_clob,1,14);
  END LOOP;
  dbms_output.put_line( SYSTIMESTAMP - t || ' (CLOB SUBSTR)');

  t := SYSTIMESTAMP;
  FOR i IN 1..1000000 LOOP
    l_substr := DBMS_LOB.SUBSTR(l_clob,14,1);
  END LOOP;
  dbms_output.put_line( SYSTIMESTAMP - t || ' (DBMS_LOB.SUBSTR with 14,1)');

  t := SYSTIMESTAMP;
  FOR i IN 1..1000000 LOOP
    l_substr := DBMS_LOB.SUBSTR(l_clob,14.0,1.0);
  END LOOP;
  dbms_output.put_line( SYSTIMESTAMP - t || ' (DBMS_LOB.SUBSTR with 14.0,1.0)');

  t := SYSTIMESTAMP;
  FOR i IN 1..1000000 LOOP
    l_substr := DBMS_LOB.SUBSTR(l_clob,cast(14 as number), cast(1 as number));
  END LOOP;
  dbms_output.put_line( SYSTIMESTAMP - t || ' (DBMS_LOB.SUBSTR with casts)');
END;
/
+000000000 00:00:00.043019000 (VARCHAR2)
+000000000 00:00:03.671621000 (CLOB SUBSTR)
+000000000 00:00:05.704337000 (DBMS_LOB.SUBSTR with 14,1)
+000000000 00:00:00.040097000 (DBMS_LOB.SUBSTR with 14.0,1.0)
+000000000 00:00:00.040907000 (DBMS_LOB.SUBSTR with casts)

11gR2 docs 将形式参数显示为 INTEGER 类型,但实际上传递整数(或 pls_integer 或 binary_double)很慢,而显式传递数字则很快。

从您的原始问题和 Bob 的结果来看,这看起来像是在 11.1 和 11.2 之间发生了变化。我没有要测试的 12c 实例,所以不知道它是否再次更改。是由于dbms_lob 的变化还是PL/SQL 默认处理数值的方式发生了更广泛的变化,目前尚不清楚。我在 MOS 上没有找到任何看起来相关的内容。

【讨论】:

    【解决方案2】:

    我看到在 11gR1 上,DBMS_LOB.substr 的测试运行顺利,但在 11gR2 上,该功能很慢。

    低于我在 AIX6 上对 Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production 的测试。

    +000000000 00:00:00.009440000 (VARCHAR2)
    +000000000 00:00:00.749113000 (CLOB SUBSTR)
    +000000000 00:00:01.177685000 (DBMS_LOB.SUBSTR)
    

    【讨论】:

      【解决方案3】:

      在以下系统上运行您的脚本 3 次:

      Oracle 数据库 11g 企业版版本 11.1.0.7.0 - 64 位生产

      结果如下:

      +000000000 00:00:00.007787000 (VARCHAR2)
      +000000000 00:00:03.093258000 (CLOB SUBSTR)
      +000000000 00:00:00.340017000 (DBMS_LOB.SUBSTR)
      
      +000000000 00:00:00.019460000 (VARCHAR2)
      +000000000 00:00:03.302425000 (CLOB SUBSTR)
      +000000000 00:00:00.336915000 (DBMS_LOB.SUBSTR)
      
      +000000000 00:00:00.007773000 (VARCHAR2)
      +000000000 00:00:03.210619000 (CLOB SUBSTR)
      +000000000 00:00:00.336689000 (DBMS_LOB.SUBSTR)
      

      【讨论】:

      • 谢谢!您介意分享您使用的操作系统吗,以防有什么不同?
      • @PeterLang 当然可以。红帽企业 Linux:2.6.9-67.ELsmp
      【解决方案4】:

      (谎言,该死的谎言和基准......)

      我重新运行您的测试 10 次,将字符串扩展为整整 30 个字符,并得到以下平均结果:

      +000000000 00:00:00.011694200 (VARCHAR2)
      +000000000 00:00:00.901000600 (CLOB SUBSTR)
      +000000000 00:00:00.013169200 (DBMS_LOB.SUBSTR)
      

      然后我将子字符串范围更改为 5,14(DBMS_LOB.SUBSTR 为 14,5)并得到:

      +000000000 00:00:00.011731000 (VARCHAR2)
      +000000000 00:00:01.010840000 (CLOB SUBSTR)
      +000000000 00:00:00.011427000 (DBMS_LOB.SUBSTR)
      

      然后我将范围更改为 17,14(DBMS_LOB.SUBSTR 为 14,17)并得到

      +000000000 00:00:00.013578900 (VARCHAR2)
      +000000000 00:00:00.964527400 (CLOB SUBSTR)
      +000000000 00:00:00.011416800 (DBMS_LOB.SUBSTR)
      

      最后,我将范围更改为 25,14(DBMS_LOB.SUBSTR 为 14,25)并得到了

      +000000000 00:00:00.011210200 (VARCHAR2)
      +000000000 00:00:00.916439800 (CLOB SUBSTR)
      +000000000 00:00:00.013781300 (DBMS_LOB.SUBSTR)
      

      我的结论是,在处理 CLOB 时,最好使用 DBMS_LOB.SUBSTR,因为与对“正常”VARCHAR2 使用 SUBSTR 相比,它似乎实际上没有性能损失。针对 CLOB 的 SUBSTR 似乎遭受了显着的性能损失。作为记录 - 操作系统 = HP/UX(Unix 变体),Oracle 版本 = 11.1,处理器 = HP Itanium 2-plex。 YMMV。

      分享和享受。


      而且因为如果值得做就值得过度做,这里有更多的结果,字符串扩展到 32767 个字符。每组结果给出的子字符串范围:

      1, 25000
      +000000000 00:00:00.198466400 (VARCHAR2)
      +000000000 00:00:02.870958700 (CLOB SUBSTR)
      +000000000 00:00:00.174490100 (DBMS_LOB.SUBSTR)
      
      1000, 25000
      +000000000 00:00:00.253447900 (VARCHAR2)
      +000000000 00:00:02.491790500 (CLOB SUBSTR)
      +000000000 00:00:00.193560100 (DBMS_LOB.SUBSTR)
      
      10000, 25000
      +000000000 00:00:00.217812000 (VARCHAR2)
      +000000000 00:00:02.268794800 (CLOB SUBSTR)
      +000000000 00:00:00.222200200 (DBMS_LOB.SUBSTR)
      

      同一天,同样的结论。

      Cthulhu fhtagn.


      (再次突破,亲爱的朋友们,再次......)

      重新运行基准测试,将 CLOB 的大小更改为 3276700,并从 2475000 开始从中间获取子字符串,长度为 25000 我得到:

      +000000000 00:00:00.176883200 (VARCHAR2)
      +000000000 00:00:02.069482600 (CLOB SUBSTR)
      +000000000 00:00:00.175341500 (DBMS_LOB.SUBSTR)
      

      (请注意,更改只影响最后两个测试)。

      并且...相同的结果,不同的日子。

      YMMV.

      【讨论】:

      • 谢谢!我尝试了你的最后一个测试(将l_text 扩展到 50 个字符,因为 30 不再有意义),但结果仍然与我最初的测试相当(在多次调用之后):0.0060.679、@ 987654330@。您使用的是哪个版本的 Oracle?多次调用时你能重现你的结果吗?
      • 甲骨文 11.1。是的,测试结果是可重复的。我将循环更改为每次迭代 100 万次,然后将生成的时间间隔除以 10,以使它们与原始测试大致相当。我多次重新运行测试,每个结果都具有可比性。
      • 这很奇怪。我在三个不同的实例(Windows 上的 11.2、Linux 上的 11.2 和 Linux 上的 10.2)上进行了测试,结果与我的帖子中的结果大致相同。从其他人那里获得更多结果会很棒...
      • 什么处理器架构?多少 L1 和 L2 缓存?有多少受害者^H^H^H^H^H^H^H祭祀上古之神?小事意义重大... :->
      • "Itanium... YMMV" - 你的钱可能会消失吗?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-12-03
      • 2018-09-25
      • 2012-11-20
      • 1970-01-01
      • 1970-01-01
      • 2010-11-21
      • 2016-09-21
      相关资源
      最近更新 更多