SQL解析导致Oracle延迟响应

时间:2019-03-07 14:38:47

标签: sql oracle performance plsql query-optimization

我有一个Java Web服务方法getCardInformation,它使用OracleJDBC数据库中获取数据。每秒大约进行300个呼叫。

Java调用此pl / sql函数:

PROCEDURE GET_CARDS_BY_ID(p_card_id IN NUMBER
                         ,o_result  OUT VARCHAR2) IS
BEGIN

  SELECT 'some data'
    INTO o_result
    FROM 'my complicated SQL' c
   WHERE c.card_id = p_card_id;

END GET_CARDS_BY_ID;

通常SQL Select statement/procedure会在30毫秒内返回结果,但是有时在某些情况下,它需要超过20秒的时间,并且所有其他线程(调用)都在等待此延迟的调用。 尽管这些调用没有相互连接,但是它们使用不同的输入参数运行相同的SQL Select statement。 所有参数都是使用bind variables从Java设置的。

在延迟期间,我们选择了活动会话并获得很多光标:S针等待X

    SELECT DISTINCT a.*, s.*
  FROM V$ACTIVE_SESSION_HISTORY a
      ,v$sql                    s
 WHERE a.sql_id = s.sql_id
   AND blocking_session IS NOT NULL
   AND a.sample_time > sysdate - 1

enter image description here

我们通过在hint中添加Select解决了该问题。 我们认为,在此延迟期间,Oracle试图解析此Select Statement,并重新为其计算计划,这需要一些时间。使用同一条语句的所有其他调用都等待Oracle结束计算。这就是延迟的原因。设置提示时,我们告诉oracle不要进行任何计算,因此没有延迟。

但是,为什么Oracle试图重新计算计划?

我可以在不对索引进行硬编码的情况下解决此问题吗?

当数据库中的数据量发生变化时,由于此提示,我不想更改代码。

SQL语句:

    SELECT (nvl((SELECT SUM(amount)
          FROM locks l
         WHERE l.account = a.account),
        0) - nvl((SELECT SUM(sl.amount)
                    FROM locks   sl
                        ,locks_2 m
                   WHERE sl.id = m.id
                     AND sl.account = a.account
                     AND m.text = '...'),
                  0)) / 100
  ,a.credit_limit / 100
  ,nvl((SELECT SUM(decode(ia.account,
                         ra.id,
                         ia.bal,
                         ceil(CASE
                                WHEN cc.ccy_num = '100' THEN
                                 (SELECT ia.bal / r.ccy_rate
                                    FROM my_ccy_rate r
                                   WHERE r.ccy_num = a.account_ccy)
                                WHEN a.account_ccy = '100' THEN
                                 (SELECT ia.bal * r.ccy_rate
                                    FROM my_ccy_rates r
                                   WHERE r.ccy_num = cc.ccy_num)
                                ELSE
                                 (SELECT ia.bal * r.ccy_rate / c.ccy_rate + 15000
                                    FROM my_ccy_rates r
                                        ,my_ccy_rates c
                                   WHERE r.ccy_num = cc.ccy_num
                                     AND c.ccy_num = a.account_ccy)
                              END)))
         FROM my_v           ia
             ,currency_codes cc
             ,my_table       pe
        WHERE ia.account = a.account_id
          AND cc.ccy_alpha = ia.ccy),
       0) / 100
  ,a.initial_amount / 100
  ,nvl((SELECT a.bonus_amount - nvl((SELECT SUM(sl.amount)
                                     FROM locks   sl
                                         ,locks_2 m
                                    WHERE sl.id = m.id
                                      AND sl.account_id = a.account_id
                                      AND m.text = '...'),
                                   0)
         FROM my_table_1 ra
             ,my_accounts  a
        WHERE ra.centre_id = o_centre_id
          AND ra.card_number = o_card
          AND a.centre_id = ra.centre_id
          AND a.account_id = ra.account_id),
       0) / 100
INTO o_amt_1
  ,o_amt_2
  ,o_amt_3
  ,o_amt_4
  ,o_amt_5
FROM my_table_1 ra
  ,my_table_2 a
  ,my_table_3 p
WHERE ra.card_number = &card_number_input_parameter
  AND a.account_id = ra.account_id;

2 个答案:

答案 0 :(得分:1)

可能有一个解析错误,因为SQL语句使用了一个关联的子查询,该子查询引用了一个多于两个级别的变量。

这部分代码可能会引起问题:

SELECT ... (SELECT ... (SELECT ... = a.account_ccy ... ) ... )
...
FROM ...
  ,my_table_2 a

据说ANSI SQL标准限制表仅引用了一个级别。 (我之所以这样说是因为该标准令人讨厌地无法免费获得,因此我无法亲自对其进行验证。)Oracle偶尔执行的历史很糟糕  那条规则。上面的查询在大多数版本9、10和11中都失败了,但是这些查询在至少一个版本10中有效(然后在我升级到更高版本的10时失败),并且有一个补丁可以使他们在11岁时工作,  再次在12。

我只看到此问题会导致诸如“找不到列”之类的错误,但是如果它还会引起解析性能问题,这也不会令我感到惊讶。尝试重新编写查询,使其仅深一层引用外部表。

答案 1 :(得分:0)

如果要获得一致的结果,则应使用绑定变量。查询计划不会以这种方式更改:

PROCEDURE GET_CARDS_BY_ID(p_card_id IN NUMBER ,o_result  OUT VARCHAR2) IS
BEGIN
  v_sql := 'SELECT 'some data'  INTO o_result  FROM 'my complicated SQL' c
     WHERE c.card_id = :1';
  execute immediate v_sql into o_result using p_card;
END GET_CARDS_BY_ID;